diff options
Diffstat (limited to 'drivers/char/ip2/ip2main.c')
-rw-r--r-- | drivers/char/ip2/ip2main.c | 3211 |
1 files changed, 3211 insertions, 0 deletions
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c new file mode 100644 index 00000000000..03db1cb3fa9 --- /dev/null +++ b/drivers/char/ip2/ip2main.c @@ -0,0 +1,3211 @@ +/* +* +* (c) 1999 by Computone Corporation +* +******************************************************************************** +* +* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport +* serial I/O controllers. +* +* DESCRIPTION: Mainline code for the device driver +* +*******************************************************************************/ +// ToDo: +// +// Fix the immediate DSS_NOW problem. +// Work over the channel stats return logic in ip2_ipl_ioctl so they +// make sense for all 256 possible channels and so the user space +// utilities will compile and work properly. +// +// Done: +// +// 1.2.14 /\/\|=mhw=|\/\/ +// Added bounds checking to ip2_ipl_ioctl to avoid potential terroristic acts. +// Changed the definition of ip2trace to be more consistent with kernel style +// Thanks to Andreas Dilger <adilger@turbolabs.com> for these updates +// +// 1.2.13 /\/\|=mhw=|\/\/ +// DEVFS: Renamed ttf/{n} to tts/F{n} and cuf/{n} to cua/F{n} to conform +// to agreed devfs serial device naming convention. +// +// 1.2.12 /\/\|=mhw=|\/\/ +// Cleaned up some remove queue cut and paste errors +// +// 1.2.11 /\/\|=mhw=|\/\/ +// Clean up potential NULL pointer dereferences +// Clean up devfs registration +// Add kernel command line parsing for io and irq +// Compile defaults for io and irq are now set in ip2.c not ip2.h! +// Reworked poll_only hack for explicit parameter setting +// You must now EXPLICITLY set poll_only = 1 or set all irqs to 0 +// Merged ip2_loadmain and old_ip2_init +// Converted all instances of interruptible_sleep_on into queue calls +// Most of these had no race conditions but better to clean up now +// +// 1.2.10 /\/\|=mhw=|\/\/ +// Fixed the bottom half interrupt handler and enabled USE_IQI +// to split the interrupt handler into a formal top-half / bottom-half +// Fixed timing window on high speed processors that queued messages to +// the outbound mail fifo faster than the board could handle. +// +// 1.2.9 +// Four box EX was barfing on >128k kmalloc, made structure smaller by +// reducing output buffer size +// +// 1.2.8 +// Device file system support (MHW) +// +// 1.2.7 +// Fixed +// Reload of ip2 without unloading ip2main hangs system on cat of /proc/modules +// +// 1.2.6 +//Fixes DCD problems +// DCD was not reported when CLOCAL was set on call to TIOCMGET +// +//Enhancements: +// TIOCMGET requests and waits for status return +// No DSS interrupts enabled except for DCD when needed +// +// For internal use only +// +//#define IP2DEBUG_INIT +//#define IP2DEBUG_OPEN +//#define IP2DEBUG_WRITE +//#define IP2DEBUG_READ +//#define IP2DEBUG_IOCTL +//#define IP2DEBUG_IPL + +//#define IP2DEBUG_TRACE +//#define DEBUG_FIFO + +/************/ +/* Includes */ +/************/ +#include <linux/config.h> + +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/major.h> +#include <linux/wait.h> +#include <linux/device.h> + +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/termios.h> +#include <linux/tty_driver.h> +#include <linux/serial.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> + +#include <linux/cdk.h> +#include <linux/comstats.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> + +#include <linux/vmalloc.h> +#include <linux/init.h> + +#include <asm/uaccess.h> + +#include "ip2types.h" +#include "ip2trace.h" +#include "ip2ioctl.h" +#include "ip2.h" +#include "i2ellis.h" +#include "i2lib.h" + +/***************** + * /proc/ip2mem * + *****************/ + +#include <linux/proc_fs.h> + +static int ip2_read_procmem(char *, char **, off_t, int); +static int ip2_read_proc(char *, char **, off_t, int, int *, void * ); + +/********************/ +/* Type Definitions */ +/********************/ + +/*************/ +/* Constants */ +/*************/ + +/* String constants to identify ourselves */ +static char *pcName = "Computone IntelliPort Plus multiport driver"; +static char *pcVersion = "1.2.14"; + +/* String constants for port names */ +static char *pcDriver_name = "ip2"; +static char *pcIpl = "ip2ipl"; + +/* Serial subtype definitions */ +#define SERIAL_TYPE_NORMAL 1 + +// cheezy kludge or genius - you decide? +int ip2_loadmain(int *, int *, unsigned char *, int); +static unsigned char *Fip_firmware; +static int Fip_firmware_size; + +/***********************/ +/* Function Prototypes */ +/***********************/ + +/* Global module entry functions */ + +/* Private (static) functions */ +static int ip2_open(PTTY, struct file *); +static void ip2_close(PTTY, struct file *); +static int ip2_write(PTTY, const unsigned char *, int); +static void ip2_putchar(PTTY, unsigned char); +static void ip2_flush_chars(PTTY); +static int ip2_write_room(PTTY); +static int ip2_chars_in_buf(PTTY); +static void ip2_flush_buffer(PTTY); +static int ip2_ioctl(PTTY, struct file *, UINT, ULONG); +static void ip2_set_termios(PTTY, struct termios *); +static void ip2_set_line_discipline(PTTY); +static void ip2_throttle(PTTY); +static void ip2_unthrottle(PTTY); +static void ip2_stop(PTTY); +static void ip2_start(PTTY); +static void ip2_hangup(PTTY); +static int ip2_tiocmget(struct tty_struct *tty, struct file *file); +static int ip2_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); + +static void set_irq(int, int); +static void ip2_interrupt_bh(i2eBordStrPtr pB); +static irqreturn_t ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static void ip2_poll(unsigned long arg); +static inline void service_all_boards(void); +static void do_input(void *p); +static void do_status(void *p); + +static void ip2_wait_until_sent(PTTY,int); + +static void set_params (i2ChanStrPtr, struct termios *); +static int get_serial_info(i2ChanStrPtr, struct serial_struct __user *); +static int set_serial_info(i2ChanStrPtr, struct serial_struct __user *); + +static ssize_t ip2_ipl_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t ip2_ipl_write(struct file *, const char __user *, size_t, loff_t *); +static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG); +static int ip2_ipl_open(struct inode *, struct file *); + +static int DumpTraceBuffer(char __user *, int); +static int DumpFifoBuffer( char __user *, int); + +static void ip2_init_board(int); +static unsigned short find_eisa_board(int); + +/***************/ +/* Static Data */ +/***************/ + +static struct tty_driver *ip2_tty_driver; + +/* Here, then is a table of board pointers which the interrupt routine should + * scan through to determine who it must service. + */ +static unsigned short i2nBoards; // Number of boards here + +static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS]; + +static i2ChanStrPtr DevTable[IP2_MAX_PORTS]; +//DevTableMem just used to save addresses for kfree +static void *DevTableMem[IP2_MAX_BOARDS]; + +/* This is the driver descriptor for the ip2ipl device, which is used to + * download the loadware to the boards. + */ +static struct file_operations ip2_ipl = { + .owner = THIS_MODULE, + .read = ip2_ipl_read, + .write = ip2_ipl_write, + .ioctl = ip2_ipl_ioctl, + .open = ip2_ipl_open, +}; + +static unsigned long irq_counter = 0; +static unsigned long bh_counter = 0; + +// Use immediate queue to service interrupts +#define USE_IQI +//#define USE_IQ // PCI&2.2 needs work + +/* The timer_list entry for our poll routine. If interrupt operation is not + * selected, the board is serviced periodically to see if anything needs doing. + */ +#define POLL_TIMEOUT (jiffies + 1) +static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0); +static char TimerOn; + +#ifdef IP2DEBUG_TRACE +/* Trace (debug) buffer data */ +#define TRACEMAX 1000 +static unsigned long tracebuf[TRACEMAX]; +static int tracestuff; +static int tracestrip; +static int tracewrap; +#endif + +/**********/ +/* Macros */ +/**********/ + +#if defined(MODULE) && defined(IP2DEBUG_OPEN) +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ + tty->name,(pCh->flags),ip2_tty_driver->refcount, \ + tty->count,/*GET_USE_COUNT(module)*/0,s) +#else +#define DBG_CNT(s) +#endif + +/********/ +/* Code */ +/********/ + +#include "i2ellis.c" /* Extremely low-level interface services */ +#include "i2cmd.c" /* Standard loadware command definitions */ +#include "i2lib.c" /* High level interface services */ + +/* Configuration area for modprobe */ + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); + +static int poll_only = 0; + +static int Eisa_irq; +static int Eisa_slot; + +static int iindx; +static char rirqs[IP2_MAX_BOARDS]; +static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; + +/* for sysfs class support */ +static struct class *ip2_class; + +// Some functions to keep track of what irq's we have + +static int __init +is_valid_irq(int irq) +{ + int *i = Valid_Irqs; + + while ((*i != 0) && (*i != irq)) { + i++; + } + return (*i); +} + +static void __init +mark_requested_irq( char irq ) +{ + rirqs[iindx++] = irq; +} + +#ifdef MODULE +static int __init +clear_requested_irq( char irq ) +{ + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) { + rirqs[i] = 0; + return 1; + } + } + return 0; +} +#endif + +static int __init +have_requested_irq( char irq ) +{ + // array init to zeros so 0 irq will not be requested as a side effect + int i; + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (rirqs[i] == irq) + return 1; + } + return 0; +} + +/******************************************************************************/ +/* Function: init_module() */ +/* Parameters: None */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It simply calls */ +/* the driver initialisation function and returns what it returns. */ +/******************************************************************************/ +#ifdef MODULE +int +init_module(void) +{ +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Loading module ...\n" ); +#endif + return 0; +} +#endif /* MODULE */ + +/******************************************************************************/ +/* Function: cleanup_module() */ +/* Parameters: None */ +/* Returns: Nothing */ +/* */ +/* Description: */ +/* This is a required entry point for an installable module. It has to return */ +/* the device and the driver to a passive state. It should not be necessary */ +/* to reset the board fully, especially as the loadware is downloaded */ +/* externally rather than in the driver. We just want to disable the board */ +/* and clear the loadware to a reset state. To allow this there has to be a */ +/* way to detect whether the board has the loadware running at init time to */ +/* handle subsequent installations of the driver. All memory allocated by the */ +/* driver should be returned since it may be unloaded from memory. */ +/******************************************************************************/ +#ifdef MODULE +void +cleanup_module(void) +{ + int err; + int i; + +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion ); +#endif + /* Stop poll timer if we had one. */ + if ( TimerOn ) { + del_timer ( &PollTimer ); + TimerOn = 0; + } + + /* Reset the boards we have. */ + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiReset( i2BoardPtrTable[i] ); + } + } + + /* The following is done at most once, if any boards were installed. */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + iiResetDelay( i2BoardPtrTable[i] ); + /* free io addresses and Tibet */ + release_region( ip2config.addr[i], 8 ); + class_device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i)); + devfs_remove("ip2/ipl%d", i); + class_device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i + 1)); + devfs_remove("ip2/stat%d", i); + } + /* Disable and remove interrupt handler. */ + if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) { + free_irq ( ip2config.irq[i], (void *)&pcName); + clear_requested_irq( ip2config.irq[i]); + } + } + class_destroy(ip2_class); + devfs_remove("ip2"); + if ( ( err = tty_unregister_driver ( ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err); + } + put_tty_driver(ip2_tty_driver); + if ( ( err = unregister_chrdev ( IP2_IPL_MAJOR, pcIpl ) ) ) { + printk(KERN_ERR "IP2: failed to unregister IPL driver (%d)\n", err); + } + remove_proc_entry("ip2mem", &proc_root); + + // free memory + for (i = 0; i < IP2_MAX_BOARDS; i++) { + void *pB; +#ifdef CONFIG_PCI + if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) { + pci_disable_device(ip2config.pci_dev[i]); + ip2config.pci_dev[i] = NULL; + } +#endif + if ((pB = i2BoardPtrTable[i]) != 0 ) { + kfree ( pB ); + i2BoardPtrTable[i] = NULL; + } + if ((DevTableMem[i]) != NULL ) { + kfree ( DevTableMem[i] ); + DevTableMem[i] = NULL; + } + } + + /* Cleanup the iiEllis subsystem. */ + iiEllisCleanup(); +#ifdef IP2DEBUG_INIT + printk (KERN_DEBUG "IP2 Unloaded\n" ); +#endif +} +#endif /* MODULE */ + +static struct tty_operations ip2_ops = { + .open = ip2_open, + .close = ip2_close, + .write = ip2_write, + .put_char = ip2_putchar, + .flush_chars = ip2_flush_chars, + .write_room = ip2_write_room, + .chars_in_buffer = ip2_chars_in_buf, + .flush_buffer = ip2_flush_buffer, + .ioctl = ip2_ioctl, + .throttle = ip2_throttle, + .unthrottle = ip2_unthrottle, + .set_termios = ip2_set_termios, + .set_ldisc = ip2_set_line_discipline, + .stop = ip2_stop, + .start = ip2_start, + .hangup = ip2_hangup, + .read_proc = ip2_read_proc, + .tiocmget = ip2_tiocmget, + .tiocmset = ip2_tiocmset, +}; + +/******************************************************************************/ +/* Function: ip2_loadmain() */ +/* Parameters: irq, io from command line of insmod et. al. */ +/* pointer to fip firmware and firmware size for boards */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This was the required entry point for all drivers (now in ip2.c) */ +/* It performs all */ +/* initialisation of the devices and driver structures, and registers itself */ +/* with the relevant kernel modules. */ +/******************************************************************************/ +/* SA_INTERRUPT- if set blocks all interrupts else only this line */ +/* SA_SHIRQ - for shared irq PCI or maybe EISA only */ +/* SA_RANDOM - can be source for cert. random number generators */ +#define IP2_SA_FLAGS 0 + +int +ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) +{ + int i, j, box; + int err = 0; + int status = 0; + static int loaded; + i2eBordStrPtr pB = NULL; + int rc = -1; + + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 ); + + /* process command line arguments to modprobe or + insmod i.e. iop & irqp */ + /* irqp and iop should ALWAYS be specified now... But we check + them individually just to be sure, anyways... */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if (iop) { + ip2config.addr[i] = iop[i]; + if (irqp) { + if( irqp[i] >= 0 ) { + ip2config.irq[i] = irqp[i]; + } else { + ip2config.irq[i] = 0; + } + // This is a little bit of a hack. If poll_only=1 on command + // line back in ip2.c OR all IRQs on all specified boards are + // explicitly set to 0, then drop to poll only mode and override + // PCI or EISA interrupts. This superceeds the old hack of + // triggering if all interrupts were zero (like da default). + // Still a hack but less prone to random acts of terrorism. + // + // What we really should do, now that the IRQ default is set + // to -1, is to use 0 as a hard coded, do not probe. + // + // /\/\|=mhw=|\/\/ + poll_only |= irqp[i]; + } + } + } + poll_only = !poll_only; + + Fip_firmware = firmware; + Fip_firmware_size = firmsize; + + /* Announce our presence */ + printk( KERN_INFO "%s version %s\n", pcName, pcVersion ); + + // ip2 can be unloaded and reloaded for no good reason + // we can't let that happen here or bad things happen + // second load hoses board but not system - fixme later + if (loaded) { + printk( KERN_INFO "Still loaded\n" ); + return 0; + } + loaded++; + + ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS); + if (!ip2_tty_driver) + return -ENOMEM; + + /* Initialise the iiEllis subsystem. */ + iiEllisInit(); + + /* Initialize arrays. */ + memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable ); + memset( DevTable, 0, sizeof DevTable ); + + /* Initialise all the boards we can find (up to the maximum). */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + switch ( ip2config.addr[i] ) { + case 0: /* skip this slot even if card is present */ + break; + default: /* ISA */ + /* ISA address must be specified */ + if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) { + printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n", + i, ip2config.addr[i] ); + ip2config.addr[i] = 0; + } else { + ip2config.type[i] = ISA; + + /* Check for valid irq argument, set for polling if invalid */ + if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) { + printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]); + ip2config.irq[i] = 0;// 0 is polling and is valid in that sense + } + } + break; + case PCI: +#ifdef CONFIG_PCI + { + struct pci_dev *pci_dev_i = NULL; + pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE, + PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i); + if (pci_dev_i != NULL) { + unsigned int addr; + + if (pci_enable_device(pci_dev_i)) { + printk( KERN_ERR "IP2: can't enable PCI device at %s\n", + pci_name(pci_dev_i)); + break; + } + ip2config.type[i] = PCI; + ip2config.pci_dev[i] = pci_dev_i; + status = + pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr); + if ( addr & 1 ) { + ip2config.addr[i]=(USHORT)(addr&0xfffe); + } else { + printk( KERN_ERR "IP2: PCI I/O address error\n"); + } + +// If the PCI BIOS assigned it, lets try and use it. If we +// can't acquire it or it screws up, deal with it then. + +// if (!is_valid_irq(pci_irq)) { +// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq); +// pci_irq = 0; +// } + ip2config.irq[i] = pci_dev_i->irq; + } else { // ann error + ip2config.addr[i] = 0; + if (status == PCIBIOS_DEVICE_NOT_FOUND) { + printk( KERN_ERR "IP2: PCI board %d not found\n", i ); + } else { + printk( KERN_ERR "IP2: PCI error 0x%x \n", status ); + } + } + } +#else + printk( KERN_ERR "IP2: PCI card specified but PCI support not\n"); + printk( KERN_ERR "IP2: configured in this kernel.\n"); + printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n"); +#endif /* CONFIG_PCI */ + break; + case EISA: + if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) { + /* Eisa_irq set as side effect, boo */ + ip2config.type[i] = EISA; + } + ip2config.irq[i] = Eisa_irq; + break; + } /* switch */ + } /* for */ + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ip2config.addr[i] ) { + pB = kmalloc( sizeof(i2eBordStr), GFP_KERNEL); + if ( pB != NULL ) { + i2BoardPtrTable[i] = pB; + memset( pB, 0, sizeof(i2eBordStr) ); + iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer ); + iiReset( pB ); + } else { + printk(KERN_ERR "IP2: board memory allocation error\n"); + } + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( ( pB = i2BoardPtrTable[i] ) != NULL ) { + iiResetDelay( pB ); + break; + } + } + for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] != NULL ) { + ip2_init_board( i ); + } + } + + ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 ); + + ip2_tty_driver->owner = THIS_MODULE; + ip2_tty_driver->name = "ttyF"; + ip2_tty_driver->devfs_name = "tts/F"; + ip2_tty_driver->driver_name = pcDriver_name; + ip2_tty_driver->major = IP2_TTY_MAJOR; + ip2_tty_driver->minor_start = 0; + ip2_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + ip2_tty_driver->subtype = SERIAL_TYPE_NORMAL; + ip2_tty_driver->init_termios = tty_std_termios; + ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + tty_set_operations(ip2_tty_driver, &ip2_ops); + + ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 ); + + /* Register the tty devices. */ + if ( ( err = tty_register_driver ( ip2_tty_driver ) ) ) { + printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err); + put_tty_driver(ip2_tty_driver); + return -EINVAL; + } else + /* Register the IPL driver. */ + if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) { + printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err ); + } else { + /* create the sysfs class */ + ip2_class = class_create(THIS_MODULE, "ip2"); + if (IS_ERR(ip2_class)) { + err = PTR_ERR(ip2_class); + goto out_chrdev; + } + } + /* Register the read_procmem thing */ + if (!create_proc_info_entry("ip2mem",0,&proc_root,ip2_read_procmem)) { + printk(KERN_ERR "IP2: failed to register read_procmem\n"); + } else { + + ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 ); + /* Register the interrupt handler or poll handler, depending upon the + * specified interrupt. + */ + + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( 0 == ip2config.addr[i] ) { + continue; + } + + if ( NULL != ( pB = i2BoardPtrTable[i] ) ) { + class_device_create(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i), + NULL, "ipl%d", i); + err = devfs_mk_cdev(MKDEV(IP2_IPL_MAJOR, 4 * i), + S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, + "ip2/ipl%d", i); + if (err) { + class_device_destroy(ip2_class, + MKDEV(IP2_IPL_MAJOR, 4 * i)); + goto out_class; + } + + class_device_create(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i + 1), + NULL, "stat%d", i); + err = devfs_mk_cdev(MKDEV(IP2_IPL_MAJOR, 4 * i + 1), + S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, + "ip2/stat%d", i); + if (err) { + class_device_destroy(ip2_class, + MKDEV(IP2_IPL_MAJOR, 4 * i + 1)); + goto out_class; + } + + for ( box = 0; box < ABS_MAX_BOXES; ++box ) + { + for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) + { + if ( pB->i2eChannelMap[box] & (1 << j) ) + { + tty_register_device(ip2_tty_driver, + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES), NULL); + } + } + } + } + + if (poll_only) { +// Poll only forces driver to only use polling and +// to ignore the probed PCI or EISA interrupts. + ip2config.irq[i] = CIR_POLL; + } + if ( ip2config.irq[i] == CIR_POLL ) { +retry: + if (!TimerOn) { + PollTimer.expires = POLL_TIMEOUT; + add_timer ( &PollTimer ); + TimerOn = 1; + printk( KERN_INFO "IP2: polling\n"); + } + } else { + if (have_requested_irq(ip2config.irq[i])) + continue; + rc = request_irq( ip2config.irq[i], ip2_interrupt, + IP2_SA_FLAGS | (ip2config.type[i] == PCI ? SA_SHIRQ : 0), + pcName, (void *)&pcName); + if (rc) { + printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc); + ip2config.irq[i] = CIR_POLL; + printk( KERN_INFO "IP2: Polling %ld/sec.\n", + (POLL_TIMEOUT - jiffies)); + goto retry; + } + mark_requested_irq(ip2config.irq[i]); + /* Initialise the interrupt handler bottom half (aka slih). */ + } + } + for( i = 0; i < IP2_MAX_BOARDS; ++i ) { + if ( i2BoardPtrTable[i] ) { + set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */ + } + } + } + ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 ); + goto out; + +out_class: + class_destroy(ip2_class); +out_chrdev: + unregister_chrdev(IP2_IPL_MAJOR, "ip2"); +out: + return err; +} + +EXPORT_SYMBOL(ip2_loadmain); + +/******************************************************************************/ +/* Function: ip2_init_board() */ +/* Parameters: Index of board in configuration structure */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/* This function initializes the specified board. The loadware is copied to */ +/* the board, the channel structures are initialized, and the board details */ +/* are reported on the console. */ +/******************************************************************************/ +static void __init +ip2_init_board( int boardnum ) +{ + int i; + int nports = 0, nboxes = 0; + i2ChanStrPtr pCh; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + + if ( !iiInitialize ( pB ) ) { + printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n", + pB->i2eBase, pB->i2eError ); + goto err_initialize; + } + printk(KERN_INFO "IP2: Board %d: addr=0x%x irq=%d\n", boardnum + 1, + ip2config.addr[boardnum], ip2config.irq[boardnum] ); + + if (!request_region( ip2config.addr[boardnum], 8, pcName )) { + printk(KERN_ERR "IP2: bad addr=0x%x\n", ip2config.addr[boardnum]); + goto err_initialize; + } + + if ( iiDownloadAll ( pB, (loadHdrStrPtr)Fip_firmware, 1, Fip_firmware_size ) + != II_DOWN_GOOD ) { + printk ( KERN_ERR "IP2: failed to download loadware\n" ); + goto err_release_region; + } else { + printk ( KERN_INFO "IP2: fv=%d.%d.%d lv=%d.%d.%d\n", + pB->i2ePom.e.porVersion, + pB->i2ePom.e.porRevision, + pB->i2ePom.e.porSubRev, pB->i2eLVersion, + pB->i2eLRevision, pB->i2eLSub ); + } + + switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) { + + default: + printk( KERN_ERR "IP2: Unknown board type, ID = %x\n", + pB->i2ePom.e.porID ); + nports = 0; + goto err_release_region; + break; + + case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */ + printk ( KERN_INFO "IP2: ISA-4\n" ); + nports = 4; + break; + + case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */ + printk ( KERN_INFO "IP2: ISA-8 std\n" ); + nports = 8; + break; + + case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */ + printk ( KERN_INFO "IP2: ISA-8 RJ11\n" ); + nports = 8; + break; + + case POR_ID_FIIEX: /* IntelliPort IIEX */ + { + int portnum = IP2_PORTS_PER_BOARD * boardnum; + int box; + + for( box = 0; box < ABS_MAX_BOXES; ++box ) { + if ( pB->i2eChannelMap[box] != 0 ) { + ++nboxes; + } + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & 1<< i ) { + ++nports; + } + } + } + DevTableMem[boardnum] = pCh = + kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL ); + if ( !pCh ) { + printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n"); + goto err_release_region; + } + if ( !i2InitChannels( pB, nports, pCh ) ) { + printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError); + kfree ( pCh ); + goto err_release_region; + } + pB->i2eChannelPtr = &DevTable[portnum]; + pB->i2eChannelCnt = ABS_MOST_PORTS; + + for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) { + for( i = 0; i < ABS_BIGGEST_BOX; ++i ) { + if ( pB->i2eChannelMap[box] & (1 << i) ) { + DevTable[portnum + i] = pCh; + pCh->port_index = portnum + i; + pCh++; + } + } + } + printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit\n", + nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 ); + } + goto ex_exit; + } + DevTableMem[boardnum] = pCh = + kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL ); + if ( !pCh ) { + printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n"); + goto err_release_region; + } + pB->i2eChannelPtr = pCh; + pB->i2eChannelCnt = nports; + if ( !i2InitChannels( pB, nports, pCh ) ) { + printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError); + kfree ( pCh ); + goto err_release_region; + } + pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum]; + + for( i = 0; i < pB->i2eChannelCnt; ++i ) { + DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh; + pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i; + pCh++; + } +ex_exit: + INIT_WORK(&pB->tqueue_interrupt, (void(*)(void*)) ip2_interrupt_bh, pB); + return; + +err_release_region: + release_region(ip2config.addr[boardnum], 8); +err_initialize: + kfree ( pB ); + i2BoardPtrTable[boardnum] = NULL; + return; +} + +/******************************************************************************/ +/* Function: find_eisa_board ( int start_slot ) */ +/* Parameters: First slot to check */ +/* Returns: Address of EISA IntelliPort II controller */ +/* */ +/* Description: */ +/* This function searches for an EISA IntelliPort controller, starting */ +/* from the specified slot number. If the motherboard is not identified as an */ +/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */ +/* it returns the base address of the controller. */ +/******************************************************************************/ +static unsigned short __init +find_eisa_board( int start_slot ) +{ + int i, j; + unsigned int idm = 0; + unsigned int idp = 0; + unsigned int base = 0; + unsigned int value; + int setup_address; + int setup_irq; + int ismine = 0; + + /* + * First a check for an EISA motherboard, which we do by comparing the + * EISA ID registers for the system board and the first couple of slots. + * No slot ID should match the system board ID, but on an ISA or PCI + * machine the odds are that an empty bus will return similar values for + * each slot. + */ + i = 0x0c80; + value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3); + for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) { + j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3); + if ( value == j ) + return 0; + } + + /* + * OK, so we are inclined to believe that this is an EISA machine. Find + * an IntelliPort controller. + */ + for( i = start_slot; i < 16; i++ ) { + base = i << 12; + idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff); + idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff); + ismine = 0; + if ( idm == 0x0e8e ) { + if ( idp == 0x0281 || idp == 0x0218 ) { + ismine = 1; + } else if ( idp == 0x0282 || idp == 0x0283 ) { + ismine = 3; /* Can do edge-trigger */ + } + if ( ismine ) { + Eisa_slot = i; + break; + } + } + } + if ( !ismine ) + return 0; + + /* It's some sort of EISA card, but at what address is it configured? */ + + setup_address = base + 0xc88; + value = inb(base + 0xc86); + setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0; + + if ( (ismine & 2) && !(value & 0x10) ) { + ismine = 1; /* Could be edging, but not */ + } + + if ( Eisa_irq == 0 ) { + Eisa_irq = setup_irq; + } else if ( Eisa_irq != setup_irq ) { + printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" ); + } + +#ifdef IP2DEBUG_INIT +printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x", + base >> 12, idm, idp, setup_address); + if ( Eisa_irq ) { + printk(KERN_DEBUG ", Interrupt %d %s\n", + setup_irq, (ismine & 2) ? "(edge)" : "(level)"); + } else { + printk(KERN_DEBUG ", (polled)\n"); + } +#endif + return setup_address; +} + +/******************************************************************************/ +/* Function: set_irq() */ +/* Parameters: index to board in board table */ +/* IRQ to use */ +/* Returns: Success (0) */ +/* */ +/* Description: */ +/******************************************************************************/ +static void +set_irq( int boardnum, int boardIrq ) +{ + unsigned char tempCommand[16]; + i2eBordStrPtr pB = i2BoardPtrTable[boardnum]; + unsigned long flags; + + /* + * Notify the boards they may generate interrupts. This is done by + * sending an in-line command to channel 0 on each board. This is why + * the channels have to be defined already. For each board, if the + * interrupt has never been defined, we must do so NOW, directly, since + * board will not send flow control or even give an interrupt until this + * is done. If polling we must send 0 as the interrupt parameter. + */ + + // We will get an interrupt here at the end of this function + + iiDisableMailIrq(pB); + + /* We build up the entire packet header. */ + CHANNEL_OF(tempCommand) = 0; + PTYPE_OF(tempCommand) = PTYPE_INLINE; + CMD_COUNT_OF(tempCommand) = 2; + (CMD_OF(tempCommand))[0] = CMDVALUE_IRQ; + (CMD_OF(tempCommand))[1] = boardIrq; + /* + * Write to FIFO; don't bother to adjust fifo capacity for this, since + * board will respond almost immediately after SendMail hit. + */ + WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags); + iiWriteBuf(pB, tempCommand, 4); + WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags); + pB->i2eUsingIrq = boardIrq; + pB->i2eOutMailWaiting |= MB_OUT_STUFFED; + + /* Need to update number of boards before you enable mailbox int */ |