add another 3G modems support to umsm(4).
authoryuo <yuo@openbsd.org>
Mon, 28 Jul 2008 10:42:12 +0000 (10:42 +0000)
committeryuo <yuo@openbsd.org>
Mon, 28 Jul 2008 10:42:12 +0000 (10:42 +0000)
 - Option GlobeSuffer ICON7.2
 - Option GlobeTrotter HSDPA ICON 225

These modems require special request to change its mode to modem.
It is similar with huawei E220, but these modems using UMASS
standard query to change mode.

tested by pyr@ and ajacoutot@
ok jsg@ and pyr@

share/man/man4/umsm.4
sys/dev/usb/umsm.c

index 150e06d..6f95b34 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: umsm.4,v 1.30 2008/07/20 14:24:49 yuo Exp $
+.\"    $OpenBSD: umsm.4,v 1.31 2008/07/28 10:42:12 yuo Exp $
 .\"
 .\" Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: July 20 2008 $
+.Dd $Mdocdate: July 28 2008 $
 .Dt UMSM 4
 .Os
 .Sh NAME
@@ -50,6 +50,8 @@ driver:
 .It Li "NTT DoCoMo A2502" Ta Ta USB
 .It Li "ONDA Communication H600" Ta Ta CardBus
 .It Li "Option GlobeTrotter 3G+" Ta Ta CardBus
+.It Li "Option GlobeSurfer ICON7.2" Ta Ta USB
+.It Li "Option GlobeTrotter HSDPA ICON225" Ta Ta USB
 .It Li "Sierra Wireless MC8755" Ta Ta PCI Express Mini Card
 .It Li "Sierra Wireless AirCard 580" Ta Ta CardBus
 .It Li "Sierra Wireless AirCard 875" Ta Ta CardBus
@@ -160,7 +162,10 @@ The
 .Nm
 driver was written by
 .An Jonathan Gray
-.Aq jsg@openbsd.org .
+.Aq jsg@openbsd.org ,
+and
+.An Yojiro UO
+.Aq yuo@nui.org .
 .Sh CAVEATS
 For Verizon Wireless (and possibly other services),
 cards require a one-time activation before they will work;
index c14aa96..09471c6 100644 (file)
@@ -1,6 +1,7 @@
-/*     $OpenBSD: umsm.c,v 1.31 2008/07/20 14:24:49 yuo Exp $   */
+/*     $OpenBSD: umsm.c,v 1.32 2008/07/28 10:42:12 yuo Exp $   */
 
 /*
+ * Copyright (c) 2008 Yojiro UO <yuo@nui.org>
  * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -32,6 +33,8 @@
 #include <dev/usb/usbdevs.h>
 #include <dev/usb/ucomvar.h>
 #include <dev/usb/usbcdc.h>
+#include <dev/usb/umassvar.h>
+#undef DPRINTF /* undef DPRINTF for umass */
 
 #ifdef USB_DEBUG
 #define UMSM_DEBUG
@@ -46,7 +49,6 @@ int     umsmdebug = 1;
 
 #define DPRINTF(x) DPRINTFN(0, x)
 
-
 #define UMSMBUFSZ      4096
 #define        UMSM_INTR_INTERVAL      100     /* ms */
 #define E220_MODE_CHANGE_REQUEST 0x2
@@ -62,8 +64,6 @@ void umsm_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
 void umsm_get_status(void *, int, u_char *, u_char *);
 void umsm_set(void *, int, int, int);
 
-usbd_status umsm_e220_changemode(usbd_device_handle);
-
 struct umsm_softc {
        struct device            sc_dev;
        usbd_device_handle       sc_udev;
@@ -71,6 +71,7 @@ struct umsm_softc {
        int                      sc_iface_no;
        struct device           *sc_subdev;
        u_char                   sc_dying;
+       uint16_t                 sc_flag;
 
        /* interrupt ep */
        int                      sc_intr_number;
@@ -84,6 +85,9 @@ struct umsm_softc {
        u_char                   sc_rts;        /* current RTS state */
 };
 
+usbd_status umsm_huawei_changemode(usbd_device_handle);
+usbd_status umsm_umass_changemode(struct umsm_softc *);
+
 struct ucom_methods umsm_methods = {
        umsm_get_status,
        umsm_set,
@@ -95,38 +99,52 @@ struct ucom_methods umsm_methods = {
        NULL,
 };
 
-static const struct usb_devno umsm_devs[] = {
-       { USB_VENDOR_AIRPRIME,  USB_PRODUCT_AIRPRIME_PC5220 },
-       { USB_VENDOR_ANYDATA,   USB_PRODUCT_ANYDATA_A2502 },
-       { USB_VENDOR_ANYDATA,   USB_PRODUCT_ANYDATA_ADU_500A },
-       { USB_VENDOR_DELL,      USB_PRODUCT_DELL_W5500 },
-       { USB_VENDOR_HUAWEI,    USB_PRODUCT_HUAWEI_E220 },
-       { USB_VENDOR_HUAWEI,    USB_PRODUCT_HUAWEI_E618 },
-       { USB_VENDOR_KYOCERA2,  USB_PRODUCT_KYOCERA2_KPC650 },
-       { USB_VENDOR_NOVATEL1,  USB_PRODUCT_NOVATEL1_FLEXPACKGPS },
-       { USB_VENDOR_NOVATEL,   USB_PRODUCT_NOVATEL_EXPRESSCARD },
-       { USB_VENDOR_NOVATEL,   USB_PRODUCT_NOVATEL_MERLINV620 },
-       { USB_VENDOR_NOVATEL,   USB_PRODUCT_NOVATEL_S720 },
-       { USB_VENDOR_NOVATEL,   USB_PRODUCT_NOVATEL_U720 },
-       { USB_VENDOR_NOVATEL,   USB_PRODUCT_NOVATEL_XU870 },
-       { USB_VENDOR_NOVATEL,   USB_PRODUCT_NOVATEL_ES620 },
-       { USB_VENDOR_OPTION,    USB_PRODUCT_OPTION_GT3GPLUS },
-       { USB_VENDOR_QUALCOMM,  USB_PRODUCT_QUALCOMM_MSM_DRIVER },
-       { USB_VENDOR_QUALCOMM,  USB_PRODUCT_QUALCOMM_MSM_HSDPA },
-       { USB_VENDOR_QUALCOMM,  USB_PRODUCT_QUALCOMM_MSM_HSDPA2 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_EM5625 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_AIRCARD_580 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_AIRCARD_595 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_AIRCARD_875 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_MC5720 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_MC5725 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_MC8755 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_MC8755_2 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_MC8755_3 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_MC8765 },
-       { USB_VENDOR_SIERRA,    USB_PRODUCT_SIERRA_MC8775 },
+struct umsm_type {
+       struct usb_devno        umsm_dev;
+       uint16_t                umsm_flag;
+/* device type */
+#define        DEV_NORMAL      0x0000
+#define        DEV_HUAWEI      0x0001
+#define        DEV_UMASS1      0x0010
+#define        DEV_UMASS2      0x0020
+#define DEV_UMASS      (DEV_UMASS1 | DEV_UMASS2)
+};
+static const struct umsm_type umsm_devs[] = {
+       {{ USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220 }, 0},
+       {{ USB_VENDOR_ANYDATA,  USB_PRODUCT_ANYDATA_A2502 }, 0},
+       {{ USB_VENDOR_ANYDATA,  USB_PRODUCT_ANYDATA_ADU_500A }, 0},
+       {{ USB_VENDOR_DELL,     USB_PRODUCT_DELL_W5500 }, 0},
+       {{ USB_VENDOR_HUAWEI,   USB_PRODUCT_HUAWEI_E220 }, DEV_HUAWEI},
+       {{ USB_VENDOR_HUAWEI,   USB_PRODUCT_HUAWEI_E618 }, 0},
+       {{ USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_KPC650 }, 0},
+       {{ USB_VENDOR_NOVATEL1, USB_PRODUCT_NOVATEL1_FLEXPACKGPS }, 0},
+       {{ USB_VENDOR_NOVATEL,  USB_PRODUCT_NOVATEL_EXPRESSCARD }, 0},
+       {{ USB_VENDOR_NOVATEL,  USB_PRODUCT_NOVATEL_MERLINV620 }, 0},
+       {{ USB_VENDOR_NOVATEL,  USB_PRODUCT_NOVATEL_S720 }, 0},
+       {{ USB_VENDOR_NOVATEL,  USB_PRODUCT_NOVATEL_U720 }, 0},
+       {{ USB_VENDOR_NOVATEL,  USB_PRODUCT_NOVATEL_XU870 }, 0},
+       {{ USB_VENDOR_NOVATEL,  USB_PRODUCT_NOVATEL_ES620 }, 0},
+       {{ USB_VENDOR_OPTION,   USB_PRODUCT_OPTION_GT3GPLUS }, 0},
+       {{ USB_VENDOR_OPTION,   USB_PRODUCT_OPTION_GSICON72 }, DEV_UMASS1},
+       {{ USB_VENDOR_OPTION,   USB_PRODUCT_OPTION_GTHSDPA225 }, DEV_UMASS2},
+       {{ USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_MSM_DRIVER }, DEV_UMASS1},
+       {{ USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_MSM_HSDPA }, 0},
+       {{ USB_VENDOR_QUALCOMM, USB_PRODUCT_QUALCOMM_MSM_HSDPA2 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_EM5625 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_AIRCARD_580 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_AIRCARD_595 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_AIRCARD_875 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_MC5720 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_MC5725 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_MC8755 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_MC8755_2 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_MC8755_3 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_MC8765 }, 0},
+       {{ USB_VENDOR_SIERRA,   USB_PRODUCT_SIERRA_MC8775 }, 0},
 };
 
+#define umsm_lookup(v, p) ((const struct umsm_type *)usb_lookup(umsm_devs, v, p))
 
 struct cfdriver umsm_cd = { 
        NULL, "umsm", DV_DULL 
@@ -145,32 +163,37 @@ umsm_match(struct device *parent, void *match, void *aux)
 {
        struct usb_attach_arg *uaa = aux;
        usb_interface_descriptor_t *id;
+       uint16_t flag;
 
        if (uaa->iface == NULL)
                return UMATCH_NONE;
 
        /*
-        * Some devices(eg Huawei E220) have multiple interfaces and some
+        * Some devices (eg Huawei E220) have multiple interfaces and some
         * of them are of class umass. Don't claim ownership in such case.
         */
-       id = usbd_get_interface_descriptor(uaa->iface);
-       if (id == NULL || id->bInterfaceClass == UICLASS_MASS) {
-               /*
-                * E220 is a dual mode device, so we have to deal with
-                * it differently.
-                */
-               if (uaa->vendor  == USB_VENDOR_HUAWEI &&
-                    uaa->product == USB_PRODUCT_HUAWEI_E220) {
-                       if  (uaa->ifaceno != 2) 
+       if (umsm_lookup(uaa->vendor, uaa->product) != NULL) {
+               id = usbd_get_interface_descriptor(uaa->iface);
+               flag = umsm_lookup(uaa->vendor, uaa->product)->umsm_flag;
+
+               if (id == NULL || id->bInterfaceClass == UICLASS_MASS) {
+                       /*
+                        * Some high-speed modem requre special care.
+                        */
+                       if (flag & DEV_HUAWEI) {
+                               if  (uaa->ifaceno != 2) 
+                                       return UMATCH_VENDOR_IFACESUBCLASS;
+                               else
+                                       return UMATCH_NONE;
+                       } else if (flag & DEV_UMASS)
                                return UMATCH_VENDOR_IFACESUBCLASS;
                        else
                                return UMATCH_NONE;
                } else
-                       return UMATCH_NONE;
-       }
+                       return UMATCH_VENDOR_IFACESUBCLASS;
+       } 
 
-       return (usb_lookup(umsm_devs, uaa->vendor, uaa->product) != NULL) ?
-           UMATCH_VENDOR_IFACESUBCLASS : UMATCH_NONE;
+       return UMATCH_NONE;
 }
 
 void
@@ -186,26 +209,31 @@ umsm_attach(struct device *parent, struct device *self, void *aux)
        bzero(&uca, sizeof(uca));
        sc->sc_udev = uaa->device;
        sc->sc_iface = uaa->iface;
+       sc->sc_flag  = umsm_lookup(uaa->vendor, uaa->product)->umsm_flag;
 
        id = usbd_get_interface_descriptor(sc->sc_iface);
 
+       /*
+        * Some 3G modem devices have multiple interface and some 
+        * of them are umass class. Don't claim ownership in such case.
+        */
        if (id == NULL || id->bInterfaceClass == UICLASS_MASS) {
                /*
-                * Huawei E220 requires a special command to change into
-                * modem mode
+                * Some 3G modems require special request to
+                * enable it's modem function.
                 */
-               if (uaa->vendor  == USB_VENDOR_HUAWEI &&
-                   uaa->product == USB_PRODUCT_HUAWEI_E220 &&
-                   uaa->ifaceno == 0) {
-                        umsm_e220_changemode(uaa->device);
-                       /*
-                        * The device will reset its own bus from the
-                        * device side, so we just return from this device
-                        * probe process. 
-                        */
+               if ((sc->sc_flag & DEV_HUAWEI) && uaa->ifaceno == 0) {
+                        umsm_huawei_changemode(uaa->device);
                        printf("%s: umass only mode. need to reattach\n", 
                                sc->sc_dev.dv_xname);
+               } else if ((sc->sc_flag & DEV_UMASS) && uaa->ifaceno == 0) {
+                       umsm_umass_changemode(sc);
                }
+
+               /*
+                * The device will reset its own bus from the device side 
+                * when its mode was changed, so just return. 
+                */
                return;
        }
 
@@ -359,7 +387,6 @@ umsm_close(void *addr, int portno)
                free(sc->sc_intr_buf, M_USBDEV);
                sc->sc_intr_pipe = NULL;
        }
-
 }
 
 void
@@ -469,9 +496,8 @@ umsm_set(void *addr, int portno, int reg, int onoff)
        (void)usbd_do_request(sc->sc_udev, &req, 0);
 }
 
-
 usbd_status
-umsm_e220_changemode(usbd_device_handle dev)
+umsm_huawei_changemode(usbd_device_handle dev)
 {
        usb_device_request_t req;
        usbd_status err;
@@ -488,3 +514,88 @@ umsm_e220_changemode(usbd_device_handle dev)
 
        return (0);
 }
+
+usbd_status
+umsm_umass_changemode(struct umsm_softc *sc) 
+{
+#define UMASS_CMD_REZERO_UNIT  0x01
+       usb_interface_descriptor_t *id;
+       usb_endpoint_descriptor_t *ed;
+       usbd_xfer_handle xfer;
+       usbd_pipe_handle cmdpipe;
+       usbd_status err;
+       u_int32_t n;
+       void *bufp;
+       int target_ep, i;
+
+       umass_bbb_cbw_t cbw;
+       static int dCBWTag = 0x12345678;
+
+       USETDW(cbw.dCBWSignature, CBWSIGNATURE);
+       USETDW(cbw.dCBWTag, dCBWTag);
+       cbw.bCBWLUN   = 0;
+       cbw.bCDBLength= 6; 
+       bzero(cbw.CBWCDB, sizeof(cbw.CBWCDB));
+       cbw.CBWCDB[0] = UMASS_CMD_REZERO_UNIT;
+       cbw.CBWCDB[1] = 0x0;    /* target LUN: 0 */
+
+       switch (sc->sc_flag) {
+       case DEV_UMASS1:
+               USETDW(cbw.dCBWDataTransferLength, 0x0); 
+               cbw.bCBWFlags = CBWFLAGS_OUT;
+               break;
+       case DEV_UMASS2:
+               USETDW(cbw.dCBWDataTransferLength, 0x1); 
+               cbw.bCBWFlags = CBWFLAGS_IN;
+               break;
+       default:
+               DPRINTF(("%s: unknown device type.\n", sc->sc_dev.dv_xname));
+               break;
+       }
+
+       /* get command endpoint address */
+       id = usbd_get_interface_descriptor(sc->sc_iface);
+       for (i = 0; i < id->bNumEndpoints; i++) {
+               ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+               if (ed == NULL) {
+                       return (USBD_IOERROR);
+               }
+
+               if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+                   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+                       target_ep = ed->bEndpointAddress;
+       }
+
+       /* open command endppoint */
+       err = usbd_open_pipe(sc->sc_iface, target_ep,
+               USBD_EXCLUSIVE_USE, &cmdpipe);
+       if (err) {
+               DPRINTF(("%s: open pipe for modem change cmd failed: %s\n",
+                   sc->sc_dev.dv_xname, usbd_errstr(err)));
+               return (err);
+       }
+
+       xfer = usbd_alloc_xfer(sc->sc_udev);
+       if (xfer == NULL) {
+               usbd_close_pipe(cmdpipe);
+               return (USBD_NOMEM);
+       } else {
+               bufp = usbd_alloc_buffer(xfer, UMASS_BBB_CBW_SIZE);
+               if (bufp == NULL)
+                       err = USBD_NOMEM;
+               else {
+                       n = UMASS_BBB_CBW_SIZE;
+                       memcpy(bufp, &cbw, UMASS_BBB_CBW_SIZE);
+                       err = usbd_bulk_transfer(xfer, cmdpipe, USBD_NO_COPY,
+                           USBD_NO_TIMEOUT, bufp, &n, "umsm");
+                       if (err)
+                               DPRINTF(("%s: send error:%s", __func__,
+                                   usbd_errstr(err)));
+               }
+               usbd_close_pipe(cmdpipe);
+               usbd_free_buffer(xfer);
+               usbd_free_xfer(xfer);
+       }
+               
+       return (err);
+}