diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/applicom.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/applicom.c')
-rw-r--r-- | drivers/char/applicom.c | 862 |
1 files changed, 862 insertions, 0 deletions
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c new file mode 100644 index 000000000000..6bf2e27dc23a --- /dev/null +++ b/drivers/char/applicom.c | |||
@@ -0,0 +1,862 @@ | |||
1 | /* Derived from Applicom driver ac.c for SCO Unix */ | ||
2 | /* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */ | ||
3 | /* dwmw2@infradead.org 30/8/98 */ | ||
4 | /* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $ */ | ||
5 | /* This module is for Linux 2.1 and 2.2 series kernels. */ | ||
6 | /*****************************************************************************/ | ||
7 | /* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt */ | ||
8 | /* ceci pour reseter correctement apres une sortie sauvage */ | ||
9 | /* J PAGET 02/05/94 passage V2.4.3 dans le traitement de d'interruption, */ | ||
10 | /* LoopCount n'etait pas initialise a 0. */ | ||
11 | /* F LAFORSE 04/07/95 version V2.6.0 lecture bidon apres acces a une carte */ | ||
12 | /* pour liberer le bus */ | ||
13 | /* J.PAGET 19/11/95 version V2.6.1 Nombre, addresse,irq n'est plus configure */ | ||
14 | /* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter */ | ||
15 | /* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3 */ | ||
16 | /* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes */ | ||
17 | /* adresses de base des cartes, IOCTL 6 plus complet */ | ||
18 | /* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification */ | ||
19 | /* de code autre que le texte V2.6.1 en V2.8.0 */ | ||
20 | /*****************************************************************************/ | ||
21 | |||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/miscdevice.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/wait.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/fs.h> | ||
33 | |||
34 | #include <asm/io.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | |||
37 | #include "applicom.h" | ||
38 | |||
39 | |||
40 | /* NOTE: We use for loops with {write,read}b() instead of | ||
41 | memcpy_{from,to}io throughout this driver. This is because | ||
42 | the board doesn't correctly handle word accesses - only | ||
43 | bytes. | ||
44 | */ | ||
45 | |||
46 | |||
47 | #undef DEBUG | ||
48 | |||
49 | #define MAX_BOARD 8 /* maximum of pc board possible */ | ||
50 | #define MAX_ISA_BOARD 4 | ||
51 | #define LEN_RAM_IO 0x800 | ||
52 | #define AC_MINOR 157 | ||
53 | |||
54 | #ifndef PCI_VENDOR_ID_APPLICOM | ||
55 | #define PCI_VENDOR_ID_APPLICOM 0x1389 | ||
56 | #define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 | ||
57 | #define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 | ||
58 | #define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 | ||
59 | #endif | ||
60 | #define MAX_PCI_DEVICE_NUM 3 | ||
61 | |||
62 | static char *applicom_pci_devnames[] = { | ||
63 | "PCI board", | ||
64 | "PCI2000IBS / PCI2000CAN", | ||
65 | "PCI2000PFB" | ||
66 | }; | ||
67 | |||
68 | static struct pci_device_id applicom_pci_tbl[] = { | ||
69 | { PCI_VENDOR_ID_APPLICOM, PCI_DEVICE_ID_APPLICOM_PCIGENERIC, | ||
70 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, | ||
71 | { PCI_VENDOR_ID_APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN, | ||
72 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, | ||
73 | { PCI_VENDOR_ID_APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000PFB, | ||
74 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, | ||
75 | { 0 } | ||
76 | }; | ||
77 | MODULE_DEVICE_TABLE(pci, applicom_pci_tbl); | ||
78 | |||
79 | MODULE_AUTHOR("David Woodhouse & Applicom International"); | ||
80 | MODULE_DESCRIPTION("Driver for Applicom Profibus card"); | ||
81 | MODULE_LICENSE("GPL"); | ||
82 | |||
83 | MODULE_SUPPORTED_DEVICE("ac"); | ||
84 | |||
85 | |||
86 | static struct applicom_board { | ||
87 | unsigned long PhysIO; | ||
88 | void __iomem *RamIO; | ||
89 | wait_queue_head_t FlagSleepSend; | ||
90 | long irq; | ||
91 | spinlock_t mutex; | ||
92 | } apbs[MAX_BOARD]; | ||
93 | |||
94 | static unsigned int irq = 0; /* interrupt number IRQ */ | ||
95 | static unsigned long mem = 0; /* physical segment of board */ | ||
96 | |||
97 | module_param(irq, uint, 0); | ||
98 | MODULE_PARM_DESC(irq, "IRQ of the Applicom board"); | ||
99 | module_param(mem, ulong, 0); | ||
100 | MODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board"); | ||
101 | |||
102 | static unsigned int numboards; /* number of installed boards */ | ||
103 | static volatile unsigned char Dummy; | ||
104 | static DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec); | ||
105 | static unsigned int WriteErrorCount; /* number of write error */ | ||
106 | static unsigned int ReadErrorCount; /* number of read error */ | ||
107 | static unsigned int DeviceErrorCount; /* number of device error */ | ||
108 | |||
109 | static ssize_t ac_read (struct file *, char __user *, size_t, loff_t *); | ||
110 | static ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *); | ||
111 | static int ac_ioctl(struct inode *, struct file *, unsigned int, | ||
112 | unsigned long); | ||
113 | static irqreturn_t ac_interrupt(int, void *, struct pt_regs *); | ||
114 | |||
115 | static struct file_operations ac_fops = { | ||
116 | .owner = THIS_MODULE, | ||
117 | .llseek = no_llseek, | ||
118 | .read = ac_read, | ||
119 | .write = ac_write, | ||
120 | .ioctl = ac_ioctl, | ||
121 | }; | ||
122 | |||
123 | static struct miscdevice ac_miscdev = { | ||
124 | AC_MINOR, | ||
125 | "ac", | ||
126 | &ac_fops | ||
127 | }; | ||
128 | |||
129 | static int dummy; /* dev_id for request_irq() */ | ||
130 | |||
131 | static int ac_register_board(unsigned long physloc, void __iomem *loc, | ||
132 | unsigned char boardno) | ||
133 | { | ||
134 | volatile unsigned char byte_reset_it; | ||
135 | |||
136 | if((readb(loc + CONF_END_TEST) != 0x00) || | ||
137 | (readb(loc + CONF_END_TEST + 1) != 0x55) || | ||
138 | (readb(loc + CONF_END_TEST + 2) != 0xAA) || | ||
139 | (readb(loc + CONF_END_TEST + 3) != 0xFF)) | ||
140 | return 0; | ||
141 | |||
142 | if (!boardno) | ||
143 | boardno = readb(loc + NUMCARD_OWNER_TO_PC); | ||
144 | |||
145 | if (!boardno && boardno > MAX_BOARD) { | ||
146 | printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n", | ||
147 | boardno, physloc, MAX_BOARD); | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | if (apbs[boardno - 1].RamIO) { | ||
152 | printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n", | ||
153 | boardno, physloc, boardno, apbs[boardno-1].PhysIO); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | boardno--; | ||
158 | |||
159 | apbs[boardno].PhysIO = physloc; | ||
160 | apbs[boardno].RamIO = loc; | ||
161 | init_waitqueue_head(&apbs[boardno].FlagSleepSend); | ||
162 | spin_lock_init(&apbs[boardno].mutex); | ||
163 | byte_reset_it = readb(loc + RAM_IT_TO_PC); | ||
164 | |||
165 | numboards++; | ||
166 | return boardno + 1; | ||
167 | } | ||
168 | |||
169 | #ifdef MODULE | ||
170 | |||
171 | #define applicom_init init_module | ||
172 | |||
173 | void cleanup_module(void) | ||
174 | { | ||
175 | int i; | ||
176 | |||
177 | misc_deregister(&ac_miscdev); | ||
178 | |||
179 | for (i = 0; i < MAX_BOARD; i++) { | ||
180 | |||
181 | if (!apbs[i].RamIO) | ||
182 | continue; | ||
183 | |||
184 | if (apbs[i].irq) | ||
185 | free_irq(apbs[i].irq, &dummy); | ||
186 | |||
187 | iounmap(apbs[i].RamIO); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | #endif /* MODULE */ | ||
192 | |||
193 | int __init applicom_init(void) | ||
194 | { | ||
195 | int i, numisa = 0; | ||
196 | struct pci_dev *dev = NULL; | ||
197 | void __iomem *RamIO; | ||
198 | int boardno; | ||
199 | |||
200 | printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $\n"); | ||
201 | |||
202 | /* No mem and irq given - check for a PCI card */ | ||
203 | |||
204 | while ( (dev = pci_get_class(PCI_CLASS_OTHERS << 16, dev))) { | ||
205 | |||
206 | if (dev->vendor != PCI_VENDOR_ID_APPLICOM) | ||
207 | continue; | ||
208 | |||
209 | if (dev->device > MAX_PCI_DEVICE_NUM || dev->device == 0) | ||
210 | continue; | ||
211 | |||
212 | if (pci_enable_device(dev)) | ||
213 | return -EIO; | ||
214 | |||
215 | RamIO = ioremap(dev->resource[0].start, LEN_RAM_IO); | ||
216 | |||
217 | if (!RamIO) { | ||
218 | printk(KERN_INFO "ac.o: Failed to ioremap PCI memory space at 0x%lx\n", dev->resource[0].start); | ||
219 | pci_disable_device(dev); | ||
220 | return -EIO; | ||
221 | } | ||
222 | |||
223 | printk(KERN_INFO "Applicom %s found at mem 0x%lx, irq %d\n", | ||
224 | applicom_pci_devnames[dev->device-1], dev->resource[0].start, | ||
225 | dev->irq); | ||
226 | |||
227 | boardno = ac_register_board(dev->resource[0].start, RamIO,0); | ||
228 | if (!boardno) { | ||
229 | printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n"); | ||
230 | iounmap(RamIO); | ||
231 | pci_disable_device(dev); | ||
232 | continue; | ||
233 | } | ||
234 | |||
235 | if (request_irq(dev->irq, &ac_interrupt, SA_SHIRQ, "Applicom PCI", &dummy)) { | ||
236 | printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq); | ||
237 | iounmap(RamIO); | ||
238 | pci_disable_device(dev); | ||
239 | apbs[boardno - 1].RamIO = NULL; | ||
240 | continue; | ||
241 | } | ||
242 | |||
243 | /* Enable interrupts. */ | ||
244 | |||
245 | writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC); | ||
246 | |||
247 | apbs[boardno - 1].irq = dev->irq; | ||
248 | } | ||
249 | |||
250 | /* Finished with PCI cards. If none registered, | ||
251 | * and there was no mem/irq specified, exit */ | ||
252 | |||
253 | if (!mem || !irq) { | ||
254 | if (numboards) | ||
255 | goto fin; | ||
256 | else { | ||
257 | printk(KERN_INFO "ac.o: No PCI boards found.\n"); | ||
258 | printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n"); | ||
259 | return -ENXIO; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | /* Now try the specified ISA cards */ | ||
264 | |||
265 | for (i = 0; i < MAX_ISA_BOARD; i++) { | ||
266 | RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO); | ||
267 | |||
268 | if (!RamIO) { | ||
269 | printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1); | ||
270 | continue; | ||
271 | } | ||
272 | |||
273 | if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i), | ||
274 | RamIO,i+1))) { | ||
275 | iounmap(RamIO); | ||
276 | continue; | ||
277 | } | ||
278 | |||
279 | printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq); | ||
280 | |||
281 | if (!numisa) { | ||
282 | if (request_irq(irq, &ac_interrupt, SA_SHIRQ, "Applicom ISA", &dummy)) { | ||
283 | printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.\n", irq); | ||
284 | iounmap(RamIO); | ||
285 | apbs[boardno - 1].RamIO = NULL; | ||
286 | } | ||
287 | else | ||
288 | apbs[boardno - 1].irq = irq; | ||
289 | } | ||
290 | else | ||
291 | apbs[boardno - 1].irq = 0; | ||
292 | |||
293 | numisa++; | ||
294 | } | ||
295 | |||
296 | if (!numisa) | ||
297 | printk(KERN_WARNING"ac.o: No valid ISA Applicom boards found at mem 0x%lx\n",mem); | ||
298 | |||
299 | fin: | ||
300 | init_waitqueue_head(&FlagSleepRec); | ||
301 | |||
302 | WriteErrorCount = 0; | ||
303 | ReadErrorCount = 0; | ||
304 | DeviceErrorCount = 0; | ||
305 | |||
306 | if (numboards) { | ||
307 | misc_register(&ac_miscdev); | ||
308 | for (i = 0; i < MAX_BOARD; i++) { | ||
309 | int serial; | ||
310 | char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; | ||
311 | |||
312 | if (!apbs[i].RamIO) | ||
313 | continue; | ||
314 | |||
315 | for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) | ||
316 | boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); | ||
317 | |||
318 | boardname[serial] = 0; | ||
319 | |||
320 | |||
321 | printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d", | ||
322 | i+1, boardname, | ||
323 | (int)(readb(apbs[i].RamIO + VERS) >> 4), | ||
324 | (int)(readb(apbs[i].RamIO + VERS) & 0xF)); | ||
325 | |||
326 | serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + | ||
327 | (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + | ||
328 | (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); | ||
329 | |||
330 | if (serial != 0) | ||
331 | printk(" S/N %d\n", serial); | ||
332 | else | ||
333 | printk("\n"); | ||
334 | } | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | else | ||
339 | return -ENXIO; | ||
340 | } | ||
341 | |||
342 | |||
343 | #ifndef MODULE | ||
344 | __initcall(applicom_init); | ||
345 | #endif | ||
346 | |||
347 | static ssize_t ac_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) | ||
348 | { | ||
349 | unsigned int NumCard; /* Board number 1 -> 8 */ | ||
350 | unsigned int IndexCard; /* Index board number 0 -> 7 */ | ||
351 | unsigned char TicCard; /* Board TIC to send */ | ||
352 | unsigned long flags; /* Current priority */ | ||
353 | struct st_ram_io st_loc; | ||
354 | struct mailbox tmpmailbox; | ||
355 | #ifdef DEBUG | ||
356 | int c; | ||
357 | #endif | ||
358 | DECLARE_WAITQUEUE(wait, current); | ||
359 | |||
360 | if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { | ||
361 | static int warncount = 5; | ||
362 | if (warncount) { | ||
363 | printk(KERN_INFO "Hmmm. write() of Applicom card, length %zd != expected %zd\n", | ||
364 | count, sizeof(struct st_ram_io) + sizeof(struct mailbox)); | ||
365 | warncount--; | ||
366 | } | ||
367 | return -EINVAL; | ||
368 | } | ||
369 | |||
370 | if(copy_from_user(&st_loc, buf, sizeof(struct st_ram_io))) | ||
371 | return -EFAULT; | ||
372 | |||
373 | if(copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)], | ||
374 | sizeof(struct mailbox))) | ||
375 | return -EFAULT; | ||
376 | |||
377 | NumCard = st_loc.num_card; /* board number to send */ | ||
378 | TicCard = st_loc.tic_des_from_pc; /* tic number to send */ | ||
379 | IndexCard = NumCard - 1; | ||
380 | |||
381 | if((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO) | ||
382 | return -EINVAL; | ||
383 | |||
384 | #ifdef DEBUG | ||
385 | printk("Write to applicom card #%d. struct st_ram_io follows:", | ||
386 | IndexCard+1); | ||
387 | |||
388 | for (c = 0; c < sizeof(struct st_ram_io);) { | ||
389 | |||
390 | printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]); | ||
391 | |||
392 | for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) { | ||
393 | printk(" %2.2X", ((unsigned char *) &st_loc)[c]); | ||
394 | } | ||
395 | } | ||
396 | |||
397 | printk("\nstruct mailbox follows:"); | ||
398 | |||
399 | for (c = 0; c < sizeof(struct mailbox);) { | ||
400 | printk("\n%5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]); | ||
401 | |||
402 | for (c++; c % 8 && c < sizeof(struct mailbox); c++) { | ||
403 | printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | printk("\n"); | ||
408 | #endif | ||
409 | |||
410 | spin_lock_irqsave(&apbs[IndexCard].mutex, flags); | ||
411 | |||
412 | /* Test octet ready correct */ | ||
413 | if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) { | ||
414 | Dummy = readb(apbs[IndexCard].RamIO + VERS); | ||
415 | spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); | ||
416 | printk(KERN_WARNING "APPLICOM driver write error board %d, DataFromPcReady = %d\n", | ||
417 | IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY)); | ||
418 | DeviceErrorCount++; | ||
419 | return -EIO; | ||
420 | } | ||
421 | |||
422 | /* Place ourselves on the wait queue */ | ||
423 | set_current_state(TASK_INTERRUPTIBLE); | ||
424 | add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); | ||
425 | |||
426 | /* Check whether the card is ready for us */ | ||
427 | while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) { | ||
428 | Dummy = readb(apbs[IndexCard].RamIO + VERS); | ||
429 | /* It's busy. Sleep. */ | ||
430 | |||
431 | spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); | ||
432 | schedule(); | ||
433 | if (signal_pending(current)) { | ||
434 | remove_wait_queue(&apbs[IndexCard].FlagSleepSend, | ||
435 | &wait); | ||
436 | return -EINTR; | ||
437 | } | ||
438 | spin_lock_irqsave(&apbs[IndexCard].mutex, flags); | ||
439 | set_current_state(TASK_INTERRUPTIBLE); | ||
440 | } | ||
441 | |||
442 | /* We may not have actually slept */ | ||
443 | set_current_state(TASK_RUNNING); | ||
444 | remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait); | ||
445 | |||
446 | writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); | ||
447 | |||
448 | /* Which is best - lock down the pages with rawio and then | ||
449 | copy directly, or use bounce buffers? For now we do the latter | ||
450 | because it works with 2.2 still */ | ||
451 | { | ||
452 | unsigned char *from = (unsigned char *) &tmpmailbox; | ||
453 | void __iomem *to = apbs[IndexCard].RamIO + RAM_FROM_PC; | ||
454 | int c; | ||
455 | |||
456 | for (c = 0; c < sizeof(struct mailbox); c++) | ||
457 | writeb(*(from++), to++); | ||
458 | } | ||
459 | |||
460 | writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC); | ||
461 | writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC); | ||
462 | writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC); | ||
463 | writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); | ||
464 | writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); | ||
465 | writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); | ||
466 | Dummy = readb(apbs[IndexCard].RamIO + VERS); | ||
467 | spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags); | ||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static int do_ac_read(int IndexCard, char __user *buf, | ||
472 | struct st_ram_io *st_loc, struct mailbox *mailbox) | ||
473 | { | ||
474 | void __iomem *from = apbs[IndexCard].RamIO + RAM_TO_PC; | ||
475 | unsigned char *to = (unsigned char *)&mailbox; | ||
476 | #ifdef DEBUG | ||
477 | int c; | ||
478 | #endif | ||
479 | |||
480 | st_loc->tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC); | ||
481 | st_loc->numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); | ||
482 | |||
483 | |||
484 | { | ||
485 | int c; | ||
486 | |||
487 | for (c = 0; c < sizeof(struct mailbox); c++) | ||
488 | *(to++) = readb(from++); | ||
489 | } | ||
490 | writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY); | ||
491 | writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC); | ||
492 | writeb(IndexCard+1, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); | ||
493 | writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), | ||
494 | apbs[IndexCard].RamIO + TIC_ACK_FROM_PC); | ||
495 | writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY); | ||
496 | writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY); | ||
497 | writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); | ||
498 | Dummy = readb(apbs[IndexCard].RamIO + VERS); | ||
499 | |||
500 | #ifdef DEBUG | ||
501 | printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard); | ||
502 | |||
503 | for (c = 0; c < sizeof(struct st_ram_io);) { | ||
504 | printk("\n%5.5X: %2.2X", c, ((unsigned char *)st_loc)[c]); | ||
505 | |||
506 | for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) { | ||
507 | printk(" %2.2X", ((unsigned char *)st_loc)[c]); | ||
508 | } | ||
509 | } | ||
510 | |||
511 | printk("\nstruct mailbox follows:"); | ||
512 | |||
513 | for (c = 0; c < sizeof(struct mailbox);) { | ||
514 | printk("\n%5.5X: %2.2X", c, ((unsigned char *)mailbox)[c]); | ||
515 | |||
516 | for (c++; c % 8 && c < sizeof(struct mailbox); c++) { | ||
517 | printk(" %2.2X", ((unsigned char *)mailbox)[c]); | ||
518 | } | ||
519 | } | ||
520 | printk("\n"); | ||
521 | #endif | ||
522 | return (sizeof(struct st_ram_io) + sizeof(struct mailbox)); | ||
523 | } | ||
524 | |||
525 | static ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_t *ptr) | ||
526 | { | ||
527 | unsigned long flags; | ||
528 | unsigned int i; | ||
529 | unsigned char tmp; | ||
530 | int ret = 0; | ||
531 | DECLARE_WAITQUEUE(wait, current); | ||
532 | #ifdef DEBUG | ||
533 | int loopcount=0; | ||
534 | #endif | ||
535 | /* No need to ratelimit this. Only root can trigger it anyway */ | ||
536 | if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) { | ||
537 | printk( KERN_WARNING "Hmmm. read() of Applicom card, length %zd != expected %zd\n", | ||
538 | count,sizeof(struct st_ram_io) + sizeof(struct mailbox)); | ||
539 | return -EINVAL; | ||
540 | } | ||
541 | |||
542 | while(1) { | ||
543 | /* Stick ourself on the wait queue */ | ||
544 | set_current_state(TASK_INTERRUPTIBLE); | ||
545 | add_wait_queue(&FlagSleepRec, &wait); | ||
546 | |||
547 | /* Scan each board, looking for one which has a packet for us */ | ||
548 | for (i=0; i < MAX_BOARD; i++) { | ||
549 | if (!apbs[i].RamIO) | ||
550 | continue; | ||
551 | spin_lock_irqsave(&apbs[i].mutex, flags); | ||
552 | |||
553 | tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY); | ||
554 | |||
555 | if (tmp == 2) { | ||
556 | struct st_ram_io st_loc; | ||
557 | struct mailbox mailbox; | ||
558 | |||
559 | /* Got a packet for us */ | ||
560 | ret = do_ac_read(i, buf, &st_loc, &mailbox); | ||
561 | spin_unlock_irqrestore(&apbs[i].mutex, flags); | ||
562 | set_current_state(TASK_RUNNING); | ||
563 | remove_wait_queue(&FlagSleepRec, &wait); | ||
564 | |||
565 | if (copy_to_user(buf, &st_loc, sizeof(st_loc))) | ||
566 | return -EFAULT; | ||
567 | if (copy_to_user(buf + sizeof(st_loc), &mailbox, sizeof(mailbox))) | ||
568 | return -EFAULT; | ||
569 | return tmp; | ||
570 | } | ||
571 | |||
572 | if (tmp > 2) { | ||
573 | /* Got an error */ | ||
574 | Dummy = readb(apbs[i].RamIO + VERS); | ||
575 | |||
576 | spin_unlock_irqrestore(&apbs[i].mutex, flags); | ||
577 | set_current_state(TASK_RUNNING); | ||
578 | remove_wait_queue(&FlagSleepRec, &wait); | ||
579 | |||
580 | printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n", | ||
581 | i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); | ||
582 | DeviceErrorCount++; | ||
583 | return -EIO; | ||
584 | } | ||
585 | |||
586 | /* Nothing for us. Try the next board */ | ||
587 | Dummy = readb(apbs[i].RamIO + VERS); | ||
588 | spin_unlock_irqrestore(&apbs[i].mutex, flags); | ||
589 | |||
590 | } /* per board */ | ||
591 | |||
592 | /* OK - No boards had data for us. Sleep now */ | ||
593 | |||
594 | schedule(); | ||
595 | remove_wait_queue(&FlagSleepRec, &wait); | ||
596 | |||
597 | if (signal_pending(current)) | ||
598 | return -EINTR; | ||
599 | |||
600 | #ifdef DEBUG | ||
601 | if (loopcount++ > 2) { | ||
602 | printk("Looping in ac_read. loopcount %d\n", loopcount); | ||
603 | } | ||
604 | #endif | ||
605 | } | ||
606 | } | ||
607 | |||
608 | static irqreturn_t ac_interrupt(int vec, void *dev_instance, struct pt_regs *regs) | ||
609 | { | ||
610 | unsigned int i; | ||
611 | unsigned int FlagInt; | ||
612 | unsigned int LoopCount; | ||
613 | int handled = 0; | ||
614 | |||
615 | // printk("Applicom interrupt on IRQ %d occurred\n", vec); | ||
616 | |||
617 | LoopCount = 0; | ||
618 | |||
619 | do { | ||
620 | FlagInt = 0; | ||
621 | for (i = 0; i < MAX_BOARD; i++) { | ||
622 | |||
623 | /* Skip if this board doesn't exist */ | ||
624 | if (!apbs[i].RamIO) | ||
625 | continue; | ||
626 | |||
627 | spin_lock(&apbs[i].mutex); | ||
628 | |||
629 | /* Skip if this board doesn't want attention */ | ||
630 | if(readb(apbs[i].RamIO + RAM_IT_TO_PC) == 0) { | ||
631 | spin_unlock(&apbs[i].mutex); | ||
632 | continue; | ||
633 | } | ||
634 | |||
635 | handled = 1; | ||
636 | FlagInt = 1; | ||
637 | writeb(0, apbs[i].RamIO + RAM_IT_TO_PC); | ||
638 | |||
639 | if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) { | ||
640 | printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n", | ||
641 | i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY)); | ||
642 | DeviceErrorCount++; | ||
643 | } | ||
644 | |||
645 | if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && | ||
646 | (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) { | ||
647 | |||
648 | printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n", | ||
649 | i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY)); | ||
650 | DeviceErrorCount++; | ||
651 | } | ||
652 | |||
653 | if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) { /* mailbox sent by the card ? */ | ||
654 | if (waitqueue_active(&FlagSleepRec)) { | ||
655 | wake_up_interruptible(&FlagSleepRec); | ||
656 | } | ||
657 | } | ||
658 | |||
659 | if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) { /* ram i/o free for write by pc ? */ | ||
660 | if (waitqueue_active(&apbs[i].FlagSleepSend)) { /* process sleep during read ? */ | ||
661 | wake_up_interruptible(&apbs[i].FlagSleepSend); | ||
662 | } | ||
663 | } | ||
664 | Dummy = readb(apbs[i].RamIO + VERS); | ||
665 | |||
666 | if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) { | ||
667 | /* There's another int waiting on this card */ | ||
668 | spin_unlock(&apbs[i].mutex); | ||
669 | i--; | ||
670 | } else { | ||
671 | spin_unlock(&apbs[i].mutex); | ||
672 | } | ||
673 | } | ||
674 | if (FlagInt) | ||
675 | LoopCount = 0; | ||
676 | else | ||
677 | LoopCount++; | ||
678 | } while(LoopCount < 2); | ||
679 | return IRQ_RETVAL(handled); | ||
680 | } | ||
681 | |||
682 | |||
683 | |||
684 | static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | ||
685 | |||
686 | { /* @ ADG ou ATO selon le cas */ | ||
687 | int i; | ||
688 | unsigned char IndexCard; | ||
689 | void __iomem *pmem; | ||
690 | int ret = 0; | ||
691 | volatile unsigned char byte_reset_it; | ||
692 | struct st_ram_io *adgl; | ||
693 | void __user *argp = (void __user *)arg; | ||
694 | |||
695 | /* In general, the device is only openable by root anyway, so we're not | ||
696 | particularly concerned that bogus ioctls can flood the console. */ | ||
697 | |||
698 | adgl = kmalloc(sizeof(struct st_ram_io), GFP_KERNEL); | ||
699 | if (!adgl) | ||
700 | return -ENOMEM; | ||
701 | |||
702 | if (copy_from_user(adgl, argp, sizeof(struct st_ram_io))) { | ||
703 | kfree(adgl); | ||
704 | return -EFAULT; | ||
705 | } | ||
706 | |||
707 | IndexCard = adgl->num_card-1; | ||
708 | |||
709 | if(cmd != 0 && cmd != 6 && | ||
710 | ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) { | ||
711 | static int warncount = 10; | ||
712 | if (warncount) { | ||
713 | printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1); | ||
714 | warncount--; | ||
715 | } | ||
716 | kfree(adgl); | ||
717 | return -EINVAL; | ||
718 | } | ||
719 | |||
720 | switch (cmd) { | ||
721 | |||
722 | case 0: | ||
723 | pmem = apbs[IndexCard].RamIO; | ||
724 | for (i = 0; i < sizeof(struct st_ram_io); i++) | ||
725 | ((unsigned char *)adgl)[i]=readb(pmem++); | ||
726 | if (copy_to_user(argp, adgl, sizeof(struct st_ram_io))) | ||
727 | ret = -EFAULT; | ||
728 | break; | ||
729 | case 1: | ||
730 | pmem = apbs[IndexCard].RamIO + CONF_END_TEST; | ||
731 | for (i = 0; i < 4; i++) | ||
732 | adgl->conf_end_test[i] = readb(pmem++); | ||
733 | for (i = 0; i < 2; i++) | ||
734 | adgl->error_code[i] = readb(pmem++); | ||
735 | for (i = 0; i < 4; i++) | ||
736 | adgl->parameter_error[i] = readb(pmem++); | ||
737 | pmem = apbs[IndexCard].RamIO + VERS; | ||
738 | adgl->vers = readb(pmem); | ||
739 | pmem = apbs[IndexCard].RamIO + TYPE_CARD; | ||
740 | for (i = 0; i < 20; i++) | ||
741 | adgl->reserv1[i] = readb(pmem++); | ||
742 | *(int *)&adgl->reserv1[20] = | ||
743 | (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + | ||
744 | (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + | ||
745 | (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) ); | ||
746 | |||
747 | if (copy_to_user(argp, adgl, sizeof(struct st_ram_io))) | ||
748 | ret = -EFAULT; | ||
749 | break; | ||
750 | case 2: | ||
751 | pmem = apbs[IndexCard].RamIO + CONF_END_TEST; | ||
752 | for (i = 0; i < 10; i++) | ||
753 | writeb(0xff, pmem++); | ||
754 | writeb(adgl->data_from_pc_ready, | ||
755 | apbs[IndexCard].RamIO + DATA_FROM_PC_READY); | ||
756 | |||
757 | writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); | ||
758 | |||
759 | for (i = 0; i < MAX_BOARD; i++) { | ||
760 | if (apbs[i].RamIO) { | ||
761 | byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC); | ||
762 | } | ||
763 | } | ||
764 | break; | ||
765 | case 3: | ||
766 | pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC; | ||
767 | writeb(adgl->tic_des_from_pc, pmem); | ||
768 | break; | ||
769 | case 4: | ||
770 | pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC; | ||
771 | adgl->tic_owner_to_pc = readb(pmem++); | ||
772 | adgl->numcard_owner_to_pc = readb(pmem); | ||
773 | if (copy_to_user(argp, adgl,sizeof(struct st_ram_io))) | ||
774 | ret = -EFAULT; | ||
775 | break; | ||
776 | case 5: | ||
777 | writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC); | ||
778 | writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC); | ||
779 | writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC); | ||
780 | writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY); | ||
781 | writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC); | ||
782 | break; | ||
783 | case 6: | ||
784 | printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)\n"); | ||
785 | printk(KERN_INFO "Number of installed boards . %d\n", (int) numboards); | ||
786 | printk(KERN_INFO "Segment of board ........... %X\n", (int) mem); | ||
787 | printk(KERN_INFO "Interrupt IRQ number ....... %d\n", (int) irq); | ||
788 | for (i = 0; i < MAX_BOARD; i++) { | ||
789 | int serial; | ||
790 | char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1]; | ||
791 | |||
792 | if (!apbs[i].RamIO) | ||
793 | continue; | ||
794 | |||
795 | for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++) | ||
796 | boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial); | ||
797 | boardname[serial] = 0; | ||
798 | |||
799 | printk(KERN_INFO "Prom version board %d ....... V%d.%d %s", | ||
800 | i+1, | ||
801 | (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4), | ||
802 | (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF), | ||
803 | boardname); | ||
804 | |||
805 | |||
806 | serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + | ||
807 | (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + | ||
808 | (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) ); | ||
809 | |||
810 | if (serial != 0) | ||
811 | printk(" S/N %d\n", serial); | ||
812 | else | ||
813 | printk("\n"); | ||
814 | } | ||
815 | if (DeviceErrorCount != 0) | ||
816 | printk(KERN_INFO "DeviceErrorCount ........... %d\n", DeviceErrorCount); | ||
817 | if (ReadErrorCount != 0) | ||
818 | printk(KERN_INFO "ReadErrorCount ............. %d\n", ReadErrorCount); | ||
819 | if (WriteErrorCount != 0) | ||
820 | printk(KERN_INFO "WriteErrorCount ............ %d\n", WriteErrorCount); | ||
821 | if (waitqueue_active(&FlagSleepRec)) | ||
822 | printk(KERN_INFO "Process in read pending\n"); | ||
823 | for (i = 0; i < MAX_BOARD; i++) { | ||
824 | if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend)) | ||
825 | printk(KERN_INFO "Process in write pending board %d\n",i+1); | ||
826 | } | ||
827 | break; | ||
828 | default: | ||
829 | printk(KERN_INFO "APPLICOM driver ioctl, unknown function code %d\n",cmd) ; | ||
830 | ret = -EINVAL; | ||
831 | break; | ||
832 | } | ||
833 | Dummy = readb(apbs[IndexCard].RamIO + VERS); | ||
834 | kfree(adgl); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | #ifndef MODULE | ||
839 | static int __init applicom_setup(char *str) | ||
840 | { | ||
841 | int ints[4]; | ||
842 | |||
843 | (void) get_options(str, 4, ints); | ||
844 | |||
845 | if (ints[0] > 2) { | ||
846 | printk(KERN_WARNING "Too many arguments to 'applicom=', expected mem,irq only.\n"); | ||
847 | } | ||
848 | |||
849 | if (ints[0] < 2) { | ||
850 | printk(KERN_INFO"applicom numargs: %d\n", ints[0]); | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | mem = ints[1]; | ||
855 | irq = ints[2]; | ||
856 | return 1; | ||
857 | } | ||
858 | |||
859 | __setup("applicom=", applicom_setup); | ||
860 | |||
861 | #endif /* MODULE */ | ||
862 | |||