📄 cm4000_cs.c
字号:
/* * A driver for the PCMCIA Smartcard Reader "Omnikey CardMan Mobile 4000" * * cm4000_cs.c support.linux@omnikey.com * * Tue Oct 23 11:32:43 GMT 2001 herp - cleaned up header files * Sun Jan 20 10:11:15 MET 2002 herp - added modversion header files * Thu Nov 14 16:34:11 GMT 2002 mh - added PPS functionality * Tue Nov 19 16:36:27 GMT 2002 mh - added SUSPEND/RESUME functionailty * Wed Jul 28 12:55:01 CEST 2004 mh - kernel 2.6 adjustments * * current version: 2.4.0gm4 * * (C) 2000,2001,2002,2003,2004 Omnikey AG * * (C) 2005 Harald Welte <laforge@gnumonks.org> * - Adhere to Kernel CodingStyle * - Port to 2.6.13 "new" style PCMCIA * - Check for copy_{from,to}_user return values * - Use nonseekable_open() * * All rights reserved. Licensed under dual BSD/GPL license. *//* #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 <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 <linux/cm4000_cs.h>/* #define ATR_CSUM */#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 = "cm4000_cs.c v2.4.0gm5 - All bugs added by Harald Welte";#define T_1SEC (HZ)#define T_10MSEC msecs_to_jiffies(10)#define T_20MSEC msecs_to_jiffies(20)#define T_40MSEC msecs_to_jiffies(40)#define T_50MSEC msecs_to_jiffies(50)#define T_100MSEC msecs_to_jiffies(100)#define T_500MSEC msecs_to_jiffies(500)static void cm4000_detach(dev_link_t *link);static void cm4000_release(dev_link_t *link);static int major; /* major number we get from the kernel *//* note: the first state has to have number 0 always */#define M_FETCH_ATR 0#define M_TIMEOUT_WAIT 1#define M_READ_ATR_LEN 2#define M_READ_ATR 3#define M_ATR_PRESENT 4#define M_BAD_CARD 5#define M_CARDOFF 6#define LOCK_IO 0#define LOCK_MONITOR 1#define IS_AUTOPPS_ACT 6#define IS_PROCBYTE_PRESENT 7#define IS_INVREV 8#define IS_ANY_T0 9#define IS_ANY_T1 10#define IS_ATR_PRESENT 11#define IS_ATR_VALID 12#define IS_CMM_ABSENT 13#define IS_BAD_LENGTH 14#define IS_BAD_CSUM 15#define IS_BAD_CARD 16#define REG_FLAGS0(x) (x + 0)#define REG_FLAGS1(x) (x + 1)#define REG_NUM_BYTES(x) (x + 2)#define REG_BUF_ADDR(x) (x + 3)#define REG_BUF_DATA(x) (x + 4)#define REG_NUM_SEND(x) (x + 5)#define REG_BAUDRATE(x) (x + 6)#define REG_STOPBITS(x) (x + 7)struct cm4000_dev { dev_link_t link; /* pcmcia link */ dev_node_t node; /* OS node (major,minor) */ unsigned char atr[MAX_ATR]; unsigned char rbuf[512]; unsigned char sbuf[512]; wait_queue_head_t devq; /* when removing cardman must not be zeroed! */ wait_queue_head_t ioq; /* if IO is locked, wait on this Q */ wait_queue_head_t atrq; /* wait for ATR valid */ wait_queue_head_t readq; /* used by write to wake blk.read */ /* warning: do not move this fields. * initialising to zero depends on it - see ZERO_DEV below. */ unsigned char atr_csum; unsigned char atr_len_retry; unsigned short atr_len; unsigned short rlen; /* bytes avail. after write */ unsigned short rpos; /* latest read pos. write zeroes */ unsigned char procbyte; /* T=0 procedure byte */ unsigned char mstate; /* state of card monitor */ unsigned char cwarn; /* slow down warning */ unsigned char flags0; /* cardman IO-flags 0 */ unsigned char flags1; /* cardman IO-flags 1 */ unsigned int mdelay; /* variable monitor speeds, in jiffies */ unsigned int baudv; /* baud value for speed */ unsigned char ta1; unsigned char proto; /* T=0, T=1, ... */ unsigned long flags; /* lock+flags (MONITOR,IO,ATR) * for concurrent access */ unsigned char pts[4]; struct timer_list timer; /* used to keep monitor running */ int monitor_running;};#define ZERO_DEV(dev) \ memset(&dev->atr_csum,0, \ sizeof(struct cm4000_dev) - \ /*link*/ sizeof(dev_link_t) - \ /*node*/ sizeof(dev_node_t) - \ /*atr*/ MAX_ATR*sizeof(char) - \ /*rbuf*/ 512*sizeof(char) - \ /*sbuf*/ 512*sizeof(char) - \ /*queue*/ 4*sizeof(wait_queue_head_t))static dev_info_t dev_info = MODULE_NAME;static dev_link_t *dev_table[CM4000_MAX_DEV];/* This table doesn't use spaces after the comma between fields and thus * violates CodingStyle. However, I don't really think wrapping it around will * make it any clearer to read -HW */static unsigned char fi_di_table[10][14] = {/*FI 00 01 02 03 04 05 06 07 08 09 10 11 12 13 *//*DI *//* 0 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},/* 1 */ {0x01,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x91,0x11,0x11,0x11,0x11},/* 2 */ {0x02,0x12,0x22,0x32,0x11,0x11,0x11,0x11,0x11,0x92,0xA2,0xB2,0x11,0x11},/* 3 */ {0x03,0x13,0x23,0x33,0x43,0x53,0x63,0x11,0x11,0x93,0xA3,0xB3,0xC3,0xD3},/* 4 */ {0x04,0x14,0x24,0x34,0x44,0x54,0x64,0x11,0x11,0x94,0xA4,0xB4,0xC4,0xD4},/* 5 */ {0x00,0x15,0x25,0x35,0x45,0x55,0x65,0x11,0x11,0x95,0xA5,0xB5,0xC5,0xD5},/* 6 */ {0x06,0x16,0x26,0x36,0x46,0x56,0x66,0x11,0x11,0x96,0xA6,0xB6,0xC6,0xD6},/* 7 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},/* 8 */ {0x08,0x11,0x28,0x38,0x48,0x58,0x68,0x11,0x11,0x98,0xA8,0xB8,0xC8,0xD8},/* 9 */ {0x09,0x19,0x29,0x39,0x49,0x59,0x69,0x11,0x11,0x99,0xA9,0xB9,0xC9,0xD9}};#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#define b_0000 15#define b_0001 14#define b_0010 13#define b_0011 12#define b_0100 11#define b_0101 10#define b_0110 9#define b_0111 8#define b_1000 7#define b_1001 6#define b_1010 5#define b_1011 4#define b_1100 3#define b_1101 2#define b_1110 1#define b_1111 0static unsigned char irtab[16] = { b_0000, b_1000, b_0100, b_1100, b_0010, b_1010, b_0110, b_1110, b_0001, b_1001, b_0101, b_1101, b_0011, b_1011, b_0111, b_1111};static void str_invert_revert(unsigned char *b, int len){ int i; for (i = 0; i < len; i++) b[i] = (irtab[b[i] & 0x0f] << 4) | irtab[b[i] >> 4];}static unsigned char invert_revert(unsigned char ch){ return (irtab[ch & 0x0f] << 4) | irtab[ch >> 4];}#define ATRLENCK(dev,pos) \ if (pos>=dev->atr_len || pos>=MAX_ATR) \ goto return_0;static unsigned int calc_baudv(unsigned char fidi){ unsigned int wcrcf, wbrcf, fi_rfu, di_rfu; fi_rfu = 372; di_rfu = 1; /* FI */ switch ((fidi >> 4) & 0x0F) { case 0x00: wcrcf = 372; break; case 0x01: wcrcf = 372; break; case 0x02: wcrcf = 558; break; case 0x03: wcrcf = 744; break; case 0x04: wcrcf = 1116; break; case 0x05: wcrcf = 1488; break; case 0x06: wcrcf = 1860; break; case 0x07: wcrcf = fi_rfu; break; case 0x08: wcrcf = fi_rfu; break; case 0x09: wcrcf = 512; break; case 0x0A: wcrcf = 768; break; case 0x0B: wcrcf = 1024; break; case 0x0C: wcrcf = 1536; break; case 0x0D: wcrcf = 2048; break; default: wcrcf = fi_rfu; break; } /* DI */ switch (fidi & 0x0F) { case 0x00: wbrcf = di_rfu; break; case 0x01: wbrcf = 1; break; case 0x02: wbrcf = 2; break; case 0x03: wbrcf = 4; break; case 0x04: wbrcf = 8; break; case 0x05: wbrcf = 16; break; case 0x06: wbrcf = 32; break; case 0x07: wbrcf = di_rfu; break; case 0x08: wbrcf = 12; break; case 0x09: wbrcf = 20; break; default: wbrcf = di_rfu; break; } return (wcrcf / wbrcf);}static unsigned short io_read_num_rec_bytes(ioaddr_t iobase, unsigned short *s){ unsigned short tmp; tmp = *s = 0; do { *s = tmp; tmp = inb(REG_NUM_BYTES(iobase)) | (inb(REG_FLAGS0(iobase)) & 4 ? 0x100 : 0); } while (tmp != *s); return *s;}static int parse_atr(struct cm4000_dev *dev){ unsigned char any_t1, any_t0; unsigned char ch, ifno; int ix, done; DEBUGP(3, dev, "-> parse_atr: dev->atr_len = %i\n", dev->atr_len); if (dev->atr_len < 3) { DEBUGP(5, dev, "parse_atr: atr_len < 3\n"); return 0; } if (dev->atr[0] == 0x3f) set_bit(IS_INVREV, &dev->flags); else clear_bit(IS_INVREV, &dev->flags); ix = 1; ifno = 1; ch = dev->atr[1]; dev->proto = 0; /* XXX PROTO */ any_t1 = any_t0 = done = 0; dev->ta1 = 0x11; /* defaults to 9600 baud */ do { if (ifno == 1 && (ch & 0x10)) { /* read first interface byte and TA1 is present */ dev->ta1 = dev->atr[2]; DEBUGP(5, dev, "Card says FiDi is 0x%.2x\n", dev->ta1); ifno++; } else if ((ifno == 2) && (ch & 0x10)) { /* TA(2) */ dev->ta1 = 0x11; ifno++; } DEBUGP(5, dev, "Yi=%.2x\n", ch & 0xf0); ix += ((ch & 0x10) >> 4) /* no of int.face chars */ +((ch & 0x20) >> 5) + ((ch & 0x40) >> 6) + ((ch & 0x80) >> 7); /* ATRLENCK(dev,ix); */ if (ch & 0x80) { /* TDi */ ch = dev->atr[ix]; if ((ch & 0x0f)) { any_t1 = 1; DEBUGP(5, dev, "card is capable of T=1\n"); } else { any_t0 = 1; DEBUGP(5, dev, "card is capable of T=0\n"); } } else done = 1; } while (!done); DEBUGP(5, dev, "ix=%d noHist=%d any_t1=%d\n", ix, dev->atr[1] & 15, any_t1); if (ix + 1 + (dev->atr[1] & 0x0f) + any_t1 != dev->atr_len) { DEBUGP(5, dev, "length error\n"); return 0; } if (any_t0) set_bit(IS_ANY_T0, &dev->flags); if (any_t1) { /* compute csum */ dev->atr_csum = 0;#ifdef ATR_CSUM for (i = 1; i < dev->atr_len; i++) dev->atr_csum ^= dev->atr[i]; if (dev->atr_csum) { set_bit(IS_BAD_CSUM, &dev->flags); DEBUGP(5, dev, "bad checksum\n"); goto return_0; }#endif if (any_t0 == 0) dev->proto = 1; /* XXX PROTO */ set_bit(IS_ANY_T1, &dev->flags); } return 1;}struct card_fixup { char atr[12]; u_int8_t atr_len; u_int8_t stopbits;};static struct card_fixup card_fixups[] = { { /* ACOS */ .atr = { 0x3b, 0xb3, 0x11, 0x00, 0x00, 0x41, 0x01 }, .atr_len = 7, .stopbits = 0x03, }, { /* Motorola */ .atr = {0x3b, 0x76, 0x13, 0x00, 0x00, 0x80, 0x62, 0x07, 0x41, 0x81, 0x81 }, .atr_len = 11, .stopbits = 0x04, },};static void set_cardparameter(struct cm4000_dev *dev){ int i; ioaddr_t iobase = dev->link.io.BasePort1; u_int8_t stopbits = 0x02; /* ISO default */ DEBUGP(3, dev, "-> set_cardparameter\n"); dev->flags1 = dev->flags1 | (((dev->baudv - 1) & 0x0100) >> 8); xoutb(dev->flags1, REG_FLAGS1(iobase)); DEBUGP(5, dev, "flags1 = 0x%02x\n", dev->flags1); /* set baudrate */ xoutb((unsigned char)((dev->baudv - 1) & 0xFF), REG_BAUDRATE(iobase)); DEBUGP(5, dev, "baudv = %i -> write 0x%02x\n", dev->baudv, ((dev->baudv - 1) & 0xFF)); /* set stopbits */ for (i = 0; i < ARRAY_SIZE(card_fixups); i++) { if (!memcmp(dev->atr, card_fixups[i].atr, card_fixups[i].atr_len)) stopbits = card_fixups[i].stopbits; } xoutb(stopbits, REG_STOPBITS(iobase)); DEBUGP(3, dev, "<- set_cardparameter\n");}static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq){ unsigned long tmp, i; unsigned short num_bytes_read; unsigned char pts_reply[4]; ssize_t rc; ioaddr_t iobase = dev->link.io.BasePort1; rc = 0; DEBUGP(3, dev, "-> set_protocol\n"); DEBUGP(5, dev, "ptsreq->Protocol = 0x%.8x, ptsreq->Flags=0x%.8x, " "ptsreq->pts1=0x%.2x, ptsreq->pts2=0x%.2x, " "ptsreq->pts3=0x%.2x\n", (unsigned int)ptsreq->protocol, (unsigned int)ptsreq->flags, ptsreq->pts1, ptsreq->pts2, ptsreq->pts3); /* Fill PTS structure */ dev->pts[0] = 0xff; dev->pts[1] = 0x00; tmp = ptsreq->protocol; while ((tmp = (tmp >> 1)) > 0) dev->pts[1]++; dev->proto = dev->pts[1]; /* Set new protocol */ dev->pts[1] = (0x01 << 4) | (dev->pts[1]); /* Correct Fi/Di according to CM4000 Fi/Di table */ DEBUGP(5, dev, "Ta(1) from ATR is 0x%.2x\n", dev->ta1); /* set Fi/Di according to ATR TA(1) */ dev->pts[2] = fi_di_table[dev->ta1 & 0x0F][(dev->ta1 >> 4) & 0x0F]; /* Calculate PCK character */ dev->pts[3] = dev->pts[0] ^ dev->pts[1] ^ dev->pts[2]; DEBUGP(5, dev, "pts0=%.2x, pts1=%.2x, pts2=%.2x, pts3=%.2x\n", dev->pts[0], dev->pts[1], dev->pts[2], dev->pts[3]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -