Merge branch 'classmate' into release
[safe/jmp/linux-2.6] / drivers / char / nozomi.c
index 6076e66..7d73cd4 100644 (file)
@@ -2,7 +2,7 @@
  * nozomi.c  -- HSDPA driver Broadband Wireless Data Card - Globe Trotter
  *
  * Written by: Ulf Jakobsson,
- *             Jan erfeldt,
+ *             Jan Åkerfeldt,
  *             Stefan Thomasson,
  *
  * Maintained by: Paul Hardwick (p.hardwick@option.com)
  * --------------------------------------------------------------------------
  */
 
-/*
- * CHANGELOG
- * Version 2.1d
- * 11-November-2007 Jiri Slaby, Frank Seidel
- * - Big rework of multicard support by Jiri
- * - Major cleanups (semaphore to mutex, endianess, no major reservation)
- * - Optimizations
- *
- * Version 2.1c
- * 30-October-2007 Frank Seidel
- * - Completed multicard support
- * - Minor cleanups
- *
- * Version 2.1b
- * 07-August-2007 Frank Seidel
- * - Minor cleanups
- * - theoretical multicard support
- *
- * Version 2.1
- * 03-July-2006 Paul Hardwick
- *
- * - Stability Improvements. Incorporated spinlock wraps patch.
- * - Updated for newer 2.6.14+ kernels (tty_buffer_request_room)
- * - using __devexit macro for tty
- *
- *
- * Version 2.0
- * 08-feb-2006 15:34:10:Ulf
- *
- * -Fixed issue when not waking up line disipine layer, could probably result
- *  in better uplink performance for 2.4.
- *
- * -Fixed issue with big endian during initalization, now proper toggle flags
- *  are handled between preloader and maincode.
- *
- * -Fixed flow control issue.
- *
- * -Added support for setting DTR.
- *
- * -For 2.4 kernels, removing temporary buffer that's not needed.
- *
- * -Reading CTS only for modem port (only port that supports it).
- *
- * -Return 0 in write_room instead of netative value, it's not handled in
- *  upper layer.
- *
- * --------------------------------------------------------------------------
- * Version 1.0
- *
- * First version of driver, only tested with card of type F32_2.
- * Works fine with 2.4 and 2.6 kernels.
- * Driver also support big endian architecture.
- */
-
 /* Enable this to have a lot of debug printouts */
 #define DEBUG
 
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
+#include <linux/sched.h>
 #include <linux/serial.h>
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
@@ -127,7 +74,7 @@ do {                                                         \
        char tmp[P_BUF_SIZE];                                   \
        snprintf(tmp, sizeof(tmp), ##args);                     \
        printk(_err_flag_ "[%d] %s(): %s\n", __LINE__,          \
-               __FUNCTION__, tmp);                             \
+               __func__, tmp);                         \
 } while (0)
 
 #define DBG1(args...) D_(0x01, ##args)
@@ -143,8 +90,9 @@ do {                                                         \
 /* Do we need this settable at runtime? */
 static int debug = NOZOMI_DEBUG_LEVEL;
 
-#define D(lvl, args...)  do {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \
-                               while (0)
+#define D(lvl, args...)  do \
+                       {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \
+                       while (0)
 #define D_(lvl, args...) D(lvl, ##args)
 
 /* These printouts are always printed */
@@ -243,6 +191,14 @@ enum card_type {
        F32_8 = 8192,   /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */
 };
 
+/* Initialization states a card can be in */
+enum card_state {
+       NOZOMI_STATE_UKNOWN     = 0,
+       NOZOMI_STATE_ENABLED    = 1,    /* pci device enabled */
+       NOZOMI_STATE_ALLOCATED  = 2,    /* config setup done */
+       NOZOMI_STATE_READY      = 3,    /* flowcontrols received */
+};
+
 /* Two different toggle channels exist */
 enum channel_type {
        CH_A = 0,
@@ -273,13 +229,13 @@ enum port_type {
 /* Big endian */
 
 struct toggles {
-       unsigned enabled:5;     /*
+       unsigned int enabled:5; /*
                                 * Toggle fields are valid if enabled is 0,
                                 * else A-channels must always be used.
                                 */
-       unsigned diag_dl:1;
-       unsigned mdm_dl:1;
-       unsigned mdm_ul:1;
+       unsigned int diag_dl:1;
+       unsigned int mdm_dl:1;
+       unsigned int mdm_ul:1;
 } __attribute__ ((packed));
 
 /* Configuration table to read at startup of card */
@@ -320,19 +276,19 @@ struct config_table {
 /* This stores all control downlink flags */
 struct ctrl_dl {
        u8 port;
-       unsigned reserved:4;
-       unsigned CTS:1;
-       unsigned RI:1;
-       unsigned DCD:1;
-       unsigned DSR:1;
+       unsigned int reserved:4;
+       unsigned int CTS:1;
+       unsigned int RI:1;
+       unsigned int DCD:1;
+       unsigned int DSR:1;
 } __attribute__ ((packed));
 
 /* This stores all control uplink flags */
 struct ctrl_ul {
        u8 port;
-       unsigned reserved:6;
-       unsigned RTS:1;
-       unsigned DTR:1;
+       unsigned int reserved:6;
+       unsigned int RTS:1;
+       unsigned int DTR:1;
 } __attribute__ ((packed));
 
 #else
@@ -340,10 +296,10 @@ struct ctrl_ul {
 
 /* This represents the toggle information */
 struct toggles {
-       unsigned mdm_ul:1;
-       unsigned mdm_dl:1;
-       unsigned diag_dl:1;
-       unsigned enabled:5;     /*
+       unsigned int mdm_ul:1;
+       unsigned int mdm_dl:1;
+       unsigned int diag_dl:1;
+       unsigned int enabled:5; /*
                                 * Toggle fields are valid if enabled is 0,
                                 * else A-channels must always be used.
                                 */
@@ -379,29 +335,30 @@ struct config_table {
 
 /* This stores all control downlink flags */
 struct ctrl_dl {
-       unsigned DSR:1;
-       unsigned DCD:1;
-       unsigned RI:1;
-       unsigned CTS:1;
-       unsigned reserverd:4;
+       unsigned int DSR:1;
+       unsigned int DCD:1;
+       unsigned int RI:1;
+       unsigned int CTS:1;
+       unsigned int reserverd:4;
        u8 port;
 } __attribute__ ((packed));
 
 /* This stores all control uplink flags */
 struct ctrl_ul {
-       unsigned DTR:1;
-       unsigned RTS:1;
-       unsigned reserved:6;
+       unsigned int DTR:1;
+       unsigned int RTS:1;
+       unsigned int reserved:6;
        u8 port;
 } __attribute__ ((packed));
 #endif
 
 /* This holds all information that is needed regarding a port */
 struct port {
+       struct tty_port port;
        u8 update_flow_control;
        struct ctrl_ul ctrl_ul;
        struct ctrl_dl ctrl_dl;
-       struct kfifo *fifo_ul;
+       struct kfifo fifo_ul;
        void __iomem *dl_addr[2];
        u32 dl_size[2];
        u8 toggle_dl;
@@ -410,8 +367,6 @@ struct port {
        u8 toggle_ul;
        u16 token_dl;
 
-       struct tty_struct *tty;
-       int tty_open_count;
        /* mutex to ensure one access patch to this port */
        struct mutex tty_sem;
        wait_queue_head_t tty_wait;
@@ -438,6 +393,7 @@ struct nozomi {
        spinlock_t spin_mutex;  /* secures access to registers and tty */
 
        unsigned int index_start;
+       enum card_state state;
        u32 open_ttys;
 };
 
@@ -448,7 +404,7 @@ struct buffer {
 } __attribute__ ((packed));
 
 /*    Global variables */
-static struct pci_device_id nozomi_pci_tbl[] = {
+static const struct pci_device_id nozomi_pci_tbl[] __devinitconst = {
        {PCI_DEVICE(VENDOR1, DEVICE1)},
        {},
 };
@@ -482,7 +438,7 @@ static void read_mem32(u32 *buf, const void __iomem *mem_addr_start,
                        u32 size_bytes)
 {
        u32 i = 0;
-       const u32 *ptr = (__force u32 *) mem_addr_start;
+       const u32 __iomem *ptr = mem_addr_start;
        u16 *buf16;
 
        if (unlikely(!ptr || !buf))
@@ -492,11 +448,11 @@ static void read_mem32(u32 *buf, const void __iomem *mem_addr_start,
        switch (size_bytes) {
        case 2: /* 2 bytes */
                buf16 = (u16 *) buf;
-               *buf16 = __le16_to_cpu(readw((void __iomem *)ptr));
+               *buf16 = __le16_to_cpu(readw(ptr));
                goto out;
                break;
        case 4: /* 4 bytes */
-               *(buf) = __le32_to_cpu(readl((void __iomem *)ptr));
+               *(buf) = __le32_to_cpu(readl(ptr));
                goto out;
                break;
        }
@@ -505,11 +461,11 @@ static void read_mem32(u32 *buf, const void __iomem *mem_addr_start,
                if (size_bytes - i == 2) {
                        /* Handle 2 bytes in the end */
                        buf16 = (u16 *) buf;
-                       *(buf16) = __le16_to_cpu(readw((void __iomem *)ptr));
+                       *(buf16) = __le16_to_cpu(readw(ptr));
                        i += 2;
                } else {
                        /* Read 4 bytes */
-                       *(buf) = __le32_to_cpu(readl((void __iomem *)ptr));
+                       *(buf) = __le32_to_cpu(readl(ptr));
                        i += 4;
                }
                buf++;
@@ -524,12 +480,12 @@ out:
  * -Optimize
  * -Rewrite cleaner
  */
-static u32 write_mem32(void __iomem *mem_addr_start, u32 *buf,
+static u32 write_mem32(void __iomem *mem_addr_start, const u32 *buf,
                        u32 size_bytes)
 {
        u32 i = 0;
-       u32 *ptr = (__force u32 *) mem_addr_start;
-       u16 *buf16;
+       u32 __iomem *ptr = mem_addr_start;
+       const u16 *buf16;
 
        if (unlikely(!ptr || !buf))
                return 0;
@@ -537,8 +493,8 @@ static u32 write_mem32(void __iomem *mem_addr_start, u32 *buf,
        /* shortcut for extremely often used cases */
        switch (size_bytes) {
        case 2: /* 2 bytes */
-               buf16 = (u16 *) buf;
-               writew(__cpu_to_le16(*buf16), (void __iomem *)ptr);
+               buf16 = (const u16 *)buf;
+               writew(__cpu_to_le16(*buf16), ptr);
                return 2;
                break;
        case 1: /*
@@ -546,7 +502,7 @@ static u32 write_mem32(void __iomem *mem_addr_start, u32 *buf,
                 * so falling through..
                 */
        case 4: /* 4 bytes */
-               writel(__cpu_to_le32(*buf), (void __iomem *)ptr);
+               writel(__cpu_to_le32(*buf), ptr);
                return 4;
                break;
        }
@@ -554,12 +510,12 @@ static u32 write_mem32(void __iomem *mem_addr_start, u32 *buf,
        while (i < size_bytes) {
                if (size_bytes - i == 2) {
                        /* 2 bytes */
-                       buf16 = (u16 *) buf;
-                       writew(__cpu_to_le16(*buf16), (void __iomem *)ptr);
+                       buf16 = (const u16 *)buf;
+                       writew(__cpu_to_le16(*buf16), ptr);
                        i += 2;
                } else {
                        /* 4 bytes */
-                       writel(__cpu_to_le32(*buf), (void __iomem *)ptr);
+                       writel(__cpu_to_le32(*buf), ptr);
                        i += 4;
                }
                buf++;
@@ -694,7 +650,7 @@ static void dump_table(const struct nozomi *dc)
           dc->config_table.ul_ctrl_len);
 }
 #else
-static __inline__ void dump_table(const struct nozomi *dc) { }
+static inline void dump_table(const struct nozomi *dc) { }
 #endif
 
 /*
@@ -729,8 +685,6 @@ static int nozomi_read_config_table(struct nozomi *dc)
                dump_table(dc);
 
                for (i = PORT_MDM; i < MAX_PORT; i++) {
-                       dc->port[i].fifo_ul =
-                           kfifo_alloc(FIFO_BUFFER_SIZE_UL, GFP_ATOMIC, NULL);
                        memset(&dc->port[i].ctrl_dl, 0, sizeof(struct ctrl_dl));
                        memset(&dc->port[i].ctrl_ul, 0, sizeof(struct ctrl_ul));
                }
@@ -739,6 +693,7 @@ static int nozomi_read_config_table(struct nozomi *dc)
                dc->last_ier = dc->last_ier | CTRL_DL;
                writew(dc->last_ier, dc->reg_ier);
 
+               dc->state = NOZOMI_STATE_ALLOCATED;
                dev_info(&dc->pdev->dev, "Initialization OK!\n");
                return 1;
        }
@@ -776,8 +731,7 @@ static int nozomi_read_config_table(struct nozomi *dc)
 /* Enable uplink interrupts  */
 static void enable_transmit_ul(enum port_type port, struct nozomi *dc)
 {
-       u16 mask[NOZOMI_MAX_PORTS] = \
-                       {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL};
+       static const u16 mask[] = {MDM_UL, DIAG_UL, APP1_UL, APP2_UL, CTRL_UL};
 
        if (port < NOZOMI_MAX_PORTS) {
                dc->last_ier |= mask[port];
@@ -790,8 +744,8 @@ static void enable_transmit_ul(enum port_type port, struct nozomi *dc)
 /* Disable uplink interrupts  */
 static void disable_transmit_ul(enum port_type port, struct nozomi *dc)
 {
-       u16 mask[NOZOMI_MAX_PORTS] = \
-                       {~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL};
+       static const u16 mask[] =
+               {~MDM_UL, ~DIAG_UL, ~APP1_UL, ~APP2_UL, ~CTRL_UL};
 
        if (port < NOZOMI_MAX_PORTS) {
                dc->last_ier &= mask[port];
@@ -804,8 +758,7 @@ static void disable_transmit_ul(enum port_type port, struct nozomi *dc)
 /* Enable downlink interrupts */
 static void enable_transmit_dl(enum port_type port, struct nozomi *dc)
 {
-       u16 mask[NOZOMI_MAX_PORTS] = \
-                       {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL};
+       static const u16 mask[] = {MDM_DL, DIAG_DL, APP1_DL, APP2_DL, CTRL_DL};
 
        if (port < NOZOMI_MAX_PORTS) {
                dc->last_ier |= mask[port];
@@ -818,8 +771,8 @@ static void enable_transmit_dl(enum port_type port, struct nozomi *dc)
 /* Disable downlink interrupts */
 static void disable_transmit_dl(enum port_type port, struct nozomi *dc)
 {
-       u16 mask[NOZOMI_MAX_PORTS] = \
-                       {~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL};
+       static const u16 mask[] =
+               {~MDM_DL, ~DIAG_DL, ~APP1_DL, ~APP2_DL, ~CTRL_DL};
 
        if (port < NOZOMI_MAX_PORTS) {
                dc->last_ier &= mask[port];
@@ -837,17 +790,18 @@ static int send_data(enum port_type index, struct nozomi *dc)
 {
        u32 size = 0;
        struct port *port = &dc->port[index];
-       u8 toggle = port->toggle_ul;
+       const u8 toggle = port->toggle_ul;
        void __iomem *addr = port->ul_addr[toggle];
-       u32 ul_size = port->ul_size[toggle];
-       struct tty_struct *tty = port->tty;
+       const u32 ul_size = port->ul_size[toggle];
+       struct tty_struct *tty = tty_port_tty_get(&port->port);
 
        /* Get data from tty and place in buf for now */
-       size = __kfifo_get(port->fifo_ul, dc->send_buf,
+       size = kfifo_out(&port->fifo_ul, dc->send_buf,
                           ul_size < SEND_BUF_MAX ? ul_size : SEND_BUF_MAX);
 
        if (size == 0) {
                DBG4("No more data to send, disable link:");
+               tty_kref_put(tty);
                return 0;
        }
 
@@ -860,6 +814,7 @@ static int send_data(enum port_type index, struct nozomi *dc)
        if (tty)
                tty_wakeup(tty);
 
+       tty_kref_put(tty);
        return 1;
 }
 
@@ -871,8 +826,8 @@ static int receive_data(enum port_type index, struct nozomi *dc)
        u32 offset = 4;
        struct port *port = &dc->port[index];
        void __iomem *addr = port->dl_addr[port->toggle_dl];
-       struct tty_struct *tty = port->tty;
-       int i;
+       struct tty_struct *tty = tty_port_tty_get(&port->port);
+       int i, ret;
 
        if (unlikely(!tty)) {
                DBG1("tty not open for port: %d?", index);
@@ -888,12 +843,14 @@ static int receive_data(enum port_type index, struct nozomi *dc)
 
                /* disable interrupt in downlink... */
                disable_transmit_dl(index, dc);
-               return 0;
+               ret = 0;
+               goto put;
        }
 
        if (unlikely(size == 0)) {
                dev_err(&dc->pdev->dev, "size == 0?\n");
-               return 1;
+               ret = 1;
+               goto put;
        }
 
        tty_buffer_request_room(tty, size);
@@ -915,8 +872,10 @@ static int receive_data(enum port_type index, struct nozomi *dc)
        }
 
        set_bit(index, &dc->flip);
-
-       return 1;
+       ret = 1;
+put:
+       tty_kref_put(tty);
+       return ret;
 }
 
 /* Debug for interrupts */
@@ -999,6 +958,14 @@ static int receive_flow_control(struct nozomi *dc)
        case CTRL_APP2:
                port = PORT_APP2;
                enable_ier = APP2_DL;
+               if (dc->state == NOZOMI_STATE_ALLOCATED) {
+                       /*
+                        * After card initialization the flow control
+                        * received for APP2 is always the last
+                        */
+                       dc->state = NOZOMI_STATE_READY;
+                       dev_info(&dc->pdev->dev, "Device READY!\n");
+               }
                break;
        default:
                dev_err(&dc->pdev->dev,
@@ -1019,11 +986,11 @@ static int receive_flow_control(struct nozomi *dc)
 
        } else if (old_ctrl.CTS == 0 && ctrl_dl.CTS == 1) {
 
-               if (__kfifo_len(dc->port[port].fifo_ul)) {
+               if (kfifo_len(&dc->port[port].fifo_ul)) {
                        DBG1("Enable interrupt (0x%04X) on port: %d",
                                enable_ier, port);
                        DBG1("Data in buffer [%d], enable transmit! ",
-                               __kfifo_len(dc->port[port].fifo_ul));
+                               kfifo_len(&dc->port[port].fifo_ul));
                        enable_transmit_ul(port, dc);
                } else {
                        DBG1("No data in buffer...");
@@ -1102,7 +1069,7 @@ static int send_flow_control(struct nozomi *dc)
 }
 
 /*
- * Handle donlink data, ports that are handled are modem and diagnostics
+ * Handle downlink data, ports that are handled are modem and diagnostics
  * Return 1 - ok
  * Return 0 - toggle fields are out of sync
  */
@@ -1313,9 +1280,15 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id)
 
 exit_handler:
        spin_unlock(&dc->spin_mutex);
-       for (a = 0; a < NOZOMI_MAX_PORTS; a++)
-               if (test_and_clear_bit(a, &dc->flip))
-                       tty_flip_buffer_push(dc->port[a].tty);
+       for (a = 0; a < NOZOMI_MAX_PORTS; a++) {
+               struct tty_struct *tty;
+               if (test_and_clear_bit(a, &dc->flip)) {
+                       tty = tty_port_tty_get(&dc->port[a].port);
+                       if (tty)
+                               tty_flip_buffer_push(tty);
+                       tty_kref_put(tty);
+               }
+       }
        return IRQ_HANDLED;
 none:
        spin_unlock(&dc->spin_mutex);
@@ -1359,20 +1332,20 @@ static void nozomi_setup_private_data(struct nozomi *dc)
 static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
 {
-       struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+       const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
 
        return sprintf(buf, "%d\n", dc->card_type);
 }
-static DEVICE_ATTR(card_type, 0444, card_type_show, NULL);
+static DEVICE_ATTR(card_type, S_IRUGO, card_type_show, NULL);
 
 static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
 {
-       struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+       const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
 
        return sprintf(buf, "%u\n", dc->open_ttys);
 }
-static DEVICE_ATTR(open_ttys, 0444, open_ttys_show, NULL);
+static DEVICE_ATTR(open_ttys, S_IRUGO, open_ttys_show, NULL);
 
 static void make_sysfs_files(struct nozomi *dc)
 {
@@ -1421,22 +1394,12 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
 
        dc->pdev = pdev;
 
-       /* Find out what card type it is */
-       nozomi_get_card_type(dc);
-
        ret = pci_enable_device(dc->pdev);
        if (ret) {
                dev_err(&pdev->dev, "Failed to enable PCI Device\n");
                goto err_free;
        }
 
-       start = pci_resource_start(dc->pdev, 0);
-       if (start == 0) {
-               dev_err(&pdev->dev, "No I/O address for card detected\n");
-               ret = -ENODEV;
-               goto err_disable_device;
-       }
-
        ret = pci_request_regions(dc->pdev, NOZOMI_NAME);
        if (ret) {
                dev_err(&pdev->dev, "I/O address 0x%04x already in use\n",
@@ -1444,7 +1407,17 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
                goto err_disable_device;
        }
 
-       dc->base_addr = ioremap(start, dc->card_type);
+       start = pci_resource_start(dc->pdev, 0);
+       if (start == 0) {
+               dev_err(&pdev->dev, "No I/O address for card detected\n");
+               ret = -ENODEV;
+               goto err_rel_regs;
+       }
+
+       /* Find out what card type it is */
+       nozomi_get_card_type(dc);
+
+       dc->base_addr = ioremap_nocache(start, dc->card_type);
        if (!dc->base_addr) {
                dev_err(&pdev->dev, "Unable to map card MMIO\n");
                ret = -ENODEV;
@@ -1458,6 +1431,16 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
                goto err_free_sbuf;
        }
 
+       for (i = PORT_MDM; i < MAX_PORT; i++) {
+               if (kfifo_alloc(&dc->port[i].fifo_ul,
+                     FIFO_BUFFER_SIZE_UL, GFP_ATOMIC)) {
+                       dev_err(&pdev->dev,
+                                       "Could not allocate kfifo buffer\n");
+                       ret = -ENOMEM;
+                       goto err_free_kfifo;
+               }
+       }
+
        spin_lock_init(&dc->spin_mutex);
 
        nozomi_setup_private_data(dc);
@@ -1470,7 +1453,7 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
                        NOZOMI_NAME, dc);
        if (unlikely(ret)) {
                dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq);
-               goto err_free_sbuf;
+               goto err_free_kfifo;
        }
 
        DBG1("base_addr: %p", dc->base_addr);
@@ -1480,22 +1463,37 @@ static int __devinit nozomi_card_init(struct pci_dev *pdev,
        dc->index_start = ndev_idx * MAX_PORT;
        ndevs[ndev_idx] = dc;
 
+       pci_set_drvdata(pdev, dc);
+
+       /* Enable RESET interrupt */
+       dc->last_ier = RESET;
+       iowrite16(dc->last_ier, dc->reg_ier);
+
+       dc->state = NOZOMI_STATE_ENABLED;
+
        for (i = 0; i < MAX_PORT; i++) {
+               struct device *tty_dev;
+
                mutex_init(&dc->port[i].tty_sem);
-               dc->port[i].tty_open_count = 0;
-               dc->port[i].tty = NULL;
-               tty_register_device(ntty_driver, dc->index_start + i,
+               tty_port_init(&dc->port[i].port);
+               tty_dev = tty_register_device(ntty_driver, dc->index_start + i,
                                                        &pdev->dev);
-       }
-
-       /* Enable  RESET interrupt. */
-       dc->last_ier = RESET;
-       writew(dc->last_ier, dc->reg_ier);
 
-       pci_set_drvdata(pdev, dc);
+               if (IS_ERR(tty_dev)) {
+                       ret = PTR_ERR(tty_dev);
+                       dev_err(&pdev->dev, "Could not allocate tty?\n");
+                       goto err_free_tty;
+               }
+       }
 
        return 0;
 
+err_free_tty:
+       for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
+               tty_unregister_device(ntty_driver, i);
+err_free_kfifo:
+       for (i = 0; i < MAX_PORT; i++)
+               kfifo_free(&dc->port[i].fifo_ul);
 err_free_sbuf:
        kfree(dc->send_buf);
        iounmap(dc->base_addr);
@@ -1517,14 +1515,16 @@ static void __devexit tty_exit(struct nozomi *dc)
 
        flush_scheduled_work();
 
-       for (i = 0; i < MAX_PORT; ++i)
-               if (dc->port[i].tty && \
-                               list_empty(&dc->port[i].tty->hangup_work.entry))
-                       tty_hangup(dc->port[i].tty);
-
+       for (i = 0; i < MAX_PORT; ++i) {
+               struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port);
+               if (tty && list_empty(&tty->hangup_work.entry))
+                       tty_hangup(tty);
+               tty_kref_put(tty);
+       }
+       /* Racy below - surely should wait for scheduled work to be done or
+          complete off a hangup method ? */
        while (dc->open_ttys)
                msleep(1);
-
        for (i = dc->index_start; i < dc->index_start + MAX_PORT; ++i)
                tty_unregister_device(ntty_driver, i);
 }
@@ -1559,8 +1559,7 @@ static void __devexit nozomi_card_exit(struct pci_dev *pdev)
        free_irq(pdev->irq, dc);
 
        for (i = 0; i < MAX_PORT; i++)
-               if (dc->port[i].fifo_ul)
-                       kfifo_free(dc->port[i].fifo_ul);
+               kfifo_free(&dc->port[i].fifo_ul);
 
        kfree(dc->send_buf);
 
@@ -1608,61 +1607,60 @@ static int ntty_open(struct tty_struct *tty, struct file *file)
        struct nozomi *dc = get_dc_by_tty(tty);
        unsigned long flags;
 
-       if (!port || !dc)
+       if (!port || !dc || dc->state != NOZOMI_STATE_READY)
                return -ENODEV;
 
        if (mutex_lock_interruptible(&port->tty_sem))
                return -ERESTARTSYS;
 
-       port->tty_open_count++;
+       port->port.count++;
        dc->open_ttys++;
 
        /* Enable interrupt downlink for channel */
-       if (port->tty_open_count == 1) {
-               tty->low_latency = 1;
+       if (port->port.count == 1) {
                tty->driver_data = port;
-               port->tty = tty;
+               tty_port_tty_set(&port->port, tty);
                DBG1("open: %d", port->token_dl);
                spin_lock_irqsave(&dc->spin_mutex, flags);
                dc->last_ier = dc->last_ier | port->token_dl;
                writew(dc->last_ier, dc->reg_ier);
                spin_unlock_irqrestore(&dc->spin_mutex, flags);
        }
-
        mutex_unlock(&port->tty_sem);
-
        return 0;
 }
 
-/* Called when the userspace process close the tty, /dev/noz*. */
+/* Called when the userspace process close the tty, /dev/noz*. Also
+   called immediately if ntty_open fails in which case tty->driver_data
+   will be NULL an we exit by the first return */
+
 static void ntty_close(struct tty_struct *tty, struct file *file)
 {
        struct nozomi *dc = get_dc_by_tty(tty);
-       struct port *port = tty->driver_data;
+       struct port *nport = tty->driver_data;
+       struct tty_port *port = &nport->port;
        unsigned long flags;
 
-       if (!dc || !port)
+       if (!dc || !nport)
                return;
 
-       if (mutex_lock_interruptible(&port->tty_sem))
-               return;
+       /* Users cannot interrupt a close */
+       mutex_lock(&nport->tty_sem);
 
-       if (!port->tty_open_count)
-               goto exit;
+       WARN_ON(!port->count);
 
        dc->open_ttys--;
-       port->tty_open_count--;
+       port->count--;
+       tty_port_tty_set(port, NULL);
 
-       if (port->tty_open_count == 0) {
-               DBG1("close: %d", port->token_dl);
+       if (port->count == 0) {
+               DBG1("close: %d", nport->token_dl);
                spin_lock_irqsave(&dc->spin_mutex, flags);
-               dc->last_ier &= ~(port->token_dl);
+               dc->last_ier &= ~(nport->token_dl);
                writew(dc->last_ier, dc->reg_ier);
                spin_unlock_irqrestore(&dc->spin_mutex, flags);
        }
-
-exit:
-       mutex_unlock(&port->tty_sem);
+       mutex_unlock(&nport->tty_sem);
 }
 
 /*
@@ -1692,12 +1690,12 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer,
                return -EAGAIN;
        }
 
-       if (unlikely(!port->tty_open_count)) {
+       if (unlikely(!port->port.count)) {
                DBG1(" ");
                goto exit;
        }
 
-       rval = __kfifo_put(port->fifo_ul, (unsigned char *)buffer, count);
+       rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count);
 
        /* notify card */
        if (unlikely(dc == NULL)) {
@@ -1735,17 +1733,17 @@ static int ntty_write_room(struct tty_struct *tty)
 {
        struct port *port = tty->driver_data;
        int room = 0;
-       struct nozomi *dc = get_dc_by_tty(tty);
+       const struct nozomi *dc = get_dc_by_tty(tty);
 
        if (!dc || !port)
                return 0;
        if (!mutex_trylock(&port->tty_sem))
                return 0;
 
-       if (!port->tty_open_count)
+       if (!port->port.count)
                goto exit;
 
-       room = port->fifo_ul->size - __kfifo_len(port->fifo_ul);
+       room = port->fifo_ul.size - kfifo_len(&port->fifo_ul);
 
 exit:
        mutex_unlock(&port->tty_sem);
@@ -1755,10 +1753,12 @@ exit:
 /* Gets io control parameters */
 static int ntty_tiocmget(struct tty_struct *tty, struct file *file)
 {
-       struct port *port = tty->driver_data;
-       struct ctrl_dl *ctrl_dl = &port->ctrl_dl;
-       struct ctrl_ul *ctrl_ul = &port->ctrl_ul;
+       const struct port *port = tty->driver_data;
+       const struct ctrl_dl *ctrl_dl = &port->ctrl_dl;
+       const struct ctrl_ul *ctrl_ul = &port->ctrl_ul;
 
+       /* Note: these could change under us but it is not clear this
+          matters if so */
        return  (ctrl_ul->RTS ? TIOCM_RTS : 0) |
                (ctrl_ul->DTR ? TIOCM_DTR : 0) |
                (ctrl_dl->DCD ? TIOCM_CAR : 0) |
@@ -1771,6 +1771,10 @@ static int ntty_tiocmget(struct tty_struct *tty, struct file *file)
 static int ntty_tiocmset(struct tty_struct *tty, struct file *file,
        unsigned int set, unsigned int clear)
 {
+       struct nozomi *dc = get_dc_by_tty(tty);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dc->spin_mutex, flags);
        if (set & TIOCM_RTS)
                set_rts(tty, 1);
        else if (clear & TIOCM_RTS)
@@ -1780,6 +1784,7 @@ static int ntty_tiocmset(struct tty_struct *tty, struct file *file,
                set_dtr(tty, 1);
        else if (clear & TIOCM_DTR)
                set_dtr(tty, 0);
+       spin_unlock_irqrestore(&dc->spin_mutex, flags);
 
        return 0;
 }
@@ -1787,7 +1792,7 @@ static int ntty_tiocmset(struct tty_struct *tty, struct file *file,
 static int ntty_cflags_changed(struct port *port, unsigned long flags,
                struct async_icount *cprev)
 {
-       struct async_icount cnow = port->tty_icount;
+       const struct async_icount cnow = port->tty_icount;
        int ret;
 
        ret =   ((flags & TIOCM_RNG) && (cnow.rng != cprev->rng)) ||
@@ -1802,7 +1807,7 @@ static int ntty_cflags_changed(struct port *port, unsigned long flags,
 
 static int ntty_ioctl_tiocgicount(struct port *port, void __user *argp)
 {
-       struct async_icount cnow = port->tty_icount;
+       const struct async_icount cnow = port->tty_icount;
        struct serial_icounter_struct icount;
 
        icount.cts = cnow.cts;
@@ -1817,7 +1822,7 @@ static int ntty_ioctl_tiocgicount(struct port *port, void __user *argp)
        icount.brk = cnow.brk;
        icount.buf_overrun = cnow.buf_overrun;
 
-       return copy_to_user(argp, &icount, sizeof(icount));
+       return copy_to_user(argp, &icount, sizeof(icount)) ? -EFAULT : 0;
 }
 
 static int ntty_ioctl(struct tty_struct *tty, struct file *file,
@@ -1879,38 +1884,29 @@ static void ntty_throttle(struct tty_struct *tty)
        spin_unlock_irqrestore(&dc->spin_mutex, flags);
 }
 
-/* just to discard single character writes */
-static void ntty_put_char(struct tty_struct *tty, unsigned char c)
-{
-       /* FIXME !!! */
-       DBG2("PUT CHAR Function: %c", c);
-}
-
 /* Returns number of chars in buffer, called by tty layer */
 static s32 ntty_chars_in_buffer(struct tty_struct *tty)
 {
        struct port *port = tty->driver_data;
        struct nozomi *dc = get_dc_by_tty(tty);
-       s32 rval;
+       s32 rval = 0;
 
        if (unlikely(!dc || !port)) {
-               rval = -ENODEV;
                goto exit_in_buffer;
        }
 
-       if (unlikely(!port->tty_open_count)) {
+       if (unlikely(!port->port.count)) {
                dev_err(&dc->pdev->dev, "No tty open?\n");
-               rval = -ENODEV;
                goto exit_in_buffer;
        }
 
-       rval = __kfifo_len(port->fifo_ul);
+       rval = kfifo_len(&port->fifo_ul);
 
 exit_in_buffer:
        return rval;
 }
 
-static struct tty_operations tty_ops = {
+static const struct tty_operations tty_ops = {
        .ioctl = ntty_ioctl,
        .open = ntty_open,
        .close = ntty_close,
@@ -1919,7 +1915,6 @@ static struct tty_operations tty_ops = {
        .unthrottle = ntty_unthrottle,
        .throttle = ntty_throttle,
        .chars_in_buffer = ntty_chars_in_buffer,
-       .put_char = ntty_put_char,
        .tiocmget = ntty_tiocmget,
        .tiocmset = ntty_tiocmset,
 };