aboutsummaryrefslogtreecommitdiff
path: root/drivers/char/ip2/ip2main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/ip2/ip2main.c')
-rw-r--r--drivers/char/ip2/ip2main.c3211
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 */