在本机运行可以直接make,在开发板上运行,需要用交叉编译。

文件一:

bus.c

/*
 * USB Serial Converter Bus specific functions
 *
 * Copyright (C) 2002 Greg Kroah-Hartman (greg@kroah.com)
 *
 *    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.
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>

static int usb_serial_device_match(struct device *dev,
                        struct device_driver *drv)
{
    struct usb_serial_driver *driver;
    const struct usb_serial_port *port;

/*
     * drivers are already assigned to ports in serial_probe so it's
     * a simple check here.
     */
    port = to_usb_serial_port(dev);
    if (!port)
        return 0;

driver = to_usb_serial_driver(drv);

if (driver == port->serial->type)
        return 1;

return 0;
}

static ssize_t show_port_number(struct device *dev,
                struct device_attribute *attr, char *buf)
{
    struct usb_serial_port *port = to_usb_serial_port(dev);

return sprintf(buf, "%d\n", port->number - port->serial->minor);
}

static DEVICE_ATTR(port_number, S_IRUGO, show_port_number, NULL);

static int usb_serial_device_probe(struct device *dev)
{
    struct usb_serial_driver *driver;
    struct usb_serial_port *port;
    int retval = 0;
    int minor;

port = to_usb_serial_port(dev);
    if (!port) {
        retval = -ENODEV;
        goto exit;
    }
    if (port->dev_state != PORT_REGISTERING)
        goto exit;

driver = port->serial->type;
    if (driver->port_probe) {
        retval = driver->port_probe(port);
        if (retval)
            goto exit;
    }

retval = device_create_file(dev, &dev_attr_port_number);
    if (retval) {
        if (driver->port_remove)
            retval = driver->port_remove(port);
        goto exit;
    }

minor = port->number;
    tty_register_device(usb_serial_tty_driver, minor, dev);
    dev_info(&port->serial->dev->dev,
         "%s converter now attached to ttyUSB%d\n",
         driver->description, minor);

exit:
    return retval;
}

static int usb_serial_device_remove(struct device *dev)
{
    struct usb_serial_driver *driver;
    struct usb_serial_port *port;
    int retval = 0;
    int minor;

port = to_usb_serial_port(dev);
    if (!port)
        return -ENODEV;

if (port->dev_state != PORT_UNREGISTERING)
        return retval;

device_remove_file(&port->dev, &dev_attr_port_number);

driver = port->serial->type;
    if (driver->port_remove)
        retval = driver->port_remove(port);

minor = port->number;
    tty_unregister_device(usb_serial_tty_driver, minor);
    dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
         driver->description, minor);

return retval;
}

#ifdef CONFIG_HOTPLUG
static ssize_t store_new_id(struct device_driver *driver,
                const char *buf, size_t count)
{
    struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver);
    ssize_t retval = usb_store_new_id(&usb_drv->dynids, driver, buf, count);

if (retval >= 0 && usb_drv->usb_driver != NULL)
        retval = usb_store_new_id(&usb_drv->usb_driver->dynids,
                      &usb_drv->usb_driver->drvwrap.driver,
                      buf, count);
    return retval;
}

static struct driver_attribute drv_attrs[] = {
    __ATTR(new_id, S_IWUSR, NULL, store_new_id),
    __ATTR_NULL,
};

static void free_dynids(struct usb_serial_driver *drv)
{
    struct usb_dynid *dynid, *n;

spin_lock(&drv->dynids.lock);
    list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
        list_del(&dynid->node);
        kfree(dynid);
    }
    spin_unlock(&drv->dynids.lock);
}

#else
static struct driver_attribute drv_attrs[] = {
    __ATTR_NULL,
};
static inline void free_dynids(struct usb_serial_driver *drv)
{
}
#endif

struct bus_type usb_serial_bus_type = {
    .name =        "usb-serial",
    .match =    usb_serial_device_match,
    .probe =    usb_serial_device_probe,
    .remove =    usb_serial_device_remove,
    .drv_attrs =     drv_attrs,
};

int usb_serial_bus_register(struct usb_serial_driver *driver)
{
    int retval;

driver->driver.bus = &usb_serial_bus_type;
    spin_lock_init(&driver->dynids.lock);
    INIT_LIST_HEAD(&driver->dynids.list);

retval = driver_register(&driver->driver);

return retval;
}

void usb_serial_bus_deregister(struct usb_serial_driver *driver)
{
    free_dynids(driver);
    driver_unregister(&driver->driver);
}

文件二:

generic.c

/*
 * USB Serial Converter Generic functions
 *
 * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
 *
 *    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.
 *
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
#include <linux/kfifo.h>

static int debug;

#ifdef CONFIG_USB_SERIAL_GENERIC

static int generic_probe(struct usb_interface *interface,
             const struct usb_device_id *id);

static __u16 vendor  = 0x05f9;
static __u16 product = 0xffff;

module_param(vendor, ushort, 0);
MODULE_PARM_DESC(vendor, "User specified USB idVendor");

module_param(product, ushort, 0);
MODULE_PARM_DESC(product, "User specified USB idProduct");

static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */

/* we want to look at all devices, as the vendor/product id can change
 * depending on the command line argument */
static struct usb_device_id generic_serial_ids[] = {
    {.driver_info = 42},
    {}
};

static struct usb_driver generic_driver = {
    .name =        "usbserial_generic",
    .probe =    generic_probe,
    .disconnect =    usb_serial_disconnect,
    .id_table =    generic_serial_ids,
    .no_dynamic_id =    1,
};

/* All of the device info needed for the Generic Serial Converter */
struct usb_serial_driver usb_serial_generic_device = {
    .driver = {
        .owner =    THIS_MODULE,
        .name =        "generic",
    },
    .id_table =        generic_device_ids,
    .usb_driver =         &generic_driver,
    .num_ports =        1,
    .disconnect =        usb_serial_generic_disconnect,
    .release =        usb_serial_generic_release,
    .throttle =        usb_serial_generic_throttle,
    .unthrottle =        usb_serial_generic_unthrottle,
    .resume =        usb_serial_generic_resume,
};

static int generic_probe(struct usb_interface *interface,
                   const struct usb_device_id *id)
{
    const struct usb_device_id *id_pattern;

id_pattern = usb_match_id(interface, generic_device_ids);
    if (id_pattern != NULL)
        return usb_serial_probe(interface, id);
    return -ENODEV;
}
#endif

int usb_serial_generic_register(int _debug)
{
    int retval = 0;

debug = _debug;
#ifdef CONFIG_USB_SERIAL_GENERIC
    generic_device_ids[0].idVendor = vendor;
    generic_device_ids[0].idProduct = product;
    generic_device_ids[0].match_flags =
        USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;

/* register our generic driver with ourselves */
    retval = usb_serial_register(&usb_serial_generic_device);
    if (retval)
        goto exit;
    retval = usb_register(&generic_driver);
    if (retval)
        usb_serial_deregister(&usb_serial_generic_device);
exit:
#endif
    return retval;
}

void usb_serial_generic_deregister(void)
{
#ifdef CONFIG_USB_SERIAL_GENERIC
    /* remove our generic driver */
    usb_deregister(&generic_driver);
    usb_serial_deregister(&usb_serial_generic_device);
#endif
}

int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port)
{
    struct usb_serial *serial = port->serial;
    int result = 0;
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

/* clear the throttle flags */
    spin_lock_irqsave(&port->lock, flags);
    port->throttled = 0;
    port->throttle_req = 0;
    spin_unlock_irqrestore(&port->lock, flags);

/* if we have a bulk endpoint, start reading from it */
    if (serial->num_bulk_in) {
        /* Start reading from the device */
        usb_fill_bulk_urb(port->read_urb, serial->dev,
                   usb_rcvbulkpipe(serial->dev,
                        port->bulk_in_endpointAddress),
                   port->read_urb->transfer_buffer,
                   port->read_urb->transfer_buffer_length,
                   ((serial->type->read_bulk_callback) ?
                     serial->type->read_bulk_callback :
                     usb_serial_generic_read_bulk_callback),
                   port);
        result = usb_submit_urb(port->read_urb, GFP_KERNEL);
        if (result)
            dev_err(&port->dev,
                "%s - failed resubmitting read urb, error %d\n",
                            __func__, result);
    }

return result;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_open);

static void generic_cleanup(struct usb_serial_port *port)
{
    struct usb_serial *serial = port->serial;

dbg("%s - port %d", __func__, port->number);

if (serial->dev) {
        /* shutdown any bulk reads that might be going on */
        if (serial->num_bulk_out)
            usb_kill_urb(port->write_urb);
        if (serial->num_bulk_in)
            usb_kill_urb(port->read_urb);
    }
}

void usb_serial_generic_close(struct usb_serial_port *port)
{
    dbg("%s - port %d", __func__, port->number);
    generic_cleanup(port);
}

static int usb_serial_multi_urb_write(struct tty_struct *tty,
    struct usb_serial_port *port, const unsigned char *buf, int count)
{
    unsigned long flags;
    struct urb *urb;
    unsigned char *buffer;
    int status;
    int towrite;
    int bwrite = 0;

dbg("%s - port %d", __func__, port->number);

if (count == 0)
        dbg("%s - write request of 0 bytes", __func__);

while (count > 0) {
        towrite = (count > port->bulk_out_size) ?
            port->bulk_out_size : count;
        spin_lock_irqsave(&port->lock, flags);
        if (port->urbs_in_flight >
            port->serial->type->max_in_flight_urbs) {
            spin_unlock_irqrestore(&port->lock, flags);
            dbg("%s - write limit hit\n", __func__);
            return bwrite;
        }
        port->tx_bytes_flight += towrite;
        port->urbs_in_flight++;
        spin_unlock_irqrestore(&port->lock, flags);

buffer = kmalloc(towrite, GFP_ATOMIC);
        if (!buffer) {
            dev_err(&port->dev,
            "%s ran out of kernel memory for urb ...\n", __func__);
            goto error_no_buffer;
        }

urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb) {
            dev_err(&port->dev, "%s - no more free urbs\n",
                __func__);
            goto error_no_urb;
        }

/* Copy data */
        memcpy(buffer, buf + bwrite, towrite);
        usb_serial_debug_data(debug, &port->dev, __func__,
                      towrite, buffer);
        /* fill the buffer and send it */
        usb_fill_bulk_urb(urb, port->serial->dev,
            usb_sndbulkpipe(port->serial->dev,
                    port->bulk_out_endpointAddress),
            buffer, towrite,
            usb_serial_generic_write_bulk_callback, port);

status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status) {
            dev_err(&port->dev,
                "%s - failed submitting write urb, error %d\n",
                __func__, status);
            goto error;
        }

/* This urb is the responsibility of the host driver now */
        usb_free_urb(urb);
        dbg("%s write: %d", __func__, towrite);
        count -= towrite;
        bwrite += towrite;
    }
    return bwrite;

error:
    usb_free_urb(urb);
error_no_urb:
    kfree(buffer);
error_no_buffer:
    spin_lock_irqsave(&port->lock, flags);
    port->urbs_in_flight--;
    port->tx_bytes_flight -= towrite;
    spin_unlock_irqrestore(&port->lock, flags);
    return bwrite;
}

/**
 * usb_serial_generic_write_start - kick off an URB write
 * @port:    Pointer to the &struct usb_serial_port data
 *
 * Returns the number of bytes queued on success. This will be zero if there
 * was nothing to send. Otherwise, it returns a negative errno value
 */
static int usb_serial_generic_write_start(struct usb_serial_port *port)
{
    struct usb_serial *serial = port->serial;
    unsigned char *data;
    int result;
    int count;
    unsigned long flags;
    bool start_io;

/* Atomically determine whether we can and need to start a USB
     * operation. */
    spin_lock_irqsave(&port->lock, flags);
    if (port->write_urb_busy)
        start_io = false;
    else {
        start_io = (__kfifo_len(port->write_fifo) != 0);
        port->write_urb_busy = start_io;
    }
    spin_unlock_irqrestore(&port->lock, flags);

if (!start_io)
        return 0;

data = port->write_urb->transfer_buffer;
    count = kfifo_get(port->write_fifo, data, port->bulk_out_size);
    usb_serial_debug_data(debug, &port->dev, __func__, count, data);

/* set up our urb */
    usb_fill_bulk_urb(port->write_urb, serial->dev,
               usb_sndbulkpipe(serial->dev,
                port->bulk_out_endpointAddress),
               port->write_urb->transfer_buffer, count,
               ((serial->type->write_bulk_callback) ?
                 serial->type->write_bulk_callback :
                 usb_serial_generic_write_bulk_callback),
               port);

/* send the data out the bulk port */
    result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
    if (result) {
        dev_err(&port->dev,
            "%s - failed submitting write urb, error %d\n",
                        __func__, result);
        /* don't have to grab the lock here, as we will
           retry if != 0 */
        port->write_urb_busy = 0;
    } else
        result = count;

return result;
}

/**
 * usb_serial_generic_write - generic write function for serial USB devices
 * @tty:    Pointer to &struct tty_struct for the device
 * @port:    Pointer to the &usb_serial_port structure for the device
 * @buf:    Pointer to the data to write
 * @count:    Number of bytes to write
 *
 * Returns the number of characters actually written, which may be anything
 * from zero to @count. If an error occurs, it returns the negative errno
 * value.
 */
int usb_serial_generic_write(struct tty_struct *tty,
    struct usb_serial_port *port, const unsigned char *buf, int count)
{
    struct usb_serial *serial = port->serial;
    int result;

dbg("%s - port %d", __func__, port->number);

if (count == 0) {
        dbg("%s - write request of 0 bytes", __func__);
        return 0;
    }

/* only do something if we have a bulk out endpoint */
    if (!serial->num_bulk_out)
        return 0;

if (serial->type->max_in_flight_urbs)
        return usb_serial_multi_urb_write(tty, port,
                          buf, count);

count = kfifo_put(port->write_fifo, buf, count);
    result = usb_serial_generic_write_start(port);

if (result >= 0)
        result = count;

return result;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write);

int usb_serial_generic_write_room(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    struct usb_serial *serial = port->serial;
    unsigned long flags;
    int room = 0;

dbg("%s - port %d", __func__, port->number);
    spin_lock_irqsave(&port->lock, flags);
    if (serial->type->max_in_flight_urbs) {
        if (port->urbs_in_flight < serial->type->max_in_flight_urbs)
            room = port->bulk_out_size *
                (serial->type->max_in_flight_urbs -
                 port->urbs_in_flight);
    } else if (serial->num_bulk_out)
        room = port->write_fifo->size - __kfifo_len(port->write_fifo);
    spin_unlock_irqrestore(&port->lock, flags);

dbg("%s - returns %d", __func__, room);
    return room;
}

int usb_serial_generic_chars_in_buffer(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    struct usb_serial *serial = port->serial;
    int chars = 0;
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

if (serial->type->max_in_flight_urbs) {
        spin_lock_irqsave(&port->lock, flags);
        chars = port->tx_bytes_flight;
        spin_unlock_irqrestore(&port->lock, flags);
    } else if (serial->num_bulk_out)
        chars = kfifo_len(port->write_fifo);

dbg("%s - returns %d", __func__, chars);
    return chars;
}

void usb_serial_generic_resubmit_read_urb(struct usb_serial_port *port,
            gfp_t mem_flags)
{
    struct urb *urb = port->read_urb;
    struct usb_serial *serial = port->serial;
    int result;

/* Continue reading from device */
    usb_fill_bulk_urb(urb, serial->dev,
               usb_rcvbulkpipe(serial->dev,
                    port->bulk_in_endpointAddress),
               urb->transfer_buffer,
               urb->transfer_buffer_length,
               ((serial->type->read_bulk_callback) ?
                 serial->type->read_bulk_callback :
                 usb_serial_generic_read_bulk_callback), port);
    result = usb_submit_urb(urb, mem_flags);
    if (result)
        dev_err(&port->dev,
            "%s - failed resubmitting read urb, error %d\n",
                            __func__, result);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_resubmit_read_urb);

/* Push data to tty layer and resubmit the bulk read URB */
static void flush_and_resubmit_read_urb(struct usb_serial_port *port)
{
    struct urb *urb = port->read_urb;
    struct tty_struct *tty = tty_port_tty_get(&port->port);
    char *ch = (char *)urb->transfer_buffer;
    int i;

if (!tty)
        goto done;

/* The per character mucking around with sysrq path it too slow for
       stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases
       where the USB serial is not a console anyway */
    if (!port->console || !port->sysrq)
        tty_insert_flip_string(tty, ch, urb->actual_length);
    else {
        /* Push data to tty */
        for (i = 0; i < urb->actual_length; i++, ch++) {
            if (!usb_serial_handle_sysrq_char(tty, port, *ch))
                tty_insert_flip_char(tty, *ch, TTY_NORMAL);
        }
    }
    tty_flip_buffer_push(tty);
    tty_kref_put(tty);
done:
    usb_serial_generic_resubmit_read_urb(port, GFP_ATOMIC);
}

void usb_serial_generic_read_bulk_callback(struct urb *urb)
{
    struct usb_serial_port *port = urb->context;
    unsigned char *data = urb->transfer_buffer;
    int status = urb->status;
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

if (unlikely(status != 0)) {
        dbg("%s - nonzero read bulk status received: %d",
            __func__, status);
        return;
    }

usb_serial_debug_data(debug, &port->dev, __func__,
                        urb->actual_length, data);

/* Throttle the device if requested by tty */
    spin_lock_irqsave(&port->lock, flags);
    port->throttled = port->throttle_req;
    if (!port->throttled) {
        spin_unlock_irqrestore(&port->lock, flags);
        flush_and_resubmit_read_urb(port);
    } else
        spin_unlock_irqrestore(&port->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);

void usb_serial_generic_write_bulk_callback(struct urb *urb)
{
    unsigned long flags;
    struct usb_serial_port *port = urb->context;
    int status = urb->status;

dbg("%s - port %d", __func__, port->number);

if (port->serial->type->max_in_flight_urbs) {
        spin_lock_irqsave(&port->lock, flags);
        --port->urbs_in_flight;
        port->tx_bytes_flight -= urb->transfer_buffer_length;
        if (port->urbs_in_flight < 0)
            port->urbs_in_flight = 0;
        spin_unlock_irqrestore(&port->lock, flags);

if (status) {
            dbg("%s - nonzero multi-urb write bulk status "
                "received: %d", __func__, status);
            return;
        }
    } else {
        port->write_urb_busy = 0;

if (status) {
            dbg("%s - nonzero multi-urb write bulk status "
                "received: %d", __func__, status);
            kfifo_reset(port->write_fifo);
        } else
            usb_serial_generic_write_start(port);
    }

usb_serial_port_softint(port);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);

void usb_serial_generic_throttle(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

/* Set the throttle request flag. It will be picked up
     * by usb_serial_generic_read_bulk_callback(). */
    spin_lock_irqsave(&port->lock, flags);
    port->throttle_req = 1;
    spin_unlock_irqrestore(&port->lock, flags);
}

void usb_serial_generic_unthrottle(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    int was_throttled;
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

/* Clear the throttle flags */
    spin_lock_irqsave(&port->lock, flags);
    was_throttled = port->throttled;
    port->throttled = port->throttle_req = 0;
    spin_unlock_irqrestore(&port->lock, flags);

if (was_throttled) {
        /* Resume reading from device */
        flush_and_resubmit_read_urb(port);
    }
}

int usb_serial_handle_sysrq_char(struct tty_struct *tty,
            struct usb_serial_port *port, unsigned int ch)
{
    if (port->sysrq && port->console) {
        if (ch && time_before(jiffies, port->sysrq)) {
            handle_sysrq(ch, tty);
            port->sysrq = 0;
            return 1;
        }
        port->sysrq = 0;
    }
    return 0;
}
EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char);

int usb_serial_handle_break(struct usb_serial_port *port)
{
    if (!port->sysrq) {
        port->sysrq = jiffies + HZ*5;
        return 1;
    }
    port->sysrq = 0;
    return 0;
}
EXPORT_SYMBOL_GPL(usb_serial_handle_break);

int usb_serial_generic_resume(struct usb_serial *serial)
{
    struct usb_serial_port *port;
    int i, c = 0, r;

for (i = 0; i < serial->num_ports; i++) {
        port = serial->port[i];
        if (!port->port.count)
            continue;

if (port->read_urb) {
            r = usb_submit_urb(port->read_urb, GFP_NOIO);
            if (r < 0)
                c++;
        }

if (port->write_urb) {
            r = usb_serial_generic_write_start(port);
            if (r < 0)
                c++;
        }
    }

return c ? -EIO : 0;
}
EXPORT_SYMBOL_GPL(usb_serial_generic_resume);

void usb_serial_generic_disconnect(struct usb_serial *serial)
{
    int i;

dbg("%s", __func__);

/* stop reads and writes on all ports */
    for (i = 0; i < serial->num_ports; ++i)
        generic_cleanup(serial->port[i]);
}

void usb_serial_generic_release(struct usb_serial *serial)
{
    dbg("%s", __func__);
}

文件三:

option.c

/*
  USB Driver for GSM modems

Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>

This driver is free software; you can redistribute it and/or modify
  it under the terms of Version 2 of the GNU General Public License as
  published by the Free Software Foundation.

Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>

History: see the git log.

Work sponsored by: Sigos GmbH, Germany <info@sigos.de>

This driver exists because the "normal" serial driver doesn't work too well
  with GSM modems. Issues:
  - data loss -- one single Receive URB is not nearly enough
  - nonstandard flow (Option devices) control
  - controlling the baud rate doesn't make sense

This driver is named "option" because the most common device it's
  used for is a PC-Card (with an internal OHCI-USB interface, behind
  which the GSM interface sits), made by Option Inc.

Some of the "one port" devices actually exhibit multiple USB instances
  on the USB bus. This is not a bug, these ports are used for different
  device features.
*/

#define DRIVER_VERSION "v0.7.2"
#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
#define DRIVER_DESC "USB Driver for GSM modems"

#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>

/* Function prototypes */
static int  option_probe(struct usb_serial *serial,
            const struct usb_device_id *id);
static int  option_open(struct tty_struct *tty, struct usb_serial_port *port);
static void option_close(struct usb_serial_port *port);
static void option_dtr_rts(struct usb_serial_port *port, int on);

static int  option_startup(struct usb_serial *serial);
static void option_disconnect(struct usb_serial *serial);
static void option_release(struct usb_serial *serial);
static int  option_write_room(struct tty_struct *tty);

static void option_instat_callback(struct urb *urb);

static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
            const unsigned char *buf, int count);
static int  option_chars_in_buffer(struct tty_struct *tty);
static void option_set_termios(struct tty_struct *tty,
            struct usb_serial_port *port, struct ktermios *old);
static int  option_tiocmget(struct tty_struct *tty, struct file *file);
static int  option_tiocmset(struct tty_struct *tty, struct file *file,
                unsigned int set, unsigned int clear);
static int  option_send_setup(struct usb_serial_port *port);
#ifdef CONFIG_PM
static int  option_suspend(struct usb_serial *serial, pm_message_t message);
static int  option_resume(struct usb_serial *serial);
#endif

/* Vendor and product IDs */
#define OPTION_VENDOR_ID            0x0AF0
#define OPTION_PRODUCT_COLT            0x5000
#define OPTION_PRODUCT_RICOLA            0x6000
#define OPTION_PRODUCT_RICOLA_LIGHT        0x6100
#define OPTION_PRODUCT_RICOLA_QUAD        0x6200
#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT    0x6300
#define OPTION_PRODUCT_RICOLA_NDIS        0x6050
#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT    0x6150
#define OPTION_PRODUCT_RICOLA_NDIS_QUAD        0x6250
#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT    0x6350
#define OPTION_PRODUCT_COBRA            0x6500
#define OPTION_PRODUCT_COBRA_BUS        0x6501
#define OPTION_PRODUCT_VIPER            0x6600
#define OPTION_PRODUCT_VIPER_BUS        0x6601
#define OPTION_PRODUCT_GT_MAX_READY        0x6701
#define OPTION_PRODUCT_FUJI_MODEM_LIGHT        0x6721
#define OPTION_PRODUCT_FUJI_MODEM_GT        0x6741
#define OPTION_PRODUCT_FUJI_MODEM_EX        0x6761
#define OPTION_PRODUCT_KOI_MODEM        0x6800
#define OPTION_PRODUCT_SCORPION_MODEM        0x6901
#define OPTION_PRODUCT_ETNA_MODEM        0x7001
#define OPTION_PRODUCT_ETNA_MODEM_LITE        0x7021
#define OPTION_PRODUCT_ETNA_MODEM_GT        0x7041
#define OPTION_PRODUCT_ETNA_MODEM_EX        0x7061
#define OPTION_PRODUCT_ETNA_KOI_MODEM        0x7100
#define OPTION_PRODUCT_GTM380_MODEM        0x7201

#define HUAWEI_VENDOR_ID            0x12D1
#define HUAWEI_PRODUCT_E600            0x1001
#define HUAWEI_PRODUCT_E220            0x1003
#define HUAWEI_PRODUCT_E220BIS            0x1004
#define HUAWEI_PRODUCT_E1401            0x1401
#define HUAWEI_PRODUCT_E1402            0x1402
#define HUAWEI_PRODUCT_E1403            0x1403
#define HUAWEI_PRODUCT_E1404            0x1404
#define HUAWEI_PRODUCT_E1405            0x1405
#define HUAWEI_PRODUCT_E1406            0x1406
#define HUAWEI_PRODUCT_E1407            0x1407
#define HUAWEI_PRODUCT_E1408            0x1408
#define HUAWEI_PRODUCT_E1409            0x1409
#define HUAWEI_PRODUCT_E140A            0x140A
#define HUAWEI_PRODUCT_E140B            0x140B
#define HUAWEI_PRODUCT_E140C            0x140C
#define HUAWEI_PRODUCT_E140D            0x140D
#define HUAWEI_PRODUCT_E140E            0x140E
#define HUAWEI_PRODUCT_E140F            0x140F
#define HUAWEI_PRODUCT_E1410            0x1410
#define HUAWEI_PRODUCT_E1411            0x1411
#define HUAWEI_PRODUCT_E1412            0x1412
#define HUAWEI_PRODUCT_E1413            0x1413
#define HUAWEI_PRODUCT_E1414            0x1414
#define HUAWEI_PRODUCT_E1415            0x1415
#define HUAWEI_PRODUCT_E1416            0x1416
#define HUAWEI_PRODUCT_E1417            0x1417
#define HUAWEI_PRODUCT_E1418            0x1418
#define HUAWEI_PRODUCT_E1419            0x1419
#define HUAWEI_PRODUCT_E141A            0x141A
#define HUAWEI_PRODUCT_E141B            0x141B
#define HUAWEI_PRODUCT_E141C            0x141C
#define HUAWEI_PRODUCT_E141D            0x141D
#define HUAWEI_PRODUCT_E141E            0x141E
#define HUAWEI_PRODUCT_E141F            0x141F
#define HUAWEI_PRODUCT_E1420            0x1420
#define HUAWEI_PRODUCT_E1421            0x1421
#define HUAWEI_PRODUCT_E1422            0x1422
#define HUAWEI_PRODUCT_E1423            0x1423
#define HUAWEI_PRODUCT_E1424            0x1424
#define HUAWEI_PRODUCT_E1425            0x1425
#define HUAWEI_PRODUCT_E1426            0x1426
#define HUAWEI_PRODUCT_E1427            0x1427
#define HUAWEI_PRODUCT_E1428            0x1428
#define HUAWEI_PRODUCT_E1429            0x1429
#define HUAWEI_PRODUCT_E142A            0x142A
#define HUAWEI_PRODUCT_E142B            0x142B
#define HUAWEI_PRODUCT_E142C            0x142C
#define HUAWEI_PRODUCT_E142D            0x142D
#define HUAWEI_PRODUCT_E142E            0x142E
#define HUAWEI_PRODUCT_E142F            0x142F
#define HUAWEI_PRODUCT_E1430            0x1430
#define HUAWEI_PRODUCT_E1431            0x1431
#define HUAWEI_PRODUCT_E1432            0x1432
#define HUAWEI_PRODUCT_E1433            0x1433
#define HUAWEI_PRODUCT_E1434            0x1434
#define HUAWEI_PRODUCT_E1435            0x1435
#define HUAWEI_PRODUCT_E1436            0x1436
#define HUAWEI_PRODUCT_E1437            0x1437
#define HUAWEI_PRODUCT_E1438            0x1438
#define HUAWEI_PRODUCT_E1439            0x1439
#define HUAWEI_PRODUCT_E143A            0x143A
#define HUAWEI_PRODUCT_E143B            0x143B
#define HUAWEI_PRODUCT_E143C            0x143C
#define HUAWEI_PRODUCT_E143D            0x143D
#define HUAWEI_PRODUCT_E143E            0x143E
#define HUAWEI_PRODUCT_E143F            0x143F
#define HUAWEI_PRODUCT_E14AC            0x14AC

#define QUANTA_VENDOR_ID            0x0408
#define QUANTA_PRODUCT_Q101            0xEA02
#define QUANTA_PRODUCT_Q111            0xEA03
#define QUANTA_PRODUCT_GLX            0xEA04
#define QUANTA_PRODUCT_GKE            0xEA05
#define QUANTA_PRODUCT_GLE            0xEA06

#define NOVATELWIRELESS_VENDOR_ID        0x1410

/* YISO PRODUCTS */

#define YISO_VENDOR_ID                0x0EAB
#define YISO_PRODUCT_U893            0xC893

/* MERLIN EVDO PRODUCTS */
#define NOVATELWIRELESS_PRODUCT_V640        0x1100
#define NOVATELWIRELESS_PRODUCT_V620        0x1110
#define NOVATELWIRELESS_PRODUCT_V740        0x1120
#define NOVATELWIRELESS_PRODUCT_V720        0x1130

/* MERLIN HSDPA/HSPA PRODUCTS */
#define NOVATELWIRELESS_PRODUCT_U730        0x1400
#define NOVATELWIRELESS_PRODUCT_U740        0x1410
#define NOVATELWIRELESS_PRODUCT_U870        0x1420
#define NOVATELWIRELESS_PRODUCT_XU870        0x1430
#define NOVATELWIRELESS_PRODUCT_X950D        0x1450

/* EXPEDITE PRODUCTS */
#define NOVATELWIRELESS_PRODUCT_EV620        0x2100
#define NOVATELWIRELESS_PRODUCT_ES720        0x2110
#define NOVATELWIRELESS_PRODUCT_E725        0x2120
#define NOVATELWIRELESS_PRODUCT_ES620        0x2130
#define NOVATELWIRELESS_PRODUCT_EU730        0x2400
#define NOVATELWIRELESS_PRODUCT_EU740        0x2410
#define NOVATELWIRELESS_PRODUCT_EU870D        0x2420

/* OVATION PRODUCTS */
#define NOVATELWIRELESS_PRODUCT_MC727        0x4100
#define NOVATELWIRELESS_PRODUCT_MC950D        0x4400
#define NOVATELWIRELESS_PRODUCT_U727        0x5010
#define NOVATELWIRELESS_PRODUCT_MC727_NEW    0x5100
#define NOVATELWIRELESS_PRODUCT_MC760        0x6000
#define NOVATELWIRELESS_PRODUCT_OVMC760        0x6002

/* FUTURE NOVATEL PRODUCTS */
#define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED    0X6001
#define NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED    0X7000
#define NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED    0X7001
#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED    0X8000
#define NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED    0X8001
#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED    0X9000
#define NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED    0X9001
#define NOVATELWIRELESS_PRODUCT_GLOBAL        0XA001

/* AMOI PRODUCTS */
#define AMOI_VENDOR_ID                0x1614
#define AMOI_PRODUCT_H01            0x0800
#define AMOI_PRODUCT_H01A            0x7002
#define AMOI_PRODUCT_H02            0x0802

#define DELL_VENDOR_ID                0x413C

/* Dell modems */
#define DELL_PRODUCT_5700_MINICARD        0x8114
#define DELL_PRODUCT_5500_MINICARD        0x8115
#define DELL_PRODUCT_5505_MINICARD        0x8116
#define DELL_PRODUCT_5700_EXPRESSCARD        0x8117
#define DELL_PRODUCT_5510_EXPRESSCARD        0x8118

#define DELL_PRODUCT_5700_MINICARD_SPRINT    0x8128
#define DELL_PRODUCT_5700_MINICARD_TELUS    0x8129

#define DELL_PRODUCT_5720_MINICARD_VZW        0x8133
#define DELL_PRODUCT_5720_MINICARD_SPRINT    0x8134
#define DELL_PRODUCT_5720_MINICARD_TELUS    0x8135
#define DELL_PRODUCT_5520_MINICARD_CINGULAR    0x8136
#define DELL_PRODUCT_5520_MINICARD_GENERIC_L    0x8137
#define DELL_PRODUCT_5520_MINICARD_GENERIC_I    0x8138

#define DELL_PRODUCT_5730_MINICARD_SPRINT    0x8180
#define DELL_PRODUCT_5730_MINICARD_TELUS    0x8181
#define DELL_PRODUCT_5730_MINICARD_VZW        0x8182

#define KYOCERA_VENDOR_ID            0x0c88
#define KYOCERA_PRODUCT_KPC650            0x17da
#define KYOCERA_PRODUCT_KPC680            0x180a

#define ANYDATA_VENDOR_ID            0x16d5
#define ANYDATA_PRODUCT_ADU_620UW        0x6202
#define ANYDATA_PRODUCT_ADU_E100A        0x6501
#define ANYDATA_PRODUCT_ADU_500A        0x6502

#define AXESSTEL_VENDOR_ID            0x1726
#define AXESSTEL_PRODUCT_MV110H            0x1000

#define BANDRICH_VENDOR_ID            0x1A8D
#define BANDRICH_PRODUCT_C100_1            0x1002
#define BANDRICH_PRODUCT_C100_2            0x1003
#define BANDRICH_PRODUCT_1004            0x1004
#define BANDRICH_PRODUCT_1005            0x1005
#define BANDRICH_PRODUCT_1006            0x1006
#define BANDRICH_PRODUCT_1007            0x1007
#define BANDRICH_PRODUCT_1008            0x1008
#define BANDRICH_PRODUCT_1009            0x1009
#define BANDRICH_PRODUCT_100A            0x100a

#define BANDRICH_PRODUCT_100B            0x100b
#define BANDRICH_PRODUCT_100C            0x100c
#define BANDRICH_PRODUCT_100D            0x100d
#define BANDRICH_PRODUCT_100E            0x100e

#define BANDRICH_PRODUCT_100F            0x100f
#define BANDRICH_PRODUCT_1010            0x1010
#define BANDRICH_PRODUCT_1011            0x1011
#define BANDRICH_PRODUCT_1012            0x1012

#define AMOI_VENDOR_ID            0x1614
#define AMOI_PRODUCT_9508            0x0800

#define QUALCOMM_VENDOR_ID            0x05C6

#define MAXON_VENDOR_ID                0x16d8

#define TELIT_VENDOR_ID                0x1bc7
#define TELIT_PRODUCT_UC864E            0x1003
#define TELIT_PRODUCT_UC864G            0x1004

/* ZTE PRODUCTS */
#define ZTE_VENDOR_ID                0x19d2
#define ZTE_PRODUCT_MF622            0x0001
#define ZTE_PRODUCT_MF628            0x0015
#define ZTE_PRODUCT_MF626            0x0031
#define ZTE_PRODUCT_CDMA_TECH            0xfffe
#define ZTE_PRODUCT_AC8710            0xfff1
#define ZTE_PRODUCT_AC2726            0xfff5

#define BENQ_VENDOR_ID                0x04a5
#define BENQ_PRODUCT_H10            0x4068

#define DLINK_VENDOR_ID                0x1186
#define DLINK_PRODUCT_DWM_652            0x3e04
#define DLINK_PRODUCT_DWM_652_U5        0xce16

#define QISDA_VENDOR_ID                0x1da5
#define QISDA_PRODUCT_H21_4512            0x4512
#define QISDA_PRODUCT_H21_4523            0x4523
#define QISDA_PRODUCT_H20_4515            0x4515
#define QISDA_PRODUCT_H20_4519            0x4519

/* TLAYTECH PRODUCTS */
#define TLAYTECH_VENDOR_ID            0x20B9
#define TLAYTECH_PRODUCT_TEU800            0x1682

/* TOSHIBA PRODUCTS */
#define TOSHIBA_VENDOR_ID            0x0930
#define TOSHIBA_PRODUCT_HSDPA_MINICARD        0x1302
#define TOSHIBA_PRODUCT_G450            0x0d45

#define ALINK_VENDOR_ID                0x1e0e
#define ALINK_PRODUCT_3GU            0x9200

/* ALCATEL PRODUCTS */
#define ALCATEL_VENDOR_ID            0x1bbb
#define ALCATEL_PRODUCT_X060S            0x0000

/* Airplus products */
#define AIRPLUS_VENDOR_ID            0x1011
#define AIRPLUS_PRODUCT_MCD650            0x3198

/* 4G Systems products */
#define FOUR_G_SYSTEMS_VENDOR_ID        0x1c9e
#define FOUR_G_SYSTEMS_PRODUCT_W14        0x9603

static struct usb_device_id option_ids[] = {
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) },
    { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTM380_MODEM) },
    { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q101) },
    { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q111) },
    { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) },
    { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
    { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1401, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1402, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1403, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1404, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1405, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1406, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1407, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1408, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1409, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140A, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140B, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140C, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140D, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140E, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140F, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1410, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1411, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1412, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1413, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1414, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1415, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1416, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1417, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1418, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1419, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141A, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141B, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141C, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141D, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141E, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141F, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1420, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1421, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1422, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1423, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1424, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1425, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1426, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1427, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1428, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1429, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142A, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142B, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142C, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142D, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142E, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142F, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1430, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1431, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1432, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1433, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1434, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1435, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1436, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1437, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1438, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1439, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143A, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143B, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143C, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },
    { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) },
    { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_9508) },
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) }, /* Novatel Merlin EX720/V740/X720 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V720) }, /* Novatel Merlin V720/S720/PC720 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U730) }, /* Novatel U730/U740 (VF version) */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U740) }, /* Novatel U740 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U870) }, /* Novatel U870 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_XU870) }, /* Novatel Merlin XU870 HSDPA/3G */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_X950D) }, /* Novatel X950D */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EV620) }, /* Novatel EV620/ES620 CDMA/EV-DO */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_ES720) }, /* Novatel ES620/ES720/U720/USB720 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_E725) }, /* Novatel E725/E726 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_ES620) }, /* Novatel Merlin ES620 SM Bus */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU730) }, /* Novatel EU730 and Vodafone EU740 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU740) }, /* Novatel non-Vodafone EU740 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EU870D) }, /* Novatel EU850D/EU860D/EU870D */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC950D) }, /* Novatel MC930D/MC950D */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727_NEW) }, /* Novatel MC727/U727/USB727 refresh */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U727) }, /* Novatel MC727/U727/USB727 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC760) }, /* Novatel MC760/U760/USB760 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_OVMC760) }, /* Novatel Ovation MC760 */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) }, /* Novatel HSPA product */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, /* Novatel EVDO Embedded product */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, /* Novatel HSPA Embedded product */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED) }, /* Novatel EVDO product */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED) }, /* Novatel HSPA product */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_HIGHSPEED) }, /* Novatel EVDO Embedded product */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_HIGHSPEED) }, /* Novatel HSPA Embedded product */
    { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_GLOBAL) }, /* Novatel Global product */

{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01) },
    { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H01A) },
    { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_H02) },

{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD) },        /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite EV620 CDMA/EV-DO */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5500_MINICARD) },        /* Dell Wireless 5500 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5505_MINICARD) },        /* Dell Wireless 5505 Mobile Broadband HSDPA Mini-Card == Novatel Expedite EU740 HSDPA/3G */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_EXPRESSCARD) },        /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO ExpressCard == Novatel Merlin XV620 CDMA/EV-DO */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5510_EXPRESSCARD) },        /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard == Novatel Merlin XU870 HSDPA/3G */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_SPRINT) },    /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite E720 CDMA/EV-DO */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5700_MINICARD_TELUS) },    /* Dell Wireless 5700 Mobile Broadband CDMA/EVDO Mini-Card == Novatel Expedite ET620 CDMA/EV-DO */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_VZW) },     /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_SPRINT) },     /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5720_MINICARD_TELUS) },     /* Dell Wireless 5720 == Novatel EV620 CDMA/EV-DO */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_CINGULAR) },    /* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_L) },    /* Dell Wireless HSDPA 5520 */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5520_MINICARD_GENERIC_I) },    /* Dell Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_SPRINT) },    /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_TELUS) },    /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */
    { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5730_MINICARD_VZW) },     /* Dell Wireless 5730 Mobile Broadband EVDO/HSPA Mini-Card */
    { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) },    /* ADU-E100, ADU-310 */
    { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) },
    { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) },
    { USB_DEVICE(AXESSTEL_VENDOR_ID, AXESSTEL_PRODUCT_MV110H) },
    { USB_DEVICE(YISO_VENDOR_ID, YISO_PRODUCT_U893) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1004) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1005) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1006) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1007) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1008) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1009) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100A) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100B) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100C) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100D) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100E) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_100F) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1010) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1011) },
    { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012) },
    { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) },
    { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) },
    { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */
    { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */
    { USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */
    { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) },
    { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0006, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0007, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0008, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0009, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000a, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000b, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000c, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000d, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000e, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000f, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0069, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0076, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0078, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0082, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0060, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) },
    { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) },
    { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
    { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
    { USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5) }, /* Yes, ALINK_VENDOR_ID */
    { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4512) },
    { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4523) },
    { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4515) },
    { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4519) },
    { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_G450) },
    { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */
    { USB_DEVICE(ALINK_VENDOR_ID, 0x9000) },
    { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
    { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S) },
    { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) },
    { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) },
    { USB_DEVICE(FOUR_G_SYSTEMS_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14) },
    { } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);

static struct usb_driver option_driver = {
    .name       = "option",
    .probe      = usb_serial_probe,
    .disconnect = usb_serial_disconnect,
#ifdef CONFIG_PM
    .suspend    = usb_serial_suspend,
    .resume     = usb_serial_resume,
    .supports_autosuspend =    1,
#endif
    .id_table   = option_ids,
    .no_dynamic_id =     1,
};

/* The card has three separate interfaces, which the serial driver
 * recognizes separately, thus num_port=1.
 */

static struct usb_serial_driver option_1port_device = {
    .driver = {
        .owner =    THIS_MODULE,
        .name =        "option1",
    },
    .description       = "GSM modem (1-port)",
    .usb_driver        = &option_driver,
    .id_table          = option_ids,
    .num_ports         = 1,
    .probe             = option_probe,
    .open              = option_open,
    .close             = option_close,
    .dtr_rts       = option_dtr_rts,
    .write             = option_write,
    .write_room        = option_write_room,
    .chars_in_buffer   = option_chars_in_buffer,
    .set_termios       = option_set_termios,
    .tiocmget          = option_tiocmget,
    .tiocmset          = option_tiocmset,
    .attach            = option_startup,
    .disconnect        = option_disconnect,
    .release           = option_release,
    .read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
    .suspend           = option_suspend,
    .resume            = option_resume,
#endif
};

static int debug;

/* per port private data */

#define N_IN_URB 4
#define N_OUT_URB 4
#define IN_BUFLEN 4096
#define OUT_BUFLEN 4096

struct option_intf_private {
    spinlock_t susp_lock;
    unsigned int suspended:1;
    int in_flight;
};

struct option_port_private {
    /* Input endpoints and buffer for this port */
    struct urb *in_urbs[N_IN_URB];
    u8 *in_buffer[N_IN_URB];
    /* Output endpoints and buffer for this port */
    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;

/* Settings for the port */
    int rts_state;    /* Handshaking pins (outputs) */
    int dtr_state;
    int cts_state;    /* Handshaking pins (inputs) */
    int dsr_state;
    int dcd_state;
    int ri_state;

unsigned long tx_start_time[N_OUT_URB];
};

/* Functions used by new usb-serial code. */
static int __init option_init(void)
{
    int retval;
    retval = usb_serial_register(&option_1port_device);
    if (retval)
        goto failed_1port_device_register;
    retval = usb_register(&option_driver);
    if (retval)
        goto failed_driver_register;

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

return 0;

failed_driver_register:
    usb_serial_deregister(&option_1port_device);
failed_1port_device_register:
    return retval;
}

static void __exit option_exit(void)
{
    usb_deregister(&option_driver);
    usb_serial_deregister(&option_1port_device);
}

module_init(option_init);
module_exit(option_exit);

static int option_probe(struct usb_serial *serial,
            const struct usb_device_id *id)
{
    struct option_intf_private *data;
    /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
    if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
        serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
        serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8)
        return -ENODEV;

data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL);
    if (!data)
        return -ENOMEM;
    spin_lock_init(&data->susp_lock);
    return 0;
}

static void option_set_termios(struct tty_struct *tty,
        struct usb_serial_port *port, struct ktermios *old_termios)
{
    dbg("%s", __func__);
    /* Doesn't support option setting */
    tty_termios_copy_hw(tty->termios, old_termios);
    option_send_setup(port);
}

static int option_tiocmget(struct tty_struct *tty, struct file *file)
{
    struct usb_serial_port *port = tty->driver_data;
    unsigned int value;
    struct option_port_private *portdata;

portdata = usb_get_serial_port_data(port);

value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
        ((portdata->dtr_state) ? TIOCM_DTR : 0) |
        ((portdata->cts_state) ? TIOCM_CTS : 0) |
        ((portdata->dsr_state) ? TIOCM_DSR : 0) |
        ((portdata->dcd_state) ? TIOCM_CAR : 0) |
        ((portdata->ri_state) ? TIOCM_RNG : 0);

return value;
}

static int option_tiocmset(struct tty_struct *tty, struct file *file,
            unsigned int set, unsigned int clear)
{
    struct usb_serial_port *port = tty->driver_data;
    struct option_port_private *portdata;

portdata = usb_get_serial_port_data(port);

/* FIXME: what locks portdata fields ? */
    if (set & TIOCM_RTS)
        portdata->rts_state = 1;
    if (set & TIOCM_DTR)
        portdata->dtr_state = 1;

if (clear & TIOCM_RTS)
        portdata->rts_state = 0;
    if (clear & TIOCM_DTR)
        portdata->dtr_state = 0;
    return option_send_setup(port);
}

/* Write */
static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
            const unsigned char *buf, int count)
{
    struct option_port_private *portdata;
    struct option_intf_private *intfdata;
    int i;
    int left, todo;
    struct urb *this_urb = NULL; /* spurious */
    int err;
    unsigned long flags;

portdata = usb_get_serial_port_data(port);
    intfdata = port->serial->private;

dbg("%s: write (%d chars)", __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;
        }
        dbg("%s: endpoint %d buf %d", __func__,
            usb_pipeendpoint(this_urb->pipe), i);

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(&intfdata->susp_lock, flags);
        if (intfdata->suspended) {
            usb_anchor_urb(this_urb, &portdata->delayed);
            spin_unlock_irqrestore(&intfdata->susp_lock, flags);
        } else {
            intfdata->in_flight++;
            spin_unlock_irqrestore(&intfdata->susp_lock, flags);
            err = usb_submit_urb(this_urb, GFP_ATOMIC);
            if (err) {
                dbg("usb_submit_urb %p (write bulk) failed "
                    "(%d)", this_urb, err);
                clear_bit(i, &portdata->out_busy);
                spin_lock_irqsave(&intfdata->susp_lock, flags);
                intfdata->in_flight--;
                spin_unlock_irqrestore(&intfdata->susp_lock, flags);
                continue;
            }
        }

portdata->tx_start_time[i] = jiffies;
        buf += todo;
        left -= todo;
    }

count -= left;
    dbg("%s: wrote (did %d)", __func__, count);
    return count;
}

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

dbg("%s: %p", __func__, urb);

endpoint = usb_pipeendpoint(urb->pipe);
    port =  urb->context;

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

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

}
    return;
}

static void option_outdat_callback(struct urb *urb)
{
    struct usb_serial_port *port;
    struct option_port_private *portdata;
    struct option_intf_private *intfdata;
    int i;

dbg("%s", __func__);

port =  urb->context;
    intfdata = port->serial->private;

usb_serial_port_softint(port);
    usb_autopm_put_interface_async(port->serial->interface);
    portdata = usb_get_serial_port_data(port);
    spin_lock(&intfdata->susp_lock);
    intfdata->in_flight--;
    spin_unlock(&intfdata->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 void option_instat_callback(struct urb *urb)
{
    int err;
    int status = urb->status;
    struct usb_serial_port *port =  urb->context;
    struct option_port_private *portdata = usb_get_serial_port_data(port);

dbg("%s", __func__);
    dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata);

if (status == 0) {
        struct usb_ctrlrequest *req_pkt =
                (struct usb_ctrlrequest *)urb->transfer_buffer;

if (!req_pkt) {
            dbg("%s: NULL req_pkt\n", __func__);
            return;
        }
        if ((req_pkt->bRequestType == 0xA1) &&
                (req_pkt->bRequest == 0x20)) {
            int old_dcd_state;
            unsigned char signals = *((unsigned char *)
                    urb->transfer_buffer +
                    sizeof(struct usb_ctrlrequest));

dbg("%s: signal x%x", __func__, signals);

old_dcd_state = portdata->dcd_state;
            portdata->cts_state = 1;
            portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
            portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
            portdata->ri_state = ((signals & 0x08) ? 1 : 0);

if (old_dcd_state && !portdata->dcd_state) {
                struct tty_struct *tty =
                        tty_port_tty_get(&port->port);
                if (tty && !C_CLOCAL(tty))
                    tty_hangup(tty);
                tty_kref_put(tty);
            }
        } else {
            dbg("%s: type %x req %x", __func__,
                req_pkt->bRequestType, req_pkt->bRequest);
        }
    } else
        err("%s: error %d", __func__, status);

/* Resubmit urb so we continue receiving IRQ data */
    if (status != -ESHUTDOWN && status != -ENOENT) {
        err = usb_submit_urb(urb, GFP_ATOMIC);
        if (err)
            dbg("%s: resubmit intr urb failed. (%d)",
                __func__, err);
    }
}

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

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;
    }

dbg("%s: %d", __func__, data_len);
    return data_len;
}

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

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;
    }
    dbg("%s: %d", __func__, data_len);
    return data_len;
}

static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
{
    struct option_port_private *portdata;
    struct option_intf_private *intfdata;
    struct usb_serial *serial = port->serial;
    int i, err;
    struct urb *urb;

portdata = usb_get_serial_port_data(port);
    intfdata = serial->private;

dbg("%s", __func__);

/* Start reading from the IN endpoint */
    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) {
            dbg("%s: submit urb %d failed (%d) %d",
                __func__, i, err,
                urb->transfer_buffer_length);
        }
    }

option_send_setup(port);

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

return 0;
}

static void option_dtr_rts(struct usb_serial_port *port, int on)
{
    struct usb_serial *serial = port->serial;
    struct option_port_private *portdata;

dbg("%s", __func__);
    portdata = usb_get_serial_port_data(port);
    mutex_lock(&serial->disc_mutex);
    portdata->rts_state = on;
    portdata->dtr_state = on;
    if (serial->dev)
        option_send_setup(port);
    mutex_unlock(&serial->disc_mutex);
}

static void option_close(struct usb_serial_port *port)
{
    int i;
    struct usb_serial *serial = port->serial;
    struct option_port_private *portdata;
    struct option_intf_private *intfdata = port->serial->private;

dbg("%s", __func__);
    portdata = usb_get_serial_port_data(port);

if (serial->dev) {
        /* Stop reading/writing urbs */
        spin_lock_irq(&intfdata->susp_lock);
        portdata->opened = 0;
        spin_unlock_irq(&intfdata->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;
    }
}

/* Helper functions used by option_setup_urbs */
static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
        int dir, void *ctx, char *buf, int len,
        void (*callback)(struct urb *))
{
    struct urb *urb;

if (endpoint == -1)
        return NULL;        /* endpoint not needed */

urb = usb_alloc_urb(0, GFP_KERNEL);        /* No ISO */
    if (urb == NULL) {
        dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
        return NULL;
    }

/* Fill URB using supplied data. */
    usb_fill_bulk_urb(urb, serial->dev,
              usb_sndbulkpipe(serial->dev, endpoint) | dir,
              buf, len, callback, ctx);

return urb;
}

/* Setup urbs */
static void option_setup_urbs(struct usb_serial *serial)
{
    int i, j;
    struct usb_serial_port *port;
    struct option_port_private *portdata;

dbg("%s", __func__);

for (i = 0; i < serial->num_ports; i++) {
        port = serial->port[i];
        portdata = usb_get_serial_port_data(port);

/* Do indat endpoints first */
        for (j = 0; j < N_IN_URB; ++j) {
            portdata->in_urbs[j] = option_setup_urb(serial,
                    port->bulk_in_endpointAddress,
                    USB_DIR_IN, port,
                    portdata->in_buffer[j],
                    IN_BUFLEN, option_indat_callback);
        }

/* outdat endpoints */
        for (j = 0; j < N_OUT_URB; ++j) {
            portdata->out_urbs[j] = option_setup_urb(serial,
                    port->bulk_out_endpointAddress,
                    USB_DIR_OUT, port,
                    portdata->out_buffer[j],
                    OUT_BUFLEN, option_outdat_callback);
        }
    }
}

/** send RTS/DTR state to the port.
 *
 * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN
 * CDC.
*/
static int option_send_setup(struct usb_serial_port *port)
{
    struct usb_serial *serial = port->serial;
    struct option_port_private *portdata;
    int ifNum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
    int val = 0;
    dbg("%s", __func__);

portdata = usb_get_serial_port_data(port);

if (portdata->dtr_state)
        val |= 0x01;
    if (portdata->rts_state)
        val |= 0x02;

return usb_control_msg(serial->dev,
        usb_rcvctrlpipe(serial->dev, 0),
        0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
}

static int option_startup(struct usb_serial *serial)
{
    int i, j, err;
    struct usb_serial_port *port;
    struct option_port_private *portdata;
    u8 *buffer;

dbg("%s", __func__);

/* Now setup per port private data */
    for (i = 0; i < serial->num_ports; i++) {
        port = serial->port[i];
        portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
        if (!portdata) {
            dbg("%s: kmalloc for option_port_private (%d) failed!.",
                    __func__, i);
            return 1;
        }
        init_usb_anchor(&portdata->delayed);

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

for (j = 0; j < N_OUT_URB; j++) {
            buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
            if (!buffer)
                goto bail_out_error2;
            portdata->out_buffer[j] = buffer;
        }

usb_set_serial_port_data(port, portdata);

if (!port->interrupt_in_urb)
            continue;
        err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
        if (err)
            dbg("%s: submit irq_in urb failed %d",
                __func__, err);
    }
    option_setup_urbs(serial);
    return 0;

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

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

/* Stop reading/writing urbs */
    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 option_disconnect(struct usb_serial *serial)
{
    dbg("%s", __func__);

stop_read_write_urbs(serial);
}

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

dbg("%s", __func__);

/* Now free them */
    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++) {
            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]);
                kfree(portdata->out_buffer[j]);
                portdata->out_urbs[j] = NULL;
            }
        }
    }

/* Now free per port private data */
    for (i = 0; i < serial->num_ports; i++) {
        port = serial->port[i];
        kfree(usb_get_serial_port_data(port));
    }
}

#ifdef CONFIG_PM
static int option_suspend(struct usb_serial *serial, pm_message_t message)
{
    struct option_intf_private *intfdata = serial->private;
    int b;

dbg("%s entered", __func__);

if (serial->dev->auto_pm) {
        spin_lock_irq(&intfdata->susp_lock);
        b = intfdata->in_flight;
        spin_unlock_irq(&intfdata->susp_lock);

if (b)
            return -EBUSY;
    }

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

return 0;
}

static void play_delayed(struct usb_serial_port *port)
{
    struct option_intf_private *data;
    struct option_port_private *portdata;
    struct urb *urb;
    int err;

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

static int option_resume(struct usb_serial *serial)
{
    int i, j;
    struct usb_serial_port *port;
    struct option_intf_private *intfdata = serial->private;
    struct option_port_private *portdata;
    struct urb *urb;
    int err = 0;

dbg("%s entered", __func__);
    /* get the interrupt URBs resubmitted unconditionally */
    for (i = 0; i < serial->num_ports; i++) {
        port = serial->port[i];
        if (!port->interrupt_in_urb) {
            dbg("%s: No interrupt URB for port %d\n", __func__, i);
            continue;
        }
        err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
        dbg("Submitted interrupt URB for port %d (result %d)", i, err);
        if (err < 0) {
            err("%s: Error %d for interrupt URB of port%d",
                 __func__, err, i);
            goto err_out;
        }
    }

for (i = 0; i < serial->num_ports; i++) {
        /* walk all ports */
        port = serial->port[i];
        portdata = usb_get_serial_port_data(port);

/* skip closed ports */
        spin_lock_irq(&intfdata->susp_lock);
        if (!portdata->opened) {
            spin_unlock_irq(&intfdata->susp_lock);
            continue;
        }

for (j = 0; j < N_IN_URB; j++) {
            urb = portdata->in_urbs[j];
            err = usb_submit_urb(urb, GFP_ATOMIC);
            if (err < 0) {
                err("%s: Error %d for bulk URB %d",
                     __func__, err, i);
                spin_unlock_irq(&intfdata->susp_lock);
                goto err_out;
            }
        }
        play_delayed(port);
        spin_unlock_irq(&intfdata->susp_lock);
    }
    spin_lock_irq(&intfdata->susp_lock);
    intfdata->suspended = 0;
    spin_unlock_irq(&intfdata->susp_lock);
err_out:
    return err;
}
#endif

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug messages");

文件四:

pl2303.c

/*
 * Prolific PL2303 USB to serial adaptor driver
 *
 * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2003 IBM Corp.
 *
 * Original driver for 2.2.x by anonymous
 *
 *    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.
 *
 * See Documentation/usb/usb-serial.txt for more information on using this
 * driver
 *
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include "pl2303.h"

/*
 * Version Information
 */
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"

static int debug;

#define PL2303_CLOSING_WAIT    (30*HZ)

#define PL2303_BUF_SIZE        1024
#define PL2303_TMP_BUF_SIZE    1024

struct pl2303_buf {
    unsigned int    buf_size;
    char        *buf_buf;
    char        *buf_get;
    char        *buf_put;
};

static struct usb_device_id id_table [] = {
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) },
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) },
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) },
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) },
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) },
    { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) },
    { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
    { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
    { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
    { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
    { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
    { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
    { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) },
    { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) },
    { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) },
    { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) },
    { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
    { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
    { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
    { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
    { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) },
    { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) },
    { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) },
    { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) },
    { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) },
    { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) },
    { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */
    { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) },
    { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) },
    { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) },
    { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) },
    { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) },
    { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
    { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
    { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
    { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
    { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) },
    { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) },
    { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) },
    { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) },
    { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) },
    { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) },
    { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) },
    { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) },
    { }                    /* Terminating entry */
};

MODULE_DEVICE_TABLE(usb, id_table);

static struct usb_driver pl2303_driver = {
    .name =        "pl2303",
    .probe =    usb_serial_probe,
    .disconnect =    usb_serial_disconnect,
    .id_table =    id_table,
    .suspend =      usb_serial_suspend,
    .resume =       usb_serial_resume,
    .no_dynamic_id =     1,
    .supports_autosuspend =    1,
};

#define SET_LINE_REQUEST_TYPE        0x21
#define SET_LINE_REQUEST        0x20

#define SET_CONTROL_REQUEST_TYPE    0x21
#define SET_CONTROL_REQUEST        0x22
#define CONTROL_DTR            0x01
#define CONTROL_RTS            0x02

#define BREAK_REQUEST_TYPE        0x21
#define BREAK_REQUEST            0x23
#define BREAK_ON            0xffff
#define BREAK_OFF            0x0000

#define GET_LINE_REQUEST_TYPE        0xa1
#define GET_LINE_REQUEST        0x21

#define VENDOR_WRITE_REQUEST_TYPE    0x40
#define VENDOR_WRITE_REQUEST        0x01

#define VENDOR_READ_REQUEST_TYPE    0xc0
#define VENDOR_READ_REQUEST        0x01

#define UART_STATE            0x08
#define UART_STATE_TRANSIENT_MASK    0x74
#define UART_DCD            0x01
#define UART_DSR            0x02
#define UART_BREAK_ERROR        0x04
#define UART_RING            0x08
#define UART_FRAME_ERROR        0x10
#define UART_PARITY_ERROR        0x20
#define UART_OVERRUN_ERROR        0x40
#define UART_CTS            0x80

enum pl2303_type {
    type_0,        /* don't know the difference between type 0 and */
    type_1,        /* type 1, until someone from prolific tells us... */
    HX,        /* HX version of the pl2303 chip */
};

struct pl2303_private {
    spinlock_t lock;
    struct pl2303_buf *buf;
    int write_urb_in_use;
    wait_queue_head_t delta_msr_wait;
    u8 line_control;
    u8 line_status;
    enum pl2303_type type;
};

/*
 * pl2303_buf_alloc
 *
 * Allocate a circular buffer and all associated memory.
 */
static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
{
    struct pl2303_buf *pb;

if (size == 0)
        return NULL;

pb = kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
    if (pb == NULL)
        return NULL;

pb->buf_buf = kmalloc(size, GFP_KERNEL);
    if (pb->buf_buf == NULL) {
        kfree(pb);
        return NULL;
    }

pb->buf_size = size;
    pb->buf_get = pb->buf_put = pb->buf_buf;

return pb;
}

/*
 * pl2303_buf_free
 *
 * Free the buffer and all associated memory.
 */
static void pl2303_buf_free(struct pl2303_buf *pb)
{
    if (pb) {
        kfree(pb->buf_buf);
        kfree(pb);
    }
}

/*
 * pl2303_buf_clear
 *
 * Clear out all data in the circular buffer.
 */
static void pl2303_buf_clear(struct pl2303_buf *pb)
{
    if (pb != NULL)
        pb->buf_get = pb->buf_put;
        /* equivalent to a get of all data available */
}

/*
 * pl2303_buf_data_avail
 *
 * Return the number of bytes of data available in the circular
 * buffer.
 */
static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
{
    if (pb == NULL)
        return 0;

return (pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size;
}

/*
 * pl2303_buf_space_avail
 *
 * Return the number of bytes of space available in the circular
 * buffer.
 */
static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
{
    if (pb == NULL)
        return 0;

return (pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size;
}

/*
 * pl2303_buf_put
 *
 * Copy data data from a user buffer and put it into the circular buffer.
 * Restrict to the amount of space available.
 *
 * Return the number of bytes copied.
 */
static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
                   unsigned int count)
{
    unsigned int len;

if (pb == NULL)
        return 0;

len  = pl2303_buf_space_avail(pb);
    if (count > len)
        count = len;

if (count == 0)
        return 0;

len = pb->buf_buf + pb->buf_size - pb->buf_put;
    if (count > len) {
        memcpy(pb->buf_put, buf, len);
        memcpy(pb->buf_buf, buf+len, count - len);
        pb->buf_put = pb->buf_buf + count - len;
    } else {
        memcpy(pb->buf_put, buf, count);
        if (count < len)
            pb->buf_put += count;
        else /* count == len */
            pb->buf_put = pb->buf_buf;
    }

return count;
}

/*
 * pl2303_buf_get
 *
 * Get data from the circular buffer and copy to the given buffer.
 * Restrict to the amount of data available.
 *
 * Return the number of bytes copied.
 */
static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
                   unsigned int count)
{
    unsigned int len;

if (pb == NULL)
        return 0;

len = pl2303_buf_data_avail(pb);
    if (count > len)
        count = len;

if (count == 0)
        return 0;

len = pb->buf_buf + pb->buf_size - pb->buf_get;
    if (count > len) {
        memcpy(buf, pb->buf_get, len);
        memcpy(buf+len, pb->buf_buf, count - len);
        pb->buf_get = pb->buf_buf + count - len;
    } else {
        memcpy(buf, pb->buf_get, count);
        if (count < len)
            pb->buf_get += count;
        else /* count == len */
            pb->buf_get = pb->buf_buf;
    }

return count;
}

static int pl2303_vendor_read(__u16 value, __u16 index,
        struct usb_serial *serial, unsigned char *buf)
{
    int res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
            VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
            value, index, buf, 1, 100);
    dbg("0x%x:0x%x:0x%x:0x%x  %d - %x", VENDOR_READ_REQUEST_TYPE,
            VENDOR_READ_REQUEST, value, index, res, buf[0]);
    return res;
}

static int pl2303_vendor_write(__u16 value, __u16 index,
        struct usb_serial *serial)
{
    int res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
            VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
            value, index, NULL, 0, 100);
    dbg("0x%x:0x%x:0x%x:0x%x  %d", VENDOR_WRITE_REQUEST_TYPE,
            VENDOR_WRITE_REQUEST, value, index, res);
    return res;
}

static int pl2303_startup(struct usb_serial *serial)
{
    struct pl2303_private *priv;
    enum pl2303_type type = type_0;
    unsigned char *buf;
    int i;

buf = kmalloc(10, GFP_KERNEL);
    if (buf == NULL)
        return -ENOMEM;

if (serial->dev->descriptor.bDeviceClass == 0x02)
        type = type_0;
    else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
        type = HX;
    else if (serial->dev->descriptor.bDeviceClass == 0x00)
        type = type_1;
    else if (serial->dev->descriptor.bDeviceClass == 0xFF)
        type = type_1;
    dbg("device type: %d", type);

for (i = 0; i < serial->num_ports; ++i) {
        priv = kzalloc(sizeof(struct pl2303_private), GFP_KERNEL);
        if (!priv)
            goto cleanup;
        spin_lock_init(&priv->lock);
        priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE);
        if (priv->buf == NULL) {
            kfree(priv);
            goto cleanup;
        }
        init_waitqueue_head(&priv->delta_msr_wait);
        priv->type = type;
        usb_set_serial_port_data(serial->port[i], priv);
    }

pl2303_vendor_read(0x8484, 0, serial, buf);
    pl2303_vendor_write(0x0404, 0, serial);
    pl2303_vendor_read(0x8484, 0, serial, buf);
    pl2303_vendor_read(0x8383, 0, serial, buf);
    pl2303_vendor_read(0x8484, 0, serial, buf);
    pl2303_vendor_write(0x0404, 1, serial);
    pl2303_vendor_read(0x8484, 0, serial, buf);
    pl2303_vendor_read(0x8383, 0, serial, buf);
    pl2303_vendor_write(0, 1, serial);
    pl2303_vendor_write(1, 0, serial);
    if (type == HX)
        pl2303_vendor_write(2, 0x44, serial);
    else
        pl2303_vendor_write(2, 0x24, serial);

kfree(buf);
    return 0;

cleanup:
    kfree(buf);
    for (--i; i >= 0; --i) {
        priv = usb_get_serial_port_data(serial->port[i]);
        pl2303_buf_free(priv->buf);
        kfree(priv);
        usb_set_serial_port_data(serial->port[i], NULL);
    }
    return -ENOMEM;
}

static int set_control_lines(struct usb_device *dev, u8 value)
{
    int retval;

retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                 SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
                 value, 0, NULL, 0, 100);
    dbg("%s - value = %d, retval = %d", __func__, value, retval);
    return retval;
}

static void pl2303_send(struct usb_serial_port *port)
{
    int count, result;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

spin_lock_irqsave(&priv->lock, flags);

if (priv->write_urb_in_use) {
        spin_unlock_irqrestore(&priv->lock, flags);
        return;
    }

count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
                   port->bulk_out_size);

if (count == 0) {
        spin_unlock_irqrestore(&priv->lock, flags);
        return;
    }

priv->write_urb_in_use = 1;

spin_unlock_irqrestore(&priv->lock, flags);

usb_serial_debug_data(debug, &port->dev, __func__, count,
                  port->write_urb->transfer_buffer);

port->write_urb->transfer_buffer_length = count;
    port->write_urb->dev = port->serial->dev;
    result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
    if (result) {
        dev_err(&port->dev, "%s - failed submitting write urb,"
            " error %d\n", __func__, result);
        priv->write_urb_in_use = 0;
        /* TODO: reschedule pl2303_send */
    }

usb_serial_port_softint(port);
}

static int pl2303_write(struct tty_struct *tty, struct usb_serial_port *port,
                const unsigned char *buf, int count)
{
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;

dbg("%s - port %d, %d bytes", __func__, port->number, count);

if (!count)
        return count;

spin_lock_irqsave(&priv->lock, flags);
    count = pl2303_buf_put(priv->buf, buf, count);
    spin_unlock_irqrestore(&priv->lock, flags);

pl2303_send(port);

return count;
}

static int pl2303_write_room(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    int room = 0;
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

spin_lock_irqsave(&priv->lock, flags);
    room = pl2303_buf_space_avail(priv->buf);
    spin_unlock_irqrestore(&priv->lock, flags);

dbg("%s - returns %d", __func__, room);
    return room;
}

static int pl2303_chars_in_buffer(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    int chars = 0;
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

spin_lock_irqsave(&priv->lock, flags);
    chars = pl2303_buf_data_avail(priv->buf);
    spin_unlock_irqrestore(&priv->lock, flags);

dbg("%s - returns %d", __func__, chars);
    return chars;
}

static void pl2303_set_termios(struct tty_struct *tty,
        struct usb_serial_port *port, struct ktermios *old_termios)
{
    struct usb_serial *serial = port->serial;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;
    unsigned int cflag;
    unsigned char *buf;
    int baud;
    int i;
    u8 control;
    const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600,
                             4800, 7200, 9600, 14400, 19200, 28800, 38400,
                             57600, 115200, 230400, 460800, 614400,
                             921600, 1228800, 2457600, 3000000, 6000000 };
    int baud_floor, baud_ceil;
    int k;

dbg("%s -  port %d", __func__, port->number);

/* The PL2303 is reported to lose bytes if you change
       serial settings even to the same values as before. Thus
       we actually need to filter in this specific case */

if (!tty_termios_hw_change(tty->termios, old_termios))
        return;

cflag = tty->termios->c_cflag;

buf = kzalloc(7, GFP_KERNEL);
    if (!buf) {
        dev_err(&port->dev, "%s - out of memory.\n", __func__);
        /* Report back no change occurred */
        *tty->termios = *old_termios;
        return;
    }

i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
                GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
                0, 0, buf, 7, 100);
    dbg("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
        buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);

if (cflag & CSIZE) {
        switch (cflag & CSIZE) {
        case CS5:
            buf[6] = 5;
            break;
        case CS6:
            buf[6] = 6;
            break;
        case CS7:
            buf[6] = 7;
            break;
        default:
        case CS8:
            buf[6] = 8;
            break;
        }
        dbg("%s - data bits = %d", __func__, buf[6]);
    }

/* For reference buf[0]:buf[3] baud rate value */
    /* NOTE: Only the values defined in baud_sup are supported !
     *       => if unsupported values are set, the PL2303 seems to use
     *          9600 baud (at least my PL2303X always does)
     */
    baud = tty_get_baud_rate(tty);
    dbg("%s - baud requested = %d", __func__, baud);
    if (baud) {
        /* Set baudrate to nearest supported value */
        for (k=0; k<ARRAY_SIZE(baud_sup); k++) {
            if (baud_sup[k] / baud) {
                baud_ceil = baud_sup[k];
                if (k==0) {
                    baud = baud_ceil;
                } else {
                    baud_floor = baud_sup[k-1];
                    if ((baud_ceil % baud)
                        > (baud % baud_floor))
                        baud = baud_floor;
                    else
                        baud = baud_ceil;
                }
                break;
            }
        }
        if (baud > 1228800) {
            /* type_0, type_1 only support up to 1228800 baud */
            if (priv->type != HX)
                baud = 1228800;
            else if (baud > 6000000)
                baud = 6000000;
        }
        dbg("%s - baud set = %d", __func__, baud);
        buf[0] = baud & 0xff;
        buf[1] = (baud >> 8) & 0xff;
        buf[2] = (baud >> 16) & 0xff;
        buf[3] = (baud >> 24) & 0xff;
    }

/* For reference buf[4]=0 is 1 stop bits */
    /* For reference buf[4]=1 is 1.5 stop bits */
    /* For reference buf[4]=2 is 2 stop bits */
    if (cflag & CSTOPB) {
        /* NOTE: Comply with "real" UARTs / RS232:
         *       use 1.5 instead of 2 stop bits with 5 data bits
         */
        if ((cflag & CSIZE) == CS5) {
            buf[4] = 1;
            dbg("%s - stop bits = 1.5", __func__);
        } else {
            buf[4] = 2;
            dbg("%s - stop bits = 2", __func__);
        }
    } else {
        buf[4] = 0;
        dbg("%s - stop bits = 1", __func__);
    }

if (cflag & PARENB) {
        /* For reference buf[5]=0 is none parity */
        /* For reference buf[5]=1 is odd parity */
        /* For reference buf[5]=2 is even parity */
        /* For reference buf[5]=3 is mark parity */
        /* For reference buf[5]=4 is space parity */
        if (cflag & PARODD) {
            if (cflag & CMSPAR) {
                buf[5] = 3;
                dbg("%s - parity = mark", __func__);
            } else {
                buf[5] = 1;
                dbg("%s - parity = odd", __func__);
            }
        } else {
            if (cflag & CMSPAR) {
                buf[5] = 4;
                dbg("%s - parity = space", __func__);
            } else {
                buf[5] = 2;
                dbg("%s - parity = even", __func__);
            }
        }
    } else {
        buf[5] = 0;
        dbg("%s - parity = none", __func__);
    }

i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
                0, 0, buf, 7, 100);
    dbg("0x21:0x20:0:0  %d", i);

/* change control lines if we are switching to or from B0 */
    spin_lock_irqsave(&priv->lock, flags);
    control = priv->line_control;
    if ((cflag & CBAUD) == B0)
        priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
    else
        priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
    if (control != priv->line_control) {
        control = priv->line_control;
        spin_unlock_irqrestore(&priv->lock, flags);
        set_control_lines(serial->dev, control);
    } else {
        spin_unlock_irqrestore(&priv->lock, flags);
    }

buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;

i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
                GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
                0, 0, buf, 7, 100);
    dbg("0xa1:0x21:0:0  %d - %x %x %x %x %x %x %x", i,
         buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);

if (cflag & CRTSCTS) {
        if (priv->type == HX)
            pl2303_vendor_write(0x0, 0x61, serial);
        else
            pl2303_vendor_write(0x0, 0x41, serial);
    } else {
        pl2303_vendor_write(0x0, 0x0, serial);
    }

/* Save resulting baud rate */
    if (baud)
        tty_encode_baud_rate(tty, baud, baud);

kfree(buf);
}

static void pl2303_dtr_rts(struct usb_serial_port *port, int on)
{
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;
    u8 control;

spin_lock_irqsave(&priv->lock, flags);
    /* Change DTR and RTS */
    if (on)
        priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
    else
        priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
    control = priv->line_control;
    spin_unlock_irqrestore(&priv->lock, flags);
    set_control_lines(port->serial->dev, control);
}

static void pl2303_close(struct usb_serial_port *port)
{
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;

dbg("%s - port %d", __func__, port->number);

spin_lock_irqsave(&priv->lock, flags);
    /* clear out any remaining data in the buffer */
    pl2303_buf_clear(priv->buf);
    spin_unlock_irqrestore(&priv->lock, flags);

/* shutdown our urbs */
    dbg("%s - shutting down urbs", __func__);
    usb_kill_urb(port->write_urb);
    usb_kill_urb(port->read_urb);
    usb_kill_urb(port->interrupt_in_urb);

}

static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
{
    struct ktermios tmp_termios;
    struct usb_serial *serial = port->serial;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    int result;

dbg("%s -  port %d", __func__, port->number);

if (priv->type != HX) {
        usb_clear_halt(serial->dev, port->write_urb->pipe);
        usb_clear_halt(serial->dev, port->read_urb->pipe);
    } else {
        /* reset upstream data pipes */
        pl2303_vendor_write(8, 0, serial);
        pl2303_vendor_write(9, 0, serial);
    }

/* Setup termios */
    if (tty)
        pl2303_set_termios(tty, port, &tmp_termios);

dbg("%s - submitting read urb", __func__);
    port->read_urb->dev = serial->dev;
    result = usb_submit_urb(port->read_urb, GFP_KERNEL);
    if (result) {
        dev_err(&port->dev, "%s - failed submitting read urb,"
            " error %d\n", __func__, result);
        pl2303_close(port);
        return -EPROTO;
    }

dbg("%s - submitting interrupt urb", __func__);
    port->interrupt_in_urb->dev = serial->dev;
    result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
    if (result) {
        dev_err(&port->dev, "%s - failed submitting interrupt urb,"
            " error %d\n", __func__, result);
        pl2303_close(port);
        return -EPROTO;
    }
    port->port.drain_delay = 256;
    return 0;
}

static int pl2303_tiocmset(struct tty_struct *tty, struct file *file,
               unsigned int set, unsigned int clear)
{
    struct usb_serial_port *port = tty->driver_data;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;
    u8 control;

if (!usb_get_intfdata(port->serial->interface))
        return -ENODEV;

spin_lock_irqsave(&priv->lock, flags);
    if (set & TIOCM_RTS)
        priv->line_control |= CONTROL_RTS;
    if (set & TIOCM_DTR)
        priv->line_control |= CONTROL_DTR;
    if (clear & TIOCM_RTS)
        priv->line_control &= ~CONTROL_RTS;
    if (clear & TIOCM_DTR)
        priv->line_control &= ~CONTROL_DTR;
    control = priv->line_control;
    spin_unlock_irqrestore(&priv->lock, flags);

return set_control_lines(port->serial->dev, control);
}

static int pl2303_tiocmget(struct tty_struct *tty, struct file *file)
{
    struct usb_serial_port *port = tty->driver_data;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;
    unsigned int mcr;
    unsigned int status;
    unsigned int result;

dbg("%s (%d)", __func__, port->number);

if (!usb_get_intfdata(port->serial->interface))
        return -ENODEV;

spin_lock_irqsave(&priv->lock, flags);
    mcr = priv->line_control;
    status = priv->line_status;
    spin_unlock_irqrestore(&priv->lock, flags);

result = ((mcr & CONTROL_DTR)        ? TIOCM_DTR : 0)
          | ((mcr & CONTROL_RTS)    ? TIOCM_RTS : 0)
          | ((status & UART_CTS)    ? TIOCM_CTS : 0)
          | ((status & UART_DSR)    ? TIOCM_DSR : 0)
          | ((status & UART_RING)    ? TIOCM_RI  : 0)
          | ((status & UART_DCD)    ? TIOCM_CD  : 0);

dbg("%s - result = %x", __func__, result);

return result;
}

static int pl2303_carrier_raised(struct usb_serial_port *port)
{
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    if (priv->line_status & UART_DCD)
        return 1;
    return 0;
}

static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
{
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;
    unsigned int prevstatus;
    unsigned int status;
    unsigned int changed;

spin_lock_irqsave(&priv->lock, flags);
    prevstatus = priv->line_status;
    spin_unlock_irqrestore(&priv->lock, flags);

while (1) {
        interruptible_sleep_on(&priv->delta_msr_wait);
        /* see if a signal did it */
        if (signal_pending(current))
            return -ERESTARTSYS;

spin_lock_irqsave(&priv->lock, flags);
        status = priv->line_status;
        spin_unlock_irqrestore(&priv->lock, flags);

changed = prevstatus ^ status;

if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
            ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
            ((arg & TIOCM_CD)  && (changed & UART_DCD)) ||
            ((arg & TIOCM_CTS) && (changed & UART_CTS))) {
            return 0;
        }
        prevstatus = status;
    }
    /* NOTREACHED */
    return 0;
}

static int pl2303_ioctl(struct tty_struct *tty, struct file *file,
            unsigned int cmd, unsigned long arg)
{
    struct usb_serial_port *port = tty->driver_data;
    dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);

switch (cmd) {
    case TIOCMIWAIT:
        dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
        return wait_modem_info(port, arg);
    default:
        dbg("%s not supported = 0x%04x", __func__, cmd);
        break;
    }
    return -ENOIOCTLCMD;
}

static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
{
    struct usb_serial_port *port = tty->driver_data;
    struct usb_serial *serial = port->serial;
    u16 state;
    int result;

dbg("%s - port %d", __func__, port->number);

if (break_state == 0)
        state = BREAK_OFF;
    else
        state = BREAK_ON;
    dbg("%s - turning break %s", __func__,
            state == BREAK_OFF ? "off" : "on");

result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                 BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
                 0, NULL, 0, 100);
    if (result)
        dbg("%s - error sending break = %d", __func__, result);
}

static void pl2303_release(struct usb_serial *serial)
{
    int i;
    struct pl2303_private *priv;

dbg("%s", __func__);

for (i = 0; i < serial->num_ports; ++i) {
        priv = usb_get_serial_port_data(serial->port[i]);
        if (priv) {
            pl2303_buf_free(priv->buf);
            kfree(priv);
        }
    }
}

static void pl2303_update_line_status(struct usb_serial_port *port,
                      unsigned char *data,
                      unsigned int actual_length)
{

struct pl2303_private *priv = usb_get_serial_port_data(port);
    unsigned long flags;
    u8 status_idx = UART_STATE;
    u8 length = UART_STATE + 1;
    u16 idv, idp;

idv = le16_to_cpu(port->serial->dev->descriptor.idVendor);
    idp = le16_to_cpu(port->serial->dev->descriptor.idProduct);

if (idv == SIEMENS_VENDOR_ID) {
        if (idp == SIEMENS_PRODUCT_ID_X65 ||
            idp == SIEMENS_PRODUCT_ID_SX1 ||
            idp == SIEMENS_PRODUCT_ID_X75) {

length = 1;
            status_idx = 0;
        }
    }

if (actual_length < length)
        return;

/* Save off the uart status for others to look at */
    spin_lock_irqsave(&priv->lock, flags);
    priv->line_status = data[status_idx];
    spin_unlock_irqrestore(&priv->lock, flags);
    if (priv->line_status & UART_BREAK_ERROR)
        usb_serial_handle_break(port);
    wake_up_interruptible(&priv->delta_msr_wait);
}

static void pl2303_read_int_callback(struct urb *urb)
{
    struct usb_serial_port *port =  urb->context;
    unsigned char *data = urb->transfer_buffer;
    unsigned int actual_length = urb->actual_length;
    int status = urb->status;
    int retval;

dbg("%s (%d)", __func__, port->number);

switch (status) {
    case 0:
        /* success */
        break;
    case -ECONNRESET:
    case -ENOENT:
    case -ESHUTDOWN:
        /* this urb is terminated, clean up */
        dbg("%s - urb shutting down with status: %d", __func__,
            status);
        return;
    default:
        dbg("%s - nonzero urb status received: %d", __func__,
            status);
        goto exit;
    }

usb_serial_debug_data(debug, &port->dev, __func__,
                  urb->actual_length, urb->transfer_buffer);

pl2303_update_line_status(port, data, actual_length);

exit:
    retval = usb_submit_urb(urb, GFP_ATOMIC);
    if (retval)
        dev_err(&urb->dev->dev,
            "%s - usb_submit_urb failed with result %d\n",
            __func__, retval);
}

static void pl2303_push_data(struct tty_struct *tty,
        struct usb_serial_port *port, struct urb *urb,
        u8 line_status)
{
    unsigned char *data = urb->transfer_buffer;
    /* get tty_flag from status */
    char tty_flag = TTY_NORMAL;
    /* break takes precedence over parity, */
    /* which takes precedence over framing errors */
    if (line_status & UART_BREAK_ERROR)
        tty_flag = TTY_BREAK;
    else if (line_status & UART_PARITY_ERROR)
        tty_flag = TTY_PARITY;
    else if (line_status & UART_FRAME_ERROR)
        tty_flag = TTY_FRAME;
    dbg("%s - tty_flag = %d", __func__, tty_flag);

tty_buffer_request_room(tty, urb->actual_length + 1);
    /* overrun is special, not associated with a char */
    if (line_status & UART_OVERRUN_ERROR)
        tty_insert_flip_char(tty, 0, TTY_OVERRUN);

if (tty_flag == TTY_NORMAL && !(port->console && port->sysrq))
        tty_insert_flip_string(tty, data, urb->actual_length);
    else {
        int i;
        for (i = 0; i < urb->actual_length; ++i)
            if (!usb_serial_handle_sysrq_char(tty, port, data[i]))
                tty_insert_flip_char(tty, data[i], tty_flag);
    }
    tty_flip_buffer_push(tty);
}

static void pl2303_read_bulk_callback(struct urb *urb)
{
    struct usb_serial_port *port =  urb->context;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    struct tty_struct *tty;
    unsigned long flags;
    int result;
    int status = urb->status;
    u8 line_status;

dbg("%s - port %d", __func__, port->number);

if (status) {
        dbg("%s - urb status = %d", __func__, status);
        if (!port->port.count) {
            dbg("%s - port is closed, exiting.", __func__);
            return;
        }
        if (status == -EPROTO) {
            /* PL2303 mysteriously fails with -EPROTO reschedule
             * the read */
            dbg("%s - caught -EPROTO, resubmitting the urb",
                __func__);
            urb->dev = port->serial->dev;
            result = usb_submit_urb(urb, GFP_ATOMIC);
            if (result)
                dev_err(&urb->dev->dev, "%s - failed"
                    " resubmitting read urb, error %d\n",
                    __func__, result);
            return;
        }
        dbg("%s - unable to handle the error, exiting.", __func__);
        return;
    }

usb_serial_debug_data(debug, &port->dev, __func__,
                  urb->actual_length, urb->transfer_buffer);

spin_lock_irqsave(&priv->lock, flags);
    line_status = priv->line_status;
    priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
    spin_unlock_irqrestore(&priv->lock, flags);
    wake_up_interruptible(&priv->delta_msr_wait);

tty = tty_port_tty_get(&port->port);
    if (tty && urb->actual_length) {
        pl2303_push_data(tty, port, urb, line_status);
    }
    tty_kref_put(tty);
    /* Schedule the next read _if_ we are still open */
    if (port->port.count) {
        urb->dev = port->serial->dev;
        result = usb_submit_urb(urb, GFP_ATOMIC);
        if (result)
            dev_err(&urb->dev->dev, "%s - failed resubmitting"
                " read urb, error %d\n", __func__, result);
    }

return;
}

static void pl2303_write_bulk_callback(struct urb *urb)
{
    struct usb_serial_port *port =  urb->context;
    struct pl2303_private *priv = usb_get_serial_port_data(port);
    int result;
    int status = urb->status;

dbg("%s - port %d", __func__, port->number);

switch (status) {
    case 0:
        /* success */
        break;
    case -ECONNRESET:
    case -ENOENT:
    case -ESHUTDOWN:
        /* this urb is terminated, clean up */
        dbg("%s - urb shutting down with status: %d", __func__,
            status);
        priv->write_urb_in_use = 0;
        return;
    default:
        /* error in the urb, so we have to resubmit it */
        dbg("%s - Overflow in write", __func__);
        dbg("%s - nonzero write bulk status received: %d", __func__,
            status);
        port->write_urb->transfer_buffer_length = 1;
        port->write_urb->dev = port->serial->dev;
        result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
        if (result)
            dev_err(&urb->dev->dev, "%s - failed resubmitting write"
                " urb, error %d\n", __func__, result);
        else
            return;
    }

priv->write_urb_in_use = 0;

/* send any buffered data */
    pl2303_send(port);
}

/* All of the device info needed for the PL2303 SIO serial converter */
static struct usb_serial_driver pl2303_device = {
    .driver = {
        .owner =    THIS_MODULE,
        .name =        "pl2303",
    },
    .id_table =        id_table,
    .usb_driver =         &pl2303_driver,
    .num_ports =        1,
    .open =            pl2303_open,
    .close =        pl2303_close,
    .dtr_rts =         pl2303_dtr_rts,
    .carrier_raised =    pl2303_carrier_raised,
    .write =        pl2303_write,
    .ioctl =        pl2303_ioctl,
    .break_ctl =        pl2303_break_ctl,
    .set_termios =        pl2303_set_termios,
    .tiocmget =        pl2303_tiocmget,
    .tiocmset =        pl2303_tiocmset,
    .read_bulk_callback =    pl2303_read_bulk_callback,
    .read_int_callback =    pl2303_read_int_callback,
    .write_bulk_callback =    pl2303_write_bulk_callback,
    .write_room =        pl2303_write_room,
    .chars_in_buffer =    pl2303_chars_in_buffer,
    .attach =        pl2303_startup,
    .release =        pl2303_release,
};

static int __init pl2303_init(void)
{
    int retval;

retval = usb_serial_register(&pl2303_device);
    if (retval)
        goto failed_usb_serial_register;
    retval = usb_register(&pl2303_driver);
    if (retval)
        goto failed_usb_register;
    printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
    return 0;
failed_usb_register:
    usb_serial_deregister(&pl2303_device);
failed_usb_serial_register:
    return retval;
}

static void __exit pl2303_exit(void)
{
    usb_deregister(&pl2303_driver);
    usb_serial_deregister(&pl2303_device);
}

module_init(pl2303_init);
module_exit(pl2303_exit);

MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");

文件五:

pl2303.h

/*
 * Prolific PL2303 USB to serial adaptor driver header file
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 */

#define BENQ_VENDOR_ID            0x04a5
#define BENQ_PRODUCT_ID_S81        0x4027

#define PL2303_VENDOR_ID    0x067b
#define PL2303_PRODUCT_ID    0x2303
#define PL2303_PRODUCT_ID_RSAQ2        0x04bb
#define PL2303_PRODUCT_ID_DCU11        0x1234
#define PL2303_PRODUCT_ID_PHAROS    0xaaa0
#define PL2303_PRODUCT_ID_RSAQ3        0xaaa2
#define PL2303_PRODUCT_ID_ALDIGA    0x0611
#define PL2303_PRODUCT_ID_MMX        0x0612
#define PL2303_PRODUCT_ID_GPRS        0x0609

#define ATEN_VENDOR_ID        0x0557
#define ATEN_VENDOR_ID2        0x0547
#define ATEN_PRODUCT_ID        0x2008

#define IODATA_VENDOR_ID    0x04bb
#define IODATA_PRODUCT_ID    0x0a03
#define IODATA_PRODUCT_ID_RSAQ5    0x0a0e

#define ELCOM_VENDOR_ID        0x056e
#define ELCOM_PRODUCT_ID    0x5003
#define ELCOM_PRODUCT_ID_UCSGT    0x5004

#define ITEGNO_VENDOR_ID    0x0eba
#define ITEGNO_PRODUCT_ID    0x1080
#define ITEGNO_PRODUCT_ID_2080    0x2080

#define MA620_VENDOR_ID        0x0df7
#define MA620_PRODUCT_ID    0x0620

#define RATOC_VENDOR_ID        0x0584
#define RATOC_PRODUCT_ID    0xb000

#define TRIPP_VENDOR_ID        0x2478
#define TRIPP_PRODUCT_ID    0x2008

#define RADIOSHACK_VENDOR_ID    0x1453
#define RADIOSHACK_PRODUCT_ID    0x4026

#define DCU10_VENDOR_ID        0x0731
#define DCU10_PRODUCT_ID    0x0528

#define SITECOM_VENDOR_ID    0x6189
#define SITECOM_PRODUCT_ID    0x2068

/* Alcatel OT535/735 USB cable */
#define ALCATEL_VENDOR_ID    0x11f7
#define ALCATEL_PRODUCT_ID    0x02df

/* Samsung I330 phone cradle */
#define SAMSUNG_VENDOR_ID    0x04e8
#define SAMSUNG_PRODUCT_ID    0x8001

#define SIEMENS_VENDOR_ID    0x11f5
#define SIEMENS_PRODUCT_ID_SX1    0x0001
#define SIEMENS_PRODUCT_ID_X65    0x0003
#define SIEMENS_PRODUCT_ID_X75    0x0004
#define SIEMENS_PRODUCT_ID_EF81    0x0005

#define SYNTECH_VENDOR_ID    0x0745
#define SYNTECH_PRODUCT_ID    0x0001

/* Nokia CA-42 Cable */
#define NOKIA_CA42_VENDOR_ID    0x078b
#define NOKIA_CA42_PRODUCT_ID    0x1234

/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */
#define CA_42_CA42_VENDOR_ID    0x10b5
#define CA_42_CA42_PRODUCT_ID    0xac70

#define SAGEM_VENDOR_ID        0x079b
#define SAGEM_PRODUCT_ID    0x0027

/* Leadtek GPS 9531 (ID 0413:2101) */
#define LEADTEK_VENDOR_ID    0x0413
#define LEADTEK_9531_PRODUCT_ID    0x2101

/* USB GSM cable from Speed Dragon Multimedia, Ltd */
#define SPEEDDRAGON_VENDOR_ID    0x0e55
#define SPEEDDRAGON_PRODUCT_ID    0x110b

/* DATAPILOT Universal-2 Phone Cable */
#define DATAPILOT_U2_VENDOR_ID    0x0731
#define DATAPILOT_U2_PRODUCT_ID    0x2003

/* Belkin "F5U257" Serial Adapter */
#define BELKIN_VENDOR_ID    0x050d
#define BELKIN_PRODUCT_ID    0x0257

/* Alcor Micro Corp. USB 2.0 TO RS-232 */
#define ALCOR_VENDOR_ID        0x058F
#define ALCOR_PRODUCT_ID    0x9720

/* Willcom WS002IN Data Driver (by NetIndex Inc.) */
#define WS002IN_VENDOR_ID    0x11f6
#define WS002IN_PRODUCT_ID    0x2001

/* Corega CG-USBRS232R Serial Adapter */
#define COREGA_VENDOR_ID    0x07aa
#define COREGA_PRODUCT_ID    0x002a

/* Y.C. Cable U.S.A., Inc - USB to RS-232 */
#define YCCABLE_VENDOR_ID    0x05ad
#define YCCABLE_PRODUCT_ID    0x0fba

/* "Superial" USB - Serial */
#define SUPERIAL_VENDOR_ID    0x5372
#define SUPERIAL_PRODUCT_ID    0x2303

/* Hewlett-Packard LD220-HP POS Pole Display */
#define HP_VENDOR_ID        0x03f0
#define HP_LD220_PRODUCT_ID    0x3524

/* Cressi Edy (diving computer) PC interface */
#define CRESSI_VENDOR_ID    0x04b8
#define CRESSI_EDY_PRODUCT_ID    0x0521

/* Sony, USB data cable for CMD-Jxx mobile phones */
#define SONY_VENDOR_ID        0x054c
#define SONY_QN3USB_PRODUCT_ID    0x0437

/* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */
#define SANWA_VENDOR_ID        0x11ad
#define SANWA_PRODUCT_ID    0x0001

文件六:

usb-serial.c

/*
 * USB Serial Converter driver
 *
 * Copyright (C) 1999 - 2005 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
 * Copyright (C) 2000 Al Borchers (borchers@steinerpoint.com)
 *
 *    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.
 *
 * This driver was originally based on the ACM driver by Armin Fuerst (which was
 * based on a driver by Brad Keryan)
 *
 * See Documentation/usb/usb-serial.txt for more information on using this
 * driver
 *
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/serial.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/kfifo.h>
#include "bus.c"
#include "generic.c"
#include "pl2303.h"
/*
 * Version Information
 */
#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
#define DRIVER_DESC "USB Serial Driver core"

/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
    .name =        "usbserial",
    .probe =    usb_serial_probe,
    .disconnect =    usb_serial_disconnect,
    .suspend =    usb_serial_suspend,
    .resume =    usb_serial_resume,
    .no_dynamic_id =     1,
};

/* There is no MODULE_DEVICE_TABLE for usbserial.c.  Instead
   the MODULE_DEVICE_TABLE declarations in each serial driver
   cause the "hotplug" program to pull in whatever module is necessary
   via modprobe, and modprobe will load usbserial because the serial
   drivers depend on it.
*/

static int debug;
/* initially all NULL */
static struct usb_serial *serial_table[SERIAL_TTY_MINORS];
static DEFINE_MUTEX(table_lock);
static LIST_HEAD(usb_serial_driver_list);

/*
 * Look up the serial structure.  If it is found and it hasn't been
 * disconnected, return with its disc_mutex held and its refcount
 * incremented.  Otherwise return NULL.
 */
struct usb_serial *usb_serial_get_by_index(unsigned index)
{
    struct usb_serial *serial;
    printk(KERN_ALERT "**************************usb_serial_get_by_index");
    mutex_lock(&table_lock);
    serial = serial_table[index];

if (serial) {
        mutex_lock(&serial->disc_mutex);
        if (serial->disconnected) {
            mutex_unlock(&serial->disc_mutex);
            serial = NULL;
        } else {
            kref_get(&serial->kref);
        }
    }
    mutex_unlock(&table_lock);
    return serial;
}

static struct usb_serial *get_free_serial(struct usb_serial *serial,
                    int num_ports, unsigned int *minor)
{
    unsigned int i, j;
    int good_spot;
    printk(KERN_ALERT "**************************usb_serial *get_free_serial");
    dbg("%s %d", __func__, num_ports);

*minor = 0;
    mutex_lock(&table_lock);
    for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
        if (serial_table[i])
            continue;

good_spot = 1;
        for (j = 1; j <= num_ports-1; ++j)
            if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {
                good_spot = 0;
                i += j;
                break;
            }
        if (good_spot == 0)
            continue;

*minor = i;
        j = 0;
        dbg("%s - minor base = %d", __func__, *minor);
        for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) {
            serial_table[i] = serial;
            serial->port[j++]->number = i;
        }
        mutex_unlock(&table_lock);
        return serial;
    }
    mutex_unlock(&table_lock);
    return NULL;
}

static void return_serial(struct usb_serial *serial)
{
    int i;
    printk(KERN_ALERT "**************************return_serial");
    dbg("%s", __func__);

mutex_lock(&table_lock);
    for (i = 0; i < serial->num_ports; ++i)
        serial_table[serial->minor + i] = NULL;
    mutex_unlock(&table_lock);
}

static void destroy_serial(struct kref *kref)
{
    struct usb_serial *serial;
    struct usb_serial_port *port;
    int i;
    printk(KERN_ALERT "**************************destroy_serial");
    serial = to_usb_serial(kref);

dbg("%s - %s", __func__, serial->type->description);

/* return the minor range that this device had */
    if (serial->minor != SERIAL_TTY_NO_MINOR)
        return_serial(serial);

if (serial->attached)
        serial->type->release(serial);

/* Now that nothing is using the ports, they can be freed */
    for (i = 0; i < serial->num_port_pointers; ++i) {
        port = serial->port[i];
        if (port) {
            port->serial = NULL;
            put_device(&port->dev);
        }
    }

usb_put_dev(serial->dev);
    kfree(serial);
}

void usb_serial_put(struct usb_serial *serial)
{
    kref_put(&serial->kref, destroy_serial);
}

/*****************************************************************************
 * Driver tty interface functions
 *****************************************************************************/

/**
 * serial_install - install tty
 * @driver: the driver (USB in our case)
 * @tty: the tty being created
 *
 * Create the termios objects for this tty.  We use the default
 * USB serial settings but permit them to be overridden by
 * serial->type->init_termios.
 *
 * This is the first place a new tty gets used.  Hence this is where we
 * acquire references to the usb_serial structure and the driver module,
 * where we store a pointer to the port, and where we do an autoresume.
 * All these actions are reversed in serial_cleanup().
 */
static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
{
    printk(KERN_ALERT "**************************serial_install");
    int idx = tty->index;
    struct usb_serial *serial;
    struct usb_serial_port *port;
    int retval = -ENODEV;

dbg("%s", __func__);

serial = usb_serial_get_by_index(idx);
    if (!serial)
        return retval;

port = serial->port[idx - serial->minor];
    if (!port)
        goto error_no_port;
    if (!try_module_get(serial->type->driver.owner))
        goto error_module_get;

/* perform the standard setup */
    retval = tty_init_termios(tty);
    if (retval)
        goto error_init_termios;

retval = usb_autopm_get_interface(serial->interface);
    if (retval)
        goto error_get_interface;

mutex_unlock(&serial->disc_mutex);

/* allow the driver to update the settings */
    if (serial->type->init_termios)
        serial->type->init_termios(tty);

tty->driver_data = port;

/* Final install (we use the default method) */
    tty_driver_kref_get(driver);
    tty->count++;
    driver->ttys[idx] = tty;
    printk(KERN_ALERT "**************************destroy_serial%d",idx);
    return retval;

error_get_interface:
 error_init_termios:
    module_put(serial->type->driver.owner);
 error_module_get:
 error_no_port:
    usb_serial_put(serial);
    mutex_unlock(&serial->disc_mutex);
    return retval;
}

static int serial_open(struct tty_struct *tty, struct file *filp)
{
    struct usb_serial_port *port = tty->driver_data;
    struct usb_serial *serial = port->serial;
    int retval;

dbg("%s - port %d", __func__, port->number);

spin_lock_irq(&port->port.lock);
    if (!tty_hung_up_p(filp))
        ++port->port.count;
    spin_unlock_irq(&port->port.lock);
    tty_port_tty_set(&port->port, tty);

/* Do the device-specific open only if the hardware isn't
     * already initialized.
     */
    if (!test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
        if (mutex_lock_interruptible(&port->mutex))
            return -ERESTARTSYS;
        mutex_lock(&serial->disc_mutex);
        if (serial->disconnected)
            retval = -ENODEV;
        else
            retval = port->serial->type->open(tty, port);
        mutex_unlock(&serial->disc_mutex);
        mutex_unlock(&port->mutex);
        if (retval)
            return retval;
        set_bit(ASYNCB_INITIALIZED, &port->port.flags);
    }

/* Now do the correct tty layer semantics */
    retval = tty_port_block_til_ready(&port->port, tty, filp);
    return retval;
}

/**
 * serial_down - shut down hardware
 * @port: port to shut down
 *
 * Shut down a USB serial port unless it is the console.  We never
 * shut down the console hardware as it will always be in use.
 */
static void serial_down(struct usb_serial_port *port)
{
    struct usb_serial_driver *drv = port->serial->type;

/*
     * The console is magical.  Do not hang up the console hardware
     * or there will be tears.
     */
    if (port->console)
        return;

/* Don't call the close method if the hardware hasn't been
     * initialized.
     */
    if (!test_and_clear_bit(ASYNCB_INITIALIZED, &port->port.flags))
        return;

mutex_lock(&port->mutex);
    if (drv->close)
        drv->close(port);
    mutex_unlock(&port->mutex);
}

static void serial_hangup(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;

dbg("%s - port %d", __func__, port->number);

serial_down(port);
    tty_port_hangup(&port->port);
}

static void serial_close(struct tty_struct *tty, struct file *filp)
{
    struct usb_serial_port *port = tty->driver_data;

dbg("%s - port %d", __func__, port->number);

if (tty_hung_up_p(filp))
        return;
    if (tty_port_close_start(&port->port, tty, filp) == 0)
        return;
    serial_down(port);
    tty_port_close_end(&port->port, tty);
    tty_port_tty_set(&port->port, NULL);
}

/**
 * serial_cleanup - free resources post close/hangup
 * @port: port to free up
 *
 * Do the resource freeing and refcount dropping for the port.
 * Avoid freeing the console.
 *
 * Called asynchronously after the last tty kref is dropped,
 * and the tty layer has already done the tty_shutdown(tty);
 */
static void serial_cleanup(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    struct usb_serial *serial;
    struct module *owner;

/* The console is magical.  Do not hang up the console hardware
     * or there will be tears.
     */
    if (port->console)
        return;

dbg("%s - port %d", __func__, port->number);

tty->driver_data = NULL;

serial = port->serial;
    owner = serial->type->driver.owner;

mutex_lock(&serial->disc_mutex);
    if (!serial->disconnected)
        usb_autopm_put_interface(serial->interface);
    mutex_unlock(&serial->disc_mutex);

usb_serial_put(serial);
    module_put(owner);
}

static int serial_write(struct tty_struct *tty, const unsigned char *buf,
                                int count)
{
    struct usb_serial_port *port = tty->driver_data;
    int retval = -ENODEV;

if (port->serial->dev->state == USB_STATE_NOTATTACHED)
        goto exit;

dbg("%s - port %d, %d byte(s)", __func__, port->number, count);

/* count is managed under the mutex lock for the tty so cannot
       drop to zero until after the last close completes */
    WARN_ON(!port->port.count);

/* pass on to the driver specific version of this function */
    retval = port->serial->type->write(tty, port, buf, count);

exit:
    return retval;
}

static int serial_write_room(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    dbg("%s - port %d", __func__, port->number);
    WARN_ON(!port->port.count);
    /* pass on to the driver specific version of this function */
    return port->serial->type->write_room(tty);
}

static int serial_chars_in_buffer(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    dbg("%s = port %d", __func__, port->number);

/* if the device was unplugged then any remaining characters
       fell out of the connector ;) */
    if (port->serial->disconnected)
        return 0;
    /* pass on to the driver specific version of this function */
    return port->serial->type->chars_in_buffer(tty);
}

static void serial_throttle(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    dbg("%s - port %d", __func__, port->number);

WARN_ON(!port->port.count);
    /* pass on to the driver specific version of this function */
    if (port->serial->type->throttle)
        port->serial->type->throttle(tty);
}

static void serial_unthrottle(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
    dbg("%s - port %d", __func__, port->number);

WARN_ON(!port->port.count);
    /* pass on to the driver specific version of this function */
    if (port->serial->type->unthrottle)
        port->serial->type->unthrottle(tty);
}

static int serial_ioctl(struct tty_struct *tty, struct file *file,
                    unsigned int cmd, unsigned long arg)
{
    struct usb_serial_port *port = tty->driver_data;
    int retval = -ENODEV;

dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);

WARN_ON(!port->port.count);

/* pass on to the driver specific version of this function
       if it is available */
    if (port->serial->type->ioctl) {
        retval = port->serial->type->ioctl(tty, file, cmd, arg);
    } else
        retval = -ENOIOCTLCMD;
    return retval;
}

static void serial_set_termios(struct tty_struct *tty, struct ktermios *old)
{
    struct usb_serial_port *port = tty->driver_data;
    dbg("%s - port %d", __func__, port->number);

WARN_ON(!port->port.count);
    /* pass on to the driver specific version of this function
       if it is available */
    if (port->serial->type->set_termios)
        port->serial->type->set_termios(tty, port, old);
    else
        tty_termios_copy_hw(tty->termios, old);
}

static int serial_break(struct tty_struct *tty, int break_state)
{
    struct usb_serial_port *port = tty->driver_data;

dbg("%s - port %d", __func__, port->number);

WARN_ON(!port->port.count);
    /* pass on to the driver specific version of this function
       if it is available */
    if (port->serial->type->break_ctl)
        port->serial->type->break_ctl(tty, break_state);
    return 0;
}

static int serial_proc_show(struct seq_file *m, void *v)
{
    struct usb_serial *serial;
    int i;
    char tmp[40];

dbg("%s", __func__);
    seq_puts(m, "usbserinfo:1.0 driver:2.0\n");
    for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
        serial = usb_serial_get_by_index(i);
        if (serial == NULL)
            continue;

seq_printf(m, "%d:", i);
        if (serial->type->driver.owner)
            seq_printf(m, " module:%s",
                module_name(serial->type->driver.owner));
        seq_printf(m, " name:\"%s\"",
                serial->type->description);
        seq_printf(m, " vendor:%04x product:%04x",
            le16_to_cpu(serial->dev->descriptor.idVendor),
            le16_to_cpu(serial->dev->descriptor.idProduct));
        seq_printf(m, " num_ports:%d", serial->num_ports);
        seq_printf(m, " port:%d", i - serial->minor + 1);
        usb_make_path(serial->dev, tmp, sizeof(tmp));
        seq_printf(m, " path:%s", tmp);

seq_putc(m, '\n');
        usb_serial_put(serial);
        mutex_unlock(&serial->disc_mutex);
    }
    return 0;
}

static int serial_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, serial_proc_show, NULL);
}

static const struct file_operations serial_proc_fops = {
    .owner        = THIS_MODULE,
    .open        = serial_proc_open,
    .read        = seq_read,
    .llseek        = seq_lseek,
    .release    = single_release,
};

static int serial_tiocmget(struct tty_struct *tty, struct file *file)
{
    struct usb_serial_port *port = tty->driver_data;

dbg("%s - port %d", __func__, port->number);

WARN_ON(!port->port.count);
    if (port->serial->type->tiocmget)
        return port->serial->type->tiocmget(tty, file);
    return -EINVAL;
}

static int serial_tiocmset(struct tty_struct *tty, struct file *file,
                unsigned int set, unsigned int clear)
{
    struct usb_serial_port *port = tty->driver_data;

dbg("%s - port %d", __func__, port->number);

WARN_ON(!port->port.count);
    if (port->serial->type->tiocmset)
        return port->serial->type->tiocmset(tty, file, set, clear);
    return -EINVAL;
}

/*
 * We would be calling tty_wakeup here, but unfortunately some line
 * disciplines have an annoying habit of calling tty->write from
 * the write wakeup callback (e.g. n_hdlc.c).
 */
void usb_serial_port_softint(struct usb_serial_port *port)
{
    schedule_work(&port->work);
}
EXPORT_SYMBOL_GPL(usb_serial_port_softint);

static void usb_serial_port_work(struct work_struct *work)
{
    struct usb_serial_port *port =
        container_of(work, struct usb_serial_port, work);
    struct tty_struct *tty;

dbg("%s - port %d", __func__, port->number);

tty = tty_port_tty_get(&port->port);
    if (!tty)
        return;

tty_wakeup(tty);
    tty_kref_put(tty);
}

static void kill_traffic(struct usb_serial_port *port)
{
    usb_kill_urb(port->read_urb);
    usb_kill_urb(port->write_urb);
    /*
     * This is tricky.
     * Some drivers submit the read_urb in the
     * handler for the write_urb or vice versa
     * this order determines the order in which
     * usb_kill_urb() must be used to reliably
     * kill the URBs. As it is unknown here,
     * both orders must be used in turn.
     * The call below is not redundant.
     */
    usb_kill_urb(port->read_urb);
    usb_kill_urb(port->interrupt_in_urb);
    usb_kill_urb(port->interrupt_out_urb);
}

static void port_release(struct device *dev)
{
    struct usb_serial_port *port = to_usb_serial_port(dev);

dbg ("%s - %s", __func__, dev_name(dev));

/*
     * Stop all the traffic before cancelling the work, so that
     * nobody will restart it by calling usb_serial_port_softint.
     */
    kill_traffic(port);
    cancel_work_sync(&port->work);

usb_free_urb(port->read_urb);
    usb_free_urb(port->write_urb);
    usb_free_urb(port->interrupt_in_urb);
    usb_free_urb(port->interrupt_out_urb);
    if (!IS_ERR(port->write_fifo) && port->write_fifo)
        kfifo_free(port->write_fifo);
    kfree(port->bulk_in_buffer);
    kfree(port->bulk_out_buffer);
    kfree(port->interrupt_in_buffer);
    kfree(port->interrupt_out_buffer);
    kfree(port);
}

static struct usb_serial *create_serial(struct usb_device *dev,
                    struct usb_interface *interface,
                    struct usb_serial_driver *driver)
{
    printk(KERN_ALERT "***************************usb_serial *create_serial\n");
    struct usb_serial *serial;

serial = kzalloc(sizeof(*serial), GFP_KERNEL);
    if (!serial) {
        dev_err(&dev->dev, "%s - out of memory\n", __func__);
        return NULL;
    }
    serial->dev = usb_get_dev(dev);
    serial->type = driver;
    serial->interface = interface;
    kref_init(&serial->kref);
    mutex_init(&serial->disc_mutex);
    serial->minor = SERIAL_TTY_NO_MINOR;

return serial;
}

static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf,
                        struct usb_serial_driver *drv)
{
    struct usb_dynid *dynid;
    printk(KERN_ALERT "**************************usb_device_id *match_dynamic_id\n");
    spin_lock(&drv->dynids.lock);
    list_for_each_entry(dynid, &drv->dynids.list, node) {
        if (usb_match_one_id(intf, &dynid->id)) {
            spin_unlock(&drv->dynids.lock);
            return &dynid->id;
        }
    }
    spin_unlock(&drv->dynids.lock);
    return NULL;
}

static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv,
                        struct usb_interface *intf)
{
    const struct usb_device_id *id;
    printk(KERN_ALERT "************************** usb_device_id *get_iface_id\n");
    id = usb_match_id(intf, drv->id_table);
    if (id) {
        dbg("static descriptor matches");
        goto exit;
    }
    id = match_dynamic_id(intf, drv);
    if (id)
        dbg("dynamic descriptor matches");
exit:
    return id;
}

static struct usb_serial_driver *search_serial_device(
                    struct usb_interface *iface)
{
    const struct usb_device_id *id;
    struct usb_serial_driver *drv;
    printk(KERN_ALERT "**************************usb_serial_driver *search_serial_device");
    /* Check if the usb id matches a known device */
    list_for_each_entry(drv, &usb_serial_driver_list, driver_list) {
        id = get_iface_id(drv, iface);
        if (id)
            return drv;
    }

return NULL;
}

static int serial_carrier_raised(struct tty_port *port)
{
    struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
    struct usb_serial_driver *drv = p->serial->type;
    if (drv->carrier_raised)
        return drv->carrier_raised(p);
    /* No carrier control - don't block */
    return 1;    
}

static void serial_dtr_rts(struct tty_port *port, int on)
{
    struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
    struct usb_serial_driver *drv = p->serial->type;
    if (drv->dtr_rts)
        drv->dtr_rts(p, on);
}

static const struct tty_port_operations serial_port_ops = {
    .carrier_raised = serial_carrier_raised,
    .dtr_rts = serial_dtr_rts,
};

int usb_serial_probe(struct usb_interface *interface,
                   const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(interface);
    struct usb_serial *serial = NULL;
    struct usb_serial_port *port;
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
    struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];
    struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
    struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
    struct usb_serial_driver *type = NULL;
    int retval;
    unsigned int minor;
    int buffer_size;
    int i;
    int num_interrupt_in = 0;
    int num_interrupt_out = 0;
    int num_bulk_in = 0;
    int num_bulk_out = 0;
    int num_ports = 0;
    int max_endpoints;

lock_kernel(); /* guard against unloading a serial driver module */
    type = search_serial_device(interface);
    if (!type) {
        unlock_kernel();
        dbg("none matched");
        return -ENODEV;
    }

serial = create_serial(dev, interface, type);
    if (!serial) {
        unlock_kernel();
        dev_err(&interface->dev, "%s - out of memory\n", __func__);
        return -ENOMEM;
    }

/* if this device type has a probe function, call it */
    if (type->probe) {
        const struct usb_device_id *id;

if (!try_module_get(type->driver.owner)) {
            unlock_kernel();
            dev_err(&interface->dev,
                "module get failed, exiting\n");
            kfree(serial);
            return -EIO;
        }

id = get_iface_id(type, interface);
        retval = type->probe(serial, id);
        module_put(type->driver.owner);

if (retval) {
            unlock_kernel();
            dbg("sub driver rejected device");
            kfree(serial);
            return retval;
        }
    }

/* descriptor matches, let's find the endpoints needed */
    /* check out the endpoints */
    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

if (usb_endpoint_is_bulk_in(endpoint)) {
            /* we found a bulk in endpoint */
            dbg("found bulk in on endpoint %d", i);
            bulk_in_endpoint[num_bulk_in] = endpoint;
            ++num_bulk_in;
        }

if (usb_endpoint_is_bulk_out(endpoint)) {
            /* we found a bulk out endpoint */
            dbg("found bulk out on endpoint %d", i);
            bulk_out_endpoint[num_bulk_out] = endpoint;
            ++num_bulk_out;
        }

if (usb_endpoint_is_int_in(endpoint)) {
            /* we found a interrupt in endpoint */
            dbg("found interrupt in on endpoint %d", i);
            interrupt_in_endpoint[num_interrupt_in] = endpoint;
            ++num_interrupt_in;
        }

if (usb_endpoint_is_int_out(endpoint)) {
            /* we found an interrupt out endpoint */
            dbg("found interrupt out on endpoint %d", i);
            interrupt_out_endpoint[num_interrupt_out] = endpoint;
            ++num_interrupt_out;
        }
    }

#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
    /* BEGIN HORRIBLE HACK FOR PL2303 */
    /* this is needed due to the looney way its endpoints are set up */
    if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
         (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
        ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
         (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||
        ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&
         (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID)) ||
        ((le16_to_cpu(dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
         (le16_to_cpu(dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_EF81))) {
        if (interface != dev->actconfig->interface[0]) {
            /* check out the endpoints of the other interface*/
            iface_desc = dev->actconfig->interface[0]->cur_altsetting;
            for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
                if (usb_endpoint_is_int_in(endpoint)) {
                    /* we found a interrupt in endpoint */
                    dbg("found interrupt in for Prolific device on separate interface");
                    interrupt_in_endpoint[num_interrupt_in] = endpoint;
                    ++num_interrupt_in;
                }
            }
        }

/* Now make sure the PL-2303 is configured correctly.
         * If not, give up now and hope this hack will work
         * properly during a later invocation of usb_serial_probe
         */
        if (num_bulk_in == 0 || num_bulk_out == 0) {
            unlock_kernel();
            dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
            kfree(serial);
            return -ENODEV;
        }
    }
    /* END HORRIBLE HACK FOR PL2303 */
#endif

#ifdef CONFIG_USB_SERIAL_GENERIC
    if (type == &usb_serial_generic_device) {
        num_ports = num_bulk_out;
        if (num_ports == 0) {
            unlock_kernel();
            dev_err(&interface->dev,
                "Generic device with no bulk out, not allowed.\n");
            kfree(serial);
            return -EIO;
        }
    }
#endif
    if (!num_ports) {
        /* if this device type has a calc_num_ports function, call it */
        if (type->calc_num_ports) {
            if (!try_module_get(type->driver.owner)) {
                unlock_kernel();
                dev_err(&interface->dev,
                    "module get failed, exiting\n");
                kfree(serial);
                return -EIO;
            }
            num_ports = type->calc_num_ports(serial);
            module_put(type->driver.owner);
        }
        if (!num_ports)
            num_ports = type->num_ports;
    }

serial->num_ports = num_ports;
    serial->num_bulk_in = num_bulk_in;
    serial->num_bulk_out = num_bulk_out;
    serial->num_interrupt_in = num_interrupt_in;
    serial->num_interrupt_out = num_interrupt_out;

/* found all that we need */
    dev_info(&interface->dev, "%s converter detected\n",
            type->description);

/* create our ports, we need as many as the max endpoints */
    /* we don't use num_ports here because some devices have more
       endpoint pairs than ports */
    max_endpoints = max(num_bulk_in, num_bulk_out);
    max_endpoints = max(max_endpoints, num_interrupt_in);
    max_endpoints = max(max_endpoints, num_interrupt_out);
    max_endpoints = max(max_endpoints, (int)serial->num_ports);
    serial->num_port_pointers = max_endpoints;
    unlock_kernel();

dbg("%s - setting up %d port structures for this device",
                        __func__, max_endpoints);
    for (i = 0; i < max_endpoints; ++i) {
        port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
        if (!port)
            goto probe_error;
        tty_port_init(&port->port);
        port->port.ops = &serial_port_ops;
        port->serial = serial;
        spin_lock_init(&port->lock);
        mutex_init(&port->mutex);
        INIT_WORK(&port->work, usb_serial_port_work);
        serial->port[i] = port;
        port->dev.parent = &interface->dev;
        port->dev.driver = NULL;
        port->dev.bus = &usb_serial_bus_type;
        port->dev.release = &port_release;
        device_initialize(&port->dev);
    }

/* set up the endpoint information */
    for (i = 0; i < num_bulk_in; ++i) {
        endpoint = bulk_in_endpoint[i];
        port = serial->port[i];
        port->read_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!port->read_urb) {
            dev_err(&interface->dev, "No free urbs available\n");
            goto probe_error;
        }
        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
        port->bulk_in_size = buffer_size;
        port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
        port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
        if (!port->bulk_in_buffer) {
            dev_err(&interface->dev,
                    "Couldn't allocate bulk_in_buffer\n");
            goto probe_error;
        }
        usb_fill_bulk_urb(port->read_urb, dev,
                usb_rcvbulkpipe(dev,
                        endpoint->bEndpointAddress),
                port->bulk_in_buffer, buffer_size,
                serial->type->read_bulk_callback, port);
    }

for (i = 0; i < num_bulk_out; ++i) {
        endpoint = bulk_out_endpoint[i];
        port = serial->port[i];
        port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!port->write_urb) {
            dev_err(&interface->dev, "No free urbs available\n");
            goto probe_error;
        }
        port->write_fifo = kfifo_alloc(PAGE_SIZE, GFP_KERNEL,
            &port->lock);
        if (IS_ERR(port->write_fifo))
            goto probe_error;
        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
        port->bulk_out_size = buffer_size;
        port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
        port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
        if (!port->bulk_out_buffer) {
            dev_err(&interface->dev,
                    "Couldn't allocate bulk_out_buffer\n");
            goto probe_error;
        }
        usb_fill_bulk_urb(port->write_urb, dev,
                usb_sndbulkpipe(dev,
                    endpoint->bEndpointAddress),
                port->bulk_out_buffer, buffer_size,
                serial->type->write_bulk_callback, port);
    }

if (serial->type->read_int_callback) {
        for (i = 0; i < num_interrupt_in; ++i) {
            endpoint = interrupt_in_endpoint[i];
            port = serial->port[i];
            port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
            if (!port->interrupt_in_urb) {
                dev_err(&interface->dev,
                        "No free urbs available\n");
                goto probe_error;
            }
            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
            port->interrupt_in_endpointAddress =
                        endpoint->bEndpointAddress;
            port->interrupt_in_buffer = kmalloc(buffer_size,
                                GFP_KERNEL);
            if (!port->interrupt_in_buffer) {
                dev_err(&interface->dev,
                    "Couldn't allocate interrupt_in_buffer\n");
                goto probe_error;
            }
            usb_fill_int_urb(port->interrupt_in_urb, dev,
                usb_rcvintpipe(dev,
                        endpoint->bEndpointAddress),
                port->interrupt_in_buffer, buffer_size,
                serial->type->read_int_callback, port,
                endpoint->bInterval);
        }
    } else if (num_interrupt_in) {
        dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined");
    }

if (serial->type->write_int_callback) {
        for (i = 0; i < num_interrupt_out; ++i) {
            endpoint = interrupt_out_endpoint[i];
            port = serial->port[i];
            port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
            if (!port->interrupt_out_urb) {
                dev_err(&interface->dev,
                        "No free urbs available\n");
                goto probe_error;
            }
            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
            port->interrupt_out_size = buffer_size;
            port->interrupt_out_endpointAddress =
                        endpoint->bEndpointAddress;
            port->interrupt_out_buffer = kmalloc(buffer_size,
                                GFP_KERNEL);
            if (!port->interrupt_out_buffer) {
                dev_err(&interface->dev,
                  "Couldn't allocate interrupt_out_buffer\n");
                goto probe_error;
            }
            usb_fill_int_urb(port->interrupt_out_urb, dev,
                usb_sndintpipe(dev,
                          endpoint->bEndpointAddress),
                port->interrupt_out_buffer, buffer_size,
                serial->type->write_int_callback, port,
                endpoint->bInterval);
        }
    } else if (num_interrupt_out) {
        dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined");
    }

/* if this device type has an attach function, call it */
    if (type->attach) {
        if (!try_module_get(type->driver.owner)) {
            dev_err(&interface->dev,
                    "module get failed, exiting\n");
            goto probe_error;
        }
        retval = type->attach(serial);
        module_put(type->driver.owner);
        if (retval < 0)
            goto probe_error;
        serial->attached = 1;
        if (retval > 0) {
            /* quietly accept this device, but don't bind to a
               serial port as it's about to disappear */
            serial->num_ports = 0;
            goto exit;
        }
    } else {
        serial->attached = 1;
    }

if (get_free_serial(serial, num_ports, &minor) == NULL) {
        dev_err(&interface->dev, "No more free serial devices\n");
        goto probe_error;
    }
    serial->minor = minor;

/* register all of the individual ports with the driver core */
    for (i = 0; i < num_ports; ++i) {
        port = serial->port[i];
        dev_set_name(&port->dev, "ttyUSB%d", port->number);
        dbg ("%s - registering %s", __func__, dev_name(&port->dev));
        port->dev_state = PORT_REGISTERING;
        retval = device_add(&port->dev);
        if (retval) {
            dev_err(&port->dev, "Error registering port device, "
                "continuing\n");
            port->dev_state = PORT_UNREGISTERED;
        } else {
            port->dev_state = PORT_REGISTERED;
        }
    }

usb_serial_console_init(debug, minor);

exit:
    /* success */
    usb_set_intfdata(interface, serial);
    return 0;

probe_error:
    usb_serial_put(serial);
    return -EIO;
}
EXPORT_SYMBOL_GPL(usb_serial_probe);

void usb_serial_disconnect(struct usb_interface *interface)
{
    int i;
    struct usb_serial *serial = usb_get_intfdata(interface);
    struct device *dev = &interface->dev;
    struct usb_serial_port *port;

usb_serial_console_disconnect(serial);
    dbg("%s", __func__);

mutex_lock(&serial->disc_mutex);
    usb_set_intfdata(interface, NULL);
    /* must set a flag, to signal subdrivers */
    serial->disconnected = 1;
    mutex_unlock(&serial->disc_mutex);

for (i = 0; i < serial->num_ports; ++i) {
        port = serial->port[i];
        if (port) {
            struct tty_struct *tty = tty_port_tty_get(&port->port);
            if (tty) {
                tty_vhangup(tty);
                tty_kref_put(tty);
            }
            kill_traffic(port);
            cancel_work_sync(&port->work);
            if (port->dev_state == PORT_REGISTERED) {

/* Make sure the port is bound so that the
                 * driver's port_remove method is called.
                 */
                if (!port->dev.driver) {
                    int rc;

port->dev.driver =
                            &serial->type->driver;
                    rc = device_bind_driver(&port->dev);
                }
                port->dev_state = PORT_UNREGISTERING;
                device_del(&port->dev);
                port->dev_state = PORT_UNREGISTERED;
            }
        }
    }
    serial->type->disconnect(serial);

/* let the last holder of this object cause it to be cleaned up */
    usb_serial_put(serial);
    dev_info(dev, "device disconnected\n");
}
EXPORT_SYMBOL_GPL(usb_serial_disconnect);

int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
{
    struct usb_serial *serial = usb_get_intfdata(intf);
    struct usb_serial_port *port;
    int i, r = 0;

serial->suspending = 1;

if (serial->type->suspend) {
        r = serial->type->suspend(serial, message);
        if (r < 0) {
            serial->suspending = 0;
            goto err_out;
        }
    }

for (i = 0; i < serial->num_ports; ++i) {
        port = serial->port[i];
        if (port)
            kill_traffic(port);
    }

err_out:
    return r;
}
EXPORT_SYMBOL(usb_serial_suspend);

int usb_serial_resume(struct usb_interface *intf)
{
    struct usb_serial *serial = usb_get_intfdata(intf);
    int rv;

serial->suspending = 0;
    if (serial->type->resume)
        rv = serial->type->resume(serial);
    else
        rv = usb_serial_generic_resume(serial);

return rv;
}
EXPORT_SYMBOL(usb_serial_resume);

static const struct tty_operations serial_ops = {
    .open =            serial_open,
    .close =        serial_close,
    .write =        serial_write,
    .hangup =         serial_hangup,
    .write_room =        serial_write_room,
    .ioctl =        serial_ioctl,
    .set_termios =        serial_set_termios,
    .throttle =        serial_throttle,
    .unthrottle =        serial_unthrottle,
    .break_ctl =        serial_break,
    .chars_in_buffer =    serial_chars_in_buffer,
    .tiocmget =        serial_tiocmget,
    .tiocmset =        serial_tiocmset,
    .cleanup =         serial_cleanup,
    .install =         serial_install,
    .proc_fops =        &serial_proc_fops,
};

struct tty_driver *usb_serial_tty_driver;

static int __init usb_serial_init(void)
{
    int i;
    int result;

usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
    if (!usb_serial_tty_driver)
        return -ENOMEM;

/* Initialize our global data */
    for (i = 0; i < SERIAL_TTY_MINORS; ++i)
        serial_table[i] = NULL;

result = bus_register(&usb_serial_bus_type);
    if (result) {
        printk(KERN_ERR "usb-serial: %s - registering bus driver "
               "failed\n", __func__);
        goto exit_bus;
    }

usb_serial_tty_driver->owner = THIS_MODULE;
    usb_serial_tty_driver->driver_name = "usbserial";
    usb_serial_tty_driver->name =     "ttyUSB";
    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
    usb_serial_tty_driver->minor_start = 0;
    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |
                        TTY_DRIVER_DYNAMIC_DEV;
    usb_serial_tty_driver->init_termios = tty_std_termios;
    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
                            | HUPCL | CLOCAL;
    usb_serial_tty_driver->init_termios.c_ispeed = 9600;
    usb_serial_tty_driver->init_termios.c_ospeed = 9600;
    tty_set_operations(usb_serial_tty_driver, &serial_ops);
    result = tty_register_driver(usb_serial_tty_driver);
    if (result) {
        printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",
               __func__);
        goto exit_reg_driver;
    }

/* register the USB driver */
    result = usb_register(&usb_serial_driver);
    if (result < 0) {
        printk(KERN_ERR "usb-serial: %s - usb_register failed\n",
               __func__);
        goto exit_tty;
    }

/* register the generic driver, if we should */
    result = usb_serial_generic_register(debug);
    if (result < 0) {
        printk(KERN_ERR "usb-serial: %s - registering generic "
               "driver failed\n", __func__);
        goto exit_generic;
    }

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

return result;

exit_generic:
    usb_deregister(&usb_serial_driver);

exit_tty:
    tty_unregister_driver(usb_serial_tty_driver);

exit_reg_driver:
    bus_unregister(&usb_serial_bus_type);

exit_bus:
    printk(KERN_ERR "usb-serial: %s - returning with error %d\n",
           __func__, result);
    put_tty_driver(usb_serial_tty_driver);
    return result;
}

static void __exit usb_serial_exit(void)
{
    usb_serial_console_exit();

usb_serial_generic_deregister();

usb_deregister(&usb_serial_driver);
    tty_unregister_driver(usb_serial_tty_driver);
    put_tty_driver(usb_serial_tty_driver);
    bus_unregister(&usb_serial_bus_type);
}

module_init(usb_serial_init);
module_exit(usb_serial_exit);

#define set_to_generic_if_null(type, function)                \
    do {                                \
        if (!type->function) {                    \
            type->function = usb_serial_generic_##function;    \
            dbg("Had to override the " #function        \
                " usb serial operation with the generic one.");\
            }                        \
    } while (0)

static void fixup_generic(struct usb_serial_driver *device)
{
    set_to_generic_if_null(device, open);
    set_to_generic_if_null(device, write);
    set_to_generic_if_null(device, close);
    set_to_generic_if_null(device, write_room);
    set_to_generic_if_null(device, chars_in_buffer);
    set_to_generic_if_null(device, read_bulk_callback);
    set_to_generic_if_null(device, write_bulk_callback);
    set_to_generic_if_null(device, disconnect);
    set_to_generic_if_null(device, release);
}

int usb_serial_register(struct usb_serial_driver *driver)
{
    /* must be called with BKL held */
    int retval;

if (usb_disabled())
        return -ENODEV;

fixup_generic(driver);

if (!driver->description)
        driver->description = driver->driver.name;

/* Add this device to our list of devices */
    list_add(&driver->driver_list, &usb_serial_driver_list);

retval = usb_serial_bus_register(driver);
    if (retval) {
        printk(KERN_ERR "usb-serial: problem %d when registering "
               "driver %s\n", retval, driver->description);
        list_del(&driver->driver_list);
    } else
        printk(KERN_INFO "USB Serial support registered for %s\n",
                        driver->description);

return retval;
}
EXPORT_SYMBOL_GPL(usb_serial_register);

void usb_serial_deregister(struct usb_serial_driver *device)
{
    /* must be called with BKL held */
    printk(KERN_INFO "USB Serial deregistering driver %s\n",
           device->description);
    list_del(&device->driver_list);
    usb_serial_bus_deregister(device);
}
EXPORT_SYMBOL_GPL(usb_serial_deregister);

/* Module information */
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");

文件七:

Makefile

# Makefile 2.6
obj-m += usb-serial.o option.o pl2303.o
KDIR:=/lib/modules/$(shell uname -r)/build
#PWD=$(shell pwd)
all:
    make -C $(KDIR) M=$(PWD) modules
clean:
    make -C $(KDIR) M=$(PWD) clean

依次运行:

sudo insmod ./usb-serial.ko

sudo insmod ./option.ko

sudo insmod ./pl2303.ko

自己写linux usb转串口 驱动相关推荐

  1. linux 下串口转usb不能发送数据包,Linux ,USB转串口驱动,没法读到数据

    Linux ,USB转串口驱动,无法读到数据 usb 1-1.1: new full-speed USB device number 5 using ehci-pci usb 1-1.1: New U ...

  2. linux系统查看usb转串口驱动,Linux usb转串口驱动

    Linux USB转串口驱动程序: /* * USB Serial Converter driver */ #include #include #include #include #include # ...

  3. Linux Ubuntu18.04系统 USB转串口驱动安装,查看串口号

    When you plugin your USB-UART converter, and run "> ls /dev/tty*", if you don't see the ...

  4. linux下usb转串口驱动分析

    linux下usb转串口驱动分析 分类: linux driver 2012-06-08 15:11 456人阅读 评论(0) 收藏 举报 linux struct interface returni ...

  5. Linux环境下使用 USB转串口驱动(二)

    minicom是linux下串口通信的软件,它的使用完全依靠键盘的操作,虽然没有"超级终端"那么易用,但是使用习惯之后读者将会体会到它的高效与便利,下面将讲解minicom的安装和 ...

  6. linux装pl2303驱动下载,Linux下安装USB转串口驱动(PL2303)

    主机:Gentoo Linux 11.2 内核版本:Linux 3.0.6 以前在Ubuntu下USB转串口驱动已经编译进内核,而编译的Gentoo内核没有编译进去,这里将内核中PL2303驱动 co ...

  7. Linux下安装USB转串口驱动(PL2303)

    分类: 嵌入式ARM+Linux 2012-01-11 22:26  2077人阅读  评论(0)  收藏  举报 主机:Gentoo Linux 11.2 内核版本:Linux 3.0.6 原创作品 ...

  8. USB转串口驱动分析(一)

    之前追踪代码用的grep命令效率太低了,所以这次下载C代码阅读跳转利器ctags.cscope用于分析代码 因为用的是Centos6.7所以需要用到yum install安装软件 [wuyujun@w ...

  9. 银河麒麟 安装PL2303GC USB转串口驱动

    银河麒麟系统,外接一个设备,连接线是USB转串口线,型号是PL2303,直接接到电脑上,系统没有自动加载这个设备,理论上来说应该识别为/dev/ttyUSB0. 进入USB转串口驱动的目录查看: /u ...

最新文章

  1. [ BZOJ 2456 ] Mode
  2. 民生银行IT运维架构管理可视化实践
  3. K8s 学习者绝对不能错过的最全知识图谱(内含 58个知识点链接)
  4. 搞懂function(*args,**kwargs)
  5. 高等数学上-赵立军-北京大学出版社-题解-练习2.7
  6. [html] HTML全局属性(global attribute)有哪些(包含H5)?
  7. Spring 事务初始化源码分析
  8. RabbitMQ入门:路由(Routing)
  9. 微网站|h5弹窗|手机网站 html5 弹窗、弹层、提示框、加载条
  10. 进程与线程的区别(网络摘抄)
  11. VSTT Rosario CTP
  12. Android 开发即时聊天工具 YQ 《更新》(源码下载)
  13. ERP项目组成员及各项目组职责
  14. 二乘二(二阶)魔方教程,看完教程拼不出来我给你钱!
  15. bootstrap—预定义样式风格
  16. (小脚本) (python) 批量修改文件后缀名
  17. java word插件开发_java生成word的几种方案
  18. 无线降噪蓝牙耳机对比:NANK南卡和苹果AirPods Pro哪个好用?
  19. 做题遇到的trick和想法
  20. android 开发中遇到的错误 app:mergeDebugResources

热门文章

  1. 清华毕业大牛教你涨薪5K的JVM调优骚操作是什么!如何在简历写上熟悉(精通)JVM调优,有过线上调优经验!
  2. YSlow工具相关的使用说明(转了再转)
  3. java中的byte有什么作用?
  4. Python零基础速成班-第14讲-Python处理Excel和Word,使用openpyxl和docx包详解,图表入门
  5. Vue.js入门教程(适合初学者)
  6. PHP面向对象(OOP)编程完全教程:1.什么是面向对象?
  7. 分享一个下载电子图书,文献的好网站
  8. sql复购率计算多种方法
  9. S7-1200控制S120变频器
  10. 事无巨细说Java之---Java 关键字