md: allow an md array to appear with 0 drives if it has external metadata
[safe/jmp/linux-2.6] / drivers / char / synclink_gt.c
index e4730a7..1f954ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: synclink_gt.c,v 4.36 2006/08/28 20:47:14 paulkf Exp $
+ * $Id: synclink_gt.c,v 4.50 2007/07/25 19:29:25 paulkf Exp $
  *
  * Device driver for Microgate SyncLink GT serial adapters.
  *
@@ -73,6 +73,7 @@
 #include <linux/bitops.h>
 #include <linux/workqueue.h>
 #include <linux/hdlc.h>
+#include <linux/synclink.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -81,8 +82,6 @@
 #include <asm/types.h>
 #include <asm/uaccess.h>
 
-#include "linux/synclink.h"
-
 #if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))
 #define SYNCLINK_GENERIC_HDLC 1
 #else
@@ -93,7 +92,7 @@
  * module identification
  */
 static char *driver_name     = "SyncLink GT";
-static char *driver_version  = "$Revision: 4.36 $";
+static char *driver_version  = "$Revision: 4.50 $";
 static char *tty_driver_name = "synclink_gt";
 static char *tty_dev_prefix  = "ttySLG";
 MODULE_LICENSE("GPL");
@@ -144,14 +143,12 @@ MODULE_PARM_DESC(dosyncppp, "Enable synchronous net device, 0=disable 1=enable")
 /*
  * tty support and callbacks
  */
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
 static struct tty_driver *serial_driver;
 
 static int  open(struct tty_struct *tty, struct file * filp);
 static void close(struct tty_struct *tty, struct file * filp);
 static void hangup(struct tty_struct *tty);
-static void set_termios(struct tty_struct *tty, struct termios *old_termios);
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios);
 
 static int  write(struct tty_struct *tty, const unsigned char *buf, int count);
 static void put_char(struct tty_struct *tty, unsigned char ch);
@@ -208,10 +205,10 @@ static void flush_cond_wait(struct cond_wait **head);
  */
 struct slgt_desc
 {
-       unsigned short count;
-       unsigned short status;
-       unsigned int pbuf;  /* physical address of data buffer */
-       unsigned int next;  /* physical address of next descriptor */
+       __le16 count;
+       __le16 status;
+       __le32 pbuf;  /* physical address of data buffer */
+       __le32 next;  /* physical address of next descriptor */
 
        /* driver book keeping */
        char *buf;          /* virtual  address of data buffer */
@@ -479,6 +476,7 @@ static void tx_set_idle(struct slgt_info *info);
 static unsigned int free_tbuf_count(struct slgt_info *info);
 static void reset_tbufs(struct slgt_info *info);
 static void tdma_reset(struct slgt_info *info);
+static void tdma_start(struct slgt_info *info);
 static void tx_load(struct slgt_info *info, const char *buf, unsigned int count);
 
 static void get_signals(struct slgt_info *info);
@@ -816,19 +814,13 @@ static void hangup(struct tty_struct *tty)
        wake_up_interruptible(&info->open_wait);
 }
 
-static void set_termios(struct tty_struct *tty, struct termios *old_termios)
+static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 {
        struct slgt_info *info = tty->driver_data;
        unsigned long flags;
 
        DBGINFO(("%s set_termios\n", tty->driver->name));
 
-       /* just return if nothing has changed */
-       if ((tty->termios->c_cflag == old_termios->c_cflag)
-           && (RELEVANT_IFLAG(tty->termios->c_iflag)
-               == RELEVANT_IFLAG(old_termios->c_iflag)))
-               return;
-
        change_params(info);
 
        /* Handle transition to B0 status */
@@ -912,6 +904,8 @@ start:
                spin_lock_irqsave(&info->lock,flags);
                if (!info->tx_active)
                        tx_start(info);
+               else
+                       tdma_start(info);
                spin_unlock_irqrestore(&info->lock,flags);
        }
 
@@ -1045,7 +1039,6 @@ static void flush_buffer(struct tty_struct *tty)
                info->tx_count = 0;
        spin_unlock_irqrestore(&info->lock,flags);
 
-       wake_up_interruptible(&tty->write_wait);
        tty_wakeup(tty);
 }
 
@@ -1172,6 +1165,112 @@ static int ioctl(struct tty_struct *tty, struct file *file,
 }
 
 /*
+ * support for 32 bit ioctl calls on 64 bit systems
+ */
+#ifdef CONFIG_COMPAT
+static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *user_params)
+{
+       struct MGSL_PARAMS32 tmp_params;
+
+       DBGINFO(("%s get_params32\n", info->device_name));
+       tmp_params.mode            = (compat_ulong_t)info->params.mode;
+       tmp_params.loopback        = info->params.loopback;
+       tmp_params.flags           = info->params.flags;
+       tmp_params.encoding        = info->params.encoding;
+       tmp_params.clock_speed     = (compat_ulong_t)info->params.clock_speed;
+       tmp_params.addr_filter     = info->params.addr_filter;
+       tmp_params.crc_type        = info->params.crc_type;
+       tmp_params.preamble_length = info->params.preamble_length;
+       tmp_params.preamble        = info->params.preamble;
+       tmp_params.data_rate       = (compat_ulong_t)info->params.data_rate;
+       tmp_params.data_bits       = info->params.data_bits;
+       tmp_params.stop_bits       = info->params.stop_bits;
+       tmp_params.parity          = info->params.parity;
+       if (copy_to_user(user_params, &tmp_params, sizeof(struct MGSL_PARAMS32)))
+               return -EFAULT;
+       return 0;
+}
+
+static long set_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *new_params)
+{
+       struct MGSL_PARAMS32 tmp_params;
+
+       DBGINFO(("%s set_params32\n", info->device_name));
+       if (copy_from_user(&tmp_params, new_params, sizeof(struct MGSL_PARAMS32)))
+               return -EFAULT;
+
+       spin_lock(&info->lock);
+       info->params.mode            = tmp_params.mode;
+       info->params.loopback        = tmp_params.loopback;
+       info->params.flags           = tmp_params.flags;
+       info->params.encoding        = tmp_params.encoding;
+       info->params.clock_speed     = tmp_params.clock_speed;
+       info->params.addr_filter     = tmp_params.addr_filter;
+       info->params.crc_type        = tmp_params.crc_type;
+       info->params.preamble_length = tmp_params.preamble_length;
+       info->params.preamble        = tmp_params.preamble;
+       info->params.data_rate       = tmp_params.data_rate;
+       info->params.data_bits       = tmp_params.data_bits;
+       info->params.stop_bits       = tmp_params.stop_bits;
+       info->params.parity          = tmp_params.parity;
+       spin_unlock(&info->lock);
+
+       change_params(info);
+
+       return 0;
+}
+
+static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       struct slgt_info *info = tty->driver_data;
+       int rc = -ENOIOCTLCMD;
+
+       if (sanity_check(info, tty->name, "compat_ioctl"))
+               return -ENODEV;
+       DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));
+
+       switch (cmd) {
+
+       case MGSL_IOCSPARAMS32:
+               rc = set_params32(info, compat_ptr(arg));
+               break;
+
+       case MGSL_IOCGPARAMS32:
+               rc = get_params32(info, compat_ptr(arg));
+               break;
+
+       case MGSL_IOCGPARAMS:
+       case MGSL_IOCSPARAMS:
+       case MGSL_IOCGTXIDLE:
+       case MGSL_IOCGSTATS:
+       case MGSL_IOCWAITEVENT:
+       case MGSL_IOCGIF:
+       case MGSL_IOCSGPIO:
+       case MGSL_IOCGGPIO:
+       case MGSL_IOCWAITGPIO:
+       case TIOCGICOUNT:
+               rc = ioctl(tty, file, cmd, (unsigned long)(compat_ptr(arg)));
+               break;
+
+       case MGSL_IOCSTXIDLE:
+       case MGSL_IOCTXENABLE:
+       case MGSL_IOCRXENABLE:
+       case MGSL_IOCTXABORT:
+       case TIOCMIWAIT:
+       case MGSL_IOCSIF:
+               rc = ioctl(tty, file, cmd, arg);
+               break;
+       }
+
+       DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
+       return rc;
+}
+#else
+#define slgt_compat_ioctl NULL
+#endif /* ifdef CONFIG_COMPAT */
+
+/*
  * proc fs support
  */
 static inline int line_info(char *buf, struct slgt_info *info)
@@ -1465,6 +1564,9 @@ static int hdlcdev_open(struct net_device *dev)
        int rc;
        unsigned long flags;
 
+       if (!try_module_get(THIS_MODULE))
+               return -EBUSY;
+
        DBGINFO(("%s hdlcdev_open\n", dev->name));
 
        /* generic HDLC layer open processing */
@@ -1534,6 +1636,7 @@ static int hdlcdev_close(struct net_device *dev)
        info->netcount=0;
        spin_unlock_irqrestore(&info->netlock, flags);
 
+       module_put(THIS_MODULE);
        return 0;
 }
 
@@ -1826,8 +1929,7 @@ static void rx_async(struct slgt_info *info)
                if (i < count) {
                        /* receive buffer not completed */
                        info->rbuf_index += i;
-                       info->rx_timer.expires = jiffies + 1;
-                       add_timer(&info->rx_timer);
+                       mod_timer(&info->rx_timer, jiffies + 1);
                        break;
                }
 
@@ -1933,43 +2035,45 @@ static void bh_transmit(struct slgt_info *info)
        struct tty_struct *tty = info->tty;
 
        DBGBH(("%s bh_transmit\n", info->device_name));
-       if (tty) {
+       if (tty)
                tty_wakeup(tty);
-               wake_up_interruptible(&tty->write_wait);
-       }
 }
 
-static void dsr_change(struct slgt_info *info)
+static void dsr_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT3) {
+               info->signals |= SerialSignal_DSR;
+               info->input_signal_events.dsr_up++;
+       } else {
+               info->signals &= ~SerialSignal_DSR;
+               info->input_signal_events.dsr_down++;
+       }
        DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_DSR);
                return;
        }
        info->icount.dsr++;
-       if (info->signals & SerialSignal_DSR)
-               info->input_signal_events.dsr_up++;
-       else
-               info->input_signal_events.dsr_down++;
        wake_up_interruptible(&info->status_event_wait_q);
        wake_up_interruptible(&info->event_wait_q);
        info->pending_bh |= BH_STATUS;
 }
 
-static void cts_change(struct slgt_info *info)
+static void cts_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT2) {
+               info->signals |= SerialSignal_CTS;
+               info->input_signal_events.cts_up++;
+       } else {
+               info->signals &= ~SerialSignal_CTS;
+               info->input_signal_events.cts_down++;
+       }
        DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_CTS);
                return;
        }
        info->icount.cts++;
-       if (info->signals & SerialSignal_CTS)
-               info->input_signal_events.cts_up++;
-       else
-               info->input_signal_events.cts_down++;
        wake_up_interruptible(&info->status_event_wait_q);
        wake_up_interruptible(&info->event_wait_q);
        info->pending_bh |= BH_STATUS;
@@ -1990,20 +2094,21 @@ static void cts_change(struct slgt_info *info)
        }
 }
 
-static void dcd_change(struct slgt_info *info)
+static void dcd_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT1) {
+               info->signals |= SerialSignal_DCD;
+               info->input_signal_events.dcd_up++;
+       } else {
+               info->signals &= ~SerialSignal_DCD;
+               info->input_signal_events.dcd_down++;
+       }
        DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_DCD);
                return;
        }
        info->icount.dcd++;
-       if (info->signals & SerialSignal_DCD) {
-               info->input_signal_events.dcd_up++;
-       } else {
-               info->input_signal_events.dcd_down++;
-       }
 #if SYNCLINK_GENERIC_HDLC
        if (info->netcount) {
                if (info->signals & SerialSignal_DCD)
@@ -2026,20 +2131,21 @@ static void dcd_change(struct slgt_info *info)
        }
 }
 
-static void ri_change(struct slgt_info *info)
+static void ri_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT0) {
+               info->signals |= SerialSignal_RI;
+               info->input_signal_events.ri_up++;
+       } else {
+               info->signals &= ~SerialSignal_RI;
+               info->input_signal_events.ri_down++;
+       }
        DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_RI);
                return;
        }
-       info->icount.dcd++;
-       if (info->signals & SerialSignal_RI) {
-               info->input_signal_events.ri_up++;
-       } else {
-               info->input_signal_events.ri_down++;
-       }
+       info->icount.rng++;
        wake_up_interruptible(&info->status_event_wait_q);
        wake_up_interruptible(&info->event_wait_q);
        info->pending_bh |= BH_STATUS;
@@ -2090,13 +2196,13 @@ static void isr_serial(struct slgt_info *info)
        }
 
        if (status & IRQ_DSR)
-               dsr_change(info);
+               dsr_change(info, status);
        if (status & IRQ_CTS)
-               cts_change(info);
+               cts_change(info, status);
        if (status & IRQ_DCD)
-               dcd_change(info);
+               dcd_change(info, status);
        if (status & IRQ_RI)
-               ri_change(info);
+               ri_change(info, status);
 }
 
 static void isr_rdma(struct slgt_info *info)
@@ -3320,13 +3426,12 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev
 {
        struct slgt_info *info;
 
-       info = kmalloc(sizeof(struct slgt_info), GFP_KERNEL);
+       info = kzalloc(sizeof(struct slgt_info), GFP_KERNEL);
 
        if (!info) {
                DBGERR(("%s device alloc failed adapter=%d port=%d\n",
                        driver_name, adapter_num, port_num));
        } else {
-               memset(info, 0, sizeof(struct slgt_info));
                info->magic = MGSL_MAGIC;
                INIT_WORK(&info->task, bh_handler);
                info->max_frame_size = 4096;
@@ -3343,13 +3448,8 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev
                info->adapter_num = adapter_num;
                info->port_num = port_num;
 
-               init_timer(&info->tx_timer);
-               info->tx_timer.data = (unsigned long)info;
-               info->tx_timer.function = tx_timeout;
-
-               init_timer(&info->rx_timer);
-               info->rx_timer.data = (unsigned long)info;
-               info->rx_timer.function = rx_timeout;
+               setup_timer(&info->tx_timer, tx_timeout, (unsigned long)info);
+               setup_timer(&info->rx_timer, rx_timeout, (unsigned long)info);
 
                /* Copy configuration info to device instance data */
                info->pdev = pdev;
@@ -3424,6 +3524,9 @@ static void device_init(int adapter_num, struct pci_dev *pdev)
                        }
                }
        }
+
+       for (i=0; i < port_count; ++i)
+               tty_register_device(serial_driver, port_array[i]->line, &(port_array[i]->pdev->dev));
 }
 
 static int __devinit init_one(struct pci_dev *dev,
@@ -3452,6 +3555,7 @@ static const struct tty_operations ops = {
        .chars_in_buffer = chars_in_buffer,
        .flush_buffer = flush_buffer,
        .ioctl = ioctl,
+       .compat_ioctl = slgt_compat_ioctl,
        .throttle = throttle,
        .unthrottle = unthrottle,
        .send_xchar = send_xchar,
@@ -3475,6 +3579,8 @@ static void slgt_cleanup(void)
        printk("unload %s %s\n", driver_name, driver_version);
 
        if (serial_driver) {
+               for (info=slgt_device_list ; info != NULL ; info=info->next_device)
+                       tty_unregister_device(serial_driver, info->line);
                if ((rc = tty_unregister_driver(serial_driver)))
                        DBGERR(("tty_unregister_driver error=%d\n", rc));
                put_tty_driver(serial_driver);
@@ -3515,23 +3621,10 @@ static int __init slgt_init(void)
 
        printk("%s %s\n", driver_name, driver_version);
 
-       slgt_device_count = 0;
-       if ((rc = pci_register_driver(&pci_driver)) < 0) {
-               printk("%s pci_register_driver error=%d\n", driver_name, rc);
-               return rc;
-       }
-       pci_registered = 1;
-
-       if (!slgt_device_list) {
-               printk("%s no devices found\n",driver_name);
-               pci_unregister_driver(&pci_driver);
-               return -ENODEV;
-       }
-
        serial_driver = alloc_tty_driver(MAX_DEVICES);
        if (!serial_driver) {
-               rc = -ENOMEM;
-               goto error;
+               printk("%s can't allocate tty driver\n", driver_name);
+               return -ENOMEM;
        }
 
        /* Initialize the tty_driver structure */
@@ -3546,7 +3639,9 @@ static int __init slgt_init(void)
        serial_driver->init_termios = tty_std_termios;
        serial_driver->init_termios.c_cflag =
                B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       serial_driver->flags = TTY_DRIVER_REAL_RAW;
+       serial_driver->init_termios.c_ispeed = 9600;
+       serial_driver->init_termios.c_ospeed = 9600;
+       serial_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
        tty_set_operations(serial_driver, &ops);
        if ((rc = tty_register_driver(serial_driver)) < 0) {
                DBGERR(("%s can't register serial driver\n", driver_name));
@@ -3559,6 +3654,16 @@ static int __init slgt_init(void)
                driver_name, driver_version,
                serial_driver->major);
 
+       slgt_device_count = 0;
+       if ((rc = pci_register_driver(&pci_driver)) < 0) {
+               printk("%s pci_register_driver error=%d\n", driver_name, rc);
+               goto error;
+       }
+       pci_registered = 1;
+
+       if (!slgt_device_list)
+               printk("%s no devices found\n",driver_name);
+
        return 0;
 
 error:
@@ -3778,45 +3883,58 @@ static void tx_start(struct slgt_info *info)
                        slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
                        /* clear tx idle and underrun status bits */
                        wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
-
-                       if (!(rd_reg32(info, TDCSR) & BIT0)) {
-                               /* tx DMA stopped, restart tx DMA */
-                               tdma_reset(info);
-                               /* set 1st descriptor address */
-                               wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
-                               switch(info->params.mode) {
-                               case MGSL_MODE_RAW:
-                               case MGSL_MODE_MONOSYNC:
-                               case MGSL_MODE_BISYNC:
-                                       wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
-                                       break;
-                               default:
-                                       wr_reg32(info, TDCSR, BIT0); /* DMA enable */
-                               }
-                       }
-
-                       if (info->params.mode == MGSL_MODE_HDLC) {
-                               info->tx_timer.expires = jiffies + msecs_to_jiffies(5000);
-                               add_timer(&info->tx_timer);
-                       }
+                       if (info->params.mode == MGSL_MODE_HDLC)
+                               mod_timer(&info->tx_timer, jiffies +
+                                               msecs_to_jiffies(5000));
                } else {
-                       tdma_reset(info);
-                       /* set 1st descriptor address */
-                       wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
-
                        slgt_irq_off(info, IRQ_TXDATA);
                        slgt_irq_on(info, IRQ_TXIDLE);
                        /* clear tx idle status bit */
                        wr_reg16(info, SSR, IRQ_TXIDLE);
-
-                       /* enable tx DMA */
-                       wr_reg32(info, TDCSR, BIT0);
                }
-
+               tdma_start(info);
                info->tx_active = 1;
        }
 }
 
+/*
+ * start transmit DMA if inactive and there are unsent buffers
+ */
+static void tdma_start(struct slgt_info *info)
+{
+       unsigned int i;
+
+       if (rd_reg32(info, TDCSR) & BIT0)
+               return;
+
+       /* transmit DMA inactive, check for unsent buffers */
+       i = info->tbuf_start;
+       while (!desc_count(info->tbufs[i])) {
+               if (++i == info->tbuf_count)
+                       i = 0;
+               if (i == info->tbuf_current)
+                       return;
+       }
+       info->tbuf_start = i;
+
+       /* there are unsent buffers, start transmit DMA */
+
+       /* reset needed if previous error condition */
+       tdma_reset(info);
+
+       /* set 1st descriptor address */
+       wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
+       switch(info->params.mode) {
+       case MGSL_MODE_RAW:
+       case MGSL_MODE_MONOSYNC:
+       case MGSL_MODE_BISYNC:
+               wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
+               break;
+       default:
+               wr_reg32(info, TDCSR, BIT0); /* DMA enable */
+       }
+}
+
 static void tx_stop(struct slgt_info *info)
 {
        unsigned short val;
@@ -4550,8 +4668,8 @@ static unsigned int free_tbuf_count(struct slgt_info *info)
                        i=0;
        } while (i != info->tbuf_current);
 
-       /* last buffer with zero count may be in use, assume it is */
-       if (count)
+       /* if tx DMA active, last zero count buffer is in use */
+       if (count && (rd_reg32(info, TDCSR) & BIT0))
                --count;
 
        return count;