162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Derived from Applicom driver ac.c for SCO Unix                            */
362306a36Sopenharmony_ci/* Ported by David Woodhouse, Axiom (Cambridge) Ltd.                         */
462306a36Sopenharmony_ci/* dwmw2@infradead.org 30/8/98                                               */
562306a36Sopenharmony_ci/* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $			     */
662306a36Sopenharmony_ci/* This module is for Linux 2.1 and 2.2 series kernels.                      */
762306a36Sopenharmony_ci/*****************************************************************************/
862306a36Sopenharmony_ci/* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt  */
962306a36Sopenharmony_ci/* ceci pour reseter correctement apres une sortie sauvage                   */
1062306a36Sopenharmony_ci/* J PAGET 02/05/94 passage V2.4.3 dans le traitement de d'interruption,     */
1162306a36Sopenharmony_ci/* LoopCount n'etait pas initialise a 0.                                     */
1262306a36Sopenharmony_ci/* F LAFORSE 04/07/95 version V2.6.0 lecture bidon apres acces a une carte   */
1362306a36Sopenharmony_ci/*           pour liberer le bus                                             */
1462306a36Sopenharmony_ci/* J.PAGET 19/11/95 version V2.6.1 Nombre, addresse,irq n'est plus configure */
1562306a36Sopenharmony_ci/* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter  */
1662306a36Sopenharmony_ci/* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3    */
1762306a36Sopenharmony_ci/* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes       */
1862306a36Sopenharmony_ci/* addresses de base des cartes, IOCTL 6 plus complet                         */
1962306a36Sopenharmony_ci/* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification  */
2062306a36Sopenharmony_ci/* de code autre que le texte V2.6.1 en V2.8.0                               */
2162306a36Sopenharmony_ci/*****************************************************************************/
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/module.h>
2662306a36Sopenharmony_ci#include <linux/interrupt.h>
2762306a36Sopenharmony_ci#include <linux/sched/signal.h>
2862306a36Sopenharmony_ci#include <linux/slab.h>
2962306a36Sopenharmony_ci#include <linux/errno.h>
3062306a36Sopenharmony_ci#include <linux/mutex.h>
3162306a36Sopenharmony_ci#include <linux/miscdevice.h>
3262306a36Sopenharmony_ci#include <linux/pci.h>
3362306a36Sopenharmony_ci#include <linux/wait.h>
3462306a36Sopenharmony_ci#include <linux/init.h>
3562306a36Sopenharmony_ci#include <linux/fs.h>
3662306a36Sopenharmony_ci#include <linux/nospec.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <asm/io.h>
3962306a36Sopenharmony_ci#include <linux/uaccess.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include "applicom.h"
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* NOTE: We use for loops with {write,read}b() instead of
4562306a36Sopenharmony_ci   memcpy_{from,to}io throughout this driver. This is because
4662306a36Sopenharmony_ci   the board doesn't correctly handle word accesses - only
4762306a36Sopenharmony_ci   bytes.
4862306a36Sopenharmony_ci*/
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#undef DEBUG
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define MAX_BOARD 8		/* maximum of pc board possible */
5462306a36Sopenharmony_ci#define MAX_ISA_BOARD 4
5562306a36Sopenharmony_ci#define LEN_RAM_IO 0x800
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#ifndef PCI_VENDOR_ID_APPLICOM
5862306a36Sopenharmony_ci#define PCI_VENDOR_ID_APPLICOM                0x1389
5962306a36Sopenharmony_ci#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC     0x0001
6062306a36Sopenharmony_ci#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002
6162306a36Sopenharmony_ci#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB     0x0003
6262306a36Sopenharmony_ci#endif
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic DEFINE_MUTEX(ac_mutex);
6562306a36Sopenharmony_cistatic char *applicom_pci_devnames[] = {
6662306a36Sopenharmony_ci	"PCI board",
6762306a36Sopenharmony_ci	"PCI2000IBS / PCI2000CAN",
6862306a36Sopenharmony_ci	"PCI2000PFB"
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct pci_device_id applicom_pci_tbl[] = {
7262306a36Sopenharmony_ci	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCIGENERIC) },
7362306a36Sopenharmony_ci	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN) },
7462306a36Sopenharmony_ci	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000PFB) },
7562306a36Sopenharmony_ci	{ 0 }
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, applicom_pci_tbl);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciMODULE_AUTHOR("David Woodhouse & Applicom International");
8062306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Applicom Profibus card");
8162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
8262306a36Sopenharmony_ciMODULE_ALIAS_MISCDEV(AC_MINOR);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic struct applicom_board {
8562306a36Sopenharmony_ci	unsigned long PhysIO;
8662306a36Sopenharmony_ci	void __iomem *RamIO;
8762306a36Sopenharmony_ci	wait_queue_head_t FlagSleepSend;
8862306a36Sopenharmony_ci	long irq;
8962306a36Sopenharmony_ci	spinlock_t mutex;
9062306a36Sopenharmony_ci} apbs[MAX_BOARD];
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic unsigned int irq;	/* interrupt number IRQ       */
9362306a36Sopenharmony_cistatic unsigned long mem;	/* physical segment of board  */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cimodule_param_hw(irq, uint, irq, 0);
9662306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ of the Applicom board");
9762306a36Sopenharmony_cimodule_param_hw(mem, ulong, iomem, 0);
9862306a36Sopenharmony_ciMODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic unsigned int numboards;	/* number of installed boards */
10162306a36Sopenharmony_cistatic volatile unsigned char Dummy;
10262306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec);
10362306a36Sopenharmony_cistatic unsigned int WriteErrorCount;	/* number of write error      */
10462306a36Sopenharmony_cistatic unsigned int ReadErrorCount;	/* number of read error       */
10562306a36Sopenharmony_cistatic unsigned int DeviceErrorCount;	/* number of device error     */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic ssize_t ac_read (struct file *, char __user *, size_t, loff_t *);
10862306a36Sopenharmony_cistatic ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *);
10962306a36Sopenharmony_cistatic long ac_ioctl(struct file *, unsigned int, unsigned long);
11062306a36Sopenharmony_cistatic irqreturn_t ac_interrupt(int, void *);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic const struct file_operations ac_fops = {
11362306a36Sopenharmony_ci	.owner = THIS_MODULE,
11462306a36Sopenharmony_ci	.llseek = no_llseek,
11562306a36Sopenharmony_ci	.read = ac_read,
11662306a36Sopenharmony_ci	.write = ac_write,
11762306a36Sopenharmony_ci	.unlocked_ioctl = ac_ioctl,
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic struct miscdevice ac_miscdev = {
12162306a36Sopenharmony_ci	AC_MINOR,
12262306a36Sopenharmony_ci	"ac",
12362306a36Sopenharmony_ci	&ac_fops
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int dummy;	/* dev_id for request_irq() */
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int ac_register_board(unsigned long physloc, void __iomem *loc,
12962306a36Sopenharmony_ci		      unsigned char boardno)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	volatile unsigned char byte_reset_it;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if((readb(loc + CONF_END_TEST)     != 0x00) ||
13462306a36Sopenharmony_ci	   (readb(loc + CONF_END_TEST + 1) != 0x55) ||
13562306a36Sopenharmony_ci	   (readb(loc + CONF_END_TEST + 2) != 0xAA) ||
13662306a36Sopenharmony_ci	   (readb(loc + CONF_END_TEST + 3) != 0xFF))
13762306a36Sopenharmony_ci		return 0;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (!boardno)
14062306a36Sopenharmony_ci		boardno = readb(loc + NUMCARD_OWNER_TO_PC);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!boardno || boardno > MAX_BOARD) {
14362306a36Sopenharmony_ci		printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n",
14462306a36Sopenharmony_ci		       boardno, physloc, MAX_BOARD);
14562306a36Sopenharmony_ci		return 0;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (apbs[boardno - 1].RamIO) {
14962306a36Sopenharmony_ci		printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n",
15062306a36Sopenharmony_ci		       boardno, physloc, boardno, apbs[boardno-1].PhysIO);
15162306a36Sopenharmony_ci		return 0;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	boardno--;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	apbs[boardno].PhysIO = physloc;
15762306a36Sopenharmony_ci	apbs[boardno].RamIO = loc;
15862306a36Sopenharmony_ci	init_waitqueue_head(&apbs[boardno].FlagSleepSend);
15962306a36Sopenharmony_ci	spin_lock_init(&apbs[boardno].mutex);
16062306a36Sopenharmony_ci	byte_reset_it = readb(loc + RAM_IT_TO_PC);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	numboards++;
16362306a36Sopenharmony_ci	return boardno + 1;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void __exit applicom_exit(void)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	unsigned int i;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	misc_deregister(&ac_miscdev);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	for (i = 0; i < MAX_BOARD; i++) {
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		if (!apbs[i].RamIO)
17562306a36Sopenharmony_ci			continue;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		if (apbs[i].irq)
17862306a36Sopenharmony_ci			free_irq(apbs[i].irq, &dummy);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		iounmap(apbs[i].RamIO);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int __init applicom_init(void)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	int i, numisa = 0;
18762306a36Sopenharmony_ci	struct pci_dev *dev = NULL;
18862306a36Sopenharmony_ci	void __iomem *RamIO;
18962306a36Sopenharmony_ci	int boardno, ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $\n");
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* No mem and irq given - check for a PCI card */
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	while ( (dev = pci_get_class(PCI_CLASS_OTHERS << 16, dev))) {
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		if (!pci_match_id(applicom_pci_tbl, dev))
19862306a36Sopenharmony_ci			continue;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		if (pci_enable_device(dev)) {
20162306a36Sopenharmony_ci			pci_dev_put(dev);
20262306a36Sopenharmony_ci			return -EIO;
20362306a36Sopenharmony_ci		}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		RamIO = ioremap(pci_resource_start(dev, 0), LEN_RAM_IO);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		if (!RamIO) {
20862306a36Sopenharmony_ci			printk(KERN_INFO "ac.o: Failed to ioremap PCI memory "
20962306a36Sopenharmony_ci				"space at 0x%llx\n",
21062306a36Sopenharmony_ci				(unsigned long long)pci_resource_start(dev, 0));
21162306a36Sopenharmony_ci			pci_disable_device(dev);
21262306a36Sopenharmony_ci			pci_dev_put(dev);
21362306a36Sopenharmony_ci			return -EIO;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		printk(KERN_INFO "Applicom %s found at mem 0x%llx, irq %d\n",
21762306a36Sopenharmony_ci		       applicom_pci_devnames[dev->device-1],
21862306a36Sopenharmony_ci			   (unsigned long long)pci_resource_start(dev, 0),
21962306a36Sopenharmony_ci		       dev->irq);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		boardno = ac_register_board(pci_resource_start(dev, 0),
22262306a36Sopenharmony_ci				RamIO, 0);
22362306a36Sopenharmony_ci		if (!boardno) {
22462306a36Sopenharmony_ci			printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n");
22562306a36Sopenharmony_ci			iounmap(RamIO);
22662306a36Sopenharmony_ci			pci_disable_device(dev);
22762306a36Sopenharmony_ci			continue;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		if (request_irq(dev->irq, &ac_interrupt, IRQF_SHARED, "Applicom PCI", &dummy)) {
23162306a36Sopenharmony_ci			printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq);
23262306a36Sopenharmony_ci			iounmap(RamIO);
23362306a36Sopenharmony_ci			pci_disable_device(dev);
23462306a36Sopenharmony_ci			apbs[boardno - 1].RamIO = NULL;
23562306a36Sopenharmony_ci			continue;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		/* Enable interrupts. */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		apbs[boardno - 1].irq = dev->irq;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Finished with PCI cards. If none registered,
24662306a36Sopenharmony_ci	 * and there was no mem/irq specified, exit */
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!mem || !irq) {
24962306a36Sopenharmony_ci		if (numboards)
25062306a36Sopenharmony_ci			goto fin;
25162306a36Sopenharmony_ci		else {
25262306a36Sopenharmony_ci			printk(KERN_INFO "ac.o: No PCI boards found.\n");
25362306a36Sopenharmony_ci			printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n");
25462306a36Sopenharmony_ci			return -ENXIO;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Now try the specified ISA cards */
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	for (i = 0; i < MAX_ISA_BOARD; i++) {
26162306a36Sopenharmony_ci		RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		if (!RamIO) {
26462306a36Sopenharmony_ci			printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1);
26562306a36Sopenharmony_ci			continue;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i),
26962306a36Sopenharmony_ci						  RamIO,i+1))) {
27062306a36Sopenharmony_ci			iounmap(RamIO);
27162306a36Sopenharmony_ci			continue;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		if (!numisa) {
27762306a36Sopenharmony_ci			if (request_irq(irq, &ac_interrupt, IRQF_SHARED, "Applicom ISA", &dummy)) {
27862306a36Sopenharmony_ci				printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.\n", irq);
27962306a36Sopenharmony_ci				iounmap(RamIO);
28062306a36Sopenharmony_ci				apbs[boardno - 1].RamIO = NULL;
28162306a36Sopenharmony_ci			}
28262306a36Sopenharmony_ci			else
28362306a36Sopenharmony_ci				apbs[boardno - 1].irq = irq;
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci		else
28662306a36Sopenharmony_ci			apbs[boardno - 1].irq = 0;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		numisa++;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (!numisa)
29262306a36Sopenharmony_ci		printk(KERN_WARNING "ac.o: No valid ISA Applicom boards found "
29362306a36Sopenharmony_ci				"at mem 0x%lx\n", mem);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci fin:
29662306a36Sopenharmony_ci	init_waitqueue_head(&FlagSleepRec);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	WriteErrorCount = 0;
29962306a36Sopenharmony_ci	ReadErrorCount = 0;
30062306a36Sopenharmony_ci	DeviceErrorCount = 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (numboards) {
30362306a36Sopenharmony_ci		ret = misc_register(&ac_miscdev);
30462306a36Sopenharmony_ci		if (ret) {
30562306a36Sopenharmony_ci			printk(KERN_WARNING "ac.o: Unable to register misc device\n");
30662306a36Sopenharmony_ci			goto out;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
30962306a36Sopenharmony_ci			int serial;
31062306a36Sopenharmony_ci			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci			if (!apbs[i].RamIO)
31362306a36Sopenharmony_ci				continue;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
31662306a36Sopenharmony_ci				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci			boardname[serial] = 0;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci			printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d",
32262306a36Sopenharmony_ci			       i+1, boardname,
32362306a36Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) >> 4),
32462306a36Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) & 0xF));
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) +
32762306a36Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) +
32862306a36Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci			if (serial != 0)
33162306a36Sopenharmony_ci				printk(" S/N %d\n", serial);
33262306a36Sopenharmony_ci			else
33362306a36Sopenharmony_ci				printk("\n");
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	else
33962306a36Sopenharmony_ci		return -ENXIO;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ciout:
34262306a36Sopenharmony_ci	for (i = 0; i < MAX_BOARD; i++) {
34362306a36Sopenharmony_ci		if (!apbs[i].RamIO)
34462306a36Sopenharmony_ci			continue;
34562306a36Sopenharmony_ci		if (apbs[i].irq)
34662306a36Sopenharmony_ci			free_irq(apbs[i].irq, &dummy);
34762306a36Sopenharmony_ci		iounmap(apbs[i].RamIO);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	return ret;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cimodule_init(applicom_init);
35362306a36Sopenharmony_cimodule_exit(applicom_exit);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic ssize_t ac_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	unsigned int NumCard;	/* Board number 1 -> 8           */
35962306a36Sopenharmony_ci	unsigned int IndexCard;	/* Index board number 0 -> 7     */
36062306a36Sopenharmony_ci	unsigned char TicCard;	/* Board TIC to send             */
36162306a36Sopenharmony_ci	unsigned long flags;	/* Current priority              */
36262306a36Sopenharmony_ci	struct st_ram_io st_loc;
36362306a36Sopenharmony_ci	struct mailbox tmpmailbox;
36462306a36Sopenharmony_ci#ifdef DEBUG
36562306a36Sopenharmony_ci	int c;
36662306a36Sopenharmony_ci#endif
36762306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
37062306a36Sopenharmony_ci		static int warncount = 5;
37162306a36Sopenharmony_ci		if (warncount) {
37262306a36Sopenharmony_ci			printk(KERN_INFO "Hmmm. write() of Applicom card, length %zd != expected %zd\n",
37362306a36Sopenharmony_ci			       count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
37462306a36Sopenharmony_ci			warncount--;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci		return -EINVAL;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if(copy_from_user(&st_loc, buf, sizeof(struct st_ram_io)))
38062306a36Sopenharmony_ci		return -EFAULT;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if(copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)],
38362306a36Sopenharmony_ci			  sizeof(struct mailbox)))
38462306a36Sopenharmony_ci		return -EFAULT;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	NumCard = st_loc.num_card;	/* board number to send          */
38762306a36Sopenharmony_ci	TicCard = st_loc.tic_des_from_pc;	/* tic number to send            */
38862306a36Sopenharmony_ci	IndexCard = NumCard - 1;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (IndexCard >= MAX_BOARD)
39162306a36Sopenharmony_ci		return -EINVAL;
39262306a36Sopenharmony_ci	IndexCard = array_index_nospec(IndexCard, MAX_BOARD);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (!apbs[IndexCard].RamIO)
39562306a36Sopenharmony_ci		return -EINVAL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci#ifdef DEBUG
39862306a36Sopenharmony_ci	printk("Write to applicom card #%d. struct st_ram_io follows:",
39962306a36Sopenharmony_ci	       IndexCard+1);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci		for (c = 0; c < sizeof(struct st_ram_io);) {
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
40662306a36Sopenharmony_ci				printk(" %2.2X", ((unsigned char *) &st_loc)[c]);
40762306a36Sopenharmony_ci			}
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		printk("\nstruct mailbox follows:");
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox);) {
41362306a36Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
41662306a36Sopenharmony_ci				printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]);
41762306a36Sopenharmony_ci			}
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		printk("\n");
42162306a36Sopenharmony_ci#endif
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* Test octet ready correct */
42662306a36Sopenharmony_ci	if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) {
42762306a36Sopenharmony_ci		Dummy = readb(apbs[IndexCard].RamIO + VERS);
42862306a36Sopenharmony_ci		spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
42962306a36Sopenharmony_ci		printk(KERN_WARNING "APPLICOM driver write error board %d, DataFromPcReady = %d\n",
43062306a36Sopenharmony_ci		       IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY));
43162306a36Sopenharmony_ci		DeviceErrorCount++;
43262306a36Sopenharmony_ci		return -EIO;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* Place ourselves on the wait queue */
43662306a36Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
43762306a36Sopenharmony_ci	add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* Check whether the card is ready for us */
44062306a36Sopenharmony_ci	while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) {
44162306a36Sopenharmony_ci		Dummy = readb(apbs[IndexCard].RamIO + VERS);
44262306a36Sopenharmony_ci		/* It's busy. Sleep. */
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
44562306a36Sopenharmony_ci		schedule();
44662306a36Sopenharmony_ci		if (signal_pending(current)) {
44762306a36Sopenharmony_ci			remove_wait_queue(&apbs[IndexCard].FlagSleepSend,
44862306a36Sopenharmony_ci					  &wait);
44962306a36Sopenharmony_ci			return -EINTR;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci		spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
45262306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* We may not have actually slept */
45662306a36Sopenharmony_ci	set_current_state(TASK_RUNNING);
45762306a36Sopenharmony_ci	remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* Which is best - lock down the pages with rawio and then
46262306a36Sopenharmony_ci	   copy directly, or use bounce buffers? For now we do the latter
46362306a36Sopenharmony_ci	   because it works with 2.2 still */
46462306a36Sopenharmony_ci	{
46562306a36Sopenharmony_ci		unsigned char *from = (unsigned char *) &tmpmailbox;
46662306a36Sopenharmony_ci		void __iomem *to = apbs[IndexCard].RamIO + RAM_FROM_PC;
46762306a36Sopenharmony_ci		int c;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox); c++)
47062306a36Sopenharmony_ci			writeb(*(from++), to++);
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC);
47462306a36Sopenharmony_ci	writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC);
47562306a36Sopenharmony_ci	writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC);
47662306a36Sopenharmony_ci	writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
47762306a36Sopenharmony_ci	writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
47862306a36Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
47962306a36Sopenharmony_ci	Dummy = readb(apbs[IndexCard].RamIO + VERS);
48062306a36Sopenharmony_ci	spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int do_ac_read(int IndexCard, char __user *buf,
48562306a36Sopenharmony_ci		struct st_ram_io *st_loc, struct mailbox *mailbox)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	void __iomem *from = apbs[IndexCard].RamIO + RAM_TO_PC;
48862306a36Sopenharmony_ci	unsigned char *to = (unsigned char *)mailbox;
48962306a36Sopenharmony_ci#ifdef DEBUG
49062306a36Sopenharmony_ci	int c;
49162306a36Sopenharmony_ci#endif
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	st_loc->tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC);
49462306a36Sopenharmony_ci	st_loc->numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	{
49862306a36Sopenharmony_ci		int c;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox); c++)
50162306a36Sopenharmony_ci			*(to++) = readb(from++);
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
50462306a36Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC);
50562306a36Sopenharmony_ci	writeb(IndexCard+1, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
50662306a36Sopenharmony_ci	writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC),
50762306a36Sopenharmony_ci	       apbs[IndexCard].RamIO + TIC_ACK_FROM_PC);
50862306a36Sopenharmony_ci	writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
50962306a36Sopenharmony_ci	writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY);
51062306a36Sopenharmony_ci	writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
51162306a36Sopenharmony_ci	Dummy = readb(apbs[IndexCard].RamIO + VERS);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci#ifdef DEBUG
51462306a36Sopenharmony_ci		printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		for (c = 0; c < sizeof(struct st_ram_io);) {
51762306a36Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *)st_loc)[c]);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
52062306a36Sopenharmony_ci				printk(" %2.2X", ((unsigned char *)st_loc)[c]);
52162306a36Sopenharmony_ci			}
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci		printk("\nstruct mailbox follows:");
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox);) {
52762306a36Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *)mailbox)[c]);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
53062306a36Sopenharmony_ci				printk(" %2.2X", ((unsigned char *)mailbox)[c]);
53162306a36Sopenharmony_ci			}
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci		printk("\n");
53462306a36Sopenharmony_ci#endif
53562306a36Sopenharmony_ci	return (sizeof(struct st_ram_io) + sizeof(struct mailbox));
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_t *ptr)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	unsigned long flags;
54162306a36Sopenharmony_ci	unsigned int i;
54262306a36Sopenharmony_ci	unsigned char tmp;
54362306a36Sopenharmony_ci	int ret = 0;
54462306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
54562306a36Sopenharmony_ci#ifdef DEBUG
54662306a36Sopenharmony_ci	int loopcount=0;
54762306a36Sopenharmony_ci#endif
54862306a36Sopenharmony_ci	/* No need to ratelimit this. Only root can trigger it anyway */
54962306a36Sopenharmony_ci	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
55062306a36Sopenharmony_ci		printk( KERN_WARNING "Hmmm. read() of Applicom card, length %zd != expected %zd\n",
55162306a36Sopenharmony_ci			count,sizeof(struct st_ram_io) + sizeof(struct mailbox));
55262306a36Sopenharmony_ci		return -EINVAL;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	while(1) {
55662306a36Sopenharmony_ci		/* Stick ourself on the wait queue */
55762306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
55862306a36Sopenharmony_ci		add_wait_queue(&FlagSleepRec, &wait);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		/* Scan each board, looking for one which has a packet for us */
56162306a36Sopenharmony_ci		for (i=0; i < MAX_BOARD; i++) {
56262306a36Sopenharmony_ci			if (!apbs[i].RamIO)
56362306a36Sopenharmony_ci				continue;
56462306a36Sopenharmony_ci			spin_lock_irqsave(&apbs[i].mutex, flags);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci			tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			if (tmp == 2) {
56962306a36Sopenharmony_ci				struct st_ram_io st_loc;
57062306a36Sopenharmony_ci				struct mailbox mailbox;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci				/* Got a packet for us */
57362306a36Sopenharmony_ci				memset(&st_loc, 0, sizeof(st_loc));
57462306a36Sopenharmony_ci				ret = do_ac_read(i, buf, &st_loc, &mailbox);
57562306a36Sopenharmony_ci				spin_unlock_irqrestore(&apbs[i].mutex, flags);
57662306a36Sopenharmony_ci				set_current_state(TASK_RUNNING);
57762306a36Sopenharmony_ci				remove_wait_queue(&FlagSleepRec, &wait);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci				if (copy_to_user(buf, &st_loc, sizeof(st_loc)))
58062306a36Sopenharmony_ci					return -EFAULT;
58162306a36Sopenharmony_ci				if (copy_to_user(buf + sizeof(st_loc), &mailbox, sizeof(mailbox)))
58262306a36Sopenharmony_ci					return -EFAULT;
58362306a36Sopenharmony_ci				return tmp;
58462306a36Sopenharmony_ci			}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci			if (tmp > 2) {
58762306a36Sopenharmony_ci				/* Got an error */
58862306a36Sopenharmony_ci				Dummy = readb(apbs[i].RamIO + VERS);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci				spin_unlock_irqrestore(&apbs[i].mutex, flags);
59162306a36Sopenharmony_ci				set_current_state(TASK_RUNNING);
59262306a36Sopenharmony_ci				remove_wait_queue(&FlagSleepRec, &wait);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci				printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n",
59562306a36Sopenharmony_ci				       i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
59662306a36Sopenharmony_ci				DeviceErrorCount++;
59762306a36Sopenharmony_ci				return -EIO;
59862306a36Sopenharmony_ci			}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci			/* Nothing for us. Try the next board */
60162306a36Sopenharmony_ci			Dummy = readb(apbs[i].RamIO + VERS);
60262306a36Sopenharmony_ci			spin_unlock_irqrestore(&apbs[i].mutex, flags);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		} /* per board */
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		/* OK - No boards had data for us. Sleep now */
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		schedule();
60962306a36Sopenharmony_ci		remove_wait_queue(&FlagSleepRec, &wait);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		if (signal_pending(current))
61262306a36Sopenharmony_ci			return -EINTR;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci#ifdef DEBUG
61562306a36Sopenharmony_ci		if (loopcount++ > 2) {
61662306a36Sopenharmony_ci			printk(KERN_DEBUG "Looping in ac_read. loopcount %d\n", loopcount);
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci#endif
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic irqreturn_t ac_interrupt(int vec, void *dev_instance)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	unsigned int i;
62562306a36Sopenharmony_ci	unsigned int FlagInt;
62662306a36Sopenharmony_ci	unsigned int LoopCount;
62762306a36Sopenharmony_ci	int handled = 0;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	//    printk("Applicom interrupt on IRQ %d occurred\n", vec);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	LoopCount = 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	do {
63462306a36Sopenharmony_ci		FlagInt = 0;
63562306a36Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci			/* Skip if this board doesn't exist */
63862306a36Sopenharmony_ci			if (!apbs[i].RamIO)
63962306a36Sopenharmony_ci				continue;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci			spin_lock(&apbs[i].mutex);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci			/* Skip if this board doesn't want attention */
64462306a36Sopenharmony_ci			if(readb(apbs[i].RamIO + RAM_IT_TO_PC) == 0) {
64562306a36Sopenharmony_ci				spin_unlock(&apbs[i].mutex);
64662306a36Sopenharmony_ci				continue;
64762306a36Sopenharmony_ci			}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci			handled = 1;
65062306a36Sopenharmony_ci			FlagInt = 1;
65162306a36Sopenharmony_ci			writeb(0, apbs[i].RamIO + RAM_IT_TO_PC);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) {
65462306a36Sopenharmony_ci				printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n",
65562306a36Sopenharmony_ci				       i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
65662306a36Sopenharmony_ci				DeviceErrorCount++;
65762306a36Sopenharmony_ci			}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci			if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) &&
66062306a36Sopenharmony_ci			   (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) {
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci				printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n",
66362306a36Sopenharmony_ci				       i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY));
66462306a36Sopenharmony_ci				DeviceErrorCount++;
66562306a36Sopenharmony_ci			}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) {	/* mailbox sent by the card ?   */
66862306a36Sopenharmony_ci				if (waitqueue_active(&FlagSleepRec)) {
66962306a36Sopenharmony_ci				wake_up_interruptible(&FlagSleepRec);
67062306a36Sopenharmony_ci			}
67162306a36Sopenharmony_ci			}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci			if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) {	/* ram i/o free for write by pc ? */
67462306a36Sopenharmony_ci				if (waitqueue_active(&apbs[i].FlagSleepSend)) {	/* process sleep during read ?    */
67562306a36Sopenharmony_ci					wake_up_interruptible(&apbs[i].FlagSleepSend);
67662306a36Sopenharmony_ci				}
67762306a36Sopenharmony_ci			}
67862306a36Sopenharmony_ci			Dummy = readb(apbs[i].RamIO + VERS);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci			if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) {
68162306a36Sopenharmony_ci				/* There's another int waiting on this card */
68262306a36Sopenharmony_ci				spin_unlock(&apbs[i].mutex);
68362306a36Sopenharmony_ci				i--;
68462306a36Sopenharmony_ci			} else {
68562306a36Sopenharmony_ci				spin_unlock(&apbs[i].mutex);
68662306a36Sopenharmony_ci			}
68762306a36Sopenharmony_ci		}
68862306a36Sopenharmony_ci		if (FlagInt)
68962306a36Sopenharmony_ci			LoopCount = 0;
69062306a36Sopenharmony_ci		else
69162306a36Sopenharmony_ci			LoopCount++;
69262306a36Sopenharmony_ci	} while(LoopCount < 2);
69362306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci{				/* @ ADG ou ATO selon le cas */
70162306a36Sopenharmony_ci	int i;
70262306a36Sopenharmony_ci	unsigned char IndexCard;
70362306a36Sopenharmony_ci	void __iomem *pmem;
70462306a36Sopenharmony_ci	int ret = 0;
70562306a36Sopenharmony_ci	static int warncount = 10;
70662306a36Sopenharmony_ci	volatile unsigned char byte_reset_it;
70762306a36Sopenharmony_ci	struct st_ram_io *adgl;
70862306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* In general, the device is only openable by root anyway, so we're not
71162306a36Sopenharmony_ci	   particularly concerned that bogus ioctls can flood the console. */
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	adgl = memdup_user(argp, sizeof(struct st_ram_io));
71462306a36Sopenharmony_ci	if (IS_ERR(adgl))
71562306a36Sopenharmony_ci		return PTR_ERR(adgl);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	mutex_lock(&ac_mutex);
71862306a36Sopenharmony_ci	IndexCard = adgl->num_card-1;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (cmd != 6 && IndexCard >= MAX_BOARD)
72162306a36Sopenharmony_ci		goto err;
72262306a36Sopenharmony_ci	IndexCard = array_index_nospec(IndexCard, MAX_BOARD);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (cmd != 6 && !apbs[IndexCard].RamIO)
72562306a36Sopenharmony_ci		goto err;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	switch (cmd) {
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	case 0:
73062306a36Sopenharmony_ci		pmem = apbs[IndexCard].RamIO;
73162306a36Sopenharmony_ci		for (i = 0; i < sizeof(struct st_ram_io); i++)
73262306a36Sopenharmony_ci			((unsigned char *)adgl)[i]=readb(pmem++);
73362306a36Sopenharmony_ci		if (copy_to_user(argp, adgl, sizeof(struct st_ram_io)))
73462306a36Sopenharmony_ci			ret = -EFAULT;
73562306a36Sopenharmony_ci		break;
73662306a36Sopenharmony_ci	case 1:
73762306a36Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
73862306a36Sopenharmony_ci		for (i = 0; i < 4; i++)
73962306a36Sopenharmony_ci			adgl->conf_end_test[i] = readb(pmem++);
74062306a36Sopenharmony_ci		for (i = 0; i < 2; i++)
74162306a36Sopenharmony_ci			adgl->error_code[i] = readb(pmem++);
74262306a36Sopenharmony_ci		for (i = 0; i < 4; i++)
74362306a36Sopenharmony_ci			adgl->parameter_error[i] = readb(pmem++);
74462306a36Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + VERS;
74562306a36Sopenharmony_ci		adgl->vers = readb(pmem);
74662306a36Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + TYPE_CARD;
74762306a36Sopenharmony_ci		for (i = 0; i < 20; i++)
74862306a36Sopenharmony_ci			adgl->reserv1[i] = readb(pmem++);
74962306a36Sopenharmony_ci		*(int *)&adgl->reserv1[20] =
75062306a36Sopenharmony_ci			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) +
75162306a36Sopenharmony_ci			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) +
75262306a36Sopenharmony_ci			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) );
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		if (copy_to_user(argp, adgl, sizeof(struct st_ram_io)))
75562306a36Sopenharmony_ci			ret = -EFAULT;
75662306a36Sopenharmony_ci		break;
75762306a36Sopenharmony_ci	case 2:
75862306a36Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
75962306a36Sopenharmony_ci		for (i = 0; i < 10; i++)
76062306a36Sopenharmony_ci			writeb(0xff, pmem++);
76162306a36Sopenharmony_ci		writeb(adgl->data_from_pc_ready,
76262306a36Sopenharmony_ci		       apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
76762306a36Sopenharmony_ci			if (apbs[i].RamIO) {
76862306a36Sopenharmony_ci				byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC);
76962306a36Sopenharmony_ci			}
77062306a36Sopenharmony_ci		}
77162306a36Sopenharmony_ci		break;
77262306a36Sopenharmony_ci	case 3:
77362306a36Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC;
77462306a36Sopenharmony_ci		writeb(adgl->tic_des_from_pc, pmem);
77562306a36Sopenharmony_ci		break;
77662306a36Sopenharmony_ci	case 4:
77762306a36Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC;
77862306a36Sopenharmony_ci		adgl->tic_owner_to_pc     = readb(pmem++);
77962306a36Sopenharmony_ci		adgl->numcard_owner_to_pc = readb(pmem);
78062306a36Sopenharmony_ci		if (copy_to_user(argp, adgl,sizeof(struct st_ram_io)))
78162306a36Sopenharmony_ci			ret = -EFAULT;
78262306a36Sopenharmony_ci		break;
78362306a36Sopenharmony_ci	case 5:
78462306a36Sopenharmony_ci		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
78562306a36Sopenharmony_ci		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
78662306a36Sopenharmony_ci		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
78762306a36Sopenharmony_ci		writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
78862306a36Sopenharmony_ci		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
78962306a36Sopenharmony_ci		break;
79062306a36Sopenharmony_ci	case 6:
79162306a36Sopenharmony_ci		printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)\n");
79262306a36Sopenharmony_ci		printk(KERN_INFO "Number of installed boards . %d\n", (int) numboards);
79362306a36Sopenharmony_ci		printk(KERN_INFO "Segment of board ........... %X\n", (int) mem);
79462306a36Sopenharmony_ci		printk(KERN_INFO "Interrupt IRQ number ....... %d\n", (int) irq);
79562306a36Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
79662306a36Sopenharmony_ci			int serial;
79762306a36Sopenharmony_ci			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci			if (!apbs[i].RamIO)
80062306a36Sopenharmony_ci				continue;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
80362306a36Sopenharmony_ci				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
80462306a36Sopenharmony_ci			boardname[serial] = 0;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci			printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",
80762306a36Sopenharmony_ci			       i+1,
80862306a36Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) >> 4),
80962306a36Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) & 0xF),
81062306a36Sopenharmony_ci			       boardname);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) +
81462306a36Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) +
81562306a36Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci			if (serial != 0)
81862306a36Sopenharmony_ci				printk(" S/N %d\n", serial);
81962306a36Sopenharmony_ci			else
82062306a36Sopenharmony_ci				printk("\n");
82162306a36Sopenharmony_ci		}
82262306a36Sopenharmony_ci		if (DeviceErrorCount != 0)
82362306a36Sopenharmony_ci			printk(KERN_INFO "DeviceErrorCount ........... %d\n", DeviceErrorCount);
82462306a36Sopenharmony_ci		if (ReadErrorCount != 0)
82562306a36Sopenharmony_ci			printk(KERN_INFO "ReadErrorCount ............. %d\n", ReadErrorCount);
82662306a36Sopenharmony_ci		if (WriteErrorCount != 0)
82762306a36Sopenharmony_ci			printk(KERN_INFO "WriteErrorCount ............ %d\n", WriteErrorCount);
82862306a36Sopenharmony_ci		if (waitqueue_active(&FlagSleepRec))
82962306a36Sopenharmony_ci			printk(KERN_INFO "Process in read pending\n");
83062306a36Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
83162306a36Sopenharmony_ci			if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend))
83262306a36Sopenharmony_ci				printk(KERN_INFO "Process in write pending board %d\n",i+1);
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci		break;
83562306a36Sopenharmony_ci	default:
83662306a36Sopenharmony_ci		ret = -ENOTTY;
83762306a36Sopenharmony_ci		break;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci	Dummy = readb(apbs[IndexCard].RamIO + VERS);
84062306a36Sopenharmony_ci	kfree(adgl);
84162306a36Sopenharmony_ci	mutex_unlock(&ac_mutex);
84262306a36Sopenharmony_ci	return ret;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cierr:
84562306a36Sopenharmony_ci	if (warncount) {
84662306a36Sopenharmony_ci		pr_warn("APPLICOM driver IOCTL, bad board number %d\n",
84762306a36Sopenharmony_ci			(int)IndexCard + 1);
84862306a36Sopenharmony_ci		warncount--;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci	kfree(adgl);
85162306a36Sopenharmony_ci	mutex_unlock(&ac_mutex);
85262306a36Sopenharmony_ci	return -EINVAL;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
856