linux/drivers/usb/serial/qcserial.c
<<
>>
Prefs
   1/*
   2 * Qualcomm Serial USB driver
   3 *
   4 *      Copyright (c) 2008 QUALCOMM Incorporated.
   5 *      Copyright (c) 2009 Greg Kroah-Hartman <gregkh@suse.de>
   6 *      Copyright (c) 2009 Novell Inc.
   7 *
   8 *      This program is free software; you can redistribute it and/or
   9 *      modify it under the terms of the GNU General Public License version
  10 *      2 as published by the Free Software Foundation.
  11 *
  12 */
  13
  14#include <linux/tty.h>
  15#include <linux/tty_flip.h>
  16#include <linux/module.h>
  17#include <linux/usb.h>
  18#include <linux/usb/serial.h>
  19#include <linux/slab.h>
  20#include "usb-wwan.h"
  21
  22#define DRIVER_AUTHOR "Qualcomm Inc"
  23#define DRIVER_DESC "Qualcomm USB Serial driver"
  24
  25static int debug;
  26
  27static const struct usb_device_id id_table[] = {
  28        {USB_DEVICE(0x05c6, 0x9211)},   /* Acer Gobi QDL device */
  29        {USB_DEVICE(0x05c6, 0x9212)},   /* Acer Gobi Modem Device */
  30        {USB_DEVICE(0x03f0, 0x1f1d)},   /* HP un2400 Gobi Modem Device */
  31        {USB_DEVICE(0x03f0, 0x201d)},   /* HP un2400 Gobi QDL Device */
  32        {USB_DEVICE(0x03f0, 0x371d)},   /* HP un2430 Mobile Broadband Module */
  33        {USB_DEVICE(0x04da, 0x250d)},   /* Panasonic Gobi Modem device */
  34        {USB_DEVICE(0x04da, 0x250c)},   /* Panasonic Gobi QDL device */
  35        {USB_DEVICE(0x413c, 0x8172)},   /* Dell Gobi Modem device */
  36        {USB_DEVICE(0x413c, 0x8171)},   /* Dell Gobi QDL device */
  37        {USB_DEVICE(0x1410, 0xa001)},   /* Novatel Gobi Modem device */
  38        {USB_DEVICE(0x1410, 0xa008)},   /* Novatel Gobi QDL device */
  39        {USB_DEVICE(0x0b05, 0x1776)},   /* Asus Gobi Modem device */
  40        {USB_DEVICE(0x0b05, 0x1774)},   /* Asus Gobi QDL device */
  41        {USB_DEVICE(0x19d2, 0xfff3)},   /* ONDA Gobi Modem device */
  42        {USB_DEVICE(0x19d2, 0xfff2)},   /* ONDA Gobi QDL device */
  43        {USB_DEVICE(0x1557, 0x0a80)},   /* OQO Gobi QDL device */
  44        {USB_DEVICE(0x05c6, 0x9001)},   /* Generic Gobi Modem device */
  45        {USB_DEVICE(0x05c6, 0x9002)},   /* Generic Gobi Modem device */
  46        {USB_DEVICE(0x05c6, 0x9202)},   /* Generic Gobi Modem device */
  47        {USB_DEVICE(0x05c6, 0x9203)},   /* Generic Gobi Modem device */
  48        {USB_DEVICE(0x05c6, 0x9222)},   /* Generic Gobi Modem device */
  49        {USB_DEVICE(0x05c6, 0x9008)},   /* Generic Gobi QDL device */
  50        {USB_DEVICE(0x05c6, 0x9009)},   /* Generic Gobi Modem device */
  51        {USB_DEVICE(0x05c6, 0x9201)},   /* Generic Gobi QDL device */
  52        {USB_DEVICE(0x05c6, 0x9221)},   /* Generic Gobi QDL device */
  53        {USB_DEVICE(0x05c6, 0x9231)},   /* Generic Gobi QDL device */
  54        {USB_DEVICE(0x1f45, 0x0001)},   /* Unknown Gobi QDL device */
  55        {USB_DEVICE(0x413c, 0x8185)},   /* Dell Gobi 2000 QDL device (N0218, VU936) */
  56        {USB_DEVICE(0x413c, 0x8186)},   /* Dell Gobi 2000 Modem device (N0218, VU936) */
  57        {USB_DEVICE(0x05c6, 0x9208)},   /* Generic Gobi 2000 QDL device */
  58        {USB_DEVICE(0x05c6, 0x920b)},   /* Generic Gobi 2000 Modem device */
  59        {USB_DEVICE(0x05c6, 0x9224)},   /* Sony Gobi 2000 QDL device (N0279, VU730) */
  60        {USB_DEVICE(0x05c6, 0x9225)},   /* Sony Gobi 2000 Modem device (N0279, VU730) */
  61        {USB_DEVICE(0x05c6, 0x9244)},   /* Samsung Gobi 2000 QDL device (VL176) */
  62        {USB_DEVICE(0x05c6, 0x9245)},   /* Samsung Gobi 2000 Modem device (VL176) */
  63        {USB_DEVICE(0x03f0, 0x241d)},   /* HP Gobi 2000 QDL device (VP412) */
  64        {USB_DEVICE(0x03f0, 0x251d)},   /* HP Gobi 2000 Modem device (VP412) */
  65        {USB_DEVICE(0x05c6, 0x9214)},   /* Acer Gobi 2000 QDL device (VP413) */
  66        {USB_DEVICE(0x05c6, 0x9215)},   /* Acer Gobi 2000 Modem device (VP413) */
  67        {USB_DEVICE(0x05c6, 0x9264)},   /* Asus Gobi 2000 QDL device (VR305) */
  68        {USB_DEVICE(0x05c6, 0x9265)},   /* Asus Gobi 2000 Modem device (VR305) */
  69        {USB_DEVICE(0x05c6, 0x9234)},   /* Top Global Gobi 2000 QDL device (VR306) */
  70        {USB_DEVICE(0x05c6, 0x9235)},   /* Top Global Gobi 2000 Modem device (VR306) */
  71        {USB_DEVICE(0x05c6, 0x9274)},   /* iRex Technologies Gobi 2000 QDL device (VR307) */
  72        {USB_DEVICE(0x05c6, 0x9275)},   /* iRex Technologies Gobi 2000 Modem device (VR307) */
  73        {USB_DEVICE(0x1199, 0x9000)},   /* Sierra Wireless Gobi 2000 QDL device (VT773) */
  74        {USB_DEVICE(0x1199, 0x9001)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  75        {USB_DEVICE(0x1199, 0x9002)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  76        {USB_DEVICE(0x1199, 0x9003)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  77        {USB_DEVICE(0x1199, 0x9004)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  78        {USB_DEVICE(0x1199, 0x9005)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  79        {USB_DEVICE(0x1199, 0x9006)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  80        {USB_DEVICE(0x1199, 0x9007)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  81        {USB_DEVICE(0x1199, 0x9008)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  82        {USB_DEVICE(0x1199, 0x9009)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  83        {USB_DEVICE(0x1199, 0x900a)},   /* Sierra Wireless Gobi 2000 Modem device (VT773) */
  84        {USB_DEVICE(0x1199, 0x9011)},   /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
  85        {USB_DEVICE(0x16d8, 0x8001)},   /* CMDTech Gobi 2000 QDL device (VU922) */
  86        {USB_DEVICE(0x16d8, 0x8002)},   /* CMDTech Gobi 2000 Modem device (VU922) */
  87        {USB_DEVICE(0x05c6, 0x9204)},   /* Gobi 2000 QDL device */
  88        {USB_DEVICE(0x05c6, 0x9205)},   /* Gobi 2000 Modem device */
  89        {USB_DEVICE(0x1199, 0x9013)},   /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
  90        { }                             /* Terminating entry */
  91};
  92MODULE_DEVICE_TABLE(usb, id_table);
  93
  94static struct usb_driver qcdriver = {
  95        .name                   = "qcserial",
  96        .probe                  = usb_serial_probe,
  97        .disconnect             = usb_serial_disconnect,
  98        .id_table               = id_table,
  99        .suspend                = usb_serial_suspend,
 100        .resume                 = usb_serial_resume,
 101        .supports_autosuspend   = true,
 102};
 103
 104static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
 105{
 106        struct usb_wwan_intf_private *data;
 107        struct usb_host_interface *intf = serial->interface->cur_altsetting;
 108        int retval = -ENODEV;
 109        __u8 nintf;
 110        __u8 ifnum;
 111
 112        dbg("%s", __func__);
 113
 114        nintf = serial->dev->actconfig->desc.bNumInterfaces;
 115        dbg("Num Interfaces = %d", nintf);
 116        ifnum = intf->desc.bInterfaceNumber;
 117        dbg("This Interface = %d", ifnum);
 118
 119        data = kzalloc(sizeof(struct usb_wwan_intf_private),
 120                                         GFP_KERNEL);
 121        if (!data)
 122                return -ENOMEM;
 123
 124        spin_lock_init(&data->susp_lock);
 125
 126        usb_enable_autosuspend(serial->dev);
 127
 128        switch (nintf) {
 129        case 1:
 130                /* QDL mode */
 131                /* Gobi 2000 has a single altsetting, older ones have two */
 132                if (serial->interface->num_altsetting == 2)
 133                        intf = &serial->interface->altsetting[1];
 134                else if (serial->interface->num_altsetting > 2)
 135                        break;
 136
 137                if (intf->desc.bNumEndpoints == 2 &&
 138                    usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
 139                    usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
 140                        dbg("QDL port found");
 141
 142                        if (serial->interface->num_altsetting == 1) {
 143                                retval = 0; /* Success */
 144                                break;
 145                        }
 146
 147                        retval = usb_set_interface(serial->dev, ifnum, 1);
 148                        if (retval < 0) {
 149                                dev_err(&serial->dev->dev,
 150                                        "Could not set interface, error %d\n",
 151                                        retval);
 152                                retval = -ENODEV;
 153                                kfree(data);
 154                        }
 155                }
 156                break;
 157
 158        case 3:
 159        case 4:
 160                /* Composite mode */
 161                /* ifnum == 0 is a broadband network adapter */
 162                if (ifnum == 1) {
 163                        /*
 164                         * Diagnostics Monitor (serial line 9600 8N1)
 165                         * Qualcomm DM protocol
 166                         * use "libqcdm" (ModemManager) for communication
 167                         */
 168                        dbg("Diagnostics Monitor found");
 169                        retval = usb_set_interface(serial->dev, ifnum, 0);
 170                        if (retval < 0) {
 171                                dev_err(&serial->dev->dev,
 172                                        "Could not set interface, error %d\n",
 173                                        retval);
 174                                retval = -ENODEV;
 175                                kfree(data);
 176                        }
 177                } else if (ifnum == 2) {
 178                        dbg("Modem port found");
 179                        retval = usb_set_interface(serial->dev, ifnum, 0);
 180                        if (retval < 0) {
 181                                dev_err(&serial->dev->dev,
 182                                        "Could not set interface, error %d\n",
 183                                        retval);
 184                                retval = -ENODEV;
 185                                kfree(data);
 186                        }
 187                } else if (ifnum==3) {
 188                        /*
 189                         * NMEA (serial line 9600 8N1)
 190                         * # echo "\$GPS_START" > /dev/ttyUSBx
 191                         * # echo "\$GPS_STOP"  > /dev/ttyUSBx
 192                         */
 193                        dbg("NMEA GPS interface found");
 194                        retval = usb_set_interface(serial->dev, ifnum, 0);
 195                        if (retval < 0) {
 196                                dev_err(&serial->dev->dev,
 197                                        "Could not set interface, error %d\n",
 198                                        retval);
 199                                retval = -ENODEV;
 200                                kfree(data);
 201                        }
 202                }
 203                break;
 204
 205        default:
 206                dev_err(&serial->dev->dev,
 207                        "unknown number of interfaces: %d\n", nintf);
 208                kfree(data);
 209                retval = -ENODEV;
 210        }
 211
 212        /* Set serial->private if not returning -ENODEV */
 213        if (retval != -ENODEV)
 214                usb_set_serial_data(serial, data);
 215        return retval;
 216}
 217
 218static void qc_release(struct usb_serial *serial)
 219{
 220        struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
 221
 222        dbg("%s", __func__);
 223
 224        /* Call usb_wwan release & free the private data allocated in qcprobe */
 225        usb_wwan_release(serial);
 226        usb_set_serial_data(serial, NULL);
 227        kfree(priv);
 228}
 229
 230static struct usb_serial_driver qcdevice = {
 231        .driver = {
 232                .owner     = THIS_MODULE,
 233                .name      = "qcserial",
 234        },
 235        .description         = "Qualcomm USB modem",
 236        .id_table            = id_table,
 237        .usb_driver          = &qcdriver,
 238        .num_ports           = 1,
 239        .probe               = qcprobe,
 240        .open                = usb_wwan_open,
 241        .close               = usb_wwan_close,
 242        .write               = usb_wwan_write,
 243        .write_room          = usb_wwan_write_room,
 244        .chars_in_buffer     = usb_wwan_chars_in_buffer,
 245        .attach              = usb_wwan_startup,
 246        .disconnect          = usb_wwan_disconnect,
 247        .release             = qc_release,
 248#ifdef CONFIG_PM
 249        .suspend             = usb_wwan_suspend,
 250        .resume              = usb_wwan_resume,
 251#endif
 252};
 253
 254static int __init qcinit(void)
 255{
 256        int retval;
 257
 258        retval = usb_serial_register(&qcdevice);
 259        if (retval)
 260                return retval;
 261
 262        retval = usb_register(&qcdriver);
 263        if (retval) {
 264                usb_serial_deregister(&qcdevice);
 265                return retval;
 266        }
 267
 268        return 0;
 269}
 270
 271static void __exit qcexit(void)
 272{
 273        usb_deregister(&qcdriver);
 274        usb_serial_deregister(&qcdevice);
 275}
 276
 277module_init(qcinit);
 278module_exit(qcexit);
 279
 280MODULE_AUTHOR(DRIVER_AUTHOR);
 281MODULE_DESCRIPTION(DRIVER_DESC);
 282MODULE_LICENSE("GPL v2");
 283
 284module_param(debug, bool, S_IRUGO | S_IWUSR);
 285MODULE_PARM_DESC(debug, "Debug enabled or not");
 286
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.