2005-09-05 16:38:54

by Harald Welte

[permalink] [raw]
Subject: [PATCH] Omnikey Cardman 4040 driver

Hi!

I've now incorporated all the suggested changes (thanks once again on
the many comments received). The resulting driver has been tested and
works fine.

Please consider applying the driver to the mainline tree, thanks.

************************************************************************

Add new Omnikey Cardman 4040 smartcard reader driver

Signed-off-by: Harald Welte <[email protected]>

---
commit 1c1cd1c5aba9ae1c0ea32d55c5b25f2370aaeca4
tree 016dec439275ab425575901dca5ee261bbc0aa0f
parent c4ab879b6ef599bf88d19b9b145878ef73400ce7
author Harald Welte <[email protected]> Mo, 05 Sep 2005 21:47:01 +0200
committer Harald Welte <[email protected]> Mo, 05 Sep 2005 21:47:01 +0200

MAINTAINERS | 5
drivers/char/pcmcia/Kconfig | 13 +
drivers/char/pcmcia/Makefile | 1
drivers/char/pcmcia/cm4040_cs.c | 897 +++++++++++++++++++++++++++++++++++++++
drivers/char/pcmcia/cm4040_cs.h | 47 ++
5 files changed, 963 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1737,6 +1737,11 @@ L: [email protected]
W: http://www.linuxtr.net
S: Maintained

+OMNIKEY CARDMAN 4040 DRIVER
+P: Harald Welte
+M: [email protected]
+S: Maintained
+
ONSTREAM SCSI TAPE DRIVER
P: Willem Riede
M: [email protected]
diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig
--- a/drivers/char/pcmcia/Kconfig
+++ b/drivers/char/pcmcia/Kconfig
@@ -18,5 +18,18 @@ config SYNCLINK_CS
The module will be called synclinkmp. If you want to do that, say M
here.

+config CARDMAN_4040
+ tristate "Omnikey CardMan 4040 support"
+ depends on PCMCIA
+ help
+ Enable support for the Omnikey CardMan 4040 PCMCIA Smartcard
+ reader.
+
+ This card is basically a USB CCID device connected to a FIFO
+ in I/O space. To use the kernel driver, you will need either the
+ PC/SC ifdhandler provided from the Omnikey homepage
+ (http://www.omnikey.com/), or a current development version of OpenCT
+ (http://www.opensc.org/).
+
endmenu

diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile
--- a/drivers/char/pcmcia/Makefile
+++ b/drivers/char/pcmcia/Makefile
@@ -5,3 +5,4 @@
#

obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
+obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
new file mode 100644
--- /dev/null
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -0,0 +1,897 @@
+ /*
+ * A driver for the Omnikey PCMCIA smartcard reader CardMan 4040
+ *
+ * (c) 2000-2004 Omnikey AG (http://www.omnikey.com/)
+ *
+ * (C) 2005 Harald Welte <[email protected]>
+ * - add support for poll()
+ * - driver cleanup
+ * - add waitqueues
+ * - adhere to linux kernel coding style and policies
+ * - support 2.6.13 "new style" pcmcia interface
+ *
+ * The device basically is a USB CCID compliant device that has been
+ * attached to an I/O-Mapped FIFO.
+ *
+ * All rights reserved, Dual BSD/GPL Licensed.
+ */
+
+/* #define PCMCIA_DEBUG 6 */
+
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include "cm4040_cs.h"
+
+static atomic_t cm4040_num_devices_open;
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0600);
+#define DEBUG(n, x, args...) do { if (pc_debug >= (n)) \
+ printk(KERN_DEBUG "%s:%s:" x, MODULE_NAME, \
+ __FUNCTION__, ##args); } while (0)
+#else
+#define DEBUG(n, args...)
+#endif
+
+static char *version =
+"OMNIKEY CardMan 4040 v1.1.0gm3 - All bugs added by Harald Welte";
+
+#define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*HZ)
+#define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*HZ)
+#define CCID_DRIVER_MINIMUM_TIMEOUT (3*HZ)
+#define READ_WRITE_BUFFER_SIZE 512
+#define POLL_LOOP_COUNT 1000
+
+/* how often to poll for fifo status change */
+#define POLL_PERIOD msecs_to_jiffies(10)
+
+static void reader_release(dev_link_t *link);
+static void reader_detach(dev_link_t *link);
+
+static int major;
+
+#define BS_READABLE 0x01
+#define BS_WRITABLE 0x02
+
+struct reader_dev {
+ dev_link_t link;
+ dev_node_t node;
+ wait_queue_head_t devq;
+
+ wait_queue_head_t poll_wait;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ unsigned int buffer_status;
+
+ unsigned int timer_expired;
+ struct timer_list timer;
+ unsigned long timeout;
+ unsigned char s_buf[READ_WRITE_BUFFER_SIZE];
+ unsigned char r_buf[READ_WRITE_BUFFER_SIZE];
+ struct task_struct *owner;
+};
+
+static dev_info_t dev_info = MODULE_NAME;
+static dev_link_t *dev_table[CM_MAX_DEV] = { NULL, };
+
+static struct timer_list cm4040_poll_timer;
+
+#ifndef PCMCIA_DEBUG
+#define xoutb outb
+#define xinb inb
+#else
+static inline void xoutb(unsigned char val, unsigned short port)
+{
+ DEBUG(7, "outb(val=%.2x,port=%.4x)\n", val, port);
+ outb(val,port);
+}
+
+static inline unsigned char xinb(unsigned short port)
+{
+ unsigned char val;
+
+ val = inb(port);
+ DEBUG(7, "%.2x=inb(%.4x)\n", val, port);
+ return val;
+}
+#endif
+
+/* poll the device fifo status register. not to be confused with
+ * the poll syscall. */
+static void cm4040_do_poll(unsigned long dummy)
+{
+ unsigned int i;
+ /* walk through all devices */
+ for (i = 0; dev_table[i]; i++) {
+ dev_link_t *dl = dev_table[i];
+ struct reader_dev *dev = dl->priv;
+ unsigned int obs = xinb(dl->io.BasePort1
+ + REG_OFFSET_BUFFER_STATUS);
+
+ if ((obs & BSR_BULK_IN_FULL)) {
+ set_bit(BS_READABLE, &dev->buffer_status);
+ DEBUG(4, "waking up read_wait\n");
+ wake_up_interruptible(&dev->read_wait);
+ } else
+ clear_bit(BS_READABLE, &dev->buffer_status);
+
+ if (!(obs & BSR_BULK_OUT_FULL)) {
+ set_bit(BS_WRITABLE, &dev->buffer_status);
+ DEBUG(4, "waking up write_wait\n");
+ wake_up_interruptible(&dev->write_wait);
+ } else
+ clear_bit(BS_WRITABLE, &dev->buffer_status);
+
+ if (dev->buffer_status)
+ wake_up_interruptible(&dev->poll_wait);
+ }
+
+ if (atomic_read(&cm4040_num_devices_open))
+ mod_timer(&cm4040_poll_timer, jiffies + POLL_PERIOD);
+}
+
+static int wait_for_bulk_out_ready(struct reader_dev *dev)
+{
+ int i, rc;
+ int iobase = dev->link.io.BasePort1;
+
+ for (i=0; i < POLL_LOOP_COUNT; i++) {
+ if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
+ & BSR_BULK_OUT_FULL) == 0) {
+ DEBUG(4, "BulkOut empty (i=%d)\n", i);
+ return 1;
+ }
+ }
+
+ DEBUG(4, "wait_event_interruptible_timeout(timeout=%ld\n",
+ dev->timeout);
+ rc = wait_event_interruptible_timeout(dev->write_wait,
+ test_and_clear_bit(BS_WRITABLE,
+ &dev->buffer_status),
+ dev->timeout);
+
+ if (rc > 0)
+ DEBUG(4, "woke up: BulkOut empty\n");
+ else if (rc == 0)
+ DEBUG(4, "woke up: BulkOut full, returning 0 :(\n");
+ else if (rc < 0)
+ DEBUG(4, "woke up: signal arrived\n");
+
+ return rc;
+}
+
+/* Write to Sync Control Register */
+static int write_sync_reg(unsigned char val, struct reader_dev *dev)
+{
+ int iobase = dev->link.io.BasePort1;
+ int rc;
+
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0)
+ return rc;
+
+ xoutb(val,iobase + REG_OFFSET_SYNC_CONTROL);
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0)
+ return rc;
+
+ return 1;
+}
+
+static int wait_for_bulk_in_ready(struct reader_dev *dev)
+{
+ int i, rc;
+ int iobase = dev->link.io.BasePort1;
+
+ for (i=0; i < POLL_LOOP_COUNT; i++) {
+ if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
+ & BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) {
+ DEBUG(3, "BulkIn full (i=%d)\n", i);
+ return 1;
+ }
+ }
+
+ DEBUG(4, "wait_event_interruptible_timeout(timeout=%ld\n",
+ dev->timeout);
+ rc = wait_event_interruptible_timeout(dev->read_wait,
+ test_and_clear_bit(BS_READABLE,
+ &dev->buffer_status),
+ dev->timeout);
+ if (rc > 0)
+ DEBUG(4, "woke up: BulkIn full\n");
+ else if (rc == 0)
+ DEBUG(4, "woke up: BulkIn not full, returning 0 :(\n");
+ else if (rc < 0)
+ DEBUG(4, "woke up: signal arrived\n");
+
+ return rc;
+}
+
+static ssize_t cm4040_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ int iobase = dev->link.io.BasePort1;
+ unsigned long bytes_to_read;
+ unsigned long i;
+ unsigned long min_bytes_to_read;
+ int rc;
+ unsigned char uc;
+
+ DEBUG(2, "-> cm4040_read(%s,%d)\n", current->comm,current->pid);
+
+ if (count == 0)
+ return 0;
+
+ if (count < 10)
+ return -EFAULT;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
+ DEBUG(2, "<- cm4040_read (failure)\n");
+ return -EAGAIN;
+ }
+
+ if ((dev->link.state & DEV_PRESENT)==0)
+ return -ENODEV;
+
+ schedule_timeout(1*HZ);
+ for (i=0; i<5; i++) {
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
+ DEBUG(2, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+ dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN);
+#ifdef PCMCIA_DEBUG
+ if (pc_debug >= 6)
+ printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]);
+ }
+ printk("\n");
+#else
+ }
+#endif
+
+ bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]);
+
+ DEBUG(6, "BytesToRead=%lu\n", bytes_to_read);
+
+ min_bytes_to_read = min(count, bytes_to_read + 5);
+
+ DEBUG(6, "Min=%lu\n", min_bytes_to_read);
+
+ for (i=0; i < (min_bytes_to_read-5); i++) {
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
+ DEBUG(2, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+ dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN);
+ DEBUG(6, "%lu:%2x ", i, dev->r_buf[i]);
+ }
+ DEBUG(6, "\n");
+
+ *ppos = min_bytes_to_read;
+ if (copy_to_user(buf, dev->r_buf, min_bytes_to_read))
+ return -EFAULT;
+
+
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
+ DEBUG(2, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+
+ rc = write_sync_reg(SCR_READER_TO_HOST_DONE, dev);
+ if (rc <= 0) {
+ DEBUG(5,"write_sync_reg c=%.2x\n",rc);
+ DEBUG(2, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ uc = xinb(iobase + REG_OFFSET_BULK_IN);
+
+ DEBUG(2,"<- cm4040_read (successfully)\n");
+ return min_bytes_to_read;
+}
+
+static ssize_t cm4040_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ int iobase = dev->link.io.BasePort1;
+ ssize_t rc;
+ int i;
+ unsigned int bytes_to_write;
+
+ DEBUG(2, "-> cm4040_write(%s,%d)\n", current->comm, current->pid);
+
+ if (count == 0) {
+ DEBUG(2, "<- cm4040_write nothing to do (successfully)\n");
+ return 0;
+ }
+
+ if (count < 5) {
+ DEBUG(2, "<- cm4040_write buffersize=%Zd < 5\n", count);
+ return -EIO;
+ }
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
+ DEBUG(4, "<- cm4040_write (failure)\n");
+ return -EAGAIN;
+ }
+
+ if ((dev->link.state & DEV_PRESENT) == 0)
+ return -ENODEV;
+
+ bytes_to_write = count;
+ if (copy_from_user(dev->s_buf, buf, bytes_to_write))
+ return -EFAULT;
+
+ switch (dev->s_buf[0]) {
+ case CMD_PC_TO_RDR_XFRBLOCK:
+ case CMD_PC_TO_RDR_SECURE:
+ case CMD_PC_TO_RDR_TEST_SECURE:
+ case CMD_PC_TO_RDR_OK_SECURE:
+ dev->timeout = CCID_DRIVER_BULK_DEFAULT_TIMEOUT;
+ break;
+
+ case CMD_PC_TO_RDR_ICCPOWERON:
+ dev->timeout = CCID_DRIVER_ASYNC_POWERUP_TIMEOUT;
+ break;
+
+ case CMD_PC_TO_RDR_GETSLOTSTATUS:
+ case CMD_PC_TO_RDR_ICCPOWEROFF:
+ case CMD_PC_TO_RDR_GETPARAMETERS:
+ case CMD_PC_TO_RDR_RESETPARAMETERS:
+ case CMD_PC_TO_RDR_SETPARAMETERS:
+ case CMD_PC_TO_RDR_ESCAPE:
+ case CMD_PC_TO_RDR_ICCCLOCK:
+ default:
+ dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+ break;
+ }
+
+ rc = write_sync_reg(SCR_HOST_TO_READER_START, dev);
+ if (rc <= 0) {
+ DEBUG(5, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUG(2, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+
+ DEBUG(4, "start \n");
+
+ for (i=0; i < bytes_to_write; i++) {
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0) {
+ DEBUG(5, "wait_for_bulk_out_ready rc=%.2Zx\n", rc);
+ DEBUG(2, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT);
+ DEBUG(4, "%.2x ", dev->s_buf[i]);
+ }
+ DEBUG(4, "end\n");
+
+ rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev);
+
+ if (rc <= 0) {
+ DEBUG(5, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUG(2, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ DEBUG(2, "<- cm4040_write (successfully)\n");
+ return count;
+}
+
+static unsigned int cm4040_poll(struct file *filp, poll_table *wait)
+{
+ struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &dev->poll_wait, wait);
+
+ if (test_and_clear_bit(BS_READABLE, &dev->buffer_status))
+ mask |= POLLIN | POLLRDNORM;
+ if (test_and_clear_bit(BS_WRITABLE, &dev->buffer_status))
+ mask |= POLLOUT | POLLWRNORM;
+
+ DEBUG(2, "<- cm4040_poll(%u)\n", mask);
+
+ return mask;
+}
+
+static int cm4040_open(struct inode *inode, struct file *filp)
+{
+ struct reader_dev *dev;
+ dev_link_t *link;
+ int i;
+
+ DEBUG(2, "-> cm4040_open(device=%d.%d process=%s,%d)\n",
+ MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
+ current->comm, current->pid);
+
+ i = MINOR(inode->i_rdev);
+ if (i >= CM_MAX_DEV) {
+ DEBUG(4, "MAX_DEV reached\n");
+ DEBUG(4, "<- cm4040_open (failure)\n");
+ return -ENODEV;
+ }
+ link = dev_table[MINOR(inode->i_rdev)];
+ if (link == NULL || !(DEV_OK(link))) {
+ DEBUG(4, "link== NULL || DEV_OK false\n");
+ DEBUG(4, "<- cm4040_open (failure)\n");
+ return -ENODEV;
+ }
+ if (link->open) {
+ DEBUG(4, "DEVICE BUSY\n");
+ DEBUG(4, "<- cm4040_open (failure)\n");
+ return -EBUSY;
+ }
+
+ dev = (struct reader_dev *)link->priv;
+ filp->private_data = dev;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
+ DEBUG(4, "<- cm4040_open (failure)\n");
+ return -EAGAIN;
+ }
+
+ dev->owner = current;
+ link->open = 1;
+
+ atomic_inc(&cm4040_num_devices_open);
+ mod_timer(&cm4040_poll_timer, jiffies + POLL_PERIOD);
+
+ DEBUG(2, "<- cm4040_open (successfully)\n");
+ return nonseekable_open(inode, filp);
+}
+
+static int cm4040_close(struct inode *inode,struct file *filp)
+{
+ struct reader_dev *dev;
+ dev_link_t *link;
+ int i;
+
+ DEBUG(2, "-> cm4040_close(maj/min=%d.%d)\n",
+ MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
+
+ i = MINOR(inode->i_rdev);
+ if (i >= CM_MAX_DEV)
+ return -ENODEV;
+
+ link = dev_table[MINOR(inode->i_rdev)];
+ if (link == NULL)
+ return -ENODEV;
+
+ dev = (struct reader_dev *) link->priv;
+
+ link->open = 0;
+ wake_up(&dev->devq);
+
+ atomic_dec(&cm4040_num_devices_open);
+
+ DEBUG(2, "<- cm4040_close\n");
+ return 0;
+}
+
+static void cm4040_reader_release(dev_link_t *link)
+{
+ struct reader_dev *dev = (struct reader_dev *) link->priv;
+
+ DEBUG(3, "-> cm4040_reader_release\n");
+ while (link->open) {
+ DEBUG(3, KERN_INFO MODULE_NAME ": delaying release until "
+ "process '%s', pid %d has terminated\n",
+ dev->owner->comm,dev->owner->pid);
+ wait_event(dev->devq, (link->open == 0));
+ }
+ DEBUG(3, "<- cm4040_reader_release\n");
+ return;
+}
+
+static void reader_config(dev_link_t *link, int devno)
+{
+ client_handle_t handle;
+ struct reader_dev *dev;
+ tuple_t tuple;
+ cisparse_t parse;
+ config_info_t conf;
+ u_char buf[64];
+ int fail_fn, fail_rc;
+ int rc;
+
+ DEBUG(2, "-> reader_config\n");
+
+ handle = link->handle;
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+
+ if ((fail_rc = pcmcia_get_first_tuple(handle,&tuple)) != CS_SUCCESS) {
+ fail_fn = GetFirstTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_get_tuple_data(handle,&tuple)) != CS_SUCCESS) {
+ fail_fn = GetTupleData;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_parse_tuple(handle,&tuple,&parse))
+ != CS_SUCCESS) {
+ fail_fn = ParseTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_get_configuration_info(handle,&conf))
+ != CS_SUCCESS) {
+ fail_fn = GetConfigurationInfo;
+ goto cs_failed;
+ }
+
+ link->state |= DEV_CONFIG;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+ link->conf.Vcc = conf.Vcc;
+ DEBUG(2, "link->conf.Vcc=%d\n", link->conf.Vcc);
+
+ link->io.BasePort2 = 0;
+ link->io.NumPorts2 = 0;
+ link->io.Attributes2 = 0;
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ for (rc = pcmcia_get_first_tuple(handle, &tuple);
+ rc == CS_SUCCESS;
+ rc = pcmcia_get_next_tuple(handle, &tuple)) {
+ DEBUG(2, "Examing CIS Tuple!\n");
+ rc = pcmcia_get_tuple_data(handle, &tuple);
+ if (rc != CS_SUCCESS)
+ continue;
+ rc = pcmcia_parse_tuple(handle, &tuple, &parse);
+ if (rc != CS_SUCCESS)
+ continue;
+
+ DEBUG(2, "tupleIndex=%d\n", parse.cftable_entry.index);
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+
+ if (!parse.cftable_entry.io.nwin)
+ continue;
+
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = parse.cftable_entry.io.flags
+ & CISTPL_IO_LINES_MASK;
+ DEBUG(2,"io.BasePort1=%.4x\n", link->io.BasePort1);
+ DEBUG(2,"io.NumPorts1=%.4x\n", link->io.NumPorts1);
+ DEBUG(2,"io.BasePort2=%.4x\n", link->io.BasePort2);
+ DEBUG(2,"io.NumPorts2=%.4x\n", link->io.NumPorts2);
+ DEBUG(2,"io.IOAddrLines=%.4x\n", link->io.IOAddrLines);
+ rc = pcmcia_request_io(handle, &link->io);
+ if (rc == CS_SUCCESS) {
+ DEBUG(2, "RequestIO OK\n");
+ break;
+ } else
+ DEBUG(2, "RequestIO failed\n");
+ }
+ if (rc != CS_SUCCESS) {
+ DEBUG(2, "Couldn't configure reader\n");
+ goto cs_release;
+ }
+
+ link->conf.IntType = 00000002;
+
+ if ((fail_rc = pcmcia_request_configuration(handle,&link->conf))
+ !=CS_SUCCESS) {
+ fail_fn = RequestConfiguration;
+ DEBUG(1, "pcmcia_request_configuration failed 0x%x\n", fail_rc);
+ goto cs_release;
+ }
+
+ DEBUG(2, "RequestConfiguration OK\n");
+
+ dev = link->priv;
+ sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno);
+ dev->node.major = major;
+ dev->node.minor = devno;
+ dev->node.next = NULL;
+ link->dev = &dev->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ DEBUG(2, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno,
+ link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1);
+ DEBUG(2, "<- reader_config (succ)\n");
+
+ return;
+
+cs_failed:
+ cs_error(handle, fail_fn, fail_rc);
+cs_release:
+ reader_release(link);
+ link->state &= ~DEV_CONFIG_PENDING;
+ DEBUG(2, "<- reader_config (failure)\n");
+}
+
+static int reader_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link;
+ struct reader_dev *dev;
+ int devno;
+
+ DEBUG(3,"-> reader_event\n");
+ link = args->client_data;
+ dev = link->priv;
+ for (devno = 0; devno < CM_MAX_DEV; devno++) {
+ if (dev_table[devno] == link)
+ break;
+ }
+ if (devno == CM_MAX_DEV)
+ return CS_BAD_ADAPTER;
+
+ switch (event) {
+ case CS_EVENT_CARD_INSERTION:
+ DEBUG(5, "CS_EVENT_CARD_INSERTION\n");
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ reader_config(link,devno);
+ break;
+ case CS_EVENT_CARD_REMOVAL:
+ DEBUG(5, "CS_EVENT_CARD_REMOVAL\n");
+ link->state &= ~DEV_PRESENT;
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ DEBUG(5, "CS_EVENT_PM_SUSPEND "
+ "(fall-through to CS_EVENT_RESET_PHYSICAL)\n");
+ link->state |= DEV_SUSPEND;
+
+ case CS_EVENT_RESET_PHYSICAL:
+ DEBUG(5, "CS_EVENT_RESET_PHYSICAL\n");
+ if (link->state & DEV_CONFIG) {
+ DEBUG(5, "ReleaseConfiguration\n");
+ pcmcia_release_configuration(link->handle);
+ }
+ break;
+ case CS_EVENT_PM_RESUME:
+ DEBUG(5, "CS_EVENT_PM_RESUME "
+ "(fall-through to CS_EVENT_CARD_RESET)\n");
+ link->state &= ~DEV_SUSPEND;
+
+ case CS_EVENT_CARD_RESET:
+ DEBUG(5, "CS_EVENT_CARD_RESET\n");
+ if ((link->state & DEV_CONFIG)) {
+ DEBUG(5, "RequestConfiguration\n");
+ pcmcia_request_configuration(link->handle,
+ &link->conf);
+ }
+ break;
+ default:
+ DEBUG(5, "reader_event: unknown event %.2x\n", event);
+ break;
+ }
+ DEBUG(3, "<- reader_event\n");
+ return CS_SUCCESS;
+}
+
+static void reader_release(dev_link_t *link)
+{
+ int rc;
+
+ DEBUG(3, "-> reader_release\n");
+ cm4040_reader_release(link->priv);
+ rc = pcmcia_release_configuration(link->handle);
+ if (rc != CS_SUCCESS)
+ DEBUG(5, "couldn't ReleaseConfiguration "
+ "reasoncode=%.2x\n", rc);
+ rc = pcmcia_release_io(link->handle, &link->io);
+ if (rc != CS_SUCCESS)
+ DEBUG(5, "couldn't ReleaseIO reasoncode=%.2x\n", rc);
+
+ DEBUG(3, "<- reader_release\n");
+}
+
+static dev_link_t *reader_attach(void)
+{
+ struct reader_dev *dev;
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int i;
+
+ DEBUG(3, "reader_attach\n");
+ for (i=0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i] == NULL)
+ break;
+ }
+
+ if (i == CM_MAX_DEV) {
+ printk(KERN_NOTICE "all devices in use\n");
+ return NULL;
+ }
+
+ DEBUG(5, "create reader device instance\n");
+ dev = kmalloc(sizeof(struct reader_dev), GFP_KERNEL);
+ if (dev == NULL)
+ return NULL;
+
+ memset(dev, 0, sizeof(struct reader_dev));
+ dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+ dev->timer_expired = 0;
+ dev->buffer_status = 0;
+
+ link = &dev->link;
+ link->priv = dev;
+
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ dev_table[i] = link;
+
+
+ DEBUG(5, "Register with Card Services\n");
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.EventMask=
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ i = pcmcia_register_client(&link->handle, &client_reg);
+ if (i) {
+ cs_error(link->handle, RegisterClient, i);
+ reader_detach(link);
+ return NULL;
+ }
+ init_waitqueue_head(&dev->devq);
+ init_waitqueue_head(&dev->poll_wait);
+ init_waitqueue_head(&dev->read_wait);
+ init_waitqueue_head(&dev->write_wait);
+ init_timer(&cm4040_poll_timer);
+ cm4040_poll_timer.function = &cm4040_do_poll;
+
+ return link;
+}
+
+static void reader_detach_by_devno(int devno, dev_link_t *link)
+{
+ struct reader_dev *dev = link->priv;
+
+ DEBUG(3, "-> detach_by_devno(devno=%d)\n", devno);
+ if (link->state & DEV_CONFIG) {
+ DEBUG(5, "device still configured (try to release it)\n");
+ reader_release(link);
+ }
+
+ pcmcia_deregister_client(link->handle);
+ dev_table[devno] = NULL;
+ DEBUG(5, "freeing dev=%p\n", dev);
+ kfree(dev);
+ DEBUG(3, "<- detach_by-devno\n");
+ return;
+}
+
+static void reader_detach(dev_link_t *link)
+{
+ int i;
+
+ DEBUG(3, "-> reader_detach(link=%p\n", link);
+ /* find device */
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i] == link)
+ break;
+ }
+ if (i == CM_MAX_DEV) {
+ printk(KERN_WARNING MODULE_NAME
+ ": detach for unkown device aborted\n");
+ return;
+ }
+ reader_detach_by_devno(i, link);
+ DEBUG(3, "<- reader_detach\n");
+ return;
+}
+
+static struct file_operations reader_fops = {
+ .owner = THIS_MODULE,
+ .read = cm4040_read,
+ .write = cm4040_write,
+ .open = cm4040_open,
+ .release = cm4040_close,
+ .poll = cm4040_poll,
+};
+
+static struct pcmcia_device_id cm4040_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
+ PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
+ 0xE32CDD8C, 0x8F23318B),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, cm4040_ids);
+
+static struct pcmcia_driver reader_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "cm4040_cs",
+ },
+ .attach = reader_attach,
+ .detach = reader_detach,
+ .event = reader_event,
+ .id_table = cm4040_ids,
+};
+
+static int __init cm4040_init(void)
+{
+ printk(KERN_INFO "%s\n", version);
+ pcmcia_register_driver(&reader_driver);
+ major = register_chrdev(0, DEVICE_NAME, &reader_fops);
+ if (major < 0) {
+ printk(KERN_WARNING MODULE_NAME
+ ": could not get major number\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void __exit cm4040_exit(void)
+{
+ int i;
+
+ printk(KERN_INFO MODULE_NAME ": unloading\n");
+ pcmcia_unregister_driver(&reader_driver);
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i])
+ reader_detach_by_devno(i, dev_table[i]);
+ }
+ unregister_chrdev(major, DEVICE_NAME);
+}
+
+module_init(cm4040_init);
+module_exit(cm4040_exit);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/char/pcmcia/cm4040_cs.h b/drivers/char/pcmcia/cm4040_cs.h
new file mode 100644
--- /dev/null
+++ b/drivers/char/pcmcia/cm4040_cs.h
@@ -0,0 +1,47 @@
+#ifndef _CM4040_H_
+#define _CM4040_H_
+
+#define CM_MAX_DEV 4
+
+#define DEVICE_NAME "cmx"
+#define MODULE_NAME "cm4040_cs"
+
+#define REG_OFFSET_BULK_OUT 0
+#define REG_OFFSET_BULK_IN 0
+#define REG_OFFSET_BUFFER_STATUS 1
+#define REG_OFFSET_SYNC_CONTROL 2
+
+#define BSR_BULK_IN_FULL 0x02
+#define BSR_BULK_OUT_FULL 0x01
+
+#define SCR_HOST_TO_READER_START 0x80
+#define SCR_ABORT 0x40
+#define SCR_EN_NOTIFY 0x20
+#define SCR_ACK_NOTIFY 0x10
+#define SCR_READER_TO_HOST_DONE 0x08
+#define SCR_HOST_TO_READER_DONE 0x04
+#define SCR_PULSE_INTERRUPT 0x02
+#define SCR_POWER_DOWN 0x01
+
+
+#define CMD_PC_TO_RDR_ICCPOWERON 0x62
+#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
+#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
+#define CMD_PC_TO_RDR_SECURE 0x69
+#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
+#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
+#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
+#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
+#define CMD_PC_TO_RDR_ESCAPE 0x6B
+#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
+#define CMD_PC_TO_RDR_TEST_SECURE 0x74
+#define CMD_PC_TO_RDR_OK_SECURE 0x89
+
+
+#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
+#define CMD_RDR_TO_PC_DATABLOCK 0x80
+#define CMD_RDR_TO_PC_PARAMETERS 0x82
+#define CMD_RDR_TO_PC_ESCAPE 0x83
+#define CMD_RDR_TO_PC_OK_SECURE 0x89
+
+#endif /* _CM4040_H_ */
--
- Harald Welte <[email protected]> http://gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
(ETSI EN 300 175-7 Ch. A6)


Attachments:
(No filename) (26.82 kB)
(No filename) (189.00 B)
Download all attachments

2005-09-05 19:06:41

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH] Omnikey Cardman 4040 driver

> +#include <linux/version.h>

I don't think you need this one.

> +#include <pcmcia/version.h>

you shouldn't need this one.

> +static atomic_t cm4040_num_devices_open;
> +
> +#ifdef PCMCIA_DEBUG
> +static int pc_debug = PCMCIA_DEBUG;
> +module_param(pc_debug, int, 0600);
> +#define DEBUG(n, x, args...) do { if (pc_debug >= (n)) \
> + printk(KERN_DEBUG "%s:%s:" x, MODULE_NAME, \
> + __FUNCTION__, ##args); } while (0)
> +#else
> +#define DEBUG(n, args...)
> +#endif

What about just using pr_debug (or dev_dbg where you have a struct device
handy)

> +/* poll the device fifo status register. not to be confused with
> + * the poll syscall. */
> +static void cm4040_do_poll(unsigned long dummy)
> +{
> + unsigned int i;
> + /* walk through all devices */
> + for (i = 0; dev_table[i]; i++) {

Please make the poll timer per device. We generally try to avoid
global state, and this allows to get rid of the opencount tracking aswell.

> +static ssize_t cm4040_read(struct file *filp, char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + struct reader_dev *dev = (struct reader_dev *) filp->private_data;

no need to case a void pointer.

> + if (count < 10)
> + return -EFAULT;
> +
> + if (filp->f_flags & O_NONBLOCK) {
> + DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
> + DEBUG(2, "<- cm4040_read (failure)\n");
> + return -EAGAIN;
> + }

this sounds rather pointless. letting an O_NONBLOCK open fail all
the time doesn't sound like a good idea.

> +static int cm4040_open(struct inode *inode, struct file *filp)
> +{
> + struct reader_dev *dev;
> + dev_link_t *link;
> + int i;
> +
> + DEBUG(2, "-> cm4040_open(device=%d.%d process=%s,%d)\n",
> + MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
> + current->comm, current->pid);
> +
> + i = MINOR(inode->i_rdev);

please use iminor.

> + if (filp->f_flags & O_NONBLOCK) {
> + DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
> + DEBUG(4, "<- cm4040_open (failure)\n");
> + return -EAGAIN;
> + }

given that you fail O_NONLOCK in open already the code above makes even
less sense.

> +
> + dev->owner = current;

this doesn't make a lot of sense and seems to be only used in
debug code, I'd suggest killing it.

> +static int cm4040_close(struct inode *inode,struct file *filp)
> +{
> + struct reader_dev *dev;
> + dev_link_t *link;
> + int i;
> +
> + DEBUG(2, "-> cm4040_close(maj/min=%d.%d)\n",
> + MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
> +
> + i = MINOR(inode->i_rdev);
> + if (i >= CM_MAX_DEV)
> + return -ENODEV;
> +
> + link = dev_table[MINOR(inode->i_rdev)];
> + if (link == NULL)
> + return -ENODEV;
> +
> + dev = (struct reader_dev *) link->priv;

you should be able to use file->private_data here.

> + case CS_EVENT_CARD_REMOVAL:
> + DEBUG(5, "CS_EVENT_CARD_REMOVAL\n");
> + link->state &= ~DEV_PRESENT;
> + break;
> + case CS_EVENT_PM_SUSPEND:
> + DEBUG(5, "CS_EVENT_PM_SUSPEND "
> + "(fall-through to CS_EVENT_RESET_PHYSICAL)\n");
> + link->state |= DEV_SUSPEND;
> +
> + case CS_EVENT_RESET_PHYSICAL:
> + DEBUG(5, "CS_EVENT_RESET_PHYSICAL\n");
> + if (link->state & DEV_CONFIG) {
> + DEBUG(5, "ReleaseConfiguration\n");
> + pcmcia_release_configuration(link->handle);
> + }
> + break;
> + case CS_EVENT_PM_RESUME:
> + DEBUG(5, "CS_EVENT_PM_RESUME "
> + "(fall-through to CS_EVENT_CARD_RESET)\n");
> + link->state &= ~DEV_SUSPEND;

I think these events became methods of their own recently, not sure
if it hit -mm or mainline yet.

2005-09-05 20:04:23

by Jesper Juhl

[permalink] [raw]
Subject: Re: [PATCH] Omnikey Cardman 4040 driver

On Monday 05 September 2005 21:54, Harald Welte wrote:
> Hi!
>
> I've now incorporated all the suggested changes (thanks once again on
> the many comments received). The resulting driver has been tested and
> works fine.
>
> Please consider applying the driver to the mainline tree, thanks.
>
What did you diff this against? When I applied it to the 2.6.13 source I got :

patching file MAINTAINERS
Hunk #1 succeeded at 1730 (offset -7 lines).
patching file drivers/char/pcmcia/Kconfig
patching file drivers/char/pcmcia/Makefile
patching file drivers/char/pcmcia/cm4040_cs.c
patching file drivers/char/pcmcia/cm4040_cs.h

Anyway, I did a small cleanup.
Removed all instances of trailing whitespace ( sed -r s/"[ \t]+$"/""/ ).
Removed some excessive (IMHO) use of blank lines.
A few CodingStyle related whitespace fixes (spaces after "," etc).
Removed some pointless casts.

Hope this is useful to you (applies on top of the version you just posted).


/Jesper Juhl


--- drivers/char/pcmcia/cm4040_cs.c.orig 2005-09-05 21:39:05.000000000 +0200
+++ drivers/char/pcmcia/cm4040_cs.c 2005-09-05 22:01:19.000000000 +0200
@@ -1,4 +1,4 @@
- /*
+/*
* A driver for the Omnikey PCMCIA smartcard reader CardMan 4040
*
* (c) 2000-2004 Omnikey AG (http://www.omnikey.com/)
@@ -68,28 +68,25 @@ static char *version =
static void reader_release(dev_link_t *link);
static void reader_detach(dev_link_t *link);

-static int major;
+static int major;

#define BS_READABLE 0x01
#define BS_WRITABLE 0x02

struct reader_dev {
- dev_link_t link;
- dev_node_t node;
- wait_queue_head_t devq;
-
+ dev_link_t link;
+ dev_node_t node;
+ wait_queue_head_t devq;
wait_queue_head_t poll_wait;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
-
unsigned int buffer_status;
-
unsigned int timer_expired;
struct timer_list timer;
unsigned long timeout;
unsigned char s_buf[READ_WRITE_BUFFER_SIZE];
unsigned char r_buf[READ_WRITE_BUFFER_SIZE];
- struct task_struct *owner;
+ struct task_struct *owner;
};

static dev_info_t dev_info = MODULE_NAME;
@@ -104,7 +101,7 @@ static struct timer_list cm4040_poll_tim
static inline void xoutb(unsigned char val, unsigned short port)
{
DEBUG(7, "outb(val=%.2x,port=%.4x)\n", val, port);
- outb(val,port);
+ outb(val, port);
}

static inline unsigned char xinb(unsigned short port)
@@ -122,7 +119,7 @@ static inline unsigned char xinb(unsigne
static void cm4040_do_poll(unsigned long dummy)
{
unsigned int i;
- /* walk through all devices */
+ /* walk through all devices */
for (i = 0; dev_table[i]; i++) {
dev_link_t *dl = dev_table[i];
struct reader_dev *dev = dl->priv;
@@ -156,7 +153,7 @@ static int wait_for_bulk_out_ready(struc
int i, rc;
int iobase = dev->link.io.BasePort1;

- for (i=0; i < POLL_LOOP_COUNT; i++) {
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
& BSR_BULK_OUT_FULL) == 0) {
DEBUG(4, "BulkOut empty (i=%d)\n", i);
@@ -191,7 +188,7 @@ static int write_sync_reg(unsigned char
if (rc <= 0)
return rc;

- xoutb(val,iobase + REG_OFFSET_SYNC_CONTROL);
+ xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL);
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0)
return rc;
@@ -204,7 +201,7 @@ static int wait_for_bulk_in_ready(struct
int i, rc;
int iobase = dev->link.io.BasePort1;

- for (i=0; i < POLL_LOOP_COUNT; i++) {
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
& BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) {
DEBUG(3, "BulkIn full (i=%d)\n", i);
@@ -215,7 +212,7 @@ static int wait_for_bulk_in_ready(struct
DEBUG(4, "wait_event_interruptible_timeout(timeout=%ld\n",
dev->timeout);
rc = wait_event_interruptible_timeout(dev->read_wait,
- test_and_clear_bit(BS_READABLE,
+ test_and_clear_bit(BS_READABLE,
&dev->buffer_status),
dev->timeout);
if (rc > 0)
@@ -231,7 +228,7 @@ static int wait_for_bulk_in_ready(struct
static ssize_t cm4040_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
- struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ struct reader_dev *dev = filp->private_data;
int iobase = dev->link.io.BasePort1;
unsigned long bytes_to_read;
unsigned long i;
@@ -239,7 +236,7 @@ static ssize_t cm4040_read(struct file *
int rc;
unsigned char uc;

- DEBUG(2, "-> cm4040_read(%s,%d)\n", current->comm,current->pid);
+ DEBUG(2, "-> cm4040_read(%s,%d)\n", current->comm, current->pid);

if (count == 0)
return 0;
@@ -247,17 +244,17 @@ static ssize_t cm4040_read(struct file *
if (count < 10)
return -EFAULT;

- if (filp->f_flags & O_NONBLOCK) {
+ if (filp->f_flags & O_NONBLOCK) {
DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
DEBUG(2, "<- cm4040_read (failure)\n");
return -EAGAIN;
}

- if ((dev->link.state & DEV_PRESENT)==0)
+ if ((dev->link.state & DEV_PRESENT)==0)
return -ENODEV;

schedule_timeout(1*HZ);
- for (i=0; i<5; i++) {
+ for (i = 0; i < 5; i++) {
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
@@ -284,7 +281,7 @@ static ssize_t cm4040_read(struct file *

DEBUG(6, "Min=%lu\n", min_bytes_to_read);

- for (i=0; i < (min_bytes_to_read-5); i++) {
+ for (i = 0; i < (min_bytes_to_read-5); i++) {
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
@@ -302,7 +299,6 @@ static ssize_t cm4040_read(struct file *
if (copy_to_user(buf, dev->r_buf, min_bytes_to_read))
return -EFAULT;

-
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
@@ -331,11 +327,11 @@ static ssize_t cm4040_read(struct file *
static ssize_t cm4040_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ struct reader_dev *dev = filp->private_data;
int iobase = dev->link.io.BasePort1;
ssize_t rc;
int i;
- unsigned int bytes_to_write;
+ unsigned int bytes_to_write;

DEBUG(2, "-> cm4040_write(%s,%d)\n", current->comm, current->pid);

@@ -349,13 +345,13 @@ static ssize_t cm4040_write(struct file
return -EIO;
}

- if (filp->f_flags & O_NONBLOCK) {
+ if (filp->f_flags & O_NONBLOCK) {
DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
DEBUG(4, "<- cm4040_write (failure)\n");
return -EAGAIN;
}

- if ((dev->link.state & DEV_PRESENT) == 0)
+ if ((dev->link.state & DEV_PRESENT) == 0)
return -ENODEV;

bytes_to_write = count;
@@ -377,7 +373,7 @@ static ssize_t cm4040_write(struct file
case CMD_PC_TO_RDR_GETSLOTSTATUS:
case CMD_PC_TO_RDR_ICCPOWEROFF:
case CMD_PC_TO_RDR_GETPARAMETERS:
- case CMD_PC_TO_RDR_RESETPARAMETERS:
+ case CMD_PC_TO_RDR_RESETPARAMETERS:
case CMD_PC_TO_RDR_SETPARAMETERS:
case CMD_PC_TO_RDR_ESCAPE:
case CMD_PC_TO_RDR_ICCCLOCK:
@@ -396,10 +392,9 @@ static ssize_t cm4040_write(struct file
return -EIO;
}

-
DEBUG(4, "start \n");

- for (i=0; i < bytes_to_write; i++) {
+ for (i = 0; i < bytes_to_write; i++) {
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0) {
DEBUG(5, "wait_for_bulk_out_ready rc=%.2Zx\n", rc);
@@ -409,7 +404,7 @@ static ssize_t cm4040_write(struct file
else
return -EIO;
}
-
+
xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT);
DEBUG(4, "%.2x ", dev->s_buf[i]);
}
@@ -432,7 +427,7 @@ static ssize_t cm4040_write(struct file

static unsigned int cm4040_poll(struct file *filp, poll_table *wait)
{
- struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ struct reader_dev *dev = filp->private_data;
unsigned int mask = 0;

poll_wait(filp, &dev->poll_wait, wait);
@@ -478,14 +473,14 @@ static int cm4040_open(struct inode *ino
dev = (struct reader_dev *)link->priv;
filp->private_data = dev;

- if (filp->f_flags & O_NONBLOCK) {
+ if (filp->f_flags & O_NONBLOCK) {
DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
DEBUG(4, "<- cm4040_open (failure)\n");
return -EAGAIN;
}

dev->owner = current;
- link->open = 1;
+ link->open = 1;

atomic_inc(&cm4040_num_devices_open);
mod_timer(&cm4040_poll_timer, jiffies + POLL_PERIOD);
@@ -494,7 +489,7 @@ static int cm4040_open(struct inode *ino
return nonseekable_open(inode, filp);
}

-static int cm4040_close(struct inode *inode,struct file *filp)
+static int cm4040_close(struct inode *inode, struct file *filp)
{
struct reader_dev *dev;
dev_link_t *link;
@@ -511,10 +506,10 @@ static int cm4040_close(struct inode *in
if (link == NULL)
return -ENODEV;

- dev = (struct reader_dev *) link->priv;
+ dev = link->priv;

link->open = 0;
- wake_up(&dev->devq);
+ wake_up(&dev->devq);

atomic_dec(&cm4040_num_devices_open);

@@ -524,16 +519,16 @@ static int cm4040_close(struct inode *in

static void cm4040_reader_release(dev_link_t *link)
{
- struct reader_dev *dev = (struct reader_dev *) link->priv;
+ struct reader_dev *dev = link->priv;

- DEBUG(3, "-> cm4040_reader_release\n");
+ DEBUG(3, "-> cm4040_reader_release\n");
while (link->open) {
DEBUG(3, KERN_INFO MODULE_NAME ": delaying release until "
"process '%s', pid %d has terminated\n",
- dev->owner->comm,dev->owner->pid);
+ dev->owner->comm, dev->owner->pid);
wait_event(dev->devq, (link->open == 0));
}
- DEBUG(3, "<- cm4040_reader_release\n");
+ DEBUG(3, "<- cm4040_reader_release\n");
return;
}

@@ -600,7 +595,7 @@ static void reader_config(dev_link_t *li

DEBUG(2, "tupleIndex=%d\n", parse.cftable_entry.index);
link->conf.ConfigIndex = parse.cftable_entry.index;
-
+
if (!parse.cftable_entry.io.nwin)
continue;

@@ -621,8 +616,8 @@ static void reader_config(dev_link_t *li
rc = pcmcia_request_io(handle, &link->io);
if (rc == CS_SUCCESS) {
DEBUG(2, "RequestIO OK\n");
- break;
- } else
+ break;
+ } else
DEBUG(2, "RequestIO failed\n");
}
if (rc != CS_SUCCESS) {
@@ -684,7 +679,7 @@ static int reader_event(event_t event, i
case CS_EVENT_CARD_INSERTION:
DEBUG(5, "CS_EVENT_CARD_INSERTION\n");
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
- reader_config(link,devno);
+ reader_config(link, devno);
break;
case CS_EVENT_CARD_REMOVAL:
DEBUG(5, "CS_EVENT_CARD_REMOVAL\n");
@@ -694,7 +689,7 @@ static int reader_event(event_t event, i
DEBUG(5, "CS_EVENT_PM_SUSPEND "
"(fall-through to CS_EVENT_RESET_PHYSICAL)\n");
link->state |= DEV_SUSPEND;
-
+
case CS_EVENT_RESET_PHYSICAL:
DEBUG(5, "CS_EVENT_RESET_PHYSICAL\n");
if (link->state & DEV_CONFIG) {
@@ -706,7 +701,7 @@ static int reader_event(event_t event, i
DEBUG(5, "CS_EVENT_PM_RESUME "
"(fall-through to CS_EVENT_CARD_RESET)\n");
link->state &= ~DEV_SUSPEND;
-
+
case CS_EVENT_CARD_RESET:
DEBUG(5, "CS_EVENT_CARD_RESET\n");
if ((link->state & DEV_CONFIG)) {
@@ -728,7 +723,7 @@ static void reader_release(dev_link_t *l
int rc;

DEBUG(3, "-> reader_release\n");
- cm4040_reader_release(link->priv);
+ cm4040_reader_release(link->priv);
rc = pcmcia_release_configuration(link->handle);
if (rc != CS_SUCCESS)
DEBUG(5, "couldn't ReleaseConfiguration "
@@ -748,7 +743,7 @@ static dev_link_t *reader_attach(void)
int i;

DEBUG(3, "reader_attach\n");
- for (i=0; i < CM_MAX_DEV; i++) {
+ for (i = 0; i < CM_MAX_DEV; i++) {
if (dev_table[i] == NULL)
break;
}
@@ -757,7 +752,7 @@ static dev_link_t *reader_attach(void)
printk(KERN_NOTICE "all devices in use\n");
return NULL;
}
-
+
DEBUG(5, "create reader device instance\n");
dev = kmalloc(sizeof(struct reader_dev), GFP_KERNEL);
if (dev == NULL)
@@ -774,7 +769,6 @@ static dev_link_t *reader_attach(void)
link->conf.IntType = INT_MEMORY_AND_IO;
dev_table[i] = link;

-
DEBUG(5, "Register with Card Services\n");
client_reg.dev_info = &dev_info;
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
@@ -829,7 +823,7 @@ static void reader_detach(dev_link_t *li
break;
}
if (i == CM_MAX_DEV) {
- printk(KERN_WARNING MODULE_NAME
+ printk(KERN_WARNING MODULE_NAME
": detach for unkown device aborted\n");
return;
}
@@ -849,7 +843,7 @@ static struct file_operations reader_fop

static struct pcmcia_device_id cm4040_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
- PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
+ PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
0xE32CDD8C, 0x8F23318B),
PCMCIA_DEVICE_NULL,
};
@@ -884,7 +878,7 @@ static void __exit cm4040_exit(void)
int i;

printk(KERN_INFO MODULE_NAME ": unloading\n");
- pcmcia_unregister_driver(&reader_driver);
+ pcmcia_unregister_driver(&reader_driver);
for (i = 0; i < CM_MAX_DEV; i++) {
if (dev_table[i])
reader_detach_by_devno(i, dev_table[i]);

2005-09-05 20:24:36

by Jesper Juhl

[permalink] [raw]
Subject: Re: [PATCH] Omnikey Cardman 4040 driver

On Monday 05 September 2005 22:05, Jesper Juhl wrote:
> On Monday 05 September 2005 21:54, Harald Welte wrote:
> > Hi!
> >
> > I've now incorporated all the suggested changes (thanks once again on
> > the many comments received). The resulting driver has been tested and
> > works fine.
> >
> > Please consider applying the driver to the mainline tree, thanks.
> >
> What did you diff this against? When I applied it to the 2.6.13 source I got :
>
> patching file MAINTAINERS
> Hunk #1 succeeded at 1730 (offset -7 lines).
> patching file drivers/char/pcmcia/Kconfig
> patching file drivers/char/pcmcia/Makefile
> patching file drivers/char/pcmcia/cm4040_cs.c
> patching file drivers/char/pcmcia/cm4040_cs.h
>
> Anyway, I did a small cleanup.
> Removed all instances of trailing whitespace ( sed -r s/"[ \t]+$"/""/ ).
> Removed some excessive (IMHO) use of blank lines.
> A few CodingStyle related whitespace fixes (spaces after "," etc).
> Removed some pointless casts.
>
> Hope this is useful to you (applies on top of the version you just posted).
>

Actually, forget the previous patch, I forgot a few things. Improved patch can
be found below (a few extra cleanups of spacing and make the header look a
little more pretty).

/Jesper Juhl


--- drivers/char/pcmcia/cm4040_cs.c.orig 2005-09-05 21:39:05.000000000 +0200
+++ drivers/char/pcmcia/cm4040_cs.c 2005-09-05 22:20:38.000000000 +0200
@@ -1,4 +1,4 @@
- /*
+/*
* A driver for the Omnikey PCMCIA smartcard reader CardMan 4040
*
* (c) 2000-2004 Omnikey AG (http://www.omnikey.com/)
@@ -68,28 +68,25 @@ static char *version =
static void reader_release(dev_link_t *link);
static void reader_detach(dev_link_t *link);

-static int major;
+static int major;

#define BS_READABLE 0x01
#define BS_WRITABLE 0x02

struct reader_dev {
- dev_link_t link;
- dev_node_t node;
- wait_queue_head_t devq;
-
+ dev_link_t link;
+ dev_node_t node;
+ wait_queue_head_t devq;
wait_queue_head_t poll_wait;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
-
unsigned int buffer_status;
-
unsigned int timer_expired;
struct timer_list timer;
unsigned long timeout;
unsigned char s_buf[READ_WRITE_BUFFER_SIZE];
unsigned char r_buf[READ_WRITE_BUFFER_SIZE];
- struct task_struct *owner;
+ struct task_struct *owner;
};

static dev_info_t dev_info = MODULE_NAME;
@@ -104,7 +101,7 @@ static struct timer_list cm4040_poll_tim
static inline void xoutb(unsigned char val, unsigned short port)
{
DEBUG(7, "outb(val=%.2x,port=%.4x)\n", val, port);
- outb(val,port);
+ outb(val, port);
}

static inline unsigned char xinb(unsigned short port)
@@ -122,7 +119,7 @@ static inline unsigned char xinb(unsigne
static void cm4040_do_poll(unsigned long dummy)
{
unsigned int i;
- /* walk through all devices */
+ /* walk through all devices */
for (i = 0; dev_table[i]; i++) {
dev_link_t *dl = dev_table[i];
struct reader_dev *dev = dl->priv;
@@ -156,7 +153,7 @@ static int wait_for_bulk_out_ready(struc
int i, rc;
int iobase = dev->link.io.BasePort1;

- for (i=0; i < POLL_LOOP_COUNT; i++) {
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
& BSR_BULK_OUT_FULL) == 0) {
DEBUG(4, "BulkOut empty (i=%d)\n", i);
@@ -191,7 +188,7 @@ static int write_sync_reg(unsigned char
if (rc <= 0)
return rc;

- xoutb(val,iobase + REG_OFFSET_SYNC_CONTROL);
+ xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL);
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0)
return rc;
@@ -204,7 +201,7 @@ static int wait_for_bulk_in_ready(struct
int i, rc;
int iobase = dev->link.io.BasePort1;

- for (i=0; i < POLL_LOOP_COUNT; i++) {
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
& BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) {
DEBUG(3, "BulkIn full (i=%d)\n", i);
@@ -215,7 +212,7 @@ static int wait_for_bulk_in_ready(struct
DEBUG(4, "wait_event_interruptible_timeout(timeout=%ld\n",
dev->timeout);
rc = wait_event_interruptible_timeout(dev->read_wait,
- test_and_clear_bit(BS_READABLE,
+ test_and_clear_bit(BS_READABLE,
&dev->buffer_status),
dev->timeout);
if (rc > 0)
@@ -231,7 +228,7 @@ static int wait_for_bulk_in_ready(struct
static ssize_t cm4040_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
- struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ struct reader_dev *dev = filp->private_data;
int iobase = dev->link.io.BasePort1;
unsigned long bytes_to_read;
unsigned long i;
@@ -239,7 +236,7 @@ static ssize_t cm4040_read(struct file *
int rc;
unsigned char uc;

- DEBUG(2, "-> cm4040_read(%s,%d)\n", current->comm,current->pid);
+ DEBUG(2, "-> cm4040_read(%s,%d)\n", current->comm, current->pid);

if (count == 0)
return 0;
@@ -247,17 +244,17 @@ static ssize_t cm4040_read(struct file *
if (count < 10)
return -EFAULT;

- if (filp->f_flags & O_NONBLOCK) {
+ if (filp->f_flags & O_NONBLOCK) {
DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
DEBUG(2, "<- cm4040_read (failure)\n");
return -EAGAIN;
}

- if ((dev->link.state & DEV_PRESENT)==0)
+ if ((dev->link.state & DEV_PRESENT) == 0)
return -ENODEV;

schedule_timeout(1*HZ);
- for (i=0; i<5; i++) {
+ for (i = 0; i < 5; i++) {
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
@@ -284,7 +281,7 @@ static ssize_t cm4040_read(struct file *

DEBUG(6, "Min=%lu\n", min_bytes_to_read);

- for (i=0; i < (min_bytes_to_read-5); i++) {
+ for (i = 0; i < (min_bytes_to_read-5); i++) {
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
@@ -302,7 +299,6 @@ static ssize_t cm4040_read(struct file *
if (copy_to_user(buf, dev->r_buf, min_bytes_to_read))
return -EFAULT;

-
rc = wait_for_bulk_in_ready(dev);
if (rc <= 0) {
DEBUG(5,"wait_for_bulk_in_ready rc=%.2x\n",rc);
@@ -331,11 +327,11 @@ static ssize_t cm4040_read(struct file *
static ssize_t cm4040_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ struct reader_dev *dev = filp->private_data;
int iobase = dev->link.io.BasePort1;
ssize_t rc;
int i;
- unsigned int bytes_to_write;
+ unsigned int bytes_to_write;

DEBUG(2, "-> cm4040_write(%s,%d)\n", current->comm, current->pid);

@@ -349,13 +345,13 @@ static ssize_t cm4040_write(struct file
return -EIO;
}

- if (filp->f_flags & O_NONBLOCK) {
+ if (filp->f_flags & O_NONBLOCK) {
DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
DEBUG(4, "<- cm4040_write (failure)\n");
return -EAGAIN;
}

- if ((dev->link.state & DEV_PRESENT) == 0)
+ if ((dev->link.state & DEV_PRESENT) == 0)
return -ENODEV;

bytes_to_write = count;
@@ -377,7 +373,7 @@ static ssize_t cm4040_write(struct file
case CMD_PC_TO_RDR_GETSLOTSTATUS:
case CMD_PC_TO_RDR_ICCPOWEROFF:
case CMD_PC_TO_RDR_GETPARAMETERS:
- case CMD_PC_TO_RDR_RESETPARAMETERS:
+ case CMD_PC_TO_RDR_RESETPARAMETERS:
case CMD_PC_TO_RDR_SETPARAMETERS:
case CMD_PC_TO_RDR_ESCAPE:
case CMD_PC_TO_RDR_ICCCLOCK:
@@ -396,10 +392,9 @@ static ssize_t cm4040_write(struct file
return -EIO;
}

-
DEBUG(4, "start \n");

- for (i=0; i < bytes_to_write; i++) {
+ for (i = 0; i < bytes_to_write; i++) {
rc = wait_for_bulk_out_ready(dev);
if (rc <= 0) {
DEBUG(5, "wait_for_bulk_out_ready rc=%.2Zx\n", rc);
@@ -409,7 +404,7 @@ static ssize_t cm4040_write(struct file
else
return -EIO;
}
-
+
xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT);
DEBUG(4, "%.2x ", dev->s_buf[i]);
}
@@ -432,7 +427,7 @@ static ssize_t cm4040_write(struct file

static unsigned int cm4040_poll(struct file *filp, poll_table *wait)
{
- struct reader_dev *dev = (struct reader_dev *) filp->private_data;
+ struct reader_dev *dev = filp->private_data;
unsigned int mask = 0;

poll_wait(filp, &dev->poll_wait, wait);
@@ -465,7 +460,7 @@ static int cm4040_open(struct inode *ino
}
link = dev_table[MINOR(inode->i_rdev)];
if (link == NULL || !(DEV_OK(link))) {
- DEBUG(4, "link== NULL || DEV_OK false\n");
+ DEBUG(4, "link == NULL || DEV_OK false\n");
DEBUG(4, "<- cm4040_open (failure)\n");
return -ENODEV;
}
@@ -478,14 +473,14 @@ static int cm4040_open(struct inode *ino
dev = (struct reader_dev *)link->priv;
filp->private_data = dev;

- if (filp->f_flags & O_NONBLOCK) {
+ if (filp->f_flags & O_NONBLOCK) {
DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
DEBUG(4, "<- cm4040_open (failure)\n");
return -EAGAIN;
}

dev->owner = current;
- link->open = 1;
+ link->open = 1;

atomic_inc(&cm4040_num_devices_open);
mod_timer(&cm4040_poll_timer, jiffies + POLL_PERIOD);
@@ -494,7 +489,7 @@ static int cm4040_open(struct inode *ino
return nonseekable_open(inode, filp);
}

-static int cm4040_close(struct inode *inode,struct file *filp)
+static int cm4040_close(struct inode *inode, struct file *filp)
{
struct reader_dev *dev;
dev_link_t *link;
@@ -511,10 +506,10 @@ static int cm4040_close(struct inode *in
if (link == NULL)
return -ENODEV;

- dev = (struct reader_dev *) link->priv;
+ dev = link->priv;

link->open = 0;
- wake_up(&dev->devq);
+ wake_up(&dev->devq);

atomic_dec(&cm4040_num_devices_open);

@@ -524,16 +519,16 @@ static int cm4040_close(struct inode *in

static void cm4040_reader_release(dev_link_t *link)
{
- struct reader_dev *dev = (struct reader_dev *) link->priv;
+ struct reader_dev *dev = link->priv;

- DEBUG(3, "-> cm4040_reader_release\n");
+ DEBUG(3, "-> cm4040_reader_release\n");
while (link->open) {
DEBUG(3, KERN_INFO MODULE_NAME ": delaying release until "
"process '%s', pid %d has terminated\n",
- dev->owner->comm,dev->owner->pid);
+ dev->owner->comm, dev->owner->pid);
wait_event(dev->devq, (link->open == 0));
}
- DEBUG(3, "<- cm4040_reader_release\n");
+ DEBUG(3, "<- cm4040_reader_release\n");
return;
}

@@ -600,7 +595,7 @@ static void reader_config(dev_link_t *li

DEBUG(2, "tupleIndex=%d\n", parse.cftable_entry.index);
link->conf.ConfigIndex = parse.cftable_entry.index;
-
+
if (!parse.cftable_entry.io.nwin)
continue;

@@ -621,8 +616,8 @@ static void reader_config(dev_link_t *li
rc = pcmcia_request_io(handle, &link->io);
if (rc == CS_SUCCESS) {
DEBUG(2, "RequestIO OK\n");
- break;
- } else
+ break;
+ } else
DEBUG(2, "RequestIO failed\n");
}
if (rc != CS_SUCCESS) {
@@ -684,7 +679,7 @@ static int reader_event(event_t event, i
case CS_EVENT_CARD_INSERTION:
DEBUG(5, "CS_EVENT_CARD_INSERTION\n");
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
- reader_config(link,devno);
+ reader_config(link, devno);
break;
case CS_EVENT_CARD_REMOVAL:
DEBUG(5, "CS_EVENT_CARD_REMOVAL\n");
@@ -694,7 +689,7 @@ static int reader_event(event_t event, i
DEBUG(5, "CS_EVENT_PM_SUSPEND "
"(fall-through to CS_EVENT_RESET_PHYSICAL)\n");
link->state |= DEV_SUSPEND;
-
+
case CS_EVENT_RESET_PHYSICAL:
DEBUG(5, "CS_EVENT_RESET_PHYSICAL\n");
if (link->state & DEV_CONFIG) {
@@ -706,7 +701,7 @@ static int reader_event(event_t event, i
DEBUG(5, "CS_EVENT_PM_RESUME "
"(fall-through to CS_EVENT_CARD_RESET)\n");
link->state &= ~DEV_SUSPEND;
-
+
case CS_EVENT_CARD_RESET:
DEBUG(5, "CS_EVENT_CARD_RESET\n");
if ((link->state & DEV_CONFIG)) {
@@ -728,7 +723,7 @@ static void reader_release(dev_link_t *l
int rc;

DEBUG(3, "-> reader_release\n");
- cm4040_reader_release(link->priv);
+ cm4040_reader_release(link->priv);
rc = pcmcia_release_configuration(link->handle);
if (rc != CS_SUCCESS)
DEBUG(5, "couldn't ReleaseConfiguration "
@@ -748,7 +743,7 @@ static dev_link_t *reader_attach(void)
int i;

DEBUG(3, "reader_attach\n");
- for (i=0; i < CM_MAX_DEV; i++) {
+ for (i = 0; i < CM_MAX_DEV; i++) {
if (dev_table[i] == NULL)
break;
}
@@ -757,7 +752,7 @@ static dev_link_t *reader_attach(void)
printk(KERN_NOTICE "all devices in use\n");
return NULL;
}
-
+
DEBUG(5, "create reader device instance\n");
dev = kmalloc(sizeof(struct reader_dev), GFP_KERNEL);
if (dev == NULL)
@@ -774,7 +769,6 @@ static dev_link_t *reader_attach(void)
link->conf.IntType = INT_MEMORY_AND_IO;
dev_table[i] = link;

-
DEBUG(5, "Register with Card Services\n");
client_reg.dev_info = &dev_info;
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
@@ -829,7 +823,7 @@ static void reader_detach(dev_link_t *li
break;
}
if (i == CM_MAX_DEV) {
- printk(KERN_WARNING MODULE_NAME
+ printk(KERN_WARNING MODULE_NAME
": detach for unkown device aborted\n");
return;
}
@@ -849,7 +843,7 @@ static struct file_operations reader_fop

static struct pcmcia_device_id cm4040_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
- PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
+ PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
0xE32CDD8C, 0x8F23318B),
PCMCIA_DEVICE_NULL,
};
@@ -884,7 +878,7 @@ static void __exit cm4040_exit(void)
int i;

printk(KERN_INFO MODULE_NAME ": unloading\n");
- pcmcia_unregister_driver(&reader_driver);
+ pcmcia_unregister_driver(&reader_driver);
for (i = 0; i < CM_MAX_DEV; i++) {
if (dev_table[i])
reader_detach_by_devno(i, dev_table[i]);
--- drivers/char/pcmcia/cm4040_cs.h.orig 2005-09-05 22:17:14.000000000 +0200
+++ drivers/char/pcmcia/cm4040_cs.h 2005-09-05 22:24:35.000000000 +0200
@@ -1,47 +1,45 @@
-#ifndef _CM4040_H_
-#define _CM4040_H_
+#ifndef _CM4040_H_
+#define _CM4040_H_

-#define CM_MAX_DEV 4
+#define CM_MAX_DEV 4

-#define DEVICE_NAME "cmx"
-#define MODULE_NAME "cm4040_cs"
+#define DEVICE_NAME "cmx"
+#define MODULE_NAME "cm4040_cs"

-#define REG_OFFSET_BULK_OUT 0
-#define REG_OFFSET_BULK_IN 0
-#define REG_OFFSET_BUFFER_STATUS 1
-#define REG_OFFSET_SYNC_CONTROL 2
-
-#define BSR_BULK_IN_FULL 0x02
-#define BSR_BULK_OUT_FULL 0x01
-
-#define SCR_HOST_TO_READER_START 0x80
-#define SCR_ABORT 0x40
-#define SCR_EN_NOTIFY 0x20
-#define SCR_ACK_NOTIFY 0x10
-#define SCR_READER_TO_HOST_DONE 0x08
-#define SCR_HOST_TO_READER_DONE 0x04
-#define SCR_PULSE_INTERRUPT 0x02
-#define SCR_POWER_DOWN 0x01
-
-
-#define CMD_PC_TO_RDR_ICCPOWERON 0x62
-#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
-#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
-#define CMD_PC_TO_RDR_SECURE 0x69
-#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
-#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
-#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
-#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
-#define CMD_PC_TO_RDR_ESCAPE 0x6B
-#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
-#define CMD_PC_TO_RDR_TEST_SECURE 0x74
-#define CMD_PC_TO_RDR_OK_SECURE 0x89
-
-
-#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
-#define CMD_RDR_TO_PC_DATABLOCK 0x80
-#define CMD_RDR_TO_PC_PARAMETERS 0x82
-#define CMD_RDR_TO_PC_ESCAPE 0x83
-#define CMD_RDR_TO_PC_OK_SECURE 0x89
+#define REG_OFFSET_BULK_OUT 0
+#define REG_OFFSET_BULK_IN 0
+#define REG_OFFSET_BUFFER_STATUS 1
+#define REG_OFFSET_SYNC_CONTROL 2
+
+#define BSR_BULK_IN_FULL 0x02
+#define BSR_BULK_OUT_FULL 0x01
+
+#define SCR_HOST_TO_READER_START 0x80
+#define SCR_ABORT 0x40
+#define SCR_EN_NOTIFY 0x20
+#define SCR_ACK_NOTIFY 0x10
+#define SCR_READER_TO_HOST_DONE 0x08
+#define SCR_HOST_TO_READER_DONE 0x04
+#define SCR_PULSE_INTERRUPT 0x02
+#define SCR_POWER_DOWN 0x01
+
+#define CMD_PC_TO_RDR_ICCPOWERON 0x62
+#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
+#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
+#define CMD_PC_TO_RDR_SECURE 0x69
+#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
+#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
+#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
+#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
+#define CMD_PC_TO_RDR_ESCAPE 0x6B
+#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
+#define CMD_PC_TO_RDR_TEST_SECURE 0x74
+#define CMD_PC_TO_RDR_OK_SECURE 0x89
+
+#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
+#define CMD_RDR_TO_PC_DATABLOCK 0x80
+#define CMD_RDR_TO_PC_PARAMETERS 0x82
+#define CMD_RDR_TO_PC_ESCAPE 0x83
+#define CMD_RDR_TO_PC_OK_SECURE 0x89

#endif /* _CM4040_H_ */




2005-09-06 20:21:10

by Harald Welte

[permalink] [raw]
Subject: Re: [PATCH] Omnikey Cardman 4040 driver (UPDATE)

Hi hch, thanks for your feedback.

An updated version of the patch is at the bottom of this mail.

On Mon, Sep 05, 2005 at 08:06:35PM +0100, Christoph Hellwig wrote:
> > +#include <linux/version.h>
>
> I don't think you need this one.

done

> > +#include <pcmcia/version.h>
>
> you shouldn't need this one.

done

> > +static atomic_t cm4040_num_devices_open;
> > +
> > +#ifdef PCMCIA_DEBUG
> > +static int pc_debug = PCMCIA_DEBUG;
> > +module_param(pc_debug, int, 0600);
> > +#define DEBUG(n, x, args...) do { if (pc_debug >= (n)) \
> > + printk(KERN_DEBUG "%s:%s:" x, MODULE_NAME, \
> > + __FUNCTION__, ##args); } while (0)
> > +#else
> > +#define DEBUG(n, args...)
> > +#endif
>
> What about just using pr_debug (or dev_dbg where you have a struct device
> handy)

I really appreciate the multiple debug levels of the driver, sorry.
However, I have converted the DEBUG macro to use dev_printk as a
compromise.

> Please make the poll timer per device. We generally try to avoid
> global state, and this allows to get rid of the opencount tracking aswell.

done.

> > +static ssize_t cm4040_read(struct file *filp, char __user *buf,
> > + size_t count, loff_t *ppos)
> > +{
> > + struct reader_dev *dev = (struct reader_dev *) filp->private_data;
>
> no need to case a void pointer.

well, but good style in my opioion anyway. Anway, I don't really mind,
so it has been removed.

> > + if (count < 10)
> > + return -EFAULT;
> > +
> > + if (filp->f_flags & O_NONBLOCK) {
> > + DEBUG(4, "filep->f_flags O_NONBLOCK set\n");
> > + DEBUG(2, "<- cm4040_read (failure)\n");
> > + return -EAGAIN;
> > + }
>
> this sounds rather pointless. letting an O_NONBLOCK open fail all
> the time doesn't sound like a good idea.

what about fcntl enabling NONBLOCK after the open?
Other character drivers (such as rtc) do the same, btw.
Is there any way how I can tell the VFS to make any nonblocking
operation fail?

> please use iminor.

done.

> given that you fail O_NONLOCK in open already the code above makes even
> less sense.

see my comment above.

> > + dev->owner = current;
>
> this doesn't make a lot of sense and seems to be only used in
> debug code, I'd suggest killing it.

done.

> you should be able to use file->private_data here.

done

> I think these events became methods of their own recently, not sure
> if it hit -mm or mainline yet.

I cannot find anything like that in mainline, thus my code remains
unchanged for now.

diff --git a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1737,6 +1737,11 @@ L: [email protected]
W: http://www.linuxtr.net
S: Maintained

+OMNIKEY CARDMAN 4040 DRIVER
+P: Harald Welte
+M: [email protected]
+S: Maintained
+
ONSTREAM SCSI TAPE DRIVER
P: Willem Riede
M: [email protected]
diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig
--- a/drivers/char/pcmcia/Kconfig
+++ b/drivers/char/pcmcia/Kconfig
@@ -18,5 +18,18 @@ config SYNCLINK_CS
The module will be called synclinkmp. If you want to do that, say M
here.

+config CARDMAN_4040
+ tristate "Omnikey CardMan 4040 support"
+ depends on PCMCIA
+ help
+ Enable support for the Omnikey CardMan 4040 PCMCIA Smartcard
+ reader.
+
+ This card is basically a USB CCID device connected to a FIFO
+ in I/O space. To use the kernel driver, you will need either the
+ PC/SC ifdhandler provided from the Omnikey homepage
+ (http://www.omnikey.com/), or a current development version of OpenCT
+ (http://www.opensc.org/).
+
endmenu

diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile
--- a/drivers/char/pcmcia/Makefile
+++ b/drivers/char/pcmcia/Makefile
@@ -5,3 +5,4 @@
#

obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
+obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
new file mode 100644
--- /dev/null
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -0,0 +1,835 @@
+/*
+ * A driver for the Omnikey PCMCIA smartcard reader CardMan 4040
+ *
+ * (c) 2000-2004 Omnikey AG (http://www.omnikey.com/)
+ *
+ * (C) 2005 Harald Welte <[email protected]>
+ * - add support for poll()
+ * - driver cleanup
+ * - add waitqueues
+ * - adhere to linux kernel coding style and policies
+ * - support 2.6.13 "new style" pcmcia interface
+ *
+ * The device basically is a USB CCID compliant device that has been
+ * attached to an I/O-Mapped FIFO.
+ *
+ * All rights reserved, Dual BSD/GPL Licensed.
+ */
+
+/* #define PCMCIA_DEBUG 6 */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include "cm4040_cs.h"
+
+
+#ifdef PCMCIA_DEBUG
+#define reader_to_dev(x) (&handle_to_dev(x->link.handle))
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0600);
+#define DEBUGP(n, rdr, x, args...) do { \
+ if (pc_debug >= (n)) \
+ dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \
+ __FUNCTION__, ##args); \
+ } while (0)
+#else
+#define DEBUGP(n, rdr, x, args...)
+#endif
+
+static char *version =
+"OMNIKEY CardMan 4040 v1.1.0gm4 - All bugs added by Harald Welte";
+
+#define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*HZ)
+#define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*HZ)
+#define CCID_DRIVER_MINIMUM_TIMEOUT (3*HZ)
+#define READ_WRITE_BUFFER_SIZE 512
+#define POLL_LOOP_COUNT 1000
+
+/* how often to poll for fifo status change */
+#define POLL_PERIOD msecs_to_jiffies(10)
+
+static void reader_release(dev_link_t *link);
+static void reader_detach(dev_link_t *link);
+
+static int major;
+
+#define BS_READABLE 0x01
+#define BS_WRITABLE 0x02
+
+struct reader_dev {
+ dev_link_t link;
+ dev_node_t node;
+ wait_queue_head_t devq;
+ wait_queue_head_t poll_wait;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ unsigned int buffer_status;
+ unsigned long timeout;
+ unsigned char s_buf[READ_WRITE_BUFFER_SIZE];
+ unsigned char r_buf[READ_WRITE_BUFFER_SIZE];
+ struct timer_list poll_timer;
+};
+
+static dev_info_t dev_info = MODULE_NAME;
+static dev_link_t *dev_table[CM_MAX_DEV] = { NULL, };
+
+#ifndef PCMCIA_DEBUG
+#define xoutb outb
+#define xinb inb
+#else
+static inline void xoutb(unsigned char val, unsigned short port)
+{
+ if (pc_debug >= 7)
+ printk(KERN_DEBUG "outb(val=%.2x,port=%.4x)\n", val, port);
+ outb(val, port);
+}
+
+static inline unsigned char xinb(unsigned short port)
+{
+ unsigned char val;
+
+ val = inb(port);
+ if (pc_debug >= 7)
+ printk(KERN_DEBUG "%.2x=inb(%.4x)\n", val, port);
+ return val;
+}
+#endif
+
+/* poll the device fifo status register. not to be confused with
+ * the poll syscall. */
+static void cm4040_do_poll(unsigned long dummy)
+{
+ struct reader_dev *dev = (struct reader_dev *) dummy;
+ unsigned int obs = xinb(dev->link.io.BasePort1
+ + REG_OFFSET_BUFFER_STATUS);
+
+ if ((obs & BSR_BULK_IN_FULL)) {
+ set_bit(BS_READABLE, &dev->buffer_status);
+ DEBUGP(4, dev, "waking up read_wait\n");
+ wake_up_interruptible(&dev->read_wait);
+ } else
+ clear_bit(BS_READABLE, &dev->buffer_status);
+
+ if (!(obs & BSR_BULK_OUT_FULL)) {
+ set_bit(BS_WRITABLE, &dev->buffer_status);
+ DEBUGP(4, dev, "waking up write_wait\n");
+ wake_up_interruptible(&dev->write_wait);
+ } else
+ clear_bit(BS_WRITABLE, &dev->buffer_status);
+
+ if (dev->buffer_status)
+ wake_up_interruptible(&dev->poll_wait);
+
+ mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
+}
+
+static int wait_for_bulk_out_ready(struct reader_dev *dev)
+{
+ int i, rc;
+ int iobase = dev->link.io.BasePort1;
+
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
+ if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
+ & BSR_BULK_OUT_FULL) == 0) {
+ DEBUGP(4, dev, "BulkOut empty (i=%d)\n", i);
+ return 1;
+ }
+ }
+
+ DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
+ dev->timeout);
+ rc = wait_event_interruptible_timeout(dev->write_wait,
+ test_and_clear_bit(BS_WRITABLE,
+ &dev->buffer_status),
+ dev->timeout);
+
+ if (rc > 0)
+ DEBUGP(4, dev, "woke up: BulkOut empty\n");
+ else if (rc == 0)
+ DEBUGP(4, dev, "woke up: BulkOut full, returning 0 :(\n");
+ else if (rc < 0)
+ DEBUGP(4, dev, "woke up: signal arrived\n");
+
+ return rc;
+}
+
+/* Write to Sync Control Register */
+static int write_sync_reg(unsigned char val, struct reader_dev *dev)
+{
+ int iobase = dev->link.io.BasePort1;
+ int rc;
+
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0)
+ return rc;
+
+ xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL);
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0)
+ return rc;
+
+ return 1;
+}
+
+static int wait_for_bulk_in_ready(struct reader_dev *dev)
+{
+ int i, rc;
+ int iobase = dev->link.io.BasePort1;
+
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
+ if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
+ & BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) {
+ DEBUGP(3, dev, "BulkIn full (i=%d)\n", i);
+ return 1;
+ }
+ }
+
+ DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
+ dev->timeout);
+ rc = wait_event_interruptible_timeout(dev->read_wait,
+ test_and_clear_bit(BS_READABLE,
+ &dev->buffer_status),
+ dev->timeout);
+ if (rc > 0)
+ DEBUGP(4, dev, "woke up: BulkIn full\n");
+ else if (rc == 0)
+ DEBUGP(4, dev, "woke up: BulkIn not full, returning 0 :(\n");
+ else if (rc < 0)
+ DEBUGP(4, dev, "woke up: signal arrived\n");
+
+ return rc;
+}
+
+static ssize_t cm4040_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct reader_dev *dev = filp->private_data;
+ int iobase = dev->link.io.BasePort1;
+ unsigned long bytes_to_read;
+ unsigned long i;
+ unsigned long min_bytes_to_read;
+ int rc;
+ unsigned char uc;
+
+ DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid);
+
+ if (count == 0)
+ return 0;
+
+ if (count < 10)
+ return -EFAULT;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
+ DEBUGP(2, dev, "<- cm4040_read (failure)\n");
+ return -EAGAIN;
+ }
+
+ if ((dev->link.state & DEV_PRESENT)==0)
+ return -ENODEV;
+
+ schedule_timeout(1*HZ);
+ for (i = 0; i < 5; i++) {
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+ dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN);
+#ifdef PCMCIA_DEBUG
+ if (pc_debug >= 6)
+ printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]);
+ }
+ printk("\n");
+#else
+ }
+#endif
+
+ bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]);
+
+ DEBUGP(6, dev, "BytesToRead=%lu\n", bytes_to_read);
+
+ min_bytes_to_read = min(count, bytes_to_read + 5);
+
+ DEBUGP(6, dev, "Min=%lu\n", min_bytes_to_read);
+
+ for (i = 0; i < (min_bytes_to_read-5); i++) {
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+ dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN);
+#ifdef PCMCIA_DEBUG
+ if (pc_debug >= 6)
+ printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]);
+ }
+ printk("\n");
+#else
+ }
+#endif
+
+ *ppos = min_bytes_to_read;
+ if (copy_to_user(buf, dev->r_buf, min_bytes_to_read))
+ return -EFAULT;
+
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+
+ rc = write_sync_reg(SCR_READER_TO_HOST_DONE, dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "write_sync_reg c=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ uc = xinb(iobase + REG_OFFSET_BULK_IN);
+
+ DEBUGP(2, dev, "<- cm4040_read (successfully)\n");
+ return min_bytes_to_read;
+}
+
+static ssize_t cm4040_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct reader_dev *dev = filp->private_data;
+ int iobase = dev->link.io.BasePort1;
+ ssize_t rc;
+ int i;
+ unsigned int bytes_to_write;
+
+ DEBUGP(2, dev, "-> cm4040_write(%s,%d)\n", current->comm, current->pid);
+
+ if (count == 0) {
+ DEBUGP(2, dev, "<- cm4040_write empty read (successfully)\n");
+ return 0;
+ }
+
+ if (count < 5) {
+ DEBUGP(2, dev, "<- cm4040_write buffersize=%Zd < 5\n", count);
+ return -EIO;
+ }
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
+ DEBUGP(4, dev, "<- cm4040_write (failure)\n");
+ return -EAGAIN;
+ }
+
+ if ((dev->link.state & DEV_PRESENT) == 0)
+ return -ENODEV;
+
+ bytes_to_write = count;
+ if (copy_from_user(dev->s_buf, buf, bytes_to_write))
+ return -EFAULT;
+
+ switch (dev->s_buf[0]) {
+ case CMD_PC_TO_RDR_XFRBLOCK:
+ case CMD_PC_TO_RDR_SECURE:
+ case CMD_PC_TO_RDR_TEST_SECURE:
+ case CMD_PC_TO_RDR_OK_SECURE:
+ dev->timeout = CCID_DRIVER_BULK_DEFAULT_TIMEOUT;
+ break;
+
+ case CMD_PC_TO_RDR_ICCPOWERON:
+ dev->timeout = CCID_DRIVER_ASYNC_POWERUP_TIMEOUT;
+ break;
+
+ case CMD_PC_TO_RDR_GETSLOTSTATUS:
+ case CMD_PC_TO_RDR_ICCPOWEROFF:
+ case CMD_PC_TO_RDR_GETPARAMETERS:
+ case CMD_PC_TO_RDR_RESETPARAMETERS:
+ case CMD_PC_TO_RDR_SETPARAMETERS:
+ case CMD_PC_TO_RDR_ESCAPE:
+ case CMD_PC_TO_RDR_ICCCLOCK:
+ default:
+ dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+ break;
+ }
+
+ rc = write_sync_reg(SCR_HOST_TO_READER_START, dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUGP(2, dev, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ DEBUGP(4, dev, "start \n");
+
+ for (i = 0; i < bytes_to_write; i++) {
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2Zx\n",
+ rc);
+ DEBUGP(2, dev, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT);
+ }
+ DEBUGP(4, dev, "end\n");
+
+ rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev);
+
+ if (rc <= 0) {
+ DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUGP(2, dev, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ DEBUGP(2, dev, "<- cm4040_write (successfully)\n");
+ return count;
+}
+
+static unsigned int cm4040_poll(struct file *filp, poll_table *wait)
+{
+ struct reader_dev *dev = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &dev->poll_wait, wait);
+
+ if (test_and_clear_bit(BS_READABLE, &dev->buffer_status))
+ mask |= POLLIN | POLLRDNORM;
+ if (test_and_clear_bit(BS_WRITABLE, &dev->buffer_status))
+ mask |= POLLOUT | POLLWRNORM;
+
+ DEBUGP(2, dev, "<- cm4040_poll(%u)\n", mask);
+
+ return mask;
+}
+
+static int cm4040_open(struct inode *inode, struct file *filp)
+{
+ struct reader_dev *dev;
+ dev_link_t *link;
+ int minor = iminor(inode);
+
+ if (minor >= CM_MAX_DEV)
+ return -ENODEV;
+
+ link = dev_table[minor];
+ if (link == NULL || !(DEV_OK(link)))
+ return -ENODEV;
+
+ if (link->open)
+ return -EBUSY;
+
+ dev = (struct reader_dev *)link->priv;
+ filp->private_data = dev;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
+ return -EAGAIN;
+ }
+
+ link->open = 1;
+
+ dev->poll_timer.data = (unsigned long) dev;
+ mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
+
+ DEBUGP(2, dev, "<- cm4040_open (successfully)\n");
+ return nonseekable_open(inode, filp);
+}
+
+static int cm4040_close(struct inode *inode, struct file *filp)
+{
+ struct reader_dev *dev = filp->private_data;
+ dev_link_t *link;
+ int minor = iminor(inode);
+
+ DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode),
+ iminor(inode));
+
+ if (minor >= CM_MAX_DEV)
+ return -ENODEV;
+
+ link = dev_table[minor];
+ if (link == NULL)
+ return -ENODEV;
+
+ link->open = 0;
+ wake_up(&dev->devq);
+
+ DEBUGP(2, dev, "<- cm4040_close\n");
+ return 0;
+}
+
+static void cm4040_reader_release(dev_link_t *link)
+{
+ struct reader_dev *dev = link->priv;
+
+ DEBUGP(3, dev, "-> cm4040_reader_release\n");
+ while (link->open) {
+ DEBUGP(3, dev, KERN_INFO MODULE_NAME ": delaying release "
+ "until process has terminated\n");
+ wait_event(dev->devq, (link->open == 0));
+ }
+ DEBUGP(3, dev, "<- cm4040_reader_release\n");
+ return;
+}
+
+static void reader_config(dev_link_t *link, int devno)
+{
+ client_handle_t handle;
+ struct reader_dev *dev;
+ tuple_t tuple;
+ cisparse_t parse;
+ config_info_t conf;
+ u_char buf[64];
+ int fail_fn, fail_rc;
+ int rc;
+
+ handle = link->handle;
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+
+ if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) {
+ fail_fn = GetFirstTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) {
+ fail_fn = GetTupleData;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_parse_tuple(handle, &tuple, &parse))
+ != CS_SUCCESS) {
+ fail_fn = ParseTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_get_configuration_info(handle, &conf))
+ != CS_SUCCESS) {
+ fail_fn = GetConfigurationInfo;
+ goto cs_failed;
+ }
+
+ link->state |= DEV_CONFIG;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+ link->conf.Vcc = conf.Vcc;
+
+ link->io.BasePort2 = 0;
+ link->io.NumPorts2 = 0;
+ link->io.Attributes2 = 0;
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ for (rc = pcmcia_get_first_tuple(handle, &tuple);
+ rc == CS_SUCCESS;
+ rc = pcmcia_get_next_tuple(handle, &tuple)) {
+ rc = pcmcia_get_tuple_data(handle, &tuple);
+ if (rc != CS_SUCCESS)
+ continue;
+ rc = pcmcia_parse_tuple(handle, &tuple, &parse);
+ if (rc != CS_SUCCESS)
+ continue;
+
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+
+ if (!parse.cftable_entry.io.nwin)
+ continue;
+
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = parse.cftable_entry.io.flags
+ & CISTPL_IO_LINES_MASK;
+ rc = pcmcia_request_io(handle, &link->io);
+
+ dev_printk(KERN_INFO, &handle_to_dev(handle), "foo");
+ if (rc == CS_SUCCESS)
+ break;
+ else
+ dev_printk(KERN_INFO, &handle_to_dev(handle),
+ "pcmcia_request_io failed 0x%x\n", rc);
+ }
+ if (rc != CS_SUCCESS)
+ goto cs_release;
+
+ link->conf.IntType = 00000002;
+
+ if ((fail_rc = pcmcia_request_configuration(handle,&link->conf))
+ !=CS_SUCCESS) {
+ fail_fn = RequestConfiguration;
+ dev_printk(KERN_INFO, &handle_to_dev(handle),
+ "pcmcia_request_configuration failed 0x%x\n",
+ fail_rc);
+ goto cs_release;
+ }
+
+ dev = link->priv;
+ sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno);
+ dev->node.major = major;
+ dev->node.minor = devno;
+ dev->node.next = NULL;
+ link->dev = &dev->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ DEBUGP(2, dev, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno,
+ link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1);
+ DEBUGP(2, dev, "<- reader_config (succ)\n");
+
+ return;
+
+cs_failed:
+ cs_error(handle, fail_fn, fail_rc);
+cs_release:
+ reader_release(link);
+ link->state &= ~DEV_CONFIG_PENDING;
+}
+
+static int reader_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link;
+ struct reader_dev *dev;
+ int devno;
+
+ link = args->client_data;
+ dev = link->priv;
+ DEBUGP(3, dev, "-> reader_event\n");
+ for (devno = 0; devno < CM_MAX_DEV; devno++) {
+ if (dev_table[devno] == link)
+ break;
+ }
+ if (devno == CM_MAX_DEV)
+ return CS_BAD_ADAPTER;
+
+ switch (event) {
+ case CS_EVENT_CARD_INSERTION:
+ DEBUGP(5, dev, "CS_EVENT_CARD_INSERTION\n");
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ reader_config(link, devno);
+ break;
+ case CS_EVENT_CARD_REMOVAL:
+ DEBUGP(5, dev, "CS_EVENT_CARD_REMOVAL\n");
+ link->state &= ~DEV_PRESENT;
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ DEBUGP(5, dev, "CS_EVENT_PM_SUSPEND "
+ "(fall-through to CS_EVENT_RESET_PHYSICAL)\n");
+ link->state |= DEV_SUSPEND;
+
+ case CS_EVENT_RESET_PHYSICAL:
+ DEBUGP(5, dev, "CS_EVENT_RESET_PHYSICAL\n");
+ if (link->state & DEV_CONFIG) {
+ DEBUGP(5, dev, "ReleaseConfiguration\n");
+ pcmcia_release_configuration(link->handle);
+ }
+ break;
+ case CS_EVENT_PM_RESUME:
+ DEBUGP(5, dev, "CS_EVENT_PM_RESUME "
+ "(fall-through to CS_EVENT_CARD_RESET)\n");
+ link->state &= ~DEV_SUSPEND;
+
+ case CS_EVENT_CARD_RESET:
+ DEBUGP(5, dev, "CS_EVENT_CARD_RESET\n");
+ if ((link->state & DEV_CONFIG)) {
+ DEBUGP(5, dev, "RequestConfiguration\n");
+ pcmcia_request_configuration(link->handle,
+ &link->conf);
+ }
+ break;
+ default:
+ DEBUGP(5, dev, "reader_event: unknown event %.2x\n",
+ event);
+ break;
+ }
+ DEBUGP(3, dev, "<- reader_event\n");
+ return CS_SUCCESS;
+}
+
+static void reader_release(dev_link_t *link)
+{
+ cm4040_reader_release(link->priv);
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+}
+
+static dev_link_t *reader_attach(void)
+{
+ struct reader_dev *dev;
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int i;
+
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i] == NULL)
+ break;
+ }
+
+ if (i == CM_MAX_DEV)
+ return NULL;
+
+ dev = kmalloc(sizeof(struct reader_dev), GFP_KERNEL);
+ if (dev == NULL)
+ return NULL;
+
+ memset(dev, 0, sizeof(struct reader_dev));
+ dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+ dev->buffer_status = 0;
+
+ link = &dev->link;
+ link->priv = dev;
+
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ dev_table[i] = link;
+
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.EventMask=
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ i = pcmcia_register_client(&link->handle, &client_reg);
+ if (i) {
+ cs_error(link->handle, RegisterClient, i);
+ reader_detach(link);
+ return NULL;
+ }
+ init_waitqueue_head(&dev->devq);
+ init_waitqueue_head(&dev->poll_wait);
+ init_waitqueue_head(&dev->read_wait);
+ init_waitqueue_head(&dev->write_wait);
+ init_timer(&dev->poll_timer);
+ dev->poll_timer.function = &cm4040_do_poll;
+
+ return link;
+}
+
+static void reader_detach_by_devno(int devno, dev_link_t *link)
+{
+ struct reader_dev *dev = link->priv;
+
+ if (link->state & DEV_CONFIG) {
+ DEBUGP(5, dev, "device still configured (try to release it)\n");
+ reader_release(link);
+ }
+
+ pcmcia_deregister_client(link->handle);
+ dev_table[devno] = NULL;
+ DEBUGP(5, dev, "freeing dev=%p\n", dev);
+ kfree(dev);
+ return;
+}
+
+static void reader_detach(dev_link_t *link)
+{
+ int i;
+
+ /* find device */
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i] == link)
+ break;
+ }
+ if (i == CM_MAX_DEV)
+ return;
+
+ reader_detach_by_devno(i, link);
+ return;
+}
+
+static struct file_operations reader_fops = {
+ .owner = THIS_MODULE,
+ .read = cm4040_read,
+ .write = cm4040_write,
+ .open = cm4040_open,
+ .release = cm4040_close,
+ .poll = cm4040_poll,
+};
+
+static struct pcmcia_device_id cm4040_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
+ PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
+ 0xE32CDD8C, 0x8F23318B),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, cm4040_ids);
+
+static struct pcmcia_driver reader_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "cm4040_cs",
+ },
+ .attach = reader_attach,
+ .detach = reader_detach,
+ .event = reader_event,
+ .id_table = cm4040_ids,
+};
+
+static int __init cm4040_init(void)
+{
+ printk(KERN_INFO "%s\n", version);
+ pcmcia_register_driver(&reader_driver);
+ major = register_chrdev(0, DEVICE_NAME, &reader_fops);
+ if (major < 0) {
+ printk(KERN_WARNING MODULE_NAME
+ ": could not get major number\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void __exit cm4040_exit(void)
+{
+ int i;
+
+ printk(KERN_INFO MODULE_NAME ": unloading\n");
+ pcmcia_unregister_driver(&reader_driver);
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i])
+ reader_detach_by_devno(i, dev_table[i]);
+ }
+ unregister_chrdev(major, DEVICE_NAME);
+}
+
+module_init(cm4040_init);
+module_exit(cm4040_exit);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/char/pcmcia/cm4040_cs.h b/drivers/char/pcmcia/cm4040_cs.h
new file mode 100644
--- /dev/null
+++ b/drivers/char/pcmcia/cm4040_cs.h
@@ -0,0 +1,47 @@
+#ifndef _CM4040_H_
+#define _CM4040_H_
+
+#define CM_MAX_DEV 4
+
+#define DEVICE_NAME "cmx"
+#define MODULE_NAME "cm4040_cs"
+
+#define REG_OFFSET_BULK_OUT 0
+#define REG_OFFSET_BULK_IN 0
+#define REG_OFFSET_BUFFER_STATUS 1
+#define REG_OFFSET_SYNC_CONTROL 2
+
+#define BSR_BULK_IN_FULL 0x02
+#define BSR_BULK_OUT_FULL 0x01
+
+#define SCR_HOST_TO_READER_START 0x80
+#define SCR_ABORT 0x40
+#define SCR_EN_NOTIFY 0x20
+#define SCR_ACK_NOTIFY 0x10
+#define SCR_READER_TO_HOST_DONE 0x08
+#define SCR_HOST_TO_READER_DONE 0x04
+#define SCR_PULSE_INTERRUPT 0x02
+#define SCR_POWER_DOWN 0x01
+
+
+#define CMD_PC_TO_RDR_ICCPOWERON 0x62
+#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
+#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
+#define CMD_PC_TO_RDR_SECURE 0x69
+#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
+#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
+#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
+#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
+#define CMD_PC_TO_RDR_ESCAPE 0x6B
+#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
+#define CMD_PC_TO_RDR_TEST_SECURE 0x74
+#define CMD_PC_TO_RDR_OK_SECURE 0x89
+
+
+#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
+#define CMD_RDR_TO_PC_DATABLOCK 0x80
+#define CMD_RDR_TO_PC_PARAMETERS 0x82
+#define CMD_RDR_TO_PC_ESCAPE 0x83
+#define CMD_RDR_TO_PC_OK_SECURE 0x89
+
+#endif /* _CM4040_H_ */
--
- Harald Welte <[email protected]> http://gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
(ETSI EN 300 175-7 Ch. A6)


Attachments:
(No filename) (26.57 kB)
(No filename) (189.00 B)
Download all attachments