自己写linux usb转串口 驱动

这篇博客详细介绍了如何编写Linux USB转串口驱动的过程,包括`bus.c`、`generic.c`、`option.c`、`pl2303.c`等多个关键文件的代码实现。内容涵盖设备匹配、设备探测、设备移除等核心功能,并提供了用于模块编译和加载的Makefile。通过阅读和理解这些代码,读者可以学习到USB串口驱动的开发知识。
摘要由CSDN通过智能技术生成

在本机运行可以直接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_552
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值