18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Derived from Applicom driver ac.c for SCO Unix                            */
38c2ecf20Sopenharmony_ci/* Ported by David Woodhouse, Axiom (Cambridge) Ltd.                         */
48c2ecf20Sopenharmony_ci/* dwmw2@infradead.org 30/8/98                                               */
58c2ecf20Sopenharmony_ci/* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $			     */
68c2ecf20Sopenharmony_ci/* This module is for Linux 2.1 and 2.2 series kernels.                      */
78c2ecf20Sopenharmony_ci/*****************************************************************************/
88c2ecf20Sopenharmony_ci/* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt  */
98c2ecf20Sopenharmony_ci/* ceci pour reseter correctement apres une sortie sauvage                   */
108c2ecf20Sopenharmony_ci/* J PAGET 02/05/94 passage V2.4.3 dans le traitement de d'interruption,     */
118c2ecf20Sopenharmony_ci/* LoopCount n'etait pas initialise a 0.                                     */
128c2ecf20Sopenharmony_ci/* F LAFORSE 04/07/95 version V2.6.0 lecture bidon apres acces a une carte   */
138c2ecf20Sopenharmony_ci/*           pour liberer le bus                                             */
148c2ecf20Sopenharmony_ci/* J.PAGET 19/11/95 version V2.6.1 Nombre, addresse,irq n'est plus configure */
158c2ecf20Sopenharmony_ci/* et passe en argument a acinit, mais est scrute sur le bus pour s'adapter  */
168c2ecf20Sopenharmony_ci/* au nombre de cartes presentes sur le bus. IOCL code 6 affichait V2.4.3    */
178c2ecf20Sopenharmony_ci/* F.LAFORSE 28/11/95 creation de fichiers acXX.o avec les differentes       */
188c2ecf20Sopenharmony_ci/* addresses de base des cartes, IOCTL 6 plus complet                         */
198c2ecf20Sopenharmony_ci/* J.PAGET le 19/08/96 copie de la version V2.6 en V2.8.0 sans modification  */
208c2ecf20Sopenharmony_ci/* de code autre que le texte V2.6.1 en V2.8.0                               */
218c2ecf20Sopenharmony_ci/*****************************************************************************/
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/kernel.h>
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
288c2ecf20Sopenharmony_ci#include <linux/slab.h>
298c2ecf20Sopenharmony_ci#include <linux/errno.h>
308c2ecf20Sopenharmony_ci#include <linux/mutex.h>
318c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
328c2ecf20Sopenharmony_ci#include <linux/pci.h>
338c2ecf20Sopenharmony_ci#include <linux/wait.h>
348c2ecf20Sopenharmony_ci#include <linux/init.h>
358c2ecf20Sopenharmony_ci#include <linux/fs.h>
368c2ecf20Sopenharmony_ci#include <linux/nospec.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include <asm/io.h>
398c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include "applicom.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* NOTE: We use for loops with {write,read}b() instead of
458c2ecf20Sopenharmony_ci   memcpy_{from,to}io throughout this driver. This is because
468c2ecf20Sopenharmony_ci   the board doesn't correctly handle word accesses - only
478c2ecf20Sopenharmony_ci   bytes.
488c2ecf20Sopenharmony_ci*/
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#undef DEBUG
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define MAX_BOARD 8		/* maximum of pc board possible */
548c2ecf20Sopenharmony_ci#define MAX_ISA_BOARD 4
558c2ecf20Sopenharmony_ci#define LEN_RAM_IO 0x800
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#ifndef PCI_VENDOR_ID_APPLICOM
588c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_APPLICOM                0x1389
598c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC     0x0001
608c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002
618c2ecf20Sopenharmony_ci#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB     0x0003
628c2ecf20Sopenharmony_ci#endif
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ac_mutex);
658c2ecf20Sopenharmony_cistatic char *applicom_pci_devnames[] = {
668c2ecf20Sopenharmony_ci	"PCI board",
678c2ecf20Sopenharmony_ci	"PCI2000IBS / PCI2000CAN",
688c2ecf20Sopenharmony_ci	"PCI2000PFB"
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic const struct pci_device_id applicom_pci_tbl[] = {
728c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCIGENERIC) },
738c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN) },
748c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000PFB) },
758c2ecf20Sopenharmony_ci	{ 0 }
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, applicom_pci_tbl);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Woodhouse & Applicom International");
808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Applicom Profibus card");
818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
828c2ecf20Sopenharmony_ciMODULE_ALIAS_MISCDEV(AC_MINOR);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("ac");
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct applicom_board {
888c2ecf20Sopenharmony_ci	unsigned long PhysIO;
898c2ecf20Sopenharmony_ci	void __iomem *RamIO;
908c2ecf20Sopenharmony_ci	wait_queue_head_t FlagSleepSend;
918c2ecf20Sopenharmony_ci	long irq;
928c2ecf20Sopenharmony_ci	spinlock_t mutex;
938c2ecf20Sopenharmony_ci} apbs[MAX_BOARD];
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic unsigned int irq = 0;	/* interrupt number IRQ       */
968c2ecf20Sopenharmony_cistatic unsigned long mem = 0;	/* physical segment of board  */
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cimodule_param_hw(irq, uint, irq, 0);
998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ of the Applicom board");
1008c2ecf20Sopenharmony_cimodule_param_hw(mem, ulong, iomem, 0);
1018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mem, "Shared Memory Address of Applicom board");
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic unsigned int numboards;	/* number of installed boards */
1048c2ecf20Sopenharmony_cistatic volatile unsigned char Dummy;
1058c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec);
1068c2ecf20Sopenharmony_cistatic unsigned int WriteErrorCount;	/* number of write error      */
1078c2ecf20Sopenharmony_cistatic unsigned int ReadErrorCount;	/* number of read error       */
1088c2ecf20Sopenharmony_cistatic unsigned int DeviceErrorCount;	/* number of device error     */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic ssize_t ac_read (struct file *, char __user *, size_t, loff_t *);
1118c2ecf20Sopenharmony_cistatic ssize_t ac_write (struct file *, const char __user *, size_t, loff_t *);
1128c2ecf20Sopenharmony_cistatic long ac_ioctl(struct file *, unsigned int, unsigned long);
1138c2ecf20Sopenharmony_cistatic irqreturn_t ac_interrupt(int, void *);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic const struct file_operations ac_fops = {
1168c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1178c2ecf20Sopenharmony_ci	.llseek = no_llseek,
1188c2ecf20Sopenharmony_ci	.read = ac_read,
1198c2ecf20Sopenharmony_ci	.write = ac_write,
1208c2ecf20Sopenharmony_ci	.unlocked_ioctl = ac_ioctl,
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic struct miscdevice ac_miscdev = {
1248c2ecf20Sopenharmony_ci	AC_MINOR,
1258c2ecf20Sopenharmony_ci	"ac",
1268c2ecf20Sopenharmony_ci	&ac_fops
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int dummy;	/* dev_id for request_irq() */
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int ac_register_board(unsigned long physloc, void __iomem *loc,
1328c2ecf20Sopenharmony_ci		      unsigned char boardno)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	volatile unsigned char byte_reset_it;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if((readb(loc + CONF_END_TEST)     != 0x00) ||
1378c2ecf20Sopenharmony_ci	   (readb(loc + CONF_END_TEST + 1) != 0x55) ||
1388c2ecf20Sopenharmony_ci	   (readb(loc + CONF_END_TEST + 2) != 0xAA) ||
1398c2ecf20Sopenharmony_ci	   (readb(loc + CONF_END_TEST + 3) != 0xFF))
1408c2ecf20Sopenharmony_ci		return 0;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!boardno)
1438c2ecf20Sopenharmony_ci		boardno = readb(loc + NUMCARD_OWNER_TO_PC);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (!boardno || boardno > MAX_BOARD) {
1468c2ecf20Sopenharmony_ci		printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n",
1478c2ecf20Sopenharmony_ci		       boardno, physloc, MAX_BOARD);
1488c2ecf20Sopenharmony_ci		return 0;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (apbs[boardno - 1].RamIO) {
1528c2ecf20Sopenharmony_ci		printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n",
1538c2ecf20Sopenharmony_ci		       boardno, physloc, boardno, apbs[boardno-1].PhysIO);
1548c2ecf20Sopenharmony_ci		return 0;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	boardno--;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	apbs[boardno].PhysIO = physloc;
1608c2ecf20Sopenharmony_ci	apbs[boardno].RamIO = loc;
1618c2ecf20Sopenharmony_ci	init_waitqueue_head(&apbs[boardno].FlagSleepSend);
1628c2ecf20Sopenharmony_ci	spin_lock_init(&apbs[boardno].mutex);
1638c2ecf20Sopenharmony_ci	byte_reset_it = readb(loc + RAM_IT_TO_PC);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	numboards++;
1668c2ecf20Sopenharmony_ci	return boardno + 1;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void __exit applicom_exit(void)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	unsigned int i;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	misc_deregister(&ac_miscdev);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BOARD; i++) {
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		if (!apbs[i].RamIO)
1788c2ecf20Sopenharmony_ci			continue;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		if (apbs[i].irq)
1818c2ecf20Sopenharmony_ci			free_irq(apbs[i].irq, &dummy);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		iounmap(apbs[i].RamIO);
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int __init applicom_init(void)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int i, numisa = 0;
1908c2ecf20Sopenharmony_ci	struct pci_dev *dev = NULL;
1918c2ecf20Sopenharmony_ci	void __iomem *RamIO;
1928c2ecf20Sopenharmony_ci	int boardno, ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $\n");
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* No mem and irq given - check for a PCI card */
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	while ( (dev = pci_get_class(PCI_CLASS_OTHERS << 16, dev))) {
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		if (!pci_match_id(applicom_pci_tbl, dev))
2018c2ecf20Sopenharmony_ci			continue;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		if (pci_enable_device(dev))
2048c2ecf20Sopenharmony_ci			return -EIO;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		RamIO = ioremap(pci_resource_start(dev, 0), LEN_RAM_IO);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		if (!RamIO) {
2098c2ecf20Sopenharmony_ci			printk(KERN_INFO "ac.o: Failed to ioremap PCI memory "
2108c2ecf20Sopenharmony_ci				"space at 0x%llx\n",
2118c2ecf20Sopenharmony_ci				(unsigned long long)pci_resource_start(dev, 0));
2128c2ecf20Sopenharmony_ci			pci_disable_device(dev);
2138c2ecf20Sopenharmony_ci			return -EIO;
2148c2ecf20Sopenharmony_ci		}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		printk(KERN_INFO "Applicom %s found at mem 0x%llx, irq %d\n",
2178c2ecf20Sopenharmony_ci		       applicom_pci_devnames[dev->device-1],
2188c2ecf20Sopenharmony_ci			   (unsigned long long)pci_resource_start(dev, 0),
2198c2ecf20Sopenharmony_ci		       dev->irq);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		boardno = ac_register_board(pci_resource_start(dev, 0),
2228c2ecf20Sopenharmony_ci				RamIO, 0);
2238c2ecf20Sopenharmony_ci		if (!boardno) {
2248c2ecf20Sopenharmony_ci			printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n");
2258c2ecf20Sopenharmony_ci			iounmap(RamIO);
2268c2ecf20Sopenharmony_ci			pci_disable_device(dev);
2278c2ecf20Sopenharmony_ci			continue;
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		if (request_irq(dev->irq, &ac_interrupt, IRQF_SHARED, "Applicom PCI", &dummy)) {
2318c2ecf20Sopenharmony_ci			printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq);
2328c2ecf20Sopenharmony_ci			iounmap(RamIO);
2338c2ecf20Sopenharmony_ci			pci_disable_device(dev);
2348c2ecf20Sopenharmony_ci			apbs[boardno - 1].RamIO = NULL;
2358c2ecf20Sopenharmony_ci			continue;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		/* Enable interrupts. */
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		writeb(0x40, apbs[boardno - 1].RamIO + RAM_IT_FROM_PC);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		apbs[boardno - 1].irq = dev->irq;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Finished with PCI cards. If none registered,
2468c2ecf20Sopenharmony_ci	 * and there was no mem/irq specified, exit */
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (!mem || !irq) {
2498c2ecf20Sopenharmony_ci		if (numboards)
2508c2ecf20Sopenharmony_ci			goto fin;
2518c2ecf20Sopenharmony_ci		else {
2528c2ecf20Sopenharmony_ci			printk(KERN_INFO "ac.o: No PCI boards found.\n");
2538c2ecf20Sopenharmony_ci			printk(KERN_INFO "ac.o: For an ISA board you must supply memory and irq parameters.\n");
2548c2ecf20Sopenharmony_ci			return -ENXIO;
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* Now try the specified ISA cards */
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_ISA_BOARD; i++) {
2618c2ecf20Sopenharmony_ci		RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		if (!RamIO) {
2648c2ecf20Sopenharmony_ci			printk(KERN_INFO "ac.o: Failed to ioremap the ISA card's memory space (slot #%d)\n", i + 1);
2658c2ecf20Sopenharmony_ci			continue;
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i),
2698c2ecf20Sopenharmony_ci						  RamIO,i+1))) {
2708c2ecf20Sopenharmony_ci			iounmap(RamIO);
2718c2ecf20Sopenharmony_ci			continue;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		if (!numisa) {
2778c2ecf20Sopenharmony_ci			if (request_irq(irq, &ac_interrupt, IRQF_SHARED, "Applicom ISA", &dummy)) {
2788c2ecf20Sopenharmony_ci				printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.\n", irq);
2798c2ecf20Sopenharmony_ci				iounmap(RamIO);
2808c2ecf20Sopenharmony_ci				apbs[boardno - 1].RamIO = NULL;
2818c2ecf20Sopenharmony_ci			}
2828c2ecf20Sopenharmony_ci			else
2838c2ecf20Sopenharmony_ci				apbs[boardno - 1].irq = irq;
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci		else
2868c2ecf20Sopenharmony_ci			apbs[boardno - 1].irq = 0;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		numisa++;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (!numisa)
2928c2ecf20Sopenharmony_ci		printk(KERN_WARNING "ac.o: No valid ISA Applicom boards found "
2938c2ecf20Sopenharmony_ci				"at mem 0x%lx\n", mem);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci fin:
2968c2ecf20Sopenharmony_ci	init_waitqueue_head(&FlagSleepRec);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	WriteErrorCount = 0;
2998c2ecf20Sopenharmony_ci	ReadErrorCount = 0;
3008c2ecf20Sopenharmony_ci	DeviceErrorCount = 0;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (numboards) {
3038c2ecf20Sopenharmony_ci		ret = misc_register(&ac_miscdev);
3048c2ecf20Sopenharmony_ci		if (ret) {
3058c2ecf20Sopenharmony_ci			printk(KERN_WARNING "ac.o: Unable to register misc device\n");
3068c2ecf20Sopenharmony_ci			goto out;
3078c2ecf20Sopenharmony_ci		}
3088c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
3098c2ecf20Sopenharmony_ci			int serial;
3108c2ecf20Sopenharmony_ci			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci			if (!apbs[i].RamIO)
3138c2ecf20Sopenharmony_ci				continue;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
3168c2ecf20Sopenharmony_ci				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci			boardname[serial] = 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci			printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d",
3228c2ecf20Sopenharmony_ci			       i+1, boardname,
3238c2ecf20Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) >> 4),
3248c2ecf20Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) & 0xF));
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) +
3278c2ecf20Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) +
3288c2ecf20Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci			if (serial != 0)
3318c2ecf20Sopenharmony_ci				printk(" S/N %d\n", serial);
3328c2ecf20Sopenharmony_ci			else
3338c2ecf20Sopenharmony_ci				printk("\n");
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci		return 0;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	else
3398c2ecf20Sopenharmony_ci		return -ENXIO;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ciout:
3428c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_BOARD; i++) {
3438c2ecf20Sopenharmony_ci		if (!apbs[i].RamIO)
3448c2ecf20Sopenharmony_ci			continue;
3458c2ecf20Sopenharmony_ci		if (apbs[i].irq)
3468c2ecf20Sopenharmony_ci			free_irq(apbs[i].irq, &dummy);
3478c2ecf20Sopenharmony_ci		iounmap(apbs[i].RamIO);
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci	return ret;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cimodule_init(applicom_init);
3538c2ecf20Sopenharmony_cimodule_exit(applicom_exit);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic ssize_t ac_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	unsigned int NumCard;	/* Board number 1 -> 8           */
3598c2ecf20Sopenharmony_ci	unsigned int IndexCard;	/* Index board number 0 -> 7     */
3608c2ecf20Sopenharmony_ci	unsigned char TicCard;	/* Board TIC to send             */
3618c2ecf20Sopenharmony_ci	unsigned long flags;	/* Current priority              */
3628c2ecf20Sopenharmony_ci	struct st_ram_io st_loc;
3638c2ecf20Sopenharmony_ci	struct mailbox tmpmailbox;
3648c2ecf20Sopenharmony_ci#ifdef DEBUG
3658c2ecf20Sopenharmony_ci	int c;
3668c2ecf20Sopenharmony_ci#endif
3678c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
3708c2ecf20Sopenharmony_ci		static int warncount = 5;
3718c2ecf20Sopenharmony_ci		if (warncount) {
3728c2ecf20Sopenharmony_ci			printk(KERN_INFO "Hmmm. write() of Applicom card, length %zd != expected %zd\n",
3738c2ecf20Sopenharmony_ci			       count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
3748c2ecf20Sopenharmony_ci			warncount--;
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci		return -EINVAL;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if(copy_from_user(&st_loc, buf, sizeof(struct st_ram_io)))
3808c2ecf20Sopenharmony_ci		return -EFAULT;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if(copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)],
3838c2ecf20Sopenharmony_ci			  sizeof(struct mailbox)))
3848c2ecf20Sopenharmony_ci		return -EFAULT;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	NumCard = st_loc.num_card;	/* board number to send          */
3878c2ecf20Sopenharmony_ci	TicCard = st_loc.tic_des_from_pc;	/* tic number to send            */
3888c2ecf20Sopenharmony_ci	IndexCard = NumCard - 1;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (IndexCard >= MAX_BOARD)
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci	IndexCard = array_index_nospec(IndexCard, MAX_BOARD);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (!apbs[IndexCard].RamIO)
3958c2ecf20Sopenharmony_ci		return -EINVAL;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci#ifdef DEBUG
3988c2ecf20Sopenharmony_ci	printk("Write to applicom card #%d. struct st_ram_io follows:",
3998c2ecf20Sopenharmony_ci	       IndexCard+1);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		for (c = 0; c < sizeof(struct st_ram_io);) {
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
4068c2ecf20Sopenharmony_ci				printk(" %2.2X", ((unsigned char *) &st_loc)[c]);
4078c2ecf20Sopenharmony_ci			}
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		printk("\nstruct mailbox follows:");
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox);) {
4138c2ecf20Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *) &tmpmailbox)[c]);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
4168c2ecf20Sopenharmony_ci				printk(" %2.2X", ((unsigned char *) &tmpmailbox)[c]);
4178c2ecf20Sopenharmony_ci			}
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		printk("\n");
4218c2ecf20Sopenharmony_ci#endif
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* Test octet ready correct */
4268c2ecf20Sopenharmony_ci	if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) {
4278c2ecf20Sopenharmony_ci		Dummy = readb(apbs[IndexCard].RamIO + VERS);
4288c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
4298c2ecf20Sopenharmony_ci		printk(KERN_WARNING "APPLICOM driver write error board %d, DataFromPcReady = %d\n",
4308c2ecf20Sopenharmony_ci		       IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY));
4318c2ecf20Sopenharmony_ci		DeviceErrorCount++;
4328c2ecf20Sopenharmony_ci		return -EIO;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Place ourselves on the wait queue */
4368c2ecf20Sopenharmony_ci	set_current_state(TASK_INTERRUPTIBLE);
4378c2ecf20Sopenharmony_ci	add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Check whether the card is ready for us */
4408c2ecf20Sopenharmony_ci	while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) {
4418c2ecf20Sopenharmony_ci		Dummy = readb(apbs[IndexCard].RamIO + VERS);
4428c2ecf20Sopenharmony_ci		/* It's busy. Sleep. */
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
4458c2ecf20Sopenharmony_ci		schedule();
4468c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
4478c2ecf20Sopenharmony_ci			remove_wait_queue(&apbs[IndexCard].FlagSleepSend,
4488c2ecf20Sopenharmony_ci					  &wait);
4498c2ecf20Sopenharmony_ci			return -EINTR;
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci		spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
4528c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/* We may not have actually slept */
4568c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
4578c2ecf20Sopenharmony_ci	remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/* Which is best - lock down the pages with rawio and then
4628c2ecf20Sopenharmony_ci	   copy directly, or use bounce buffers? For now we do the latter
4638c2ecf20Sopenharmony_ci	   because it works with 2.2 still */
4648c2ecf20Sopenharmony_ci	{
4658c2ecf20Sopenharmony_ci		unsigned char *from = (unsigned char *) &tmpmailbox;
4668c2ecf20Sopenharmony_ci		void __iomem *to = apbs[IndexCard].RamIO + RAM_FROM_PC;
4678c2ecf20Sopenharmony_ci		int c;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox); c++)
4708c2ecf20Sopenharmony_ci			writeb(*(from++), to++);
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC);
4748c2ecf20Sopenharmony_ci	writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC);
4758c2ecf20Sopenharmony_ci	writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC);
4768c2ecf20Sopenharmony_ci	writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
4778c2ecf20Sopenharmony_ci	writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
4788c2ecf20Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
4798c2ecf20Sopenharmony_ci	Dummy = readb(apbs[IndexCard].RamIO + VERS);
4808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
4818c2ecf20Sopenharmony_ci	return 0;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int do_ac_read(int IndexCard, char __user *buf,
4858c2ecf20Sopenharmony_ci		struct st_ram_io *st_loc, struct mailbox *mailbox)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	void __iomem *from = apbs[IndexCard].RamIO + RAM_TO_PC;
4888c2ecf20Sopenharmony_ci	unsigned char *to = (unsigned char *)mailbox;
4898c2ecf20Sopenharmony_ci#ifdef DEBUG
4908c2ecf20Sopenharmony_ci	int c;
4918c2ecf20Sopenharmony_ci#endif
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	st_loc->tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC);
4948c2ecf20Sopenharmony_ci	st_loc->numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	{
4988c2ecf20Sopenharmony_ci		int c;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox); c++)
5018c2ecf20Sopenharmony_ci			*(to++) = readb(from++);
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
5048c2ecf20Sopenharmony_ci	writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC);
5058c2ecf20Sopenharmony_ci	writeb(IndexCard+1, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
5068c2ecf20Sopenharmony_ci	writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC),
5078c2ecf20Sopenharmony_ci	       apbs[IndexCard].RamIO + TIC_ACK_FROM_PC);
5088c2ecf20Sopenharmony_ci	writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
5098c2ecf20Sopenharmony_ci	writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY);
5108c2ecf20Sopenharmony_ci	writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
5118c2ecf20Sopenharmony_ci	Dummy = readb(apbs[IndexCard].RamIO + VERS);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci#ifdef DEBUG
5148c2ecf20Sopenharmony_ci		printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		for (c = 0; c < sizeof(struct st_ram_io);) {
5178c2ecf20Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *)st_loc)[c]);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
5208c2ecf20Sopenharmony_ci				printk(" %2.2X", ((unsigned char *)st_loc)[c]);
5218c2ecf20Sopenharmony_ci			}
5228c2ecf20Sopenharmony_ci		}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci		printk("\nstruct mailbox follows:");
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		for (c = 0; c < sizeof(struct mailbox);) {
5278c2ecf20Sopenharmony_ci			printk("\n%5.5X: %2.2X", c, ((unsigned char *)mailbox)[c]);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci			for (c++; c % 8 && c < sizeof(struct mailbox); c++) {
5308c2ecf20Sopenharmony_ci				printk(" %2.2X", ((unsigned char *)mailbox)[c]);
5318c2ecf20Sopenharmony_ci			}
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci		printk("\n");
5348c2ecf20Sopenharmony_ci#endif
5358c2ecf20Sopenharmony_ci	return (sizeof(struct st_ram_io) + sizeof(struct mailbox));
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_t *ptr)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	unsigned long flags;
5418c2ecf20Sopenharmony_ci	unsigned int i;
5428c2ecf20Sopenharmony_ci	unsigned char tmp;
5438c2ecf20Sopenharmony_ci	int ret = 0;
5448c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
5458c2ecf20Sopenharmony_ci#ifdef DEBUG
5468c2ecf20Sopenharmony_ci	int loopcount=0;
5478c2ecf20Sopenharmony_ci#endif
5488c2ecf20Sopenharmony_ci	/* No need to ratelimit this. Only root can trigger it anyway */
5498c2ecf20Sopenharmony_ci	if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
5508c2ecf20Sopenharmony_ci		printk( KERN_WARNING "Hmmm. read() of Applicom card, length %zd != expected %zd\n",
5518c2ecf20Sopenharmony_ci			count,sizeof(struct st_ram_io) + sizeof(struct mailbox));
5528c2ecf20Sopenharmony_ci		return -EINVAL;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	while(1) {
5568c2ecf20Sopenharmony_ci		/* Stick ourself on the wait queue */
5578c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
5588c2ecf20Sopenharmony_ci		add_wait_queue(&FlagSleepRec, &wait);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci		/* Scan each board, looking for one which has a packet for us */
5618c2ecf20Sopenharmony_ci		for (i=0; i < MAX_BOARD; i++) {
5628c2ecf20Sopenharmony_ci			if (!apbs[i].RamIO)
5638c2ecf20Sopenharmony_ci				continue;
5648c2ecf20Sopenharmony_ci			spin_lock_irqsave(&apbs[i].mutex, flags);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci			tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci			if (tmp == 2) {
5698c2ecf20Sopenharmony_ci				struct st_ram_io st_loc;
5708c2ecf20Sopenharmony_ci				struct mailbox mailbox;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci				/* Got a packet for us */
5738c2ecf20Sopenharmony_ci				memset(&st_loc, 0, sizeof(st_loc));
5748c2ecf20Sopenharmony_ci				ret = do_ac_read(i, buf, &st_loc, &mailbox);
5758c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&apbs[i].mutex, flags);
5768c2ecf20Sopenharmony_ci				set_current_state(TASK_RUNNING);
5778c2ecf20Sopenharmony_ci				remove_wait_queue(&FlagSleepRec, &wait);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci				if (copy_to_user(buf, &st_loc, sizeof(st_loc)))
5808c2ecf20Sopenharmony_ci					return -EFAULT;
5818c2ecf20Sopenharmony_ci				if (copy_to_user(buf + sizeof(st_loc), &mailbox, sizeof(mailbox)))
5828c2ecf20Sopenharmony_ci					return -EFAULT;
5838c2ecf20Sopenharmony_ci				return tmp;
5848c2ecf20Sopenharmony_ci			}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci			if (tmp > 2) {
5878c2ecf20Sopenharmony_ci				/* Got an error */
5888c2ecf20Sopenharmony_ci				Dummy = readb(apbs[i].RamIO + VERS);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&apbs[i].mutex, flags);
5918c2ecf20Sopenharmony_ci				set_current_state(TASK_RUNNING);
5928c2ecf20Sopenharmony_ci				remove_wait_queue(&FlagSleepRec, &wait);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci				printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n",
5958c2ecf20Sopenharmony_ci				       i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
5968c2ecf20Sopenharmony_ci				DeviceErrorCount++;
5978c2ecf20Sopenharmony_ci				return -EIO;
5988c2ecf20Sopenharmony_ci			}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci			/* Nothing for us. Try the next board */
6018c2ecf20Sopenharmony_ci			Dummy = readb(apbs[i].RamIO + VERS);
6028c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&apbs[i].mutex, flags);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		} /* per board */
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		/* OK - No boards had data for us. Sleep now */
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci		schedule();
6098c2ecf20Sopenharmony_ci		remove_wait_queue(&FlagSleepRec, &wait);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci		if (signal_pending(current))
6128c2ecf20Sopenharmony_ci			return -EINTR;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci#ifdef DEBUG
6158c2ecf20Sopenharmony_ci		if (loopcount++ > 2) {
6168c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "Looping in ac_read. loopcount %d\n", loopcount);
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci#endif
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic irqreturn_t ac_interrupt(int vec, void *dev_instance)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	unsigned int i;
6258c2ecf20Sopenharmony_ci	unsigned int FlagInt;
6268c2ecf20Sopenharmony_ci	unsigned int LoopCount;
6278c2ecf20Sopenharmony_ci	int handled = 0;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	//    printk("Applicom interrupt on IRQ %d occurred\n", vec);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	LoopCount = 0;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	do {
6348c2ecf20Sopenharmony_ci		FlagInt = 0;
6358c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci			/* Skip if this board doesn't exist */
6388c2ecf20Sopenharmony_ci			if (!apbs[i].RamIO)
6398c2ecf20Sopenharmony_ci				continue;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci			spin_lock(&apbs[i].mutex);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci			/* Skip if this board doesn't want attention */
6448c2ecf20Sopenharmony_ci			if(readb(apbs[i].RamIO + RAM_IT_TO_PC) == 0) {
6458c2ecf20Sopenharmony_ci				spin_unlock(&apbs[i].mutex);
6468c2ecf20Sopenharmony_ci				continue;
6478c2ecf20Sopenharmony_ci			}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci			handled = 1;
6508c2ecf20Sopenharmony_ci			FlagInt = 1;
6518c2ecf20Sopenharmony_ci			writeb(0, apbs[i].RamIO + RAM_IT_TO_PC);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) {
6548c2ecf20Sopenharmony_ci				printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n",
6558c2ecf20Sopenharmony_ci				       i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
6568c2ecf20Sopenharmony_ci				DeviceErrorCount++;
6578c2ecf20Sopenharmony_ci			}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci			if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) &&
6608c2ecf20Sopenharmony_ci			   (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) {
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci				printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n",
6638c2ecf20Sopenharmony_ci				       i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY));
6648c2ecf20Sopenharmony_ci				DeviceErrorCount++;
6658c2ecf20Sopenharmony_ci			}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci			if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) {	/* mailbox sent by the card ?   */
6688c2ecf20Sopenharmony_ci				if (waitqueue_active(&FlagSleepRec)) {
6698c2ecf20Sopenharmony_ci				wake_up_interruptible(&FlagSleepRec);
6708c2ecf20Sopenharmony_ci			}
6718c2ecf20Sopenharmony_ci			}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci			if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) {	/* ram i/o free for write by pc ? */
6748c2ecf20Sopenharmony_ci				if (waitqueue_active(&apbs[i].FlagSleepSend)) {	/* process sleep during read ?    */
6758c2ecf20Sopenharmony_ci					wake_up_interruptible(&apbs[i].FlagSleepSend);
6768c2ecf20Sopenharmony_ci				}
6778c2ecf20Sopenharmony_ci			}
6788c2ecf20Sopenharmony_ci			Dummy = readb(apbs[i].RamIO + VERS);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci			if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) {
6818c2ecf20Sopenharmony_ci				/* There's another int waiting on this card */
6828c2ecf20Sopenharmony_ci				spin_unlock(&apbs[i].mutex);
6838c2ecf20Sopenharmony_ci				i--;
6848c2ecf20Sopenharmony_ci			} else {
6858c2ecf20Sopenharmony_ci				spin_unlock(&apbs[i].mutex);
6868c2ecf20Sopenharmony_ci			}
6878c2ecf20Sopenharmony_ci		}
6888c2ecf20Sopenharmony_ci		if (FlagInt)
6898c2ecf20Sopenharmony_ci			LoopCount = 0;
6908c2ecf20Sopenharmony_ci		else
6918c2ecf20Sopenharmony_ci			LoopCount++;
6928c2ecf20Sopenharmony_ci	} while(LoopCount < 2);
6938c2ecf20Sopenharmony_ci	return IRQ_RETVAL(handled);
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci{				/* @ ADG ou ATO selon le cas */
7018c2ecf20Sopenharmony_ci	int i;
7028c2ecf20Sopenharmony_ci	unsigned char IndexCard;
7038c2ecf20Sopenharmony_ci	void __iomem *pmem;
7048c2ecf20Sopenharmony_ci	int ret = 0;
7058c2ecf20Sopenharmony_ci	static int warncount = 10;
7068c2ecf20Sopenharmony_ci	volatile unsigned char byte_reset_it;
7078c2ecf20Sopenharmony_ci	struct st_ram_io *adgl;
7088c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	/* In general, the device is only openable by root anyway, so we're not
7118c2ecf20Sopenharmony_ci	   particularly concerned that bogus ioctls can flood the console. */
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	adgl = memdup_user(argp, sizeof(struct st_ram_io));
7148c2ecf20Sopenharmony_ci	if (IS_ERR(adgl))
7158c2ecf20Sopenharmony_ci		return PTR_ERR(adgl);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	mutex_lock(&ac_mutex);
7188c2ecf20Sopenharmony_ci	IndexCard = adgl->num_card-1;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (cmd != 6 && IndexCard >= MAX_BOARD)
7218c2ecf20Sopenharmony_ci		goto err;
7228c2ecf20Sopenharmony_ci	IndexCard = array_index_nospec(IndexCard, MAX_BOARD);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (cmd != 6 && !apbs[IndexCard].RamIO)
7258c2ecf20Sopenharmony_ci		goto err;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	switch (cmd) {
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	case 0:
7308c2ecf20Sopenharmony_ci		pmem = apbs[IndexCard].RamIO;
7318c2ecf20Sopenharmony_ci		for (i = 0; i < sizeof(struct st_ram_io); i++)
7328c2ecf20Sopenharmony_ci			((unsigned char *)adgl)[i]=readb(pmem++);
7338c2ecf20Sopenharmony_ci		if (copy_to_user(argp, adgl, sizeof(struct st_ram_io)))
7348c2ecf20Sopenharmony_ci			ret = -EFAULT;
7358c2ecf20Sopenharmony_ci		break;
7368c2ecf20Sopenharmony_ci	case 1:
7378c2ecf20Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
7388c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++)
7398c2ecf20Sopenharmony_ci			adgl->conf_end_test[i] = readb(pmem++);
7408c2ecf20Sopenharmony_ci		for (i = 0; i < 2; i++)
7418c2ecf20Sopenharmony_ci			adgl->error_code[i] = readb(pmem++);
7428c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++)
7438c2ecf20Sopenharmony_ci			adgl->parameter_error[i] = readb(pmem++);
7448c2ecf20Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + VERS;
7458c2ecf20Sopenharmony_ci		adgl->vers = readb(pmem);
7468c2ecf20Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + TYPE_CARD;
7478c2ecf20Sopenharmony_ci		for (i = 0; i < 20; i++)
7488c2ecf20Sopenharmony_ci			adgl->reserv1[i] = readb(pmem++);
7498c2ecf20Sopenharmony_ci		*(int *)&adgl->reserv1[20] =
7508c2ecf20Sopenharmony_ci			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) +
7518c2ecf20Sopenharmony_ci			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) +
7528c2ecf20Sopenharmony_ci			(readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) );
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		if (copy_to_user(argp, adgl, sizeof(struct st_ram_io)))
7558c2ecf20Sopenharmony_ci			ret = -EFAULT;
7568c2ecf20Sopenharmony_ci		break;
7578c2ecf20Sopenharmony_ci	case 2:
7588c2ecf20Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
7598c2ecf20Sopenharmony_ci		for (i = 0; i < 10; i++)
7608c2ecf20Sopenharmony_ci			writeb(0xff, pmem++);
7618c2ecf20Sopenharmony_ci		writeb(adgl->data_from_pc_ready,
7628c2ecf20Sopenharmony_ci		       apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
7678c2ecf20Sopenharmony_ci			if (apbs[i].RamIO) {
7688c2ecf20Sopenharmony_ci				byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC);
7698c2ecf20Sopenharmony_ci			}
7708c2ecf20Sopenharmony_ci		}
7718c2ecf20Sopenharmony_ci		break;
7728c2ecf20Sopenharmony_ci	case 3:
7738c2ecf20Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC;
7748c2ecf20Sopenharmony_ci		writeb(adgl->tic_des_from_pc, pmem);
7758c2ecf20Sopenharmony_ci		break;
7768c2ecf20Sopenharmony_ci	case 4:
7778c2ecf20Sopenharmony_ci		pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC;
7788c2ecf20Sopenharmony_ci		adgl->tic_owner_to_pc     = readb(pmem++);
7798c2ecf20Sopenharmony_ci		adgl->numcard_owner_to_pc = readb(pmem);
7808c2ecf20Sopenharmony_ci		if (copy_to_user(argp, adgl,sizeof(struct st_ram_io)))
7818c2ecf20Sopenharmony_ci			ret = -EFAULT;
7828c2ecf20Sopenharmony_ci		break;
7838c2ecf20Sopenharmony_ci	case 5:
7848c2ecf20Sopenharmony_ci		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
7858c2ecf20Sopenharmony_ci		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
7868c2ecf20Sopenharmony_ci		writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
7878c2ecf20Sopenharmony_ci		writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
7888c2ecf20Sopenharmony_ci		writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
7898c2ecf20Sopenharmony_ci		break;
7908c2ecf20Sopenharmony_ci	case 6:
7918c2ecf20Sopenharmony_ci		printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)\n");
7928c2ecf20Sopenharmony_ci		printk(KERN_INFO "Number of installed boards . %d\n", (int) numboards);
7938c2ecf20Sopenharmony_ci		printk(KERN_INFO "Segment of board ........... %X\n", (int) mem);
7948c2ecf20Sopenharmony_ci		printk(KERN_INFO "Interrupt IRQ number ....... %d\n", (int) irq);
7958c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
7968c2ecf20Sopenharmony_ci			int serial;
7978c2ecf20Sopenharmony_ci			char boardname[(SERIAL_NUMBER - TYPE_CARD) + 1];
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci			if (!apbs[i].RamIO)
8008c2ecf20Sopenharmony_ci				continue;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci			for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
8038c2ecf20Sopenharmony_ci				boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
8048c2ecf20Sopenharmony_ci			boardname[serial] = 0;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci			printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",
8078c2ecf20Sopenharmony_ci			       i+1,
8088c2ecf20Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) >> 4),
8098c2ecf20Sopenharmony_ci			       (int)(readb(apbs[i].RamIO + VERS) & 0xF),
8108c2ecf20Sopenharmony_ci			       boardname);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci			serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) +
8148c2ecf20Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) +
8158c2ecf20Sopenharmony_ci				(readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci			if (serial != 0)
8188c2ecf20Sopenharmony_ci				printk(" S/N %d\n", serial);
8198c2ecf20Sopenharmony_ci			else
8208c2ecf20Sopenharmony_ci				printk("\n");
8218c2ecf20Sopenharmony_ci		}
8228c2ecf20Sopenharmony_ci		if (DeviceErrorCount != 0)
8238c2ecf20Sopenharmony_ci			printk(KERN_INFO "DeviceErrorCount ........... %d\n", DeviceErrorCount);
8248c2ecf20Sopenharmony_ci		if (ReadErrorCount != 0)
8258c2ecf20Sopenharmony_ci			printk(KERN_INFO "ReadErrorCount ............. %d\n", ReadErrorCount);
8268c2ecf20Sopenharmony_ci		if (WriteErrorCount != 0)
8278c2ecf20Sopenharmony_ci			printk(KERN_INFO "WriteErrorCount ............ %d\n", WriteErrorCount);
8288c2ecf20Sopenharmony_ci		if (waitqueue_active(&FlagSleepRec))
8298c2ecf20Sopenharmony_ci			printk(KERN_INFO "Process in read pending\n");
8308c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_BOARD; i++) {
8318c2ecf20Sopenharmony_ci			if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend))
8328c2ecf20Sopenharmony_ci				printk(KERN_INFO "Process in write pending board %d\n",i+1);
8338c2ecf20Sopenharmony_ci		}
8348c2ecf20Sopenharmony_ci		break;
8358c2ecf20Sopenharmony_ci	default:
8368c2ecf20Sopenharmony_ci		ret = -ENOTTY;
8378c2ecf20Sopenharmony_ci		break;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci	Dummy = readb(apbs[IndexCard].RamIO + VERS);
8408c2ecf20Sopenharmony_ci	kfree(adgl);
8418c2ecf20Sopenharmony_ci	mutex_unlock(&ac_mutex);
8428c2ecf20Sopenharmony_ci	return 0;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cierr:
8458c2ecf20Sopenharmony_ci	if (warncount) {
8468c2ecf20Sopenharmony_ci		pr_warn("APPLICOM driver IOCTL, bad board number %d\n",
8478c2ecf20Sopenharmony_ci			(int)IndexCard + 1);
8488c2ecf20Sopenharmony_ci		warncount--;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci	kfree(adgl);
8518c2ecf20Sopenharmony_ci	mutex_unlock(&ac_mutex);
8528c2ecf20Sopenharmony_ci	return -EINVAL;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
856