diff options
author | Harald Welte <laforge@gnumonks.org> | 2005-11-13 19:06:26 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-13 21:14:10 -0500 |
commit | 77c44ab1d8e9da31bf927223e1579b44f772b579 (patch) | |
tree | 41d3ca7805c13e8f7b1c0f4d9f2c6e4e5f8b09c5 /drivers | |
parent | bca73e4bf8563d83f7856164caa44d5f42e44cca (diff) |
[PATCH] New Omnikey Cardman 4040 driver
Add new Omnikey Cardman 4040 smartcard reader driver
Signed-off-by: Harald Welte <laforge@gnumonks.org>
Cc: Dominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/pcmcia/Kconfig | 13 | ||||
-rw-r--r-- | drivers/char/pcmcia/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/pcmcia/cm4040_cs.c | 841 | ||||
-rw-r--r-- | drivers/char/pcmcia/cm4040_cs.h | 47 |
4 files changed, 902 insertions, 0 deletions
diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig index d22bfdc13563..e8d41f36635d 100644 --- a/drivers/char/pcmcia/Kconfig +++ b/drivers/char/pcmcia/Kconfig | |||
@@ -18,5 +18,18 @@ config SYNCLINK_CS | |||
18 | The module will be called synclinkmp. If you want to do that, say M | 18 | The module will be called synclinkmp. If you want to do that, say M |
19 | here. | 19 | here. |
20 | 20 | ||
21 | config CARDMAN_4040 | ||
22 | tristate "Omnikey CardMan 4040 support" | ||
23 | depends on PCMCIA | ||
24 | help | ||
25 | Enable support for the Omnikey CardMan 4040 PCMCIA Smartcard | ||
26 | reader. | ||
27 | |||
28 | This card is basically a USB CCID device connected to a FIFO | ||
29 | in I/O space. To use the kernel driver, you will need either the | ||
30 | PC/SC ifdhandler provided from the Omnikey homepage | ||
31 | (http://www.omnikey.com/), or a current development version of OpenCT | ||
32 | (http://www.opensc.org/). | ||
33 | |||
21 | endmenu | 34 | endmenu |
22 | 35 | ||
diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile index 1fcd4c591958..35cd766a0a17 100644 --- a/drivers/char/pcmcia/Makefile +++ b/drivers/char/pcmcia/Makefile | |||
@@ -5,3 +5,4 @@ | |||
5 | # | 5 | # |
6 | 6 | ||
7 | obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o | 7 | obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o |
8 | 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 index 000000000000..4c698d908ffa --- /dev/null +++ b/drivers/char/pcmcia/cm4040_cs.c | |||
@@ -0,0 +1,841 @@ | |||
1 | /* | ||
2 | * A driver for the Omnikey PCMCIA smartcard reader CardMan 4040 | ||
3 | * | ||
4 | * (c) 2000-2004 Omnikey AG (http://www.omnikey.com/) | ||
5 | * | ||
6 | * (C) 2005 Harald Welte <laforge@gnumonks.org> | ||
7 | * - add support for poll() | ||
8 | * - driver cleanup | ||
9 | * - add waitqueues | ||
10 | * - adhere to linux kernel coding style and policies | ||
11 | * - support 2.6.13 "new style" pcmcia interface | ||
12 | * | ||
13 | * The device basically is a USB CCID compliant device that has been | ||
14 | * attached to an I/O-Mapped FIFO. | ||
15 | * | ||
16 | * All rights reserved, Dual BSD/GPL Licensed. | ||
17 | */ | ||
18 | |||
19 | /* #define PCMCIA_DEBUG 6 */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/poll.h> | ||
28 | #include <linux/wait.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | #include <asm/io.h> | ||
31 | |||
32 | #include <pcmcia/cs_types.h> | ||
33 | #include <pcmcia/cs.h> | ||
34 | #include <pcmcia/cistpl.h> | ||
35 | #include <pcmcia/cisreg.h> | ||
36 | #include <pcmcia/ciscode.h> | ||
37 | #include <pcmcia/ds.h> | ||
38 | |||
39 | #include "cm4040_cs.h" | ||
40 | |||
41 | |||
42 | #ifdef PCMCIA_DEBUG | ||
43 | #define reader_to_dev(x) (&handle_to_dev(x->link.handle)) | ||
44 | static int pc_debug = PCMCIA_DEBUG; | ||
45 | module_param(pc_debug, int, 0600); | ||
46 | #define DEBUGP(n, rdr, x, args...) do { \ | ||
47 | if (pc_debug >= (n)) \ | ||
48 | dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \ | ||
49 | __FUNCTION__ , ##args); \ | ||
50 | } while (0) | ||
51 | #else | ||
52 | #define DEBUGP(n, rdr, x, args...) | ||
53 | #endif | ||
54 | |||
55 | static char *version = | ||
56 | "OMNIKEY CardMan 4040 v1.1.0gm4 - All bugs added by Harald Welte"; | ||
57 | |||
58 | #define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*HZ) | ||
59 | #define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*HZ) | ||
60 | #define CCID_DRIVER_MINIMUM_TIMEOUT (3*HZ) | ||
61 | #define READ_WRITE_BUFFER_SIZE 512 | ||
62 | #define POLL_LOOP_COUNT 1000 | ||
63 | |||
64 | /* how often to poll for fifo status change */ | ||
65 | #define POLL_PERIOD msecs_to_jiffies(10) | ||
66 | |||
67 | static void reader_release(dev_link_t *link); | ||
68 | static void reader_detach(dev_link_t *link); | ||
69 | |||
70 | static int major; | ||
71 | |||
72 | #define BS_READABLE 0x01 | ||
73 | #define BS_WRITABLE 0x02 | ||
74 | |||
75 | struct reader_dev { | ||
76 | dev_link_t link; | ||
77 | dev_node_t node; | ||
78 | wait_queue_head_t devq; | ||
79 | wait_queue_head_t poll_wait; | ||
80 | wait_queue_head_t read_wait; | ||
81 | wait_queue_head_t write_wait; | ||
82 | unsigned long buffer_status; | ||
83 | unsigned long timeout; | ||
84 | unsigned char s_buf[READ_WRITE_BUFFER_SIZE]; | ||
85 | unsigned char r_buf[READ_WRITE_BUFFER_SIZE]; | ||
86 | struct timer_list poll_timer; | ||
87 | }; | ||
88 | |||
89 | static dev_info_t dev_info = MODULE_NAME; | ||
90 | static dev_link_t *dev_table[CM_MAX_DEV]; | ||
91 | |||
92 | #ifndef PCMCIA_DEBUG | ||
93 | #define xoutb outb | ||
94 | #define xinb inb | ||
95 | #else | ||
96 | static inline void xoutb(unsigned char val, unsigned short port) | ||
97 | { | ||
98 | if (pc_debug >= 7) | ||
99 | printk(KERN_DEBUG "outb(val=%.2x,port=%.4x)\n", val, port); | ||
100 | outb(val, port); | ||
101 | } | ||
102 | |||
103 | static inline unsigned char xinb(unsigned short port) | ||
104 | { | ||
105 | unsigned char val; | ||
106 | |||
107 | val = inb(port); | ||
108 | if (pc_debug >= 7) | ||
109 | printk(KERN_DEBUG "%.2x=inb(%.4x)\n", val, port); | ||
110 | return val; | ||
111 | } | ||
112 | #endif | ||
113 | |||
114 | /* poll the device fifo status register. not to be confused with | ||
115 | * the poll syscall. */ | ||
116 | static void cm4040_do_poll(unsigned long dummy) | ||
117 | { | ||
118 | struct reader_dev *dev = (struct reader_dev *) dummy; | ||
119 | unsigned int obs = xinb(dev->link.io.BasePort1 | ||
120 | + REG_OFFSET_BUFFER_STATUS); | ||
121 | |||
122 | if ((obs & BSR_BULK_IN_FULL)) { | ||
123 | set_bit(BS_READABLE, &dev->buffer_status); | ||
124 | DEBUGP(4, dev, "waking up read_wait\n"); | ||
125 | wake_up_interruptible(&dev->read_wait); | ||
126 | } else | ||
127 | clear_bit(BS_READABLE, &dev->buffer_status); | ||
128 | |||
129 | if (!(obs & BSR_BULK_OUT_FULL)) { | ||
130 | set_bit(BS_WRITABLE, &dev->buffer_status); | ||
131 | DEBUGP(4, dev, "waking up write_wait\n"); | ||
132 | wake_up_interruptible(&dev->write_wait); | ||
133 | } else | ||
134 | clear_bit(BS_WRITABLE, &dev->buffer_status); | ||
135 | |||
136 | if (dev->buffer_status) | ||
137 | wake_up_interruptible(&dev->poll_wait); | ||
138 | |||
139 | mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD); | ||
140 | } | ||
141 | |||
142 | static void cm4040_stop_poll(struct reader_dev *dev) | ||
143 | { | ||
144 | del_timer_sync(&dev->poll_timer); | ||
145 | } | ||
146 | |||
147 | static int wait_for_bulk_out_ready(struct reader_dev *dev) | ||
148 | { | ||
149 | int i, rc; | ||
150 | int iobase = dev->link.io.BasePort1; | ||
151 | |||
152 | for (i = 0; i < POLL_LOOP_COUNT; i++) { | ||
153 | if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS) | ||
154 | & BSR_BULK_OUT_FULL) == 0) { | ||
155 | DEBUGP(4, dev, "BulkOut empty (i=%d)\n", i); | ||
156 | return 1; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n", | ||
161 | dev->timeout); | ||
162 | rc = wait_event_interruptible_timeout(dev->write_wait, | ||
163 | test_and_clear_bit(BS_WRITABLE, | ||
164 | &dev->buffer_status), | ||
165 | dev->timeout); | ||
166 | |||
167 | if (rc > 0) | ||
168 | DEBUGP(4, dev, "woke up: BulkOut empty\n"); | ||
169 | else if (rc == 0) | ||
170 | DEBUGP(4, dev, "woke up: BulkOut full, returning 0 :(\n"); | ||
171 | else if (rc < 0) | ||
172 | DEBUGP(4, dev, "woke up: signal arrived\n"); | ||
173 | |||
174 | return rc; | ||
175 | } | ||
176 | |||
177 | /* Write to Sync Control Register */ | ||
178 | static int write_sync_reg(unsigned char val, struct reader_dev *dev) | ||
179 | { | ||
180 | int iobase = dev->link.io.BasePort1; | ||
181 | int rc; | ||
182 | |||
183 | rc = wait_for_bulk_out_ready(dev); | ||
184 | if (rc <= 0) | ||
185 | return rc; | ||
186 | |||
187 | xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL); | ||
188 | rc = wait_for_bulk_out_ready(dev); | ||
189 | if (rc <= 0) | ||
190 | return rc; | ||
191 | |||
192 | return 1; | ||
193 | } | ||
194 | |||
195 | static int wait_for_bulk_in_ready(struct reader_dev *dev) | ||
196 | { | ||
197 | int i, rc; | ||
198 | int iobase = dev->link.io.BasePort1; | ||
199 | |||
200 | for (i = 0; i < POLL_LOOP_COUNT; i++) { | ||
201 | if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS) | ||
202 | & BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) { | ||
203 | DEBUGP(3, dev, "BulkIn full (i=%d)\n", i); | ||
204 | return 1; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n", | ||
209 | dev->timeout); | ||
210 | rc = wait_event_interruptible_timeout(dev->read_wait, | ||
211 | test_and_clear_bit(BS_READABLE, | ||
212 | &dev->buffer_status), | ||
213 | dev->timeout); | ||
214 | if (rc > 0) | ||
215 | DEBUGP(4, dev, "woke up: BulkIn full\n"); | ||
216 | else if (rc == 0) | ||
217 | DEBUGP(4, dev, "woke up: BulkIn not full, returning 0 :(\n"); | ||
218 | else if (rc < 0) | ||
219 | DEBUGP(4, dev, "woke up: signal arrived\n"); | ||
220 | |||
221 | return rc; | ||
222 | } | ||
223 | |||
224 | static ssize_t cm4040_read(struct file *filp, char __user *buf, | ||
225 | size_t count, loff_t *ppos) | ||
226 | { | ||
227 | struct reader_dev *dev = filp->private_data; | ||
228 | int iobase = dev->link.io.BasePort1; | ||
229 | size_t bytes_to_read; | ||
230 | unsigned long i; | ||
231 | size_t min_bytes_to_read; | ||
232 | int rc; | ||
233 | unsigned char uc; | ||
234 | |||
235 | DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid); | ||
236 | |||
237 | if (count == 0) | ||
238 | return 0; | ||
239 | |||
240 | if (count < 10) | ||
241 | return -EFAULT; | ||
242 | |||
243 | if (filp->f_flags & O_NONBLOCK) { | ||
244 | DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n"); | ||
245 | DEBUGP(2, dev, "<- cm4040_read (failure)\n"); | ||
246 | return -EAGAIN; | ||
247 | } | ||
248 | |||
249 | if ((dev->link.state & DEV_PRESENT)==0) | ||
250 | return -ENODEV; | ||
251 | |||
252 | for (i = 0; i < 5; i++) { | ||
253 | rc = wait_for_bulk_in_ready(dev); | ||
254 | if (rc <= 0) { | ||
255 | DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc); | ||
256 | DEBUGP(2, dev, "<- cm4040_read (failed)\n"); | ||
257 | if (rc == -ERESTARTSYS) | ||
258 | return rc; | ||
259 | return -EIO; | ||
260 | } | ||
261 | dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN); | ||
262 | #ifdef PCMCIA_DEBUG | ||
263 | if (pc_debug >= 6) | ||
264 | printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]); | ||
265 | } | ||
266 | printk("\n"); | ||
267 | #else | ||
268 | } | ||
269 | #endif | ||
270 | |||
271 | bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]); | ||
272 | |||
273 | DEBUGP(6, dev, "BytesToRead=%lu\n", bytes_to_read); | ||
274 | |||
275 | min_bytes_to_read = min(count, bytes_to_read + 5); | ||
276 | |||
277 | DEBUGP(6, dev, "Min=%lu\n", min_bytes_to_read); | ||
278 | |||
279 | for (i = 0; i < (min_bytes_to_read-5); i++) { | ||
280 | rc = wait_for_bulk_in_ready(dev); | ||
281 | if (rc <= 0) { | ||
282 | DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc); | ||
283 | DEBUGP(2, dev, "<- cm4040_read (failed)\n"); | ||
284 | if (rc == -ERESTARTSYS) | ||
285 | return rc; | ||
286 | return -EIO; | ||
287 | } | ||
288 | dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN); | ||
289 | #ifdef PCMCIA_DEBUG | ||
290 | if (pc_debug >= 6) | ||
291 | printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]); | ||
292 | } | ||
293 | printk("\n"); | ||
294 | #else | ||
295 | } | ||
296 | #endif | ||
297 | |||
298 | *ppos = min_bytes_to_read; | ||
299 | if (copy_to_user(buf, dev->r_buf, min_bytes_to_read)) | ||
300 | return -EFAULT; | ||
301 | |||
302 | rc = wait_for_bulk_in_ready(dev); | ||
303 | if (rc <= 0) { | ||
304 | DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc); | ||
305 | DEBUGP(2, dev, "<- cm4040_read (failed)\n"); | ||
306 | if (rc == -ERESTARTSYS) | ||
307 | return rc; | ||
308 | return -EIO; | ||
309 | } | ||
310 | |||
311 | rc = write_sync_reg(SCR_READER_TO_HOST_DONE, dev); | ||
312 | if (rc <= 0) { | ||
313 | DEBUGP(5, dev, "write_sync_reg c=%.2x\n", rc); | ||
314 | DEBUGP(2, dev, "<- cm4040_read (failed)\n"); | ||
315 | if (rc == -ERESTARTSYS) | ||
316 | return rc; | ||
317 | else | ||
318 | return -EIO; | ||
319 | } | ||
320 | |||
321 | uc = xinb(iobase + REG_OFFSET_BULK_IN); | ||
322 | |||
323 | DEBUGP(2, dev, "<- cm4040_read (successfully)\n"); | ||
324 | return min_bytes_to_read; | ||
325 | } | ||
326 | |||
327 | static ssize_t cm4040_write(struct file *filp, const char __user *buf, | ||
328 | size_t count, loff_t *ppos) | ||
329 | { | ||
330 | struct reader_dev *dev = filp->private_data; | ||
331 | int iobase = dev->link.io.BasePort1; | ||
332 | ssize_t rc; | ||
333 | int i; | ||
334 | unsigned int bytes_to_write; | ||
335 | |||
336 | DEBUGP(2, dev, "-> cm4040_write(%s,%d)\n", current->comm, current->pid); | ||
337 | |||
338 | if (count == 0) { | ||
339 | DEBUGP(2, dev, "<- cm4040_write empty read (successfully)\n"); | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | if (count < 5) { | ||
344 | DEBUGP(2, dev, "<- cm4040_write buffersize=%Zd < 5\n", count); | ||
345 | return -EIO; | ||
346 | } | ||
347 | |||
348 | if (filp->f_flags & O_NONBLOCK) { | ||
349 | DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n"); | ||
350 | DEBUGP(4, dev, "<- cm4040_write (failure)\n"); | ||
351 | return -EAGAIN; | ||
352 | } | ||
353 | |||
354 | if ((dev->link.state & DEV_PRESENT) == 0) | ||
355 | return -ENODEV; | ||
356 | |||
357 | bytes_to_write = count; | ||
358 | if (copy_from_user(dev->s_buf, buf, bytes_to_write)) | ||
359 | return -EFAULT; | ||
360 | |||
361 | switch (dev->s_buf[0]) { | ||
362 | case CMD_PC_TO_RDR_XFRBLOCK: | ||
363 | case CMD_PC_TO_RDR_SECURE: | ||
364 | case CMD_PC_TO_RDR_TEST_SECURE: | ||
365 | case CMD_PC_TO_RDR_OK_SECURE: | ||
366 | dev->timeout = CCID_DRIVER_BULK_DEFAULT_TIMEOUT; | ||
367 | break; | ||
368 | |||
369 | case CMD_PC_TO_RDR_ICCPOWERON: | ||
370 | dev->timeout = CCID_DRIVER_ASYNC_POWERUP_TIMEOUT; | ||
371 | break; | ||
372 | |||
373 | case CMD_PC_TO_RDR_GETSLOTSTATUS: | ||
374 | case CMD_PC_TO_RDR_ICCPOWEROFF: | ||
375 | case CMD_PC_TO_RDR_GETPARAMETERS: | ||
376 | case CMD_PC_TO_RDR_RESETPARAMETERS: | ||
377 | case CMD_PC_TO_RDR_SETPARAMETERS: | ||
378 | case CMD_PC_TO_RDR_ESCAPE: | ||
379 | case CMD_PC_TO_RDR_ICCCLOCK: | ||
380 | default: | ||
381 | dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT; | ||
382 | break; | ||
383 | } | ||
384 | |||
385 | rc = write_sync_reg(SCR_HOST_TO_READER_START, dev); | ||
386 | if (rc <= 0) { | ||
387 | DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc); | ||
388 | DEBUGP(2, dev, "<- cm4040_write (failed)\n"); | ||
389 | if (rc == -ERESTARTSYS) | ||
390 | return rc; | ||
391 | else | ||
392 | return -EIO; | ||
393 | } | ||
394 | |||
395 | DEBUGP(4, dev, "start \n"); | ||
396 | |||
397 | for (i = 0; i < bytes_to_write; i++) { | ||
398 | rc = wait_for_bulk_out_ready(dev); | ||
399 | if (rc <= 0) { | ||
400 | DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2Zx\n", | ||
401 | rc); | ||
402 | DEBUGP(2, dev, "<- cm4040_write (failed)\n"); | ||
403 | if (rc == -ERESTARTSYS) | ||
404 | return rc; | ||
405 | else | ||
406 | return -EIO; | ||
407 | } | ||
408 | |||
409 | xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT); | ||
410 | } | ||
411 | DEBUGP(4, dev, "end\n"); | ||
412 | |||
413 | rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev); | ||
414 | |||
415 | if (rc <= 0) { | ||
416 | DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc); | ||
417 | DEBUGP(2, dev, "<- cm4040_write (failed)\n"); | ||
418 | if (rc == -ERESTARTSYS) | ||
419 | return rc; | ||
420 | else | ||
421 | return -EIO; | ||
422 | } | ||
423 | |||
424 | DEBUGP(2, dev, "<- cm4040_write (successfully)\n"); | ||
425 | return count; | ||
426 | } | ||
427 | |||
428 | static unsigned int cm4040_poll(struct file *filp, poll_table *wait) | ||
429 | { | ||
430 | struct reader_dev *dev = filp->private_data; | ||
431 | unsigned int mask = 0; | ||
432 | |||
433 | poll_wait(filp, &dev->poll_wait, wait); | ||
434 | |||
435 | if (test_and_clear_bit(BS_READABLE, &dev->buffer_status)) | ||
436 | mask |= POLLIN | POLLRDNORM; | ||
437 | if (test_and_clear_bit(BS_WRITABLE, &dev->buffer_status)) | ||
438 | mask |= POLLOUT | POLLWRNORM; | ||
439 | |||
440 | DEBUGP(2, dev, "<- cm4040_poll(%u)\n", mask); | ||
441 | |||
442 | return mask; | ||
443 | } | ||
444 | |||
445 | static int cm4040_open(struct inode *inode, struct file *filp) | ||
446 | { | ||
447 | struct reader_dev *dev; | ||
448 | dev_link_t *link; | ||
449 | int minor = iminor(inode); | ||
450 | |||
451 | if (minor >= CM_MAX_DEV) | ||
452 | return -ENODEV; | ||
453 | |||
454 | link = dev_table[minor]; | ||
455 | if (link == NULL || !(DEV_OK(link))) | ||
456 | return -ENODEV; | ||
457 | |||
458 | if (link->open) | ||
459 | return -EBUSY; | ||
460 | |||
461 | dev = link->priv; | ||
462 | filp->private_data = dev; | ||
463 | |||
464 | if (filp->f_flags & O_NONBLOCK) { | ||
465 | DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n"); | ||
466 | return -EAGAIN; | ||
467 | } | ||
468 | |||
469 | link->open = 1; | ||
470 | |||
471 | dev->poll_timer.data = (unsigned long) dev; | ||
472 | mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD); | ||
473 | |||
474 | DEBUGP(2, dev, "<- cm4040_open (successfully)\n"); | ||
475 | return nonseekable_open(inode, filp); | ||
476 | } | ||
477 | |||
478 | static int cm4040_close(struct inode *inode, struct file *filp) | ||
479 | { | ||
480 | struct reader_dev *dev = filp->private_data; | ||
481 | dev_link_t *link; | ||
482 | int minor = iminor(inode); | ||
483 | |||
484 | DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode), | ||
485 | iminor(inode)); | ||
486 | |||
487 | if (minor >= CM_MAX_DEV) | ||
488 | return -ENODEV; | ||
489 | |||
490 | link = dev_table[minor]; | ||
491 | if (link == NULL) | ||
492 | return -ENODEV; | ||
493 | |||
494 | cm4040_stop_poll(dev); | ||
495 | |||
496 | link->open = 0; | ||
497 | wake_up(&dev->devq); | ||
498 | |||
499 | DEBUGP(2, dev, "<- cm4040_close\n"); | ||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | static void cm4040_reader_release(dev_link_t *link) | ||
504 | { | ||
505 | struct reader_dev *dev = link->priv; | ||
506 | |||
507 | DEBUGP(3, dev, "-> cm4040_reader_release\n"); | ||
508 | while (link->open) { | ||
509 | DEBUGP(3, dev, KERN_INFO MODULE_NAME ": delaying release " | ||
510 | "until process has terminated\n"); | ||
511 | wait_event(dev->devq, (link->open == 0)); | ||
512 | } | ||
513 | DEBUGP(3, dev, "<- cm4040_reader_release\n"); | ||
514 | return; | ||
515 | } | ||
516 | |||
517 | static void reader_config(dev_link_t *link, int devno) | ||
518 | { | ||
519 | client_handle_t handle; | ||
520 | struct reader_dev *dev; | ||
521 | tuple_t tuple; | ||
522 | cisparse_t parse; | ||
523 | config_info_t conf; | ||
524 | u_char buf[64]; | ||
525 | int fail_fn, fail_rc; | ||
526 | int rc; | ||
527 | |||
528 | handle = link->handle; | ||
529 | |||
530 | tuple.DesiredTuple = CISTPL_CONFIG; | ||
531 | tuple.Attributes = 0; | ||
532 | tuple.TupleData = buf; | ||
533 | tuple.TupleDataMax = sizeof(buf); | ||
534 | tuple.TupleOffset = 0; | ||
535 | |||
536 | if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) { | ||
537 | fail_fn = GetFirstTuple; | ||
538 | goto cs_failed; | ||
539 | } | ||
540 | if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) { | ||
541 | fail_fn = GetTupleData; | ||
542 | goto cs_failed; | ||
543 | } | ||
544 | if ((fail_rc = pcmcia_parse_tuple(handle, &tuple, &parse)) | ||
545 | != CS_SUCCESS) { | ||
546 | fail_fn = ParseTuple; | ||
547 | goto cs_failed; | ||
548 | } | ||
549 | if ((fail_rc = pcmcia_get_configuration_info(handle, &conf)) | ||
550 | != CS_SUCCESS) { | ||
551 | fail_fn = GetConfigurationInfo; | ||
552 | goto cs_failed; | ||
553 | } | ||
554 | |||
555 | link->state |= DEV_CONFIG; | ||
556 | link->conf.ConfigBase = parse.config.base; | ||
557 | link->conf.Present = parse.config.rmask[0]; | ||
558 | link->conf.Vcc = conf.Vcc; | ||
559 | |||
560 | link->io.BasePort2 = 0; | ||
561 | link->io.NumPorts2 = 0; | ||
562 | link->io.Attributes2 = 0; | ||
563 | tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
564 | for (rc = pcmcia_get_first_tuple(handle, &tuple); | ||
565 | rc == CS_SUCCESS; | ||
566 | rc = pcmcia_get_next_tuple(handle, &tuple)) { | ||
567 | rc = pcmcia_get_tuple_data(handle, &tuple); | ||
568 | if (rc != CS_SUCCESS) | ||
569 | continue; | ||
570 | rc = pcmcia_parse_tuple(handle, &tuple, &parse); | ||
571 | if (rc != CS_SUCCESS) | ||
572 | continue; | ||
573 | |||
574 | link->conf.ConfigIndex = parse.cftable_entry.index; | ||
575 | |||
576 | if (!parse.cftable_entry.io.nwin) | ||
577 | continue; | ||
578 | |||
579 | link->io.BasePort1 = parse.cftable_entry.io.win[0].base; | ||
580 | link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; | ||
581 | link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; | ||
582 | if (!(parse.cftable_entry.io.flags & CISTPL_IO_8BIT)) | ||
583 | link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; | ||
584 | if (!(parse.cftable_entry.io.flags & CISTPL_IO_16BIT)) | ||
585 | link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; | ||
586 | link->io.IOAddrLines = parse.cftable_entry.io.flags | ||
587 | & CISTPL_IO_LINES_MASK; | ||
588 | rc = pcmcia_request_io(handle, &link->io); | ||
589 | |||
590 | dev_printk(KERN_INFO, &handle_to_dev(handle), "foo"); | ||
591 | if (rc == CS_SUCCESS) | ||
592 | break; | ||
593 | else | ||
594 | dev_printk(KERN_INFO, &handle_to_dev(handle), | ||
595 | "pcmcia_request_io failed 0x%x\n", rc); | ||
596 | } | ||
597 | if (rc != CS_SUCCESS) | ||
598 | goto cs_release; | ||
599 | |||
600 | link->conf.IntType = 00000002; | ||
601 | |||
602 | if ((fail_rc = pcmcia_request_configuration(handle,&link->conf)) | ||
603 | !=CS_SUCCESS) { | ||
604 | fail_fn = RequestConfiguration; | ||
605 | dev_printk(KERN_INFO, &handle_to_dev(handle), | ||
606 | "pcmcia_request_configuration failed 0x%x\n", | ||
607 | fail_rc); | ||
608 | goto cs_release; | ||
609 | } | ||
610 | |||
611 | dev = link->priv; | ||
612 | sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno); | ||
613 | dev->node.major = major; | ||
614 | dev->node.minor = devno; | ||
615 | dev->node.next = NULL; | ||
616 | link->dev = &dev->node; | ||
617 | link->state &= ~DEV_CONFIG_PENDING; | ||
618 | |||
619 | DEBUGP(2, dev, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno, | ||
620 | link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1); | ||
621 | DEBUGP(2, dev, "<- reader_config (succ)\n"); | ||
622 | |||
623 | return; | ||
624 | |||
625 | cs_failed: | ||
626 | cs_error(handle, fail_fn, fail_rc); | ||
627 | cs_release: | ||
628 | reader_release(link); | ||
629 | link->state &= ~DEV_CONFIG_PENDING; | ||
630 | } | ||
631 | |||
632 | static int reader_event(event_t event, int priority, | ||
633 | event_callback_args_t *args) | ||
634 | { | ||
635 | dev_link_t *link; | ||
636 | struct reader_dev *dev; | ||
637 | int devno; | ||
638 | |||
639 | link = args->client_data; | ||
640 | dev = link->priv; | ||
641 | DEBUGP(3, dev, "-> reader_event\n"); | ||
642 | for (devno = 0; devno < CM_MAX_DEV; devno++) { | ||
643 | if (dev_table[devno] == link) | ||
644 | break; | ||
645 | } | ||
646 | if (devno == CM_MAX_DEV) | ||
647 | return CS_BAD_ADAPTER; | ||
648 | |||
649 | switch (event) { | ||
650 | case CS_EVENT_CARD_INSERTION: | ||
651 | DEBUGP(5, dev, "CS_EVENT_CARD_INSERTION\n"); | ||
652 | link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; | ||
653 | reader_config(link, devno); | ||
654 | break; | ||
655 | case CS_EVENT_CARD_REMOVAL: | ||
656 | DEBUGP(5, dev, "CS_EVENT_CARD_REMOVAL\n"); | ||
657 | link->state &= ~DEV_PRESENT; | ||
658 | break; | ||
659 | case CS_EVENT_PM_SUSPEND: | ||
660 | DEBUGP(5, dev, "CS_EVENT_PM_SUSPEND " | ||
661 | "(fall-through to CS_EVENT_RESET_PHYSICAL)\n"); | ||
662 | link->state |= DEV_SUSPEND; | ||
663 | |||
664 | case CS_EVENT_RESET_PHYSICAL: | ||
665 | DEBUGP(5, dev, "CS_EVENT_RESET_PHYSICAL\n"); | ||
666 | if (link->state & DEV_CONFIG) { | ||
667 | DEBUGP(5, dev, "ReleaseConfiguration\n"); | ||
668 | pcmcia_release_configuration(link->handle); | ||
669 | } | ||
670 | break; | ||
671 | case CS_EVENT_PM_RESUME: | ||
672 | DEBUGP(5, dev, "CS_EVENT_PM_RESUME " | ||
673 | "(fall-through to CS_EVENT_CARD_RESET)\n"); | ||
674 | link->state &= ~DEV_SUSPEND; | ||
675 | |||
676 | case CS_EVENT_CARD_RESET: | ||
677 | DEBUGP(5, dev, "CS_EVENT_CARD_RESET\n"); | ||
678 | if ((link->state & DEV_CONFIG)) { | ||
679 | DEBUGP(5, dev, "RequestConfiguration\n"); | ||
680 | pcmcia_request_configuration(link->handle, | ||
681 | &link->conf); | ||
682 | } | ||
683 | break; | ||
684 | default: | ||
685 | DEBUGP(5, dev, "reader_event: unknown event %.2x\n", | ||
686 | event); | ||
687 | break; | ||
688 | } | ||
689 | DEBUGP(3, dev, "<- reader_event\n"); | ||
690 | return CS_SUCCESS; | ||
691 | } | ||
692 | |||
693 | static void reader_release(dev_link_t *link) | ||
694 | { | ||
695 | cm4040_reader_release(link->priv); | ||
696 | pcmcia_release_configuration(link->handle); | ||
697 | pcmcia_release_io(link->handle, &link->io); | ||
698 | } | ||
699 | |||
700 | static dev_link_t *reader_attach(void) | ||
701 | { | ||
702 | struct reader_dev *dev; | ||
703 | dev_link_t *link; | ||
704 | client_reg_t client_reg; | ||
705 | int i; | ||
706 | |||
707 | for (i = 0; i < CM_MAX_DEV; i++) { | ||
708 | if (dev_table[i] == NULL) | ||
709 | break; | ||
710 | } | ||
711 | |||
712 | if (i == CM_MAX_DEV) | ||
713 | return NULL; | ||
714 | |||
715 | dev = kzalloc(sizeof(struct reader_dev), GFP_KERNEL); | ||
716 | if (dev == NULL) | ||
717 | return NULL; | ||
718 | |||
719 | dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT; | ||
720 | dev->buffer_status = 0; | ||
721 | |||
722 | link = &dev->link; | ||
723 | link->priv = dev; | ||
724 | |||
725 | link->conf.IntType = INT_MEMORY_AND_IO; | ||
726 | dev_table[i] = link; | ||
727 | |||
728 | client_reg.dev_info = &dev_info; | ||
729 | client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; | ||
730 | client_reg.EventMask= | ||
731 | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | | ||
732 | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | | ||
733 | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; | ||
734 | client_reg.Version = 0x0210; | ||
735 | client_reg.event_callback_args.client_data = link; | ||
736 | i = pcmcia_register_client(&link->handle, &client_reg); | ||
737 | if (i) { | ||
738 | cs_error(link->handle, RegisterClient, i); | ||
739 | reader_detach(link); | ||
740 | return NULL; | ||
741 | } | ||
742 | init_waitqueue_head(&dev->devq); | ||
743 | init_waitqueue_head(&dev->poll_wait); | ||
744 | init_waitqueue_head(&dev->read_wait); | ||
745 | init_waitqueue_head(&dev->write_wait); | ||
746 | init_timer(&dev->poll_timer); | ||
747 | dev->poll_timer.function = &cm4040_do_poll; | ||
748 | |||
749 | return link; | ||
750 | } | ||
751 | |||
752 | static void reader_detach_by_devno(int devno, dev_link_t *link) | ||
753 | { | ||
754 | struct reader_dev *dev = link->priv; | ||
755 | |||
756 | if (link->state & DEV_CONFIG) { | ||
757 | DEBUGP(5, dev, "device still configured (try to release it)\n"); | ||
758 | reader_release(link); | ||
759 | } | ||
760 | |||
761 | pcmcia_deregister_client(link->handle); | ||
762 | dev_table[devno] = NULL; | ||
763 | DEBUGP(5, dev, "freeing dev=%p\n", dev); | ||
764 | cm4040_stop_poll(dev); | ||
765 | kfree(dev); | ||
766 | return; | ||
767 | } | ||
768 | |||
769 | static void reader_detach(dev_link_t *link) | ||
770 | { | ||
771 | int i; | ||
772 | |||
773 | /* find device */ | ||
774 | for (i = 0; i < CM_MAX_DEV; i++) { | ||
775 | if (dev_table[i] == link) | ||
776 | break; | ||
777 | } | ||
778 | if (i == CM_MAX_DEV) | ||
779 | return; | ||
780 | |||
781 | reader_detach_by_devno(i, link); | ||
782 | return; | ||
783 | } | ||
784 | |||
785 | static struct file_operations reader_fops = { | ||
786 | .owner = THIS_MODULE, | ||
787 | .read = cm4040_read, | ||
788 | .write = cm4040_write, | ||
789 | .open = cm4040_open, | ||
790 | .release = cm4040_close, | ||
791 | .poll = cm4040_poll, | ||
792 | }; | ||
793 | |||
794 | static struct pcmcia_device_id cm4040_ids[] = { | ||
795 | PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200), | ||
796 | PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040", | ||
797 | 0xE32CDD8C, 0x8F23318B), | ||
798 | PCMCIA_DEVICE_NULL, | ||
799 | }; | ||
800 | MODULE_DEVICE_TABLE(pcmcia, cm4040_ids); | ||
801 | |||
802 | static struct pcmcia_driver reader_driver = { | ||
803 | .owner = THIS_MODULE, | ||
804 | .drv = { | ||
805 | .name = "cm4040_cs", | ||
806 | }, | ||
807 | .attach = reader_attach, | ||
808 | .detach = reader_detach, | ||
809 | .event = reader_event, | ||
810 | .id_table = cm4040_ids, | ||
811 | }; | ||
812 | |||
813 | static int __init cm4040_init(void) | ||
814 | { | ||
815 | printk(KERN_INFO "%s\n", version); | ||
816 | pcmcia_register_driver(&reader_driver); | ||
817 | major = register_chrdev(0, DEVICE_NAME, &reader_fops); | ||
818 | if (major < 0) { | ||
819 | printk(KERN_WARNING MODULE_NAME | ||
820 | ": could not get major number\n"); | ||
821 | return -1; | ||
822 | } | ||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | static void __exit cm4040_exit(void) | ||
827 | { | ||
828 | int i; | ||
829 | |||
830 | printk(KERN_INFO MODULE_NAME ": unloading\n"); | ||
831 | pcmcia_unregister_driver(&reader_driver); | ||
832 | for (i = 0; i < CM_MAX_DEV; i++) { | ||
833 | if (dev_table[i]) | ||
834 | reader_detach_by_devno(i, dev_table[i]); | ||
835 | } | ||
836 | unregister_chrdev(major, DEVICE_NAME); | ||
837 | } | ||
838 | |||
839 | module_init(cm4040_init); | ||
840 | module_exit(cm4040_exit); | ||
841 | 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 index 000000000000..9a8b805c5095 --- /dev/null +++ b/drivers/char/pcmcia/cm4040_cs.h | |||
@@ -0,0 +1,47 @@ | |||
1 | #ifndef _CM4040_H_ | ||
2 | #define _CM4040_H_ | ||
3 | |||
4 | #define CM_MAX_DEV 4 | ||
5 | |||
6 | #define DEVICE_NAME "cmx" | ||
7 | #define MODULE_NAME "cm4040_cs" | ||
8 | |||
9 | #define REG_OFFSET_BULK_OUT 0 | ||
10 | #define REG_OFFSET_BULK_IN 0 | ||
11 | #define REG_OFFSET_BUFFER_STATUS 1 | ||
12 | #define REG_OFFSET_SYNC_CONTROL 2 | ||
13 | |||
14 | #define BSR_BULK_IN_FULL 0x02 | ||
15 | #define BSR_BULK_OUT_FULL 0x01 | ||
16 | |||
17 | #define SCR_HOST_TO_READER_START 0x80 | ||
18 | #define SCR_ABORT 0x40 | ||
19 | #define SCR_EN_NOTIFY 0x20 | ||
20 | #define SCR_ACK_NOTIFY 0x10 | ||
21 | #define SCR_READER_TO_HOST_DONE 0x08 | ||
22 | #define SCR_HOST_TO_READER_DONE 0x04 | ||
23 | #define SCR_PULSE_INTERRUPT 0x02 | ||
24 | #define SCR_POWER_DOWN 0x01 | ||
25 | |||
26 | |||
27 | #define CMD_PC_TO_RDR_ICCPOWERON 0x62 | ||
28 | #define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65 | ||
29 | #define CMD_PC_TO_RDR_ICCPOWEROFF 0x63 | ||
30 | #define CMD_PC_TO_RDR_SECURE 0x69 | ||
31 | #define CMD_PC_TO_RDR_GETPARAMETERS 0x6C | ||
32 | #define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D | ||
33 | #define CMD_PC_TO_RDR_SETPARAMETERS 0x61 | ||
34 | #define CMD_PC_TO_RDR_XFRBLOCK 0x6F | ||
35 | #define CMD_PC_TO_RDR_ESCAPE 0x6B | ||
36 | #define CMD_PC_TO_RDR_ICCCLOCK 0x6E | ||
37 | #define CMD_PC_TO_RDR_TEST_SECURE 0x74 | ||
38 | #define CMD_PC_TO_RDR_OK_SECURE 0x89 | ||
39 | |||
40 | |||
41 | #define CMD_RDR_TO_PC_SLOTSTATUS 0x81 | ||
42 | #define CMD_RDR_TO_PC_DATABLOCK 0x80 | ||
43 | #define CMD_RDR_TO_PC_PARAMETERS 0x82 | ||
44 | #define CMD_RDR_TO_PC_ESCAPE 0x83 | ||
45 | #define CMD_RDR_TO_PC_OK_SECURE 0x89 | ||
46 | |||
47 | #endif /* _CM4040_H_ */ | ||