?? cm4040_cs.c
字號(hào):
/* * A driver for the Omnikey PCMCIA smartcard reader CardMan 4040 * * (c) 2000-2004 Omnikey AG (http://www.omnikey.com/) * * (C) 2005 Harald Welte <laforge@gnumonks.org> * - 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...)#endifstatic 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 0x02struct 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 long 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];#ifndef PCMCIA_DEBUG#define xoutb outb#define xinb inb#elsestatic 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 void cm4040_stop_poll(struct reader_dev *dev){ del_timer_sync(&dev->poll_timer);}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; size_t bytes_to_read; unsigned long i; size_t 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; 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;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -