Blame view

drivers/char/applicom.c 24 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /* Derived from Applicom driver ac.c for SCO Unix                            */
  /* Ported by David Woodhouse, Axiom (Cambridge) Ltd.                         */
  /* dwmw2@infradead.org 30/8/98                                               */
  /* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $			     */
  /* This module is for Linux 2.1 and 2.2 series kernels.                      */
  /*****************************************************************************/
  /* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt  */
  /* ceci pour reseter correctement apres une sortie sauvage                   */
  /* J PAGET 02/05/94 passage V2.4.3 dans le traitement de d'interruption,     */
  /* LoopCount n'etait pas initialise a 0.                                     */
  /* F LAFORSE 04/07/95 version V2.6.0 lecture bidon apres acces a une carte   */
  /*           pour liberer le bus                                             */
  /* J.PAGET 19/11/95 version V2.6.1 Nombre, addresse,irq n'est plus configure */
  /* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter  */
  /* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3    */
  /* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes       */
3ad2f3fbb   Daniel Mack   tree-wide: Assort...
17
  /* addresses de base des cartes, IOCTL 6 plus complet                         */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
25
  /* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification  */
  /* de code autre que le texte V2.6.1 en V2.8.0                               */
  /*****************************************************************************/
  
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/interrupt.h>
174cd4b1e   Ingo Molnar   sched/headers: Pr...
26
  #include <linux/sched/signal.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
  #include <linux/slab.h>
  #include <linux/errno.h>
613655fa3   Arnd Bergmann   drivers: autoconv...
29
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
  #include <linux/miscdevice.h>
  #include <linux/pci.h>
  #include <linux/wait.h>
  #include <linux/init.h>
  #include <linux/fs.h>
  
  #include <asm/io.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
37
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  
  #include "applicom.h"
  
  
  /* NOTE: We use for loops with {write,read}b() instead of 
     memcpy_{from,to}io throughout this driver. This is because
     the board doesn't correctly handle word accesses - only
     bytes. 
  */
  
  
  #undef DEBUG
  
  #define MAX_BOARD 8		/* maximum of pc board possible */
  #define MAX_ISA_BOARD 4
  #define LEN_RAM_IO 0x800
  #define AC_MINOR 157
  
  #ifndef PCI_VENDOR_ID_APPLICOM
  #define PCI_VENDOR_ID_APPLICOM                0x1389
  #define PCI_DEVICE_ID_APPLICOM_PCIGENERIC     0x0001
  #define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002
  #define PCI_DEVICE_ID_APPLICOM_PCI2000PFB     0x0003
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62

613655fa3   Arnd Bergmann   drivers: autoconv...
63
  static DEFINE_MUTEX(ac_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
68
  static char *applicom_pci_devnames[] = {
  	"PCI board",
  	"PCI2000IBS / PCI2000CAN",
  	"PCI2000PFB"
  };
2c9c4ae65   Arvind Yadav   applicom: constif...
69
  static const struct pci_device_id applicom_pci_tbl[] = {
53a7a1bb4   Jiri Slaby   Char: applicom, u...
70
71
72
  	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCIGENERIC) },
  	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN) },
  	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000PFB) },
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
77
78
79
  	{ 0 }
  };
  MODULE_DEVICE_TABLE(pci, applicom_pci_tbl);
  
  MODULE_AUTHOR("David Woodhouse & Applicom International");
  MODULE_DESCRIPTION("Driver for Applicom Profibus card");
  MODULE_LICENSE("GPL");
14f8d3ff3   Scott James Remnant   applicom: Auto-lo...
80
  MODULE_ALIAS_MISCDEV(AC_MINOR);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  
  MODULE_SUPPORTED_DEVICE("ac");
  
  
  static struct applicom_board {
  	unsigned long PhysIO;
  	void __iomem *RamIO;
  	wait_queue_head_t FlagSleepSend;
  	long irq;
  	spinlock_t mutex;
  } apbs[MAX_BOARD];
  
  static unsigned int irq = 0;	/* interrupt number IRQ       */
  static unsigned long mem = 0;	/* physical segment of board  */
1c37ab5e5   David Howells   Annotate hardware...
95
  module_param_hw(irq, uint, irq, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  MODULE_PARM_DESC(irq, "IRQ of the Applicom board");
1c37ab5e5   David Howells   Annotate hardware...
97
  module_param_hw(mem, ulong, iomem, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
104
105
106
107
108
  MODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board");
  
  static unsigned int numboards;	/* number of installed boards */
  static volatile unsigned char Dummy;
  static DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec);
  static unsigned int WriteErrorCount;	/* number of write error      */
  static unsigned int ReadErrorCount;	/* number of read error       */
  static unsigned int DeviceErrorCount;	/* number of device error     */
  
  static ssize_t ac_read (struct file *, char __user *, size_t, loff_t *);
  static ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *);
55929332c   Arnd Bergmann   drivers: Push dow...
109
  static long ac_ioctl(struct file *, unsigned int, unsigned long);
7d12e780e   David Howells   IRQ: Maintain reg...
110
  static irqreturn_t ac_interrupt(int, void *);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111

62322d255   Arjan van de Ven   [PATCH] make more...
112
  static const struct file_operations ac_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
  	.owner = THIS_MODULE,
  	.llseek = no_llseek,
  	.read = ac_read,
  	.write = ac_write,
55929332c   Arnd Bergmann   drivers: Push dow...
117
  	.unlocked_ioctl = ac_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  };
  
  static struct miscdevice ac_miscdev = {
  	AC_MINOR,
  	"ac",
  	&ac_fops
  };
  
  static int dummy;	/* dev_id for request_irq() */
  
  static int ac_register_board(unsigned long physloc, void __iomem *loc, 
  		      unsigned char boardno)
  {
  	volatile unsigned char byte_reset_it;
  
  	if((readb(loc + CONF_END_TEST)     != 0x00) ||
  	   (readb(loc + CONF_END_TEST + 1) != 0x55) ||
  	   (readb(loc + CONF_END_TEST + 2) != 0xAA) ||
  	   (readb(loc + CONF_END_TEST + 3) != 0xFF))
  		return 0;
  
  	if (!boardno)
  		boardno = readb(loc + NUMCARD_OWNER_TO_PC);
e60b6e2f7   Eric Sesterhenn   [PATCH] Wrong out...
141
  	if (!boardno || boardno > MAX_BOARD) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  		printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).
  ",
  		       boardno, physloc, MAX_BOARD);
  		return 0;
  	}
  
  	if (apbs[boardno - 1].RamIO) {
  		printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)
  ", 
  		       boardno, physloc, boardno, apbs[boardno-1].PhysIO);
  		return 0;
  	}
  
  	boardno--;
  
  	apbs[boardno].PhysIO = physloc;
  	apbs[boardno].RamIO = loc;
  	init_waitqueue_head(&apbs[boardno].FlagSleepSend);
  	spin_lock_init(&apbs[boardno].mutex);
  	byte_reset_it = readb(loc + RAM_IT_TO_PC);
  
  	numboards++;
  	return boardno + 1;
  }
2e6113908   Adrian Bunk   [PATCH] drivers/c...
166
  static void __exit applicom_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
  {
819a3eba4   Christophe Lucas   [PATCH] applicom:...
168
  	unsigned int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  
  	misc_deregister(&ac_miscdev);
  
  	for (i = 0; i < MAX_BOARD; i++) {
  
  		if (!apbs[i].RamIO)
  			continue;
  
  		if (apbs[i].irq)
  			free_irq(apbs[i].irq, &dummy);
  
  		iounmap(apbs[i].RamIO);
  	}
  }
2e6113908   Adrian Bunk   [PATCH] drivers/c...
183
  static int __init applicom_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
  {
  	int i, numisa = 0;
  	struct pci_dev *dev = NULL;
  	void __iomem *RamIO;
819a3eba4   Christophe Lucas   [PATCH] applicom:...
188
  	int boardno, ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
194
195
  
  	printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $
  ");
  
  	/* No mem and irq given - check for a PCI card */
  
  	while ( (dev = pci_get_class(PCI_CLASS_OTHERS << 16, dev))) {
53a7a1bb4   Jiri Slaby   Char: applicom, u...
196
  		if (!pci_match_id(applicom_pci_tbl, dev))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
  			continue;
  		
  		if (pci_enable_device(dev))
  			return -EIO;
24cb23352   Alan Cox   char serial: swit...
201
  		RamIO = ioremap_nocache(pci_resource_start(dev, 0), LEN_RAM_IO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
  
  		if (!RamIO) {
e29419fff   Greg Kroah-Hartman   [PATCH] 64bit res...
204
205
206
  			printk(KERN_INFO "ac.o: Failed to ioremap PCI memory "
  				"space at 0x%llx
  ",
55b29a728   Jiri Slaby   Char: applicom, u...
207
  				(unsigned long long)pci_resource_start(dev, 0));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
  			pci_disable_device(dev);
  			return -EIO;
  		}
e29419fff   Greg Kroah-Hartman   [PATCH] 64bit res...
211
212
213
  		printk(KERN_INFO "Applicom %s found at mem 0x%llx, irq %d
  ",
  		       applicom_pci_devnames[dev->device-1],
55b29a728   Jiri Slaby   Char: applicom, u...
214
  			   (unsigned long long)pci_resource_start(dev, 0),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  		       dev->irq);
55b29a728   Jiri Slaby   Char: applicom, u...
216
217
  		boardno = ac_register_board(pci_resource_start(dev, 0),
  				RamIO, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
222
223
224
  		if (!boardno) {
  			printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.
  ");
  			iounmap(RamIO);
  			pci_disable_device(dev);
  			continue;
  		}
0f2ed4c6b   Thomas Gleixner   [PATCH] irq-flags...
225
  		if (request_irq(dev->irq, &ac_interrupt, IRQF_SHARED, "Applicom PCI", &dummy)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  			printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.
  ", dev->irq);
  			iounmap(RamIO);
  			pci_disable_device(dev);
  			apbs[boardno - 1].RamIO = NULL;
  			continue;
  		}
  
  		/* Enable interrupts. */
  
  		writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC);
  
  		apbs[boardno - 1].irq = dev->irq;
  	}
  
  	/* Finished with PCI cards. If none registered, 
  	 * and there was no mem/irq specified, exit */
  
  	if (!mem || !irq) {
  		if (numboards)
  			goto fin;
  		else {
  			printk(KERN_INFO "ac.o: No PCI boards found.
  ");
  			printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.
  ");
  			return -ENXIO;
  		}
  	}
  
  	/* Now try the specified ISA cards */
  
  	for (i = 0; i < MAX_ISA_BOARD; i++) {
24cb23352   Alan Cox   char serial: swit...
259
  		RamIO = ioremap_nocache(mem + (LEN_RAM_IO * i), LEN_RAM_IO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  
  		if (!RamIO) {
  			printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)
  ", i + 1);
  			continue;
  		}
  
  		if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i),
  						  RamIO,i+1))) {
  			iounmap(RamIO);
  			continue;
  		}
  
  		printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d
  ", mem + (LEN_RAM_IO*i), irq);
  
  		if (!numisa) {
0f2ed4c6b   Thomas Gleixner   [PATCH] irq-flags...
277
  			if (request_irq(irq, &ac_interrupt, IRQF_SHARED, "Applicom ISA", &dummy)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
  				printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.
  ", irq);
  				iounmap(RamIO);
  				apbs[boardno - 1].RamIO = NULL;
  			}
  			else
  				apbs[boardno - 1].irq = irq;
  		}
  		else
  			apbs[boardno - 1].irq = 0;
  
  		numisa++;
  	}
  
  	if (!numisa)
819a3eba4   Christophe Lucas   [PATCH] applicom:...
293
294
295
  		printk(KERN_WARNING "ac.o: No valid ISA Applicom boards found "
  				"at mem 0x%lx
  ", mem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
297
298
299
300
301
302
303
304
  
   fin:
  	init_waitqueue_head(&FlagSleepRec);
  
  	WriteErrorCount = 0;
  	ReadErrorCount = 0;
  	DeviceErrorCount = 0;
  
  	if (numboards) {
819a3eba4   Christophe Lucas   [PATCH] applicom:...
305
306
307
308
309
310
  		ret = misc_register(&ac_miscdev);
  		if (ret) {
  			printk(KERN_WARNING "ac.o: Unable to register misc device
  ");
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  		for (i = 0; i < MAX_BOARD; i++) {
  			int serial;
  			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
  
  			if (!apbs[i].RamIO)
  				continue;
  
  			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
  				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
  
  			boardname[serial] = 0;
  
  
  			printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d",
  			       i+1, boardname,
  			       (int)(readb(apbs[i].RamIO + VERS) >> 4),
  			       (int)(readb(apbs[i].RamIO + VERS) & 0xF));
  			
  			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + 
  				(readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + 
  				(readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
  
  			if (serial != 0)
  				printk(" S/N %d
  ", serial);
  			else
  				printk("
  ");
  		}
  		return 0;
  	}
  
  	else
  		return -ENXIO;
819a3eba4   Christophe Lucas   [PATCH] applicom:...
345
346
347
348
349
350
351
352
353
  
  out:
  	for (i = 0; i < MAX_BOARD; i++) {
  		if (!apbs[i].RamIO)
  			continue;
  		if (apbs[i].irq)
  			free_irq(apbs[i].irq, &dummy);
  		iounmap(apbs[i].RamIO);
  	}
819a3eba4   Christophe Lucas   [PATCH] applicom:...
354
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  }
2e6113908   Adrian Bunk   [PATCH] drivers/c...
356
357
  module_init(applicom_init);
  module_exit(applicom_exit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  
  static ssize_t ac_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  {
  	unsigned int NumCard;	/* Board number 1 -> 8           */
  	unsigned int IndexCard;	/* Index board number 0 -> 7     */
  	unsigned char TicCard;	/* Board TIC to send             */
  	unsigned long flags;	/* Current priority              */
  	struct st_ram_io st_loc;
  	struct mailbox tmpmailbox;
  #ifdef DEBUG
  	int c;
  #endif
  	DECLARE_WAITQUEUE(wait, current);
  
  	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
  		static int warncount = 5;
  		if (warncount) {
  			printk(KERN_INFO "Hmmm. write() of Applicom card, length %zd != expected %zd
  ",
  			       count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
  			warncount--;
  		}
  		return -EINVAL;
  	}
  
  	if(copy_from_user(&st_loc, buf, sizeof(struct st_ram_io))) 
  		return -EFAULT;
  	
  	if(copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)],
  			  sizeof(struct mailbox))) 
  		return -EFAULT;
  
  	NumCard = st_loc.num_card;	/* board number to send          */
  	TicCard = st_loc.tic_des_from_pc;	/* tic number to send            */
  	IndexCard = NumCard - 1;
  
  	if((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO)
  		return -EINVAL;
  
  #ifdef DEBUG
  	printk("Write to applicom card #%d. struct st_ram_io follows:",
  	       IndexCard+1);
  
  		for (c = 0; c < sizeof(struct st_ram_io);) {
  		
  			printk("
  %5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]);
  
  			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
  				printk(" %2.2X", ((unsigned char *) &st_loc)[c]);
  			}
  		}
  
  		printk("
  struct mailbox follows:");
  
  		for (c = 0; c < sizeof(struct mailbox);) {
  			printk("
  %5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]);
  
  			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
  				printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]);
  			}
  		}
  
  		printk("
  ");
  #endif
  
  	spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
  
  	/* Test octet ready correct */
  	if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) { 
  		Dummy = readb(apbs[IndexCard].RamIO + VERS);
  		spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
  		printk(KERN_WARNING "APPLICOM driver write error board %d, DataFromPcReady = %d
  ",
  		       IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY));
  		DeviceErrorCount++;
  		return -EIO;
  	}
  	
  	/* Place ourselves on the wait queue */
  	set_current_state(TASK_INTERRUPTIBLE);
  	add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
  
  	/* Check whether the card is ready for us */
  	while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) {
  		Dummy = readb(apbs[IndexCard].RamIO + VERS);
  		/* It's busy. Sleep. */
  
  		spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
  		schedule();
  		if (signal_pending(current)) {
  			remove_wait_queue(&apbs[IndexCard].FlagSleepSend,
  					  &wait);
  			return -EINTR;
  		}
  		spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
  		set_current_state(TASK_INTERRUPTIBLE);
  	}
  
  	/* We may not have actually slept */
  	set_current_state(TASK_RUNNING);
  	remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
  
  	writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
  
  	/* Which is best - lock down the pages with rawio and then
  	   copy directly, or use bounce buffers? For now we do the latter 
  	   because it works with 2.2 still */
  	{
  		unsigned char *from = (unsigned char *) &tmpmailbox;
  		void __iomem *to = apbs[IndexCard].RamIO + RAM_FROM_PC;
  		int c;
  
  		for (c = 0; c < sizeof(struct mailbox); c++)
  			writeb(*(from++), to++);
  	}
  
  	writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC);
  	writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC);
  	writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC);
  	writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
  	writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
  	writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
  	Dummy = readb(apbs[IndexCard].RamIO + VERS);
  	spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
  	return 0;
  }
  
  static int do_ac_read(int IndexCard, char __user *buf,
  		struct st_ram_io *st_loc, struct mailbox *mailbox)
  {
  	void __iomem *from = apbs[IndexCard].RamIO + RAM_TO_PC;
bc20589bf   Andrew Morton   applicom.c: fix a...
494
  	unsigned char *to = (unsigned char *)mailbox;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  #ifdef DEBUG
  	int c;
  #endif
  
  	st_loc->tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC);
  	st_loc->numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
  
  
  	{
  		int c;
  
  		for (c = 0; c < sizeof(struct mailbox); c++)
  			*(to++) = readb(from++);
  	}
  	writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
  	writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC);
  	writeb(IndexCard+1, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
  	writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), 
  	       apbs[IndexCard].RamIO + TIC_ACK_FROM_PC);
  	writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
  	writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY);
  	writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
  	Dummy = readb(apbs[IndexCard].RamIO + VERS);
  
  #ifdef DEBUG
  		printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard);
  
  		for (c = 0; c < sizeof(struct st_ram_io);) {
  			printk("
  %5.5X: %2.2X", c, ((unsigned char *)st_loc)[c]);
  
  			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
  				printk(" %2.2X", ((unsigned char *)st_loc)[c]);
  			}
  		}
  
  		printk("
  struct mailbox follows:");
  
  		for (c = 0; c < sizeof(struct mailbox);) {
  			printk("
  %5.5X: %2.2X", c, ((unsigned char *)mailbox)[c]);
  
  			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
  				printk(" %2.2X", ((unsigned char *)mailbox)[c]);
  			}
  		}
  		printk("
  ");
  #endif
  	return (sizeof(struct st_ram_io) + sizeof(struct mailbox));
  }
  
  static ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_t *ptr)
  {
  	unsigned long flags;
  	unsigned int i;
  	unsigned char tmp;
  	int ret = 0;
  	DECLARE_WAITQUEUE(wait, current);
  #ifdef DEBUG
  	int loopcount=0;
  #endif
  	/* No need to ratelimit this. Only root can trigger it anyway */
  	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
  		printk( KERN_WARNING "Hmmm. read() of Applicom card, length %zd != expected %zd
  ",
  			count,sizeof(struct st_ram_io) + sizeof(struct mailbox));
  		return -EINVAL;
  	}
  	
  	while(1) {
  		/* Stick ourself on the wait queue */
  		set_current_state(TASK_INTERRUPTIBLE);
  		add_wait_queue(&FlagSleepRec, &wait);
  		
  		/* Scan each board, looking for one which has a packet for us */
  		for (i=0; i < MAX_BOARD; i++) {
  			if (!apbs[i].RamIO)
  				continue;
  			spin_lock_irqsave(&apbs[i].mutex, flags);
  			
  			tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY);
  			
  			if (tmp == 2) {
  				struct st_ram_io st_loc;
  				struct mailbox mailbox;
  
  				/* Got a packet for us */
19714a8af   Vasiliy Kulikov   drivers/char/appl...
584
  				memset(&st_loc, 0, sizeof(st_loc));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  				ret = do_ac_read(i, buf, &st_loc, &mailbox);
  				spin_unlock_irqrestore(&apbs[i].mutex, flags);
  				set_current_state(TASK_RUNNING);
  				remove_wait_queue(&FlagSleepRec, &wait);
  
  				if (copy_to_user(buf, &st_loc, sizeof(st_loc)))
  					return -EFAULT;
  				if (copy_to_user(buf + sizeof(st_loc), &mailbox, sizeof(mailbox)))
  					return -EFAULT;
  				return tmp;
  			}
  			
  			if (tmp > 2) {
  				/* Got an error */
  				Dummy = readb(apbs[i].RamIO + VERS);
  				
  				spin_unlock_irqrestore(&apbs[i].mutex, flags);
  				set_current_state(TASK_RUNNING);
  				remove_wait_queue(&FlagSleepRec, &wait);
  				
  				printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d
  ",
  				       i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
  				DeviceErrorCount++;
  				return -EIO;
  			}
  			
  			/* Nothing for us. Try the next board */
  			Dummy = readb(apbs[i].RamIO + VERS);
  			spin_unlock_irqrestore(&apbs[i].mutex, flags);
  			
  		} /* per board */
  
  		/* OK - No boards had data for us. Sleep now */
  
  		schedule();
  		remove_wait_queue(&FlagSleepRec, &wait);
  
  		if (signal_pending(current))
  			return -EINTR;
  
  #ifdef DEBUG
  		if (loopcount++ > 2) {
56003191c   Domen Puncer   [PATCH] printk: d...
628
629
  			printk(KERN_DEBUG "Looping in ac_read. loopcount %d
  ", loopcount);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
631
632
633
  		}
  #endif
  	} 
  }
7d12e780e   David Howells   IRQ: Maintain reg...
634
  static irqreturn_t ac_interrupt(int vec, void *dev_instance)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
  {
  	unsigned int i;
  	unsigned int FlagInt;
  	unsigned int LoopCount;
  	int handled = 0;
  
  	//    printk("Applicom interrupt on IRQ %d occurred
  ", vec);
  
  	LoopCount = 0;
  
  	do {
  		FlagInt = 0;
  		for (i = 0; i < MAX_BOARD; i++) {
  			
  			/* Skip if this board doesn't exist */
  			if (!apbs[i].RamIO)
  				continue;
  
  			spin_lock(&apbs[i].mutex);
  
  			/* Skip if this board doesn't want attention */
  			if(readb(apbs[i].RamIO + RAM_IT_TO_PC) == 0) {
  				spin_unlock(&apbs[i].mutex);
  				continue;
  			}
  
  			handled = 1;
  			FlagInt = 1;
  			writeb(0, apbs[i].RamIO + RAM_IT_TO_PC);
  
  			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) {
  				printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d
  ",
  				       i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
  				DeviceErrorCount++;
  			}
  
  			if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && 
  			   (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) {
  				
  				printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataFromPcReady = %d
  ",
  				       i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY));
  				DeviceErrorCount++;
  			}
  
  			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) {	/* mailbox sent by the card ?   */
  				if (waitqueue_active(&FlagSleepRec)) {
  				wake_up_interruptible(&FlagSleepRec);
  			}
  			}
  
  			if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) {	/* ram i/o free for write by pc ? */
  				if (waitqueue_active(&apbs[i].FlagSleepSend)) {	/* process sleep during read ?    */
  					wake_up_interruptible(&apbs[i].FlagSleepSend);
  				}
  			}
  			Dummy = readb(apbs[i].RamIO + VERS);
  
  			if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) {
  				/* There's another int waiting on this card */
  				spin_unlock(&apbs[i].mutex);
  				i--;
  			} else {
  				spin_unlock(&apbs[i].mutex);
  			}
  		}
  		if (FlagInt)
  			LoopCount = 0;
  		else
  			LoopCount++;
  	} while(LoopCount < 2);
  	return IRQ_RETVAL(handled);
  }
55929332c   Arnd Bergmann   drivers: Push dow...
710
  static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
714
715
716
717
718
719
720
721
722
       
  {				/* @ ADG ou ATO selon le cas */
  	int i;
  	unsigned char IndexCard;
  	void __iomem *pmem;
  	int ret = 0;
  	volatile unsigned char byte_reset_it;
  	struct st_ram_io *adgl;
  	void __user *argp = (void __user *)arg;
  
  	/* In general, the device is only openable by root anyway, so we're not
  	   particularly concerned that bogus ioctls can flood the console. */
f67231f80   Julia Lawall   drivers/char/appl...
723
724
725
  	adgl = memdup_user(argp, sizeof(struct st_ram_io));
  	if (IS_ERR(adgl))
  		return PTR_ERR(adgl);
55929332c   Arnd Bergmann   drivers: Push dow...
726

613655fa3   Arnd Bergmann   drivers: autoconv...
727
  	mutex_lock(&ac_mutex);	
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
  	IndexCard = adgl->num_card-1;
  	 
a7be18d43   Alan Cox   applicom: Fix an ...
730
  	if(cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
733
734
735
736
737
  		static int warncount = 10;
  		if (warncount) {
  			printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d
  ",(int)IndexCard+1);
  			warncount--;
  		}
  		kfree(adgl);
613655fa3   Arnd Bergmann   drivers: autoconv...
738
  		mutex_unlock(&ac_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
  		return -EINVAL;
  	}
  
  	switch (cmd) {
  		
  	case 0:
  		pmem = apbs[IndexCard].RamIO;
  		for (i = 0; i < sizeof(struct st_ram_io); i++)
  			((unsigned char *)adgl)[i]=readb(pmem++);
  		if (copy_to_user(argp, adgl, sizeof(struct st_ram_io)))
  			ret = -EFAULT;
  		break;
  	case 1:
  		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
  		for (i = 0; i < 4; i++)
  			adgl->conf_end_test[i] = readb(pmem++);
  		for (i = 0; i < 2; i++)
  			adgl->error_code[i] = readb(pmem++);
  		for (i = 0; i < 4; i++)
  			adgl->parameter_error[i] = readb(pmem++);
  		pmem = apbs[IndexCard].RamIO + VERS;
  		adgl->vers = readb(pmem);
  		pmem = apbs[IndexCard].RamIO + TYPE_CARD;
  		for (i = 0; i < 20; i++)
  			adgl->reserv1[i] = readb(pmem++);
  		*(int *)&adgl->reserv1[20] =  
  			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + 
  			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + 
  			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) );
  
  		if (copy_to_user(argp, adgl, sizeof(struct st_ram_io)))
  			ret = -EFAULT;
  		break;
  	case 2:
  		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
  		for (i = 0; i < 10; i++)
  			writeb(0xff, pmem++);
  		writeb(adgl->data_from_pc_ready, 
  		       apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
  
  		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
  		
  		for (i = 0; i < MAX_BOARD; i++) {
  			if (apbs[i].RamIO) {
  				byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC);
  			}
  		}
  		break;
  	case 3:
  		pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC;
  		writeb(adgl->tic_des_from_pc, pmem);
  		break;
  	case 4:
  		pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC;
  		adgl->tic_owner_to_pc     = readb(pmem++);
  		adgl->numcard_owner_to_pc = readb(pmem);
  		if (copy_to_user(argp, adgl,sizeof(struct st_ram_io)))
  			ret = -EFAULT;
  		break;
  	case 5:
  		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
  		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
  		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
  		writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
  		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
  		break;
  	case 6:
  		printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)
  ");
  		printk(KERN_INFO "Number of installed boards . %d
  ", (int) numboards);
  		printk(KERN_INFO "Segment of board ........... %X
  ", (int) mem);
  		printk(KERN_INFO "Interrupt IRQ number ....... %d
  ", (int) irq);
  		for (i = 0; i < MAX_BOARD; i++) {
  			int serial;
  			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
  
  			if (!apbs[i].RamIO)
  				continue;
  
  			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
  				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
  			boardname[serial] = 0;
  
  			printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",
  			       i+1,
2154e0a4f   Dan Carpenter   applicom: use cor...
827
828
  			       (int)(readb(apbs[i].RamIO + VERS) >> 4),
  			       (int)(readb(apbs[i].RamIO + VERS) & 0xF),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
  			       boardname);
  
  
  			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + 
  				(readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + 
  				(readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
  
  			if (serial != 0)
  				printk(" S/N %d
  ", serial);
  			else
  				printk("
  ");
  		}
  		if (DeviceErrorCount != 0)
  			printk(KERN_INFO "DeviceErrorCount ........... %d
  ", DeviceErrorCount);
  		if (ReadErrorCount != 0)
  			printk(KERN_INFO "ReadErrorCount ............. %d
  ", ReadErrorCount);
  		if (WriteErrorCount != 0)
  			printk(KERN_INFO "WriteErrorCount ............ %d
  ", WriteErrorCount);
  		if (waitqueue_active(&FlagSleepRec))
  			printk(KERN_INFO "Process in read pending
  ");
  		for (i = 0; i < MAX_BOARD; i++) {
  			if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend))
  				printk(KERN_INFO "Process in write pending board %d
  ",i+1);
  		}
  		break;
  	default:
a7be18d43   Alan Cox   applicom: Fix an ...
862
  		ret = -ENOTTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
863
864
865
866
  		break;
  	}
  	Dummy = readb(apbs[IndexCard].RamIO + VERS);
  	kfree(adgl);
613655fa3   Arnd Bergmann   drivers: autoconv...
867
  	mutex_unlock(&ac_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
  	return 0;
  }