⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cm4000_cs.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
 /*  * 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 + -