These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / usb / serial / qcserial.c
index ebcec8c..1bc6089 100644 (file)
@@ -22,6 +22,8 @@
 #define DRIVER_AUTHOR "Qualcomm Inc"
 #define DRIVER_DESC "Qualcomm USB Serial driver"
 
+#define QUECTEL_EC20_PID       0x9215
+
 /* standard device layouts supported by this driver */
 enum qcserial_layouts {
        QCSERIAL_G2K = 0,       /* Gobi 2000 */
@@ -143,9 +145,11 @@ static const struct usb_device_id id_table[] = {
        {DEVICE_SWI(0x0f3d, 0x68a2)},   /* Sierra Wireless MC7700 */
        {DEVICE_SWI(0x114f, 0x68a2)},   /* Sierra Wireless MC7750 */
        {DEVICE_SWI(0x1199, 0x68a2)},   /* Sierra Wireless MC7710 */
+       {DEVICE_SWI(0x1199, 0x68c0)},   /* Sierra Wireless MC7304/MC7354 */
        {DEVICE_SWI(0x1199, 0x901c)},   /* Sierra Wireless EM7700 */
        {DEVICE_SWI(0x1199, 0x901f)},   /* Sierra Wireless EM7355 */
        {DEVICE_SWI(0x1199, 0x9040)},   /* Sierra Wireless Modem */
+       {DEVICE_SWI(0x1199, 0x9041)},   /* Sierra Wireless MC7305/MC7355 */
        {DEVICE_SWI(0x1199, 0x9051)},   /* Netgear AirCard 340U */
        {DEVICE_SWI(0x1199, 0x9053)},   /* Sierra Wireless Modem */
        {DEVICE_SWI(0x1199, 0x9054)},   /* Sierra Wireless Modem */
@@ -153,12 +157,17 @@ static const struct usb_device_id id_table[] = {
        {DEVICE_SWI(0x1199, 0x9056)},   /* Sierra Wireless Modem */
        {DEVICE_SWI(0x1199, 0x9060)},   /* Sierra Wireless Modem */
        {DEVICE_SWI(0x1199, 0x9061)},   /* Sierra Wireless Modem */
+       {DEVICE_SWI(0x1199, 0x9070)},   /* Sierra Wireless MC74xx */
+       {DEVICE_SWI(0x1199, 0x9071)},   /* Sierra Wireless MC74xx */
+       {DEVICE_SWI(0x1199, 0x9078)},   /* Sierra Wireless EM74xx */
+       {DEVICE_SWI(0x1199, 0x9079)},   /* Sierra Wireless EM74xx */
        {DEVICE_SWI(0x413c, 0x81a2)},   /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
        {DEVICE_SWI(0x413c, 0x81a3)},   /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
        {DEVICE_SWI(0x413c, 0x81a4)},   /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
        {DEVICE_SWI(0x413c, 0x81a8)},   /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
        {DEVICE_SWI(0x413c, 0x81a9)},   /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
        {DEVICE_SWI(0x413c, 0x81b1)},   /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
+       {DEVICE_SWI(0x413c, 0x81b3)},   /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
 
        /* Huawei devices */
        {DEVICE_HWI(0x03f0, 0x581d)},   /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
@@ -167,6 +176,38 @@ static const struct usb_device_id id_table[] = {
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 
+static int handle_quectel_ec20(struct device *dev, int ifnum)
+{
+       int altsetting = 0;
+
+       /*
+        * Quectel EC20 Mini PCIe LTE module layout:
+        * 0: DM/DIAG (use libqcdm from ModemManager for communication)
+        * 1: NMEA
+        * 2: AT-capable modem port
+        * 3: Modem interface
+        * 4: NDIS
+        */
+       switch (ifnum) {
+       case 0:
+               dev_dbg(dev, "Quectel EC20 DM/DIAG interface found\n");
+               break;
+       case 1:
+               dev_dbg(dev, "Quectel EC20 NMEA GPS interface found\n");
+               break;
+       case 2:
+       case 3:
+               dev_dbg(dev, "Quectel EC20 Modem port found\n");
+               break;
+       case 4:
+               /* Don't claim the QMI/net interface */
+               altsetting = -1;
+               break;
+       }
+
+       return altsetting;
+}
+
 static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
 {
        struct usb_host_interface *intf = serial->interface->cur_altsetting;
@@ -175,6 +216,11 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
        __u8 nintf;
        __u8 ifnum;
        int altsetting = -1;
+       bool sendsetup = false;
+
+       /* we only support vendor specific functions */
+       if (intf->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
+               goto done;
 
        nintf = serial->dev->actconfig->desc.bNumInterfaces;
        dev_dbg(dev, "Num Interfaces = %d\n", nintf);
@@ -235,6 +281,12 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
                        altsetting = -1;
                break;
        case QCSERIAL_G2K:
+               /* handle non-standard layouts */
+               if (nintf == 5 && id->idProduct == QUECTEL_EC20_PID) {
+                       altsetting = handle_quectel_ec20(dev, ifnum);
+                       goto done;
+               }
+
                /*
                 * Gobi 2K+ USB layout:
                 * 0: QMI/net
@@ -286,6 +338,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
                        break;
                case 3:
                        dev_dbg(dev, "Modem port found\n");
+                       sendsetup = true;
                        break;
                default:
                        /* don't claim any unsupported interface */
@@ -295,29 +348,39 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
                break;
        case QCSERIAL_HWI:
                /*
-                * Huawei layout:
-                * 0: AT-capable modem port
-                * 1: DM/DIAG
-                * 2: AT-capable modem port
-                * 3: CCID-compatible PCSC interface
-                * 4: QMI/net
-                * 5: NMEA
+                * Huawei devices map functions by subclass + protocol
+                * instead of interface numbers. The protocol identify
+                * a specific function, while the subclass indicate a
+                * specific firmware source
+                *
+                * This is a blacklist of functions known to be
+                * non-serial.  The rest are assumed to be serial and
+                * will be handled by this driver
                 */
-               switch (ifnum) {
-               case 0:
-               case 2:
-                       dev_dbg(dev, "Modem port found\n");
-                       break;
-               case 1:
-                       dev_dbg(dev, "DM/DIAG interface found\n");
-                       break;
-               case 5:
-                       dev_dbg(dev, "NMEA GPS interface found\n");
-                       break;
-               default:
-                       /* don't claim any unsupported interface */
+               switch (intf->desc.bInterfaceProtocol) {
+                       /* QMI combined (qmi_wwan) */
+               case 0x07:
+               case 0x37:
+               case 0x67:
+                       /* QMI data (qmi_wwan) */
+               case 0x08:
+               case 0x38:
+               case 0x68:
+                       /* QMI control (qmi_wwan) */
+               case 0x09:
+               case 0x39:
+               case 0x69:
+                       /* NCM like (huawei_cdc_ncm) */
+               case 0x16:
+               case 0x46:
+               case 0x76:
                        altsetting = -1;
                        break;
+               default:
+                       dev_dbg(dev, "Huawei type serial port found (%02x/%02x/%02x)\n",
+                               intf->desc.bInterfaceClass,
+                               intf->desc.bInterfaceSubClass,
+                               intf->desc.bInterfaceProtocol);
                }
                break;
        default:
@@ -337,17 +400,25 @@ done:
                }
        }
 
+       if (!retval)
+               usb_set_serial_data(serial, (void *)(unsigned long)sendsetup);
+
        return retval;
 }
 
 static int qc_attach(struct usb_serial *serial)
 {
        struct usb_wwan_intf_private *data;
+       bool sendsetup;
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
+       sendsetup = !!(unsigned long)(usb_get_serial_data(serial));
+       if (sendsetup)
+               data->use_send_setup = 1;
+
        spin_lock_init(&data->susp_lock);
 
        usb_set_serial_data(serial, data);
@@ -374,6 +445,7 @@ static struct usb_serial_driver qcdevice = {
        .probe               = qcprobe,
        .open                = usb_wwan_open,
        .close               = usb_wwan_close,
+       .dtr_rts             = usb_wwan_dtr_rts,
        .write               = usb_wwan_write,
        .write_room          = usb_wwan_write_room,
        .chars_in_buffer     = usb_wwan_chars_in_buffer,