Re: Autosuspend and unbound interfaces

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Oliver,

On May 3, 2010, at 11:35 PM, Oliver Neukum wrote:

> Without your driver, syslog and/or lsusb -v nothing can be said.

Here is the lsusb -v output:

Bus 003 Device 002: ID 04e2:1410 Exar Corp.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass          239 Miscellaneous Device
  bDeviceSubClass         2 ?
  bDeviceProtocol         1 Interface Association
  bMaxPacketSize0        64
  idVendor           0x04e2 Exar Corp.
  idProduct          0x1410
  bcdDevice            0.99
  iManufacturer           0
  iProduct                0
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           75
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xc0
      Self Powered
    MaxPower               30mA
    UNRECOGNIZED:  08 0b 00 02 02 02 00 00
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass      2 Abstract (modem)
      bInterfaceProtocol      1 AT-commands (v.25ter)
      iInterface              0
      CDC Header:
        bcdCDC               1.10
      CDC ACM:
        bmCapabilities       0x06
          sends break
          line coding and serial state
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1
      CDC Call Management:
        bmCapabilities       0x01
          call management
        bDataInterface          1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x85  EP 5 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               2
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0

Here is the syslog output:

May  4 10:47:46 localhost kernel: USB Serial support registered for Exar serial
May  4 10:47:46 localhost kernel: usbcore: registered new interface driver exar
May  4 10:47:46 localhost kernel: USB Driver for Exar USB serial ports: v1.00a
May  4 10:47:55 localhost kernel: hub 1-0:1.0: state 7 ports 6 chg 0000 evt 0008
May  4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: GetStatus port 3 status 001803 POWER sig=j CSC CONNECT
May  4 10:47:55 localhost kernel: hub 1-0:1.0: port 3, status 0501, change 0001, 480 Mb/s
May  4 10:47:55 localhost kernel: hub 1-0:1.0: debounce: port 3: total 100ms stable 100ms status 0x501
May  4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: port 3 full speed --> companion
May  4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: GetStatus port 3 status 003001 POWER OWNER sig=se0 CONNECT
May  4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: auto-wakeup root hub
May  4 10:47:55 localhost kernel: hub 1-0:1.0: port 3 not reset yet, waiting 50ms
May  4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: GetStatus port 3 status 003802 POWER OWNER sig=j CSC
May  4 10:47:55 localhost kernel: hub 3-0:1.0: state 7 ports 6 chg 0000 evt 0008
May  4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: GetStatus roothub.portstatus [2] = 0x00010101 CSC PPS CCS
May  4 10:47:55 localhost kernel: hub 3-0:1.0: port 3, status 0101, change 0001, 12 Mb/s
May  4 10:47:55 localhost kernel: hub 3-0:1.0: debounce: port 3: total 100ms stable 100ms status 0x101
May  4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: GetStatus roothub.portstatus [2] = 0x00100103 PRSC PPS PES CCS
May  4 10:47:55 localhost kernel: usb 3-3: new full speed USB device using ohci_hcd and address 2
May  4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: GetStatus roothub.portstatus [2] = 0x00100103 PRSC PPS PES CCS
May  4 10:47:55 localhost kernel: usb 3-3: skipped 1 descriptor after configuration
May  4 10:47:55 localhost kernel: usb 3-3: skipped 4 descriptors after interface
May  4 10:47:55 localhost kernel: usb 3-3: udev 2, busnum 3, minor = 257
May  4 10:47:55 localhost kernel: usb 3-3: New USB device found, idVendor=04e2, idProduct=1410
May  4 10:47:55 localhost kernel: usb 3-3: New USB device strings: Mfr=0, Product=0, SerialNumber=0
May  4 10:47:56 localhost kernel: usb 3-3: usb_probe_device
May  4 10:47:56 localhost kernel: usb 3-3: configuration #1 chosen from 1 choice
May  4 10:47:56 localhost kernel: usb 3-3: adding 3-3:1.0 (config #1, interface 0)
May  4 10:47:56 localhost kernel: usbserial_generic 3-3:1.0: usb_probe_interface
May  4 10:47:56 localhost kernel: usbserial_generic 3-3:1.0: usb_probe_interface - got id
May  4 10:47:56 localhost kernel: exar 3-3:1.0: usb_probe_interface
May  4 10:47:56 localhost kernel: exar 3-3:1.0: usb_probe_interface - got id
May  4 10:47:56 localhost kernel: exar 3-3:1.0: Skipping management interface
May  4 10:47:56 localhost kernel: usb 3-3: adding 3-3:1.1 (config #1, interface 1)
May  4 10:47:56 localhost kernel: usbserial_generic 3-3:1.1: usb_probe_interface
May  4 10:47:56 localhost kernel: usbserial_generic 3-3:1.1: usb_probe_interface - got id
May  4 10:47:56 localhost kernel: exar 3-3:1.1: usb_probe_interface
May  4 10:47:56 localhost kernel: exar 3-3:1.1: usb_probe_interface - got id
May  4 10:47:56 localhost kernel: exar 3-3:1.1: Using management interface interrupt endpoints
May  4 10:47:56 localhost kernel: exar 3-3:1.1: Taking interrupt endpoint from management interface
May  4 10:47:56 localhost kernel: exar 3-3:1.1: Exar serial converter detected
May  4 10:47:56 localhost kernel: usb 3-3: exar_attach: 1410
May  4 10:47:56 localhost kernel: usb 3-3: Exar serial converter now attached to ttyUSB0
May  4 10:47:56 localhost kernel: drivers/usb/core/inode.c: creating file '002'
May  4 10:47:56 localhost kernel: hub 1-0:1.0: state 7 ports 6 chg 0000 evt 0008
May  4 10:47:56 localhost kernel: hub 3-0:1.0: state 7 ports 6 chg 0000 evt 0008

And the driver source follows:

Rob.

/*
 * USB driver for Exar serial converters
 *
 * Copyright (C) 2010 Rob Duncan <rob.duncan@xxxxxxxx>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 *
 * Heavily based on the option.c Usb serial driver by Matthias Urlichs
 * <smurf@xxxxxxxxxxxxxx>.  See that file for more information.
 */

#define DEBUG

#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/usb/cdc.h>
#include <asm/unaligned.h>
#include <linux/slab.h>
#include <linux/tty_flip.h>

#include "exar.h"

#define DRIVER_VERSION "v1.00a"
#define DRIVER_AUTHOR "Rob Duncan <rob.duncan@xxxxxxxx>"
#define DRIVER_DESC "USB Driver for Exar USB serial ports"

static int debug;

#define TR_CONFIG       0x00000001
#define TR_REG          0x00000002
#define TR_INTERRUPT    0x00000004
#define TR_FUNC         0x00000008
#define TR_URB          0x00000010
#define TR_DATA         0x00000020

#define TRACE_X(X, DEV, MSG...) if (debug & X) dev_dbg(DEV, MSG)
#define TRACE_CONFIG(DEV, MSG...)       TRACE_X(TR_CONFIG, DEV, MSG)
#define TRACE_REG(DEV, MSG...)          TRACE_X(TR_REG, DEV, MSG)
#define TRACE_INTERRUPT(DEV, MSG...)    TRACE_X(TR_INTERRUPT, DEV, MSG)
#define TRACE_FUNC(DEV, MSG...)         TRACE_X(TR_FUNC, DEV, MSG)
#define TRACE_URB(DEV, MSG...)          TRACE_X(TR_URB, DEV, MSG)
#define TRACE_DATA(DEV, MSG...)         TRACE_X(TR_DATA, DEV, MSG)
#define TRACE(DEV, MSG...)              TRACE_X(-1, DEV, MSG)

static void exar_indat_callback(struct urb *urb);
static void exar_outdat_callback(struct urb *urb);

/* ---------------------------------------------------------------- */

#define USB_RT_ACM              (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
#define ACM_CTRL_DTR            0x01
#define ACM_CTRL_RTS            0x02
#define ACM_CTRL_DCD            0x01
#define ACM_CTRL_DSR            0x02
#define ACM_CTRL_BRK            0x04
#define ACM_CTRL_RI             0x08
#define ACM_CTRL_FRAMING        0x10
#define ACM_CTRL_PARITY         0x20
#define ACM_CTRL_OVERRUN        0x40

#define N_IN_URB        4
#define IN_BUFLEN       PAGE_SIZE
#define N_OUT_URB       4
#define OUT_BUFLEN      PAGE_SIZE

/* *INDENT-OFF* */
struct exar_port_private {
        int                product_id;
        int                block;
        int                clocal;
        int                ctrlin;
        int                ctrlout;

        spinlock_t         susp_lock;
        unsigned int       suspended:1;
        int                in_flight;

        struct urb        *in_urbs[N_IN_URB];
        u8                *in_buffer[N_IN_URB];
        struct urb        *out_urbs[N_OUT_URB];
        u8                *out_buffer[N_OUT_URB];
        unsigned long      out_busy; /* Bit vector of URBs in use */
        int                opened;
        struct usb_anchor  delayed;
        unsigned long      tx_start_time[N_OUT_URB];
};
/* *INDENT-ON* */

/* ---------------------------------------------------------------- */

#define V_URM_REG_BLOCK                         4
#define V_PRM_REG_BLOCK                         5

#define V_UART_ENABLE                           0x003
#define V_UART_ENABLE_TX_M                      0x1
#define V_UART_ENABLE_TX_S                      0
#define V_UART_ENABLE_TX                        0x001
#define V_UART_ENABLE_RX_M                      0x1
#define V_UART_ENABLE_RX_S                      1
#define V_UART_ENABLE_RX                        0x002

#define V_UART_CLOCK_DIVISOR_0                  0x004
#define V_UART_CLOCK_DIVISOR_1                  0x005
#define V_UART_CLOCK_DIVISOR_2                  0x006
#define V_UART_TX_CLOCK_MASK_0                  0x007
#define V_UART_TX_CLOCK_MASK_1                  0x008
#define V_UART_RX_CLOCK_MASK_0                  0x009
#define V_UART_RX_CLOCK_MASK_1                  0x00a

#define V_UART_FORMAT                           0x00b

#define V_UART_FORMAT_SIZE_M                    0xf
#define V_UART_FORMAT_SIZE_S                    0
#define V_UART_FORMAT_SIZE                      0x00f

#define V_UART_FORMAT_SIZE_7                    (0x7 << V_UART_FORMAT_SIZE_S)
#define V_UART_FORMAT_SIZE_8                    (0x8 << V_UART_FORMAT_SIZE_S)
#define V_UART_FORMAT_SIZE_9                    (0x9 << V_UART_FORMAT_SIZE_S)

#define V_UART_FORMAT_PARITY_M                  0x7
#define V_UART_FORMAT_PARITY_S                  4
#define V_UART_FORMAT_PARITY                    0x070

#define V_UART_FORMAT_PARITY_NONE               (0x0 << V_UART_FORMAT_PARITY_S)
#define V_UART_FORMAT_PARITY_ODD                (0x1 << V_UART_FORMAT_PARITY_S)
#define V_UART_FORMAT_PARITY_EVEN               (0x2 << V_UART_FORMAT_PARITY_S)
#define V_UART_FORMAT_PARITY_1                  (0x3 << V_UART_FORMAT_PARITY_S)
#define V_UART_FORMAT_PARITY_0                  (0x4 << V_UART_FORMAT_PARITY_S)

#define V_UART_FORMAT_STOP_M                    0x1
#define V_UART_FORMAT_STOP_S                    7
#define V_UART_FORMAT_STOP                      0x080

#define V_UART_FORMAT_STOP_1                    (0x0 << V_UART_FORMAT_STOP_S)
#define V_UART_FORMAT_STOP_2                    (0x1 << V_UART_FORMAT_STOP_S)

#define V_UART_FORMAT_MODE_7N1                  0
#define V_UART_FORMAT_MODE_RES1                 1
#define V_UART_FORMAT_MODE_RES2                 2
#define V_UART_FORMAT_MODE_RES3                 3
#define V_UART_FORMAT_MODE_7N2                  4
#define V_UART_FORMAT_MODE_7P1                  5
#define V_UART_FORMAT_MODE_8N1                  6
#define V_UART_FORMAT_MODE_RES7                 7
#define V_UART_FORMAT_MODE_7P2                  8
#define V_UART_FORMAT_MODE_8N2                  9
#define V_UART_FORMAT_MODE_8P1                  10
#define V_UART_FORMAT_MODE_9N1                  11
#define V_UART_FORMAT_MODE_8P2                  12
#define V_UART_FORMAT_MODE_RESD                 13
#define V_UART_FORMAT_MODE_RESE                 14
#define V_UART_FORMAT_MODE_9N2                  15

#define V_UART_FLOW                             0x00c

#define V_UART_FLOW_MODE_M                      0x7
#define V_UART_FLOW_MODE_S                      0
#define V_UART_FLOW_MODE                        0x007

#define V_UART_FLOW_MODE_NONE                   (0x0 << V_UART_FLOW_MODE_S)
#define V_UART_FLOW_MODE_HW                     (0x1 << V_UART_FLOW_MODE_S)
#define V_UART_FLOW_MODE_SW                     (0x2 << V_UART_FLOW_MODE_S)
#define V_UART_FLOW_MODE_ADDR_MATCH             (0x3 << V_UART_FLOW_MODE_S)
#define V_UART_FLOW_MODE_ADDR_MATCH_TX          (0x4 << V_UART_FLOW_MODE_S)

#define V_UART_FLOW_HALF_DUPLEX_M               0x1
#define V_UART_FLOW_HALF_DUPLEX_S               3
#define V_UART_FLOW_HALF_DUPLEX                 0x008

#define V_UART_XON_CHAR                         0x010
#define V_UART_XOFF_CHAR                        0x011

#define V_UART_GPIO_MODE                        0x01a

#define V_UART_GPIO_MODE_SEL_M                  0x7
#define V_UART_GPIO_MODE_SEL_S                  0
#define V_UART_GPIO_MODE_SEL                    0x007

#define V_UART_GPIO_MODE_SEL_GPIO               (0x0 << V_UART_GPIO_MODE_SEL_S)
#define V_UART_GPIO_MODE_SEL_RTS_CTS            (0x1 << V_UART_GPIO_MODE_SEL_S)
#define V_UART_GPIO_MODE_SEL_DTR_DSR            (0x2 << V_UART_GPIO_MODE_SEL_S)
#define V_UART_GPIO_MODE_SEL_XCVR_EN_ACT        (0x3 << V_UART_GPIO_MODE_SEL_S)
#define V_UART_GPIO_MODE_SEL_XCVR_EN_FLOW       (0x4 << V_UART_GPIO_MODE_SEL_S)

#define V_UART_GPIO_MODE_XCVR_EN_POL_M          0x1
#define V_UART_GPIO_MODE_XCVR_EN_POL_S          3
#define V_UART_GPIO_MODE_XCVR_EN_POL            0x008

#define V_UART_GPIO_SET_RI                      0x1
#define V_UART_GPIO_SET_CD                      0x2
#define V_UART_GPIO_SET_DSR                     0x4
#define V_UART_GPIO_SET_DTR                     0x8
#define V_UART_GPIO_SET_CTS                     0x10
#define V_UART_GPIO_SET_RTS                     0x20

#define V_UART_GPIO_ITCH_MASK                   0x1c

#define V_URM_ENABLE_BASE                       0x010
#define V_URM_ENABLE_0                          0x010
#define V_URM_ENABLE_0_TX_M                     0x1
#define V_URM_ENABLE_0_TX_S                     0
#define V_URM_ENABLE_0_TX                       0x001
#define V_URM_ENABLE_0_RX_M                     0x1
#define V_URM_ENABLE_0_RX_S                     1
#define V_URM_ENABLE_0_RX                       0x002

#define V_PRM_RX_ITCH_BASE                      0x014

/* ---------------------------------------------------------------- */

#define EXAR_SET_REG              0
#define EXAR_GET_REG              1

#if 0
static int exar_v_get_reg(struct usb_serial_port *port, int block, int reg,
                          int *value)
{
        struct usb_serial *serial = port->serial;
        int result;
        u8 data;

        result = usb_control_msg(serial->dev,
                                 usb_rcvctrlpipe(serial->dev, 0),
                                 EXAR_GET_REG,
                                 USB_DIR_IN | USB_TYPE_VENDOR,
                                 0,
                                 reg | (block << 8),
                                 &data, 1, USB_CTRL_GET_TIMEOUT);
        *value = data;

        TRACE_REG(&port->dev, "%s 0x%02x:0x%02x = 0x%02x\n", __func__, block,
                  reg, *value);

        return result;
}
#endif

static int exar_v_set_reg(struct usb_serial_port *port,
                          int block, int reg, int value)
{
        struct usb_serial *serial = port->serial;
        int result;

        TRACE_REG(&port->dev, "%s 0x%02x:0x%02x = 0x%02x\n", __func__, block,
                  reg, value);

        result = usb_control_msg(serial->dev,
                                 usb_sndctrlpipe(serial->dev, 0),
                                 EXAR_SET_REG,
                                 USB_DIR_OUT | USB_TYPE_VENDOR,
                                 value,
                                 reg | (block << 8),
                                 NULL, 0, USB_CTRL_SET_TIMEOUT);

        return result;
}

/* ---------------------------------------------------------------- */

static int exar_v_disable(struct usb_serial_port *port)
{
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        int block = portdata->block;

        exar_v_set_reg(port, block, V_UART_ENABLE, 0);
        exar_v_set_reg(port, V_URM_REG_BLOCK, V_URM_ENABLE_BASE + block, 0);

        return 0;
}

static int exar_v_enable(struct usb_serial_port *port)
{
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        int block = portdata->block;

        exar_v_set_reg(port, V_URM_REG_BLOCK, V_URM_ENABLE_BASE + block,
                       V_URM_ENABLE_0_TX);
        exar_v_set_reg(port, block, V_UART_ENABLE,
                       V_UART_ENABLE_TX | V_UART_ENABLE_RX);
        exar_v_set_reg(port, V_URM_REG_BLOCK, V_URM_ENABLE_BASE + block,
                       V_URM_ENABLE_0_TX | V_URM_ENABLE_0_RX);

        return 0;
}

/* ---------------------------------------------------------------- */

static int acm_ctrl_msg(struct usb_serial_port *port,
                        int request, int value, void *buf, int len)
{
        struct usb_serial *serial = port->serial;
        int retval = usb_control_msg(serial->dev,
                                     usb_sndctrlpipe(serial->dev, 0),
                                     request,
                                     USB_RT_ACM,
                                     value,
                                     serial->interface->cur_altsetting->desc.
                                     bInterfaceNumber,
                                     buf,
                                     len,
                                     5000);
        TRACE_FUNC(&port->dev,
                   "%s rq: 0x%02x val: %#x len: %#x result: %d\n",
                   __func__, request, value, len, retval);
        return retval < 0 ? retval : 0;
}

#define acm_set_control(port, control)                                  \
        acm_ctrl_msg(port, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
#define acm_set_line(port, line)                                        \
        acm_ctrl_msg(port, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
#define acm_send_break(port, ms)                                \
        acm_ctrl_msg(port, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0)

struct exar_v_baud_rate {
        unsigned int tx;
        unsigned int rx0;
        unsigned int rx1;
};

static struct exar_v_baud_rate exar_v_baud_rates[] = {
        {0x000, 0x000, 0x000},
        {0x000, 0x000, 0x000},
        {0x100, 0x000, 0x100},
        {0x020, 0x400, 0x020},
        {0x010, 0x100, 0x010},
        {0x208, 0x040, 0x208},
        {0x104, 0x820, 0x108},
        {0x844, 0x210, 0x884},
        {0x444, 0x110, 0x444},
        {0x122, 0x888, 0x224},
        {0x912, 0x448, 0x924},
        {0x492, 0x248, 0x492},
        {0x252, 0x928, 0x292},
        {0X94A, 0X4A4, 0XA52},
        {0X52A, 0XAA4, 0X54A},
        {0XAAA, 0x954, 0X4AA},
        {0XAAA, 0x554, 0XAAA},
        {0x555, 0XAD4, 0X5AA},
        {0XB55, 0XAB4, 0X55A},
        {0X6B5, 0X5AC, 0XB56},
        {0X5B5, 0XD6C, 0X6D6},
        {0XB6D, 0XB6A, 0XDB6},
        {0X76D, 0X6DA, 0XBB6},
        {0XEDD, 0XDDA, 0X76E},
        {0XDDD, 0XBBA, 0XEEE},
        {0X7BB, 0XF7A, 0XDDE},
        {0XF7B, 0XEF6, 0X7DE},
        {0XDF7, 0XBF6, 0XF7E},
        {0X7F7, 0XFEE, 0XEFE},
        {0XFDF, 0XFBE, 0X7FE},
        {0XF7F, 0XEFE, 0XFFE},
        {0XFFF, 0XFFE, 0XFFD},
};

static int exar_v_set_baud_rate(struct usb_serial_port *port, unsigned int rate)
{
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        int block = portdata->block;
        unsigned int div = 48000000 / rate;
        unsigned int i = ((32 * 48000000) / rate) & 0x1f;
        unsigned int tx = exar_v_baud_rates[i].tx;
        unsigned int rx =
            (div & 1) ? exar_v_baud_rates[i].rx1 : exar_v_baud_rates[i].rx0;

        TRACE_CONFIG(&port->dev,
                     "Setting baud rate to %d: i=%u div=%u tx=%03x rx=%03x\n",
                     rate, i, div, tx, rx);

        exar_v_set_reg(port, block, V_UART_CLOCK_DIVISOR_0, (div >> 0) & 0xff);
        exar_v_set_reg(port, block, V_UART_CLOCK_DIVISOR_1, (div >> 8) & 0xff);
        exar_v_set_reg(port, block, V_UART_CLOCK_DIVISOR_2, (div >> 16) & 0xff);
        exar_v_set_reg(port, block, V_UART_TX_CLOCK_MASK_0, (tx >> 0) & 0xff);
        exar_v_set_reg(port, block, V_UART_TX_CLOCK_MASK_1, (tx >> 8) & 0xff);
        exar_v_set_reg(port, block, V_UART_RX_CLOCK_MASK_0, (rx >> 0) & 0xff);
        exar_v_set_reg(port, block, V_UART_RX_CLOCK_MASK_1, (rx >> 8) & 0xff);

        return -EINVAL;
}

static void exar_v_set_termios(struct tty_struct *tty_param,
                               struct usb_serial_port *port,
                               struct ktermios *old_termios)
{
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        unsigned int cflag, block;
        speed_t rate;
        unsigned int format_size, format_parity, format_stop, flow, gpio_mode,
            itch;
        struct tty_struct *tty;
        char cs, p, sb, *fl;

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        tty = port->port.tty;

        cflag = tty->termios->c_cflag;

        portdata->clocal = ((cflag & CLOCAL) != 0);

        block = portdata->block;

        exar_v_disable(port);

        if ((cflag & CSIZE) == CS7) {
                format_size = V_UART_FORMAT_SIZE_7;
                cs = '7';
        } else {
                format_size = V_UART_FORMAT_SIZE_8;
                cs = '8';
        }

        if (cflag & PARENB) {
                if (cflag & PARODD) {
                        if (cflag & CMSPAR) {
                                format_parity = V_UART_FORMAT_PARITY_1;
                                p = 'M';
                        } else {
                                format_parity = V_UART_FORMAT_PARITY_ODD;
                                p = 'O';
                        }
                } else {
                        if (cflag & CMSPAR) {
                                format_parity = V_UART_FORMAT_PARITY_0;
                                p = 'S';
                        } else {
                                format_parity = V_UART_FORMAT_PARITY_EVEN;
                                p = 'E';
                        }
                }
        } else {
                format_parity = V_UART_FORMAT_PARITY_NONE;
                p = 'n';
        }

        if (cflag & CSTOPB) {
                format_stop = V_UART_FORMAT_STOP_2;
                sb = '2';
        } else {
                format_stop = V_UART_FORMAT_STOP_1;
                sb = '1';
        }

        exar_v_set_reg(port, block, V_UART_FORMAT,
                       format_size | format_parity | format_stop);

        if (cflag & CRTSCTS) {
                flow = V_UART_FLOW_MODE_HW;
                gpio_mode = V_UART_GPIO_MODE_SEL_RTS_CTS;
                itch =
                    (V_UART_GPIO_SET_RI | V_UART_GPIO_SET_CD |
                     V_UART_GPIO_SET_DSR | V_UART_GPIO_SET_DTR);
                fl = "rtscts";
        } else if (I_IXOFF(tty) || I_IXON(tty)) {
                unsigned char start_char = START_CHAR(tty);
                unsigned char stop_char = STOP_CHAR(tty);

                flow = V_UART_FLOW_MODE_SW;
                gpio_mode = V_UART_GPIO_MODE_SEL_GPIO;
                itch =
                    (V_UART_GPIO_SET_RI | V_UART_GPIO_SET_CD |
                     V_UART_GPIO_SET_DSR | V_UART_GPIO_SET_DTR |
                     V_UART_GPIO_SET_CTS | V_UART_GPIO_SET_RTS);

                exar_v_set_reg(port, block, V_UART_XON_CHAR, start_char);
                exar_v_set_reg(port, block, V_UART_XOFF_CHAR, stop_char);
                fl = "xonxoff";
        } else {
                flow = V_UART_FLOW_MODE_NONE;
                gpio_mode = V_UART_GPIO_MODE_SEL_GPIO;
                itch =
                    (V_UART_GPIO_SET_RI | V_UART_GPIO_SET_CD |
                     V_UART_GPIO_SET_DSR | V_UART_GPIO_SET_DTR |
                     V_UART_GPIO_SET_CTS | V_UART_GPIO_SET_RTS);
                fl = "none";
        }

        TRACE_CONFIG(&port->dev, "Setting line to %c%c%c %s\n", cs, p, sb, fl);

        exar_v_set_reg(port, block, V_UART_FLOW, flow);
        exar_v_set_reg(port, block, V_UART_GPIO_MODE, gpio_mode);
        exar_v_set_reg(port, block, V_UART_GPIO_ITCH_MASK, itch);

        rate = tty_get_baud_rate(tty);
        exar_v_set_baud_rate(port, rate);

        exar_v_enable(port);
}

static void exar_set_termios(struct tty_struct *tty,
                             struct usb_serial_port *port,
                             struct ktermios *old_termios)
{
        TRACE_FUNC(&port->dev, "%s\n", __func__);

        exar_v_set_termios(tty, port, old_termios);
}

static int exar_tiocmget(struct tty_struct *tty, struct file *file)
{
        struct usb_serial_port *port = tty->driver_data;
        struct exar_port_private *portdata = usb_get_serial_port_data(port);

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        return (portdata->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
            (portdata->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
            (portdata->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
            (portdata->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) |
            (portdata->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | TIOCM_CTS;
}

static int exar_tiocmset(struct tty_struct *tty, struct file *file,
                         unsigned int set, unsigned int clear)
{
        struct usb_serial_port *port = tty->driver_data;
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        unsigned int newctrl;

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        newctrl = portdata->ctrlout;
        set =
            (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ?
                                                    ACM_CTRL_RTS : 0);
        clear =
            (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ?
                                                      ACM_CTRL_RTS : 0);

        newctrl = (newctrl & ~clear) | set;

        if (portdata->ctrlout == newctrl)
                return 0;
        return acm_set_control(port, portdata->ctrlout = newctrl);
}

/* ---------------------------------------------------------------- */

static struct urb *exar_setup_urb(struct usb_serial *serial, int endpoint,
                                  int dir, void *ctx, char *buf, int len,
                                  void (*callback) (struct urb *))
{
        struct urb *urb;

        urb = usb_alloc_urb(0, GFP_KERNEL);
        if (urb == NULL) {
                TRACE_URB(&serial->dev->dev,
                          "%s: alloc for endpoint %d failed.", __func__,
                          endpoint);
                return NULL;
        }

        usb_fill_bulk_urb(urb, serial->dev,
                          usb_sndbulkpipe(serial->dev, endpoint) | dir,
                          buf, len, callback, ctx);

        return urb;
}

static int exar_attach(struct usb_serial *serial)
{
        struct usb_serial_port *port;
        struct exar_port_private *portdata;
        u8 *buffer;
        struct urb *urb;
        int j, err;

        TRACE_FUNC(&serial->dev->dev, "%s: %04x\n", __func__,
                   serial->dev->descriptor.idProduct);

        usb_enable_autosuspend(serial->dev);

        port = serial->port[0];
        portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
        if (!portdata) {
                dev_err(&port->dev,
                        "%s: kzalloc for exar_port_private failed!\n",
                        __func__);
                return 1;
        }

        portdata->product_id = serial->dev->descriptor.idProduct;

        /* Bulk OUT endpoints 0x1..0x4 map to register blocks 0..3 */
        portdata->block = port->bulk_out_endpointAddress - 1;

        usb_set_serial_port_data(port, portdata);

        for (j = 0; j < N_IN_URB; j++) {
                buffer = (u8 *) __get_free_page(GFP_KERNEL);
                portdata->in_buffer[j] = buffer;

                urb = exar_setup_urb(serial,
                                     port->bulk_in_endpointAddress,
                                     USB_DIR_IN, port,
                                     portdata->in_buffer[j],
                                     IN_BUFLEN, exar_indat_callback);
                portdata->in_urbs[j] = urb;

                if (!buffer || !urb) {
                        dev_err(&port->dev,
                                "%s: IN allocation failed buffer=%p urb=%p",
                                __func__, buffer, urb);
                        goto bail_out_error;
                }
        }

        for (j = 0; j < N_OUT_URB; j++) {
                buffer = (u8 *) __get_free_page(GFP_KERNEL);
                portdata->out_buffer[j] = buffer;

                urb = exar_setup_urb(serial,
                                     port->bulk_out_endpointAddress,
                                     USB_DIR_OUT, port,
                                     portdata->out_buffer[j],
                                     OUT_BUFLEN, exar_outdat_callback);
                portdata->out_urbs[j] = urb;

                if (!buffer || !urb) {
                        dev_err(&port->dev,
                                "%s: OUT allocation failed buffer=%p urb=%p",
                                __func__, buffer, urb);
                        goto bail_out_error;
                }
        }

        if (port->interrupt_in_urb) {
                err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
                if (err)
                        dev_err(&port->dev,
                                "%s: submit irq_in urb failed %d",
                                __func__, err);
        }

        return 0;

      bail_out_error:
        for (j = 0; j < N_IN_URB; j++) {
                if (portdata->in_buffer[j])
                        free_page((unsigned long)portdata->in_buffer[j]);
                if (portdata->in_urbs[j])
                        usb_free_urb(portdata->in_urbs[j]);
        }
        for (j = 0; j < N_OUT_URB; j++) {
                if (portdata->out_buffer[j])
                        free_page((unsigned long)portdata->out_buffer[j]);
                if (portdata->out_urbs[j])
                        usb_free_urb(portdata->out_urbs[j]);
        }
        kfree(portdata);
        return 1;
}

static void stop_read_write_urbs(struct usb_serial *serial)
{
        struct usb_serial_port *port;
        struct exar_port_private *portdata;
        int i, j;

        for (i = 0; i < serial->num_ports; ++i) {
                port = serial->port[i];
                portdata = usb_get_serial_port_data(port);
                for (j = 0; j < N_IN_URB; j++)
                        usb_kill_urb(portdata->in_urbs[j]);
                for (j = 0; j < N_OUT_URB; j++)
                        usb_kill_urb(portdata->out_urbs[j]);
        }
}

static void exar_disconnect(struct usb_serial *serial)
{
        TRACE_FUNC(&serial->dev->dev, "%s\n", __func__);

        stop_read_write_urbs(serial);
}

static void exar_release(struct usb_serial *serial)
{
        struct usb_serial_port *port;
        struct exar_port_private *portdata;
        int j;

        TRACE_FUNC(&serial->dev->dev, "%s\n", __func__);

        port = serial->port[0];
        portdata = usb_get_serial_port_data(port);

        return;

        for (j = 0; j < N_IN_URB; j++) {
                if (portdata->in_urbs[j]) {
                        usb_free_urb(portdata->in_urbs[j]);
                        free_page((unsigned long)
                                  portdata->in_buffer[j]);
                        portdata->in_urbs[j] = NULL;
                }
        }
        for (j = 0; j < N_OUT_URB; j++) {
                if (portdata->out_urbs[j]) {
                        usb_free_urb(portdata->out_urbs[j]);
                        free_page((unsigned long)
                                  portdata->out_buffer[j]);
                        portdata->out_urbs[j] = NULL;
                }
        }

        kfree(usb_get_serial_port_data(port));
}

/* ---------------------------------------------------------------- */

static int exar_open(struct tty_struct *tty, struct usb_serial_port *port)
{
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        struct usb_serial *serial = port->serial;
        struct urb *urb;
        int i, err;

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        for (i = 0; i < N_IN_URB; i++) {
                urb = portdata->in_urbs[i];
                if (!urb)
                        continue;
                err = usb_submit_urb(urb, GFP_KERNEL);
                if (err) {
                        dev_err(&port->dev, "%s: submit urb %d failed (%d) %d",
                                __func__, i, err, urb->transfer_buffer_length);
                }
        }

/*      serial->interface->needs_remote_wakeup = 1; */
        spin_lock_irq(&portdata->susp_lock);
        portdata->opened = 1;
        spin_unlock_irq(&portdata->susp_lock);
/*      usb_autopm_put_interface(serial->interface); */

        return 0;
}

static void exar_close(struct usb_serial_port *port)
{
        struct usb_serial *serial = port->serial;
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        int i;

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        if (serial->dev) {
                /* Stop reading/writing urbs */
                spin_lock_irq(&portdata->susp_lock);
                portdata->opened = 0;
                spin_unlock_irq(&portdata->susp_lock);

                for (i = 0; i < N_IN_URB; i++)
                        usb_kill_urb(portdata->in_urbs[i]);
                for (i = 0; i < N_OUT_URB; i++)
                        usb_kill_urb(portdata->out_urbs[i]);
/*              usb_autopm_get_interface(serial->interface); */
/*              serial->interface->needs_remote_wakeup = 0; */
        }
}

/* ---------------------------------------------------------------- */

static void exar_outdat_callback(struct urb *urb)
{
        struct usb_serial_port *port;
        struct exar_port_private *portdata;
        int i;

        port = urb->context;

        TRACE_DATA(&port->dev, "%s\n", __func__);

        usb_serial_port_softint(port);
        usb_autopm_put_interface_async(port->serial->interface);
        portdata = usb_get_serial_port_data(port);
        spin_lock(&portdata->susp_lock);
        portdata->in_flight--;
        spin_unlock(&portdata->susp_lock);

        for (i = 0; i < N_OUT_URB; ++i) {
                if (portdata->out_urbs[i] == urb) {
                        smp_mb__before_clear_bit();
                        clear_bit(i, &portdata->out_busy);
                        break;
                }
        }
}

static int exar_write(struct tty_struct *tty, struct usb_serial_port *port,
                      const unsigned char *buf, int count)
{
        struct exar_port_private *portdata;
        struct urb *this_urb;
        unsigned long flags;
        int i, err, left, todo;

        portdata = usb_get_serial_port_data(port);

        TRACE_FUNC(&port->dev, "%s: write (%d chars)\n", __func__, count);

        i = 0;
        left = count;
        for (i = 0; left > 0 && i < N_OUT_URB; i++) {
                todo = left;
                if (todo > OUT_BUFLEN)
                        todo = OUT_BUFLEN;

                this_urb = portdata->out_urbs[i];
                if (test_and_set_bit(i, &portdata->out_busy)) {
                        if (time_before(jiffies,
                                        portdata->tx_start_time[i] + 10 * HZ))
                                continue;
                        usb_unlink_urb(this_urb);
                        continue;
                }

                err = usb_autopm_get_interface_async(port->serial->interface);
                if (err < 0)
                        break;

                /* send the data */
                memcpy(this_urb->transfer_buffer, buf, todo);
                this_urb->transfer_buffer_length = todo;

                spin_lock_irqsave(&portdata->susp_lock, flags);
                if (portdata->suspended) {
                        usb_anchor_urb(this_urb, &portdata->delayed);
                        spin_unlock_irqrestore(&portdata->susp_lock, flags);
                } else {
                        portdata->in_flight++;
                        spin_unlock_irqrestore(&portdata->susp_lock, flags);
                        err = usb_submit_urb(this_urb, GFP_ATOMIC);
                        if (err) {
                                TRACE_URB(&port->dev,
                                          "usb_submit_urb %p (write bulk) failed %d\n",
                                          this_urb, err);
                                clear_bit(i, &portdata->out_busy);
                                spin_lock_irqsave(&portdata->susp_lock, flags);
                                portdata->in_flight--;
                                spin_unlock_irqrestore(&portdata->susp_lock,
                                                       flags);
                                continue;
                        }
                }

                portdata->tx_start_time[i] = jiffies;
                buf += todo;
                left -= todo;
        }
        count -= left;
        TRACE_DATA(&port->dev, "%s: wrote (did %d)\n", __func__, count);
        return count;
}

static int exar_write_room(struct tty_struct *tty)
{
        struct usb_serial_port *port = tty->driver_data;
        struct exar_port_private *portdata;
        struct urb *this_urb;
        int data_len = 0;
        int i;

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        portdata = usb_get_serial_port_data(port);

        for (i = 0; i < N_OUT_URB; i++) {
                this_urb = portdata->out_urbs[i];
                if (this_urb && !test_bit(i, &portdata->out_busy))
                        data_len += OUT_BUFLEN;
        }
        TRACE_DATA(&port->dev, "%s: %d\n", __func__, data_len);
        return data_len;
}

static int exar_chars_in_buffer(struct tty_struct *tty)
{
        struct usb_serial_port *port = tty->driver_data;
        struct exar_port_private *portdata;
        struct urb *this_urb;
        int data_len = 0;
        int i;

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        portdata = usb_get_serial_port_data(port);

        for (i = 0; i < N_OUT_URB; i++) {
                this_urb = portdata->out_urbs[i];
                /* FIXME: This locking is insufficient as this_urb may
                   go unused during the test */
                if (this_urb && test_bit(i, &portdata->out_busy))
                        data_len += this_urb->transfer_buffer_length;
        }
        TRACE_DATA(&port->dev, "%s: %d\n", __func__, data_len);
        return data_len;
}

static void exar_indat_callback(struct urb *urb)
{
        struct usb_serial_port *port;
        struct tty_struct *tty;
        unsigned char *data = urb->transfer_buffer;
        int status = urb->status;
        int err, endpoint;

        port = urb->context;

        TRACE_DATA(&port->dev, "%s: %p\n", __func__, urb);

        endpoint = usb_pipeendpoint(urb->pipe);

        if (status) {
                TRACE_URB(&port->dev,
                          "%s: nonzero status: %d on endpoint %02x\n",
                          __func__, status, endpoint);
        } else {
                tty = tty_port_tty_get(&port->port);
                if (urb->actual_length) {
                        tty_insert_flip_string(tty, data, urb->actual_length);
                        tty_flip_buffer_push(tty);
                } else
                        TRACE_DATA(&port->dev, "%s: empty read urb received\n",
                                   __func__);
                tty_kref_put(tty);

                /* Resubmit urb so we continue receiving */
                if (status != -ESHUTDOWN) {
                        err = usb_submit_urb(urb, GFP_ATOMIC);
                        if (err && err != -EPERM)
                                dev_err(&port->dev,
                                        "%s: resubmit read urb failed with %d\n",
                                        __func__, err);
                        else
                                usb_mark_last_busy(port->serial->dev);
                }
        }
}

static void exar_int_callback(struct urb *urb)
{
        struct usb_serial_port *port = urb->context;
        struct exar_port_private *portdata = usb_get_serial_port_data(port);
        struct usb_cdc_notification *dr = urb->transfer_buffer;
        unsigned char *data;
        int newctrl;
        int status;

        switch (urb->status) {
        case 0:
                /* success */
                break;
        case -ECONNRESET:
        case -ENOENT:
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
                TRACE_INTERRUPT(&port->dev,
                                "urb shutting down with status: %d\n",
                                urb->status);
                return;
        default:
                dev_err(&port->dev, "nonzero urb status received: %d\n",
                        urb->status);
                goto exit;
        }

        data = (unsigned char *)(dr + 1);
        switch (dr->bNotificationType) {

        case USB_CDC_NOTIFY_NETWORK_CONNECTION:
                TRACE_INTERRUPT(&port->dev, "%s network\n",
                                dr->
                                wValue ? "connected to" : "disconnected from");
                break;

        case USB_CDC_NOTIFY_SERIAL_STATE:
                newctrl = le16_to_cpu(get_unaligned((__le16 *) data));

                if (!portdata->clocal
                    && (portdata->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
                        struct tty_struct *tty = tty_port_tty_get(&port->port);
                        TRACE_INTERRUPT(&port->dev, "calling hangup\n");
                        tty_hangup(tty);
                }

                portdata->ctrlin = newctrl;

                TRACE_INTERRUPT(&port->dev,
                                "input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c\n",
                                portdata->ctrlin & ACM_CTRL_DCD ? '+' : '-',
                                portdata->ctrlin & ACM_CTRL_DSR ? '+' : '-',
                                portdata->ctrlin & ACM_CTRL_BRK ? '+' : '-',
                                portdata->ctrlin & ACM_CTRL_RI ? '+' : '-',
                                portdata->ctrlin & ACM_CTRL_FRAMING ? '+' : '-',
                                portdata->ctrlin & ACM_CTRL_PARITY ? '+' : '-',
                                portdata->
                                ctrlin & ACM_CTRL_OVERRUN ? '+' : '-');
                break;

        default:
                TRACE_INTERRUPT(&port->dev,
                                "unknown notification %d received: index %d len %d data0 %d data1 %d\n",
                                dr->bNotificationType, dr->wIndex, dr->wLength,
                                data[0], data[1]);
                break;
        }

      exit:
        TRACE_URB(&port->dev, "resubmitting interrupt IN urb %p\n", urb);
        status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status)
                dev_err(&port->dev, "usb_submit_urb failed with result %d",
                        status);
}

#ifdef CONFIG_PM
static int exar_suspend(struct usb_serial *serial, pm_message_t message)
{
        struct usb_serial_port *port;
        struct exar_port_private *portdata;
        int b;

        TRACE_FUNC(&serial->dev->dev, "%s\n", __func__);

        port = serial->port[0];
        portdata = usb_get_serial_port_data(port);

        if (message.event & PM_EVENT_AUTO) {
                spin_lock_irq(&portdata->susp_lock);
                b = portdata->in_flight;
                spin_unlock_irq(&portdata->susp_lock);

                if (b)
                        return -EBUSY;
        }

        spin_lock_irq(&portdata->susp_lock);
        portdata->suspended = 1;
        spin_unlock_irq(&portdata->susp_lock);
        stop_read_write_urbs(serial);

        return 0;
}

static void play_delayed(struct usb_serial_port *port)
{
        struct exar_port_private *portdata;
        struct urb *urb;
        int err;

        TRACE_FUNC(&port->dev, "%s\n", __func__);

        portdata = usb_get_serial_port_data(port);
        while ((urb = usb_get_from_anchor(&portdata->delayed))) {
                err = usb_submit_urb(urb, GFP_ATOMIC);
                if (!err)
                        portdata->in_flight++;
        }
}

static int exar_resume(struct usb_serial *serial)
{
        struct usb_serial_port *port;
        struct exar_port_private *portdata;
        struct urb *urb;
        int err = 0, j;

        TRACE_FUNC(&serial->dev->dev, "%s\n", __func__);

        port = serial->port[0];
        portdata = usb_get_serial_port_data(port);

        /* get the interrupt URBs resubmitted unconditionally */
        if (port->interrupt_in_urb) {
                err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
                if (err < 0) {
                        dev_err(&port->dev, "%s: Error %d for interrupt URB of port",
                                __func__, err);
                        goto err_out;
                }
        }

        /* skip closed ports */
        spin_lock_irq(&portdata->susp_lock);
        if (portdata->opened) {
                for (j = 0; j < N_IN_URB; j++) {
                        urb = portdata->in_urbs[j];
                        err = usb_submit_urb(urb, GFP_ATOMIC);
                        if (err < 0) {
                                dev_err(&port->dev,
                                        "%s: Error %d for bulk URB\n", __func__,
                                        err);
                                spin_unlock_irq(&portdata->susp_lock);
                                goto err_out;
                        }
                }
                play_delayed(port);
        }
        portdata->suspended = 0;
        spin_unlock_irq(&portdata->susp_lock);

      err_out:
        return err;
}
#endif

static const struct usb_device_id id_table[] = {
        {USB_DEVICE(EXAR_VENDOR_ID, EXAR_PRODUCT_ID_XRV1410)},
        {USB_DEVICE(EXAR_VENDOR_ID, EXAR_PRODUCT_ID_XRV1412)},
        {USB_DEVICE(EXAR_VENDOR_ID, EXAR_PRODUCT_ID_XRV1414)},
        {},
};

MODULE_DEVICE_TABLE(usb, id_table);

/* *INDENT-OFF* */
static struct usb_driver exar_driver = {
        .name                 = "exar",
        .probe                = usb_serial_probe,
        .disconnect           = usb_serial_disconnect,
        .id_table             = id_table,
#ifdef CONFIG_PM
        .suspend              = usb_serial_suspend,
        .resume               = usb_serial_resume,
        .supports_autosuspend = 1,
#endif
        .no_dynamic_id        = 1,
};

static struct usb_serial_driver exar_device = {
        .description       = "Exar serial",
        .driver = {
                .owner = THIS_MODULE,
                .name  = "exar",
                   },
        .id_table          = id_table,
        .usb_driver        = &exar_driver,
        .num_ports         = 1,

        .set_termios       = exar_set_termios,
        .tiocmget          = exar_tiocmget,
        .tiocmset          = exar_tiocmset,

        .attach            = exar_attach,
        .disconnect        = exar_disconnect,
        .release           = exar_release,

        .open              = exar_open,
        .close             = exar_close,

        .write             = exar_write,
        .write_room        = exar_write_room,
        .chars_in_buffer   = exar_chars_in_buffer,

        .read_int_callback = exar_int_callback,

#ifdef CONFIG_PM
        .suspend           = exar_suspend,
        .resume            = exar_resume,
#endif
};
/* *INDENT-ON* */

static int __init exar_init(void)
{
        int retval;

        retval = usb_serial_register(&exar_device);
        if (retval)
                return retval;
        retval = usb_register(&exar_driver);
        if (retval)
                usb_serial_deregister(&exar_device);

        printk(KERN_INFO DRIVER_DESC ": " DRIVER_VERSION "\n");

        return retval;
}

static void __exit exar_exit(void)
{
        usb_deregister(&exar_driver);
        usb_serial_deregister(&exar_device);
}

module_init(exar_init);
module_exit(exar_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");

module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug flags");



The information and any attached documents contained in this message
may be confidential and/or legally privileged.  The message is
intended solely for the addressee(s).  If you are not the intended
recipient, you are hereby notified that any use, dissemination, or
reproduction is strictly prohibited and may be unlawful.  If you are
not the intended recipient, please contact the sender immediately by
return e-mail and destroy all copies of the original message.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux