NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/39075: uhmodem(4) fix



>Number:         39075
>Category:       kern
>Synopsis:       uhmodem(4) fix
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Jul 02 02:05:00 +0000 2008
>Originator:     Yojiro UO
>Release:        4.99.67
>Organization:
>Environment:
all which has uhmodem(4).
>Description:
current uhmodem(4) assumes that the devices are compatible with ubsa(4). 
however, uhmodem(4) device is not compatible with that.  This patch fix 
following problems.

- notify message in interrupt pipe processing
- unsolicited close of interrupt pipe when multiple coms on same device opened 
and closed
   (it depend on sequence of open/close).
>How-To-Repeat:
always
>Fix:
apply the patch

Index: share/man/man4/uhmodem.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/uhmodem.4,v
retrieving revision 1.3
diff -u -p -r1.3 uhmodem.4
--- share/man/man4/uhmodem.4    30 Apr 2008 13:10:54 -0000      1.3
+++ share/man/man4/uhmodem.4    2 Jul 2008 01:14:32 -0000
@@ -43,6 +43,7 @@ driver supports the following adapters:
 .Pp
 .Bl -tag -width Dv -offset indent -compact
 .It Huawei E220
+.It Huawei E620
 .It E-mobile D01HW
 .It E-mobile D02HW
 .It NTT DoCoMo a2502
@@ -69,7 +70,6 @@ driver which makes it behave like a
 .Xr tty 4 .
 .Sh SEE ALSO
 .Xr tty 4 ,
-.Xr ubsa 4 ,
 .Xr ucom 4 ,
 .Xr usb 4
 .Sh HISTORY
@@ -77,5 +77,4 @@ The
 .Nm
 driver first appeared in
 .Nx 5.0 .
-Separate from ubsa driver.
 
Index: sys/dev/usb/uhmodem.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/uhmodem.c,v
retrieving revision 1.7
diff -u -p -r1.7 uhmodem.c
--- sys/dev/usb/uhmodem.c       24 May 2008 16:40:58 -0000      1.7
+++ sys/dev/usb/uhmodem.c       2 Jul 2008 01:14:32 -0000
@@ -98,18 +98,14 @@ __KERNEL_RCSID(0, "$NetBSD: uhmodem.c,v 
 #include <dev/usb/usbdevs.h>
 #include <dev/usb/usb_quirks.h>
 #include <dev/usb/ucomvar.h>
-#include <dev/usb/ubsavar.h>
-
-/* vendor specific bRequest */
-#define        UHMODEM_REGWRITE        0x20
-#define        UHMODEM_REGREAD         0x21
-#define UHMODEM_SETUP          0x22
 
+#define UHMODEM_INTR_INTERVAL  100     /* ms */
 #define UHMODEMIBUFSIZE        4096
 #define UHMODEMOBUFSIZE        4096
 
+#define UHMODEM_DEBUG
 #ifdef UHMODEM_DEBUG
-Static int     uhmodemdebug = 0;
+Static int     uhmodemdebug = 1;
 #define DPRINTFN(n, x)  do { \
                                if (uhmodemdebug > (n)) \
                                        logprintf x; \
@@ -119,28 +115,72 @@ Static int        uhmodemdebug = 0;
 #endif
 #define DPRINTF(x) DPRINTFN(0, x)
 
-Static int uhmodem_open(void *, int);
-Static  usbd_status e220_modechange_request(usbd_device_handle);
-Static usbd_status uhmodem_endpointhalt(struct ubsa_softc *, int);
-Static usbd_status uhmodem_regwrite(usbd_device_handle, uint8_t *, size_t);
-Static usbd_status uhmodem_regread(usbd_device_handle, uint8_t *, size_t);
-Static  usbd_status a2502_init(usbd_device_handle);
-#if 0
-Static usbd_status uhmodem_regsetup(usbd_device_handle, uint16_t);
-Static  usbd_status e220_init(usbd_device_handle);
-#endif
+#define UHMODEM_MAXCONN                4
+
+#define E220_MODE_CHANGE_REQUEST 0x2
+
+struct uhmodem_iface {
+       struct uhmodem_softc    *parent;
+       usbd_interface_handle   sc_ifaceh;
+       int                     sc_iface_number;                
+
+       /* intrrupt endpoint */
+       int                     sc_intr_number;
+       usbd_pipe_handle        sc_intr_pipe;
+       u_char                  *sc_intr_buf;
+       int                     sc_isize;
+
+       u_char                  sc_dtr; /* current DTR state */
+       u_char                  sc_rts; /* current RTS state */
+       u_char                  sc_lsr; /* local status register */
+       u_char                  sc_msr; /* status register */
+       
+       device_ptr_t            sc_subdev;      /* ucom device */
+};
 
 struct uhmodem_softc {
-       struct ubsa_softc       sc_ubsa;        
+       USBBASEDEVICE           sc_dev;
+       usbd_device_handle      sc_udev;
+       int                     sc_config_index;
+       int                     sc_numif;
+       u_char                  sc_dying;       /* disconnecting */
+       u_int16_t               sc_devflags;
+
+       struct uhmodem_iface    sc_iface[UHMODEM_MAXCONN];
 };
 
+int    uhmodem_open(void *, int);
+void   uhmodem_close(void*, int);
+void   uhmodem_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
+void   uhmodem_get_status(void *, int, u_char *, u_char *);
+void   uhmodem_set(void *, int, int, int);
+int    uhmodem_param(void *, int, struct termios *);
+
+int    uhmodem_match(device_t, cfdata_t, void *);
+void   uhmodem_attach(device_t, device_t, void *);
+void   uhmodem_childdet(device_t, device_t);
+int    uhmodem_detach(device_t, int);
+int    uhmodem_activate(device_t, enum devact);
+
+Static  usbd_status e220_modechange_request(usbd_device_handle);
+Static usbd_status uhmodem_endpointhalt(struct uhmodem_softc *, int);
+Static usbd_status uhmodem_set_line_coding(struct uhmodem_softc *,
+     int, usb_cdc_line_state_t *);
+Static usbd_status uhmodem_get_line_coding(struct uhmodem_softc *,
+     int, usb_cdc_line_state_t *);
+Static usbd_status uhmodem_set_ctrl_line_state(struct uhmodem_softc *,
+     int, uint16_t);
+Static usbd_status uhmodem_set_comm_feature(struct uhmodem_softc *,
+     int, usb_cdc_abstract_state_t *);
+Static  usbd_status e220_init(struct uhmodem_softc *, int);
+
 struct ucom_methods uhmodem_methods = {
-       ubsa_get_status,
-       ubsa_set,
-       ubsa_param,
+       uhmodem_get_status,
+       uhmodem_set,
+       uhmodem_param,
        NULL,
        uhmodem_open,
-       ubsa_close,
+       uhmodem_close,
        NULL,
        NULL
 };
@@ -149,27 +189,20 @@ struct uhmodem_type {
        struct usb_devno        uhmodem_dev;
        u_int16_t               uhmodem_coms;   /* number of serial interfaces 
on the device */
        u_int16_t               uhmodem_flags;
-#define        E220    0x0001
-#define        A2502   0x0002
-#define        E620    0x0004          /* XXX */
-                               /* Whether or not it is a device different from 
E220 is not clear. */
+#define DEV_DEFAULT    0x0000
+#define        DEV_E220        0x0001
 };
 
 Static const struct uhmodem_type uhmodem_devs[] = {
        /* HUAWEI E220 / Emobile D0[12]HW */
-       {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }, 2,     E220},
+       {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }, 2,     DEV_E220},
        /* ANYDATA / NTT DoCoMo A2502 */
-       {{ USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_A2502 }, 3,  A2502},
+       {{ USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_A2502 }, 3,  0},
        /* HUAWEI E620 */
-       {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE }, 3,   E620},
+       {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE }, 3,   0},
 };
 #define uhmodem_lookup(v, p) ((const struct uhmodem_type 
*)usb_lookup(uhmodem_devs, v, p))
 
-int uhmodem_match(device_t, cfdata_t, void *);
-void uhmodem_attach(device_t, device_t, void *);
-void uhmodem_childdet(device_t, device_t);
-int uhmodem_detach(device_t, int);
-int uhmodem_activate(device_t, enum devact);
 extern struct cfdriver uhmodem_cd;
 CFATTACH_DECL2_NEW(uhmodem, sizeof(struct uhmodem_softc), uhmodem_match,
     uhmodem_attach, uhmodem_detach, uhmodem_activate, NULL, uhmodem_childdet);
@@ -195,6 +228,7 @@ USB_ATTACH(uhmodem)
        usb_endpoint_descriptor_t *ed;
        char *devinfop;
        usbd_status err;
+       struct uhmodem_iface *iface;
        struct ucom_attach_args uca;
        int i;
        int j;
@@ -205,23 +239,25 @@ USB_ATTACH(uhmodem)
        aprint_normal_dev(self, "%s\n", devinfop);
        usbd_devinfo_free(devinfop);
 
-       sc->sc_ubsa.sc_dev = self;
-        sc->sc_ubsa.sc_udev = dev;
-       sc->sc_ubsa.sc_config_index = UBSA_DEFAULT_CONFIG_INDEX;
-       sc->sc_ubsa.sc_numif = 1; /* defaut device has one interface */
+       sc->sc_dev = self;
+        sc->sc_udev = dev;
+       sc->sc_config_index = 0;
+       sc->sc_numif = 1; /* defaut device has one interface */
 
        /* Hauwei E220 need special request to change its mode to modem */
        if ((uaa->ifaceno == 0) && (uaa->class != 255)) {
+               aprint_error_dev(self, "HUAWEI E220 need to re-attach "
+                   "to enable modem function\n");
                err = e220_modechange_request(dev);
                if (err) {
                        aprint_error_dev(self, "failed to change mode: %s\n", 
                                usbd_errstr(err));
-                       sc->sc_ubsa.sc_dying = 1;
+                       sc->sc_dying = 1;
                        goto error;
                }
                aprint_error_dev(self,
                    "mass storage only mode, reattach to enable modem\n");
-               sc->sc_ubsa.sc_dying = 1;
+               sc->sc_dying = 1;
                goto error;
        }
 
@@ -229,60 +265,62 @@ USB_ATTACH(uhmodem)
         * initialize rts, dtr variables to something
         * different from boolean 0, 1
         */
-       sc->sc_ubsa.sc_dtr = -1;
-       sc->sc_ubsa.sc_rts = -1;
+       sc->sc_numif = uhmodem_lookup(uaa->vendor, uaa->product)->uhmodem_coms;
+       sc->sc_devflags = uhmodem_lookup(uaa->vendor, 
uaa->product)->uhmodem_flags;
 
-       sc->sc_ubsa.sc_quadumts = 1;
-       sc->sc_ubsa.sc_config_index = 0;
-       sc->sc_ubsa.sc_numif = uhmodem_lookup(uaa->vendor, 
uaa->product)->uhmodem_coms;
-       sc->sc_ubsa.sc_devflags = uhmodem_lookup(uaa->vendor, 
uaa->product)->uhmodem_flags;
-
-       DPRINTF(("uhmodem attach: sc = %p\n", sc));
+       DPRINTF(("uhmodem attach: sc = %p, sc_dying = %d\n", sc, sc->sc_dying));
 
        /* Move the device into the configured state. */
-       err = usbd_set_config_index(dev, sc->sc_ubsa.sc_config_index, 1);
+       err = usbd_set_config_index(dev, sc->sc_config_index, 1);
        if (err) {
                aprint_error_dev(self, "failed to set configuration: %s\n",
                    usbd_errstr(err));
-               sc->sc_ubsa.sc_dying = 1;
+               sc->sc_dying = 1;
                goto error;
        }
 
        /* get the config descriptor */
-       cdesc = usbd_get_config_descriptor(sc->sc_ubsa.sc_udev);
+       cdesc = usbd_get_config_descriptor(sc->sc_udev);
        if (cdesc == NULL) {
                aprint_error_dev(self,
                    "failed to get configuration descriptor\n");
-               sc->sc_ubsa.sc_dying = 1;
+               sc->sc_dying = 1;
                goto error;
        }
 
-       sc->sc_ubsa.sc_intr_number = -1;
-       sc->sc_ubsa.sc_intr_pipe = NULL;
-
        /* get the interfaces */
-       for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-               err = usbd_device2interface_handle(dev, 
UBSA_IFACE_INDEX_OFFSET+i,
-                                &sc->sc_ubsa.sc_iface[i]);
+       for (i = 0; i < sc->sc_numif; i++) {
+               iface = &sc->sc_iface[i];
+
+               /* initialize */
+               memset(iface, 0, sizeof(iface));
+               iface->sc_iface_number = -1;            
+               iface->sc_intr_number = -1;
+               iface->sc_intr_pipe = NULL;
+               iface->sc_intr_buf = NULL;
+               iface->sc_dtr = iface->sc_rts = -1;
+               iface->parent = sc;
+
+               err = usbd_device2interface_handle(dev, i, &iface->sc_ifaceh);
                if (err) {
                        if (i == 0){
                                /* can not get main interface */
-                               sc->sc_ubsa.sc_dying = 1;
+                               sc->sc_dying = 1;
                                goto error;
                        } else
                                break;
                }
 
                /* Find the endpoints */
-               id = usbd_get_interface_descriptor(sc->sc_ubsa.sc_iface[i]);
-               sc->sc_ubsa.sc_iface_number[i] = id->bInterfaceNumber;
+               id = usbd_get_interface_descriptor(iface->sc_ifaceh);
+               iface->sc_iface_number = id->bInterfaceNumber;
 
                /* initialize endpoints */
                uca.bulkin = uca.bulkout = -1;
 
                for (j = 0; j < id->bNumEndpoints; j++) {
                        ed = usbd_interface2endpoint_descriptor(
-                               sc->sc_ubsa.sc_iface[i], j);
+                               iface->sc_ifaceh, j);
                        if (ed == NULL) {
                                aprint_error_dev(self,
                                    "no endpoint descriptor for %d "
@@ -292,8 +330,10 @@ USB_ATTACH(uhmodem)
 
                        if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
                            UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
-                               sc->sc_ubsa.sc_intr_number = 
ed->bEndpointAddress;
-                               sc->sc_ubsa.sc_isize = 
UGETW(ed->wMaxPacketSize);
+                               iface->sc_intr_number = ed->bEndpointAddress;
+                               iface->sc_isize = UGETW(ed->wMaxPacketSize);
+                               aprint_normal_dev(self, "find interrupt 
endpoint "
+                                   "for if#%d\n", iface->sc_iface_number);
                        } else if (UE_GET_DIR(ed->bEndpointAddress) == 
UE_DIR_IN &&
                            UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
                                uca.bulkin = ed->bEndpointAddress;
@@ -303,27 +343,23 @@ USB_ATTACH(uhmodem)
                        }
                } /* end of Endpoint loop */
 
-               if (sc->sc_ubsa.sc_intr_number == -1) {
-                       aprint_error_dev(self, "HUAWEI E220 need to re-attach "
-                           "to enable modem function\n");
-                       if (i == 0) {
-                               /* could not get intr for main tty */
-                               sc->sc_ubsa.sc_dying = 1;
-                               goto error;
-                       } else
-                               break;
+               if (iface->sc_iface_number == 0 && iface->sc_intr_number == -1) 
{
+                       /* could not get intr for main tty */
+                       aprint_error_dev(self, "could not find intr ep. for 
main tty\n");
+                       sc->sc_dying = 1;
+                       goto error;
                }
                if (uca.bulkin == -1) {
                        aprint_error_dev(self,
                            "Could not find data bulk in\n");
-                       sc->sc_ubsa.sc_dying = 1;
+                       sc->sc_dying = 1;
                        goto error;
                }
 
                if (uca.bulkout == -1) {
                        aprint_error_dev(self,
                            "Could not find data bulk out\n");
-                       sc->sc_ubsa.sc_dying = 1;
+                       sc->sc_dying = 1;
                        goto error;
                }
 
@@ -342,32 +378,32 @@ USB_ATTACH(uhmodem)
                        break;
                }
 
+               /* build uca information , bulkin, bulkout set above */
                uca.portno = i;
-               /* bulkin, bulkout set above */
                uca.ibufsize = UHMODEMIBUFSIZE;
                uca.obufsize = UHMODEMOBUFSIZE;
                uca.ibufsizepad = UHMODEMIBUFSIZE;
                uca.opkthdrlen = 0;
                uca.device = dev;
-               uca.iface = sc->sc_ubsa.sc_iface[i];
+               uca.iface = iface->sc_ifaceh;
                uca.methods = &uhmodem_methods;
-               uca.arg = &sc->sc_ubsa;
+               uca.arg = sc;
                uca.info = comname;
-               DPRINTF(("uhmodem: int#=%d, in = 0x%x, out = 0x%x, intr = 
0x%x\n",
-                       i, uca.bulkin, uca.bulkout, 
sc->sc_ubsa.sc_intr_number));
-               sc->sc_ubsa.sc_subdevs[i] = config_found_sm_loc(self, 
"ucombus", NULL,
+               DPRINTF(("uhmodem: int#=%d, in=0x%x, out=0x%x, intr=0x%x\n",
+                       i, uca.bulkin, uca.bulkout, iface->sc_intr_number));
+               iface->sc_subdev = config_found_sm_loc(self, "ucombus", NULL,
                                 &uca, ucomprint, ucomsubmatch);
 
                /* issue endpoint halt to each interface */
-               err = uhmodem_endpointhalt(&sc->sc_ubsa, i);
+               err = uhmodem_endpointhalt(sc, i);
                if (err) 
                        aprint_error("%s: endpointhalt fail\n", __func__);
                else
-                       usbd_delay_ms(sc->sc_ubsa.sc_udev, 50);
+                       usbd_delay_ms(sc->sc_udev, 50);
        } /* end of Interface loop */
 
-       usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_ubsa.sc_udev,
-                          USBDEV(sc->sc_ubsa.sc_dev));
+       usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+                          USBDEV(sc->sc_dev));
 
        USB_ATTACH_SUCCESS_RETURN;
 
@@ -378,40 +414,50 @@ error:
 void
 uhmodem_childdet(device_t self, device_t child)
 {
-       int i;
        struct uhmodem_softc *sc = device_private(self);
+       struct uhmodem_iface *iface;
+       int i;
 
-       for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-               if (sc->sc_ubsa.sc_subdevs[i] == child)
+       iface = &sc->sc_iface[0];
+       for (i = 0; i < sc->sc_numif; i++) {
+               iface = &sc->sc_iface[i];
+               if (iface->sc_subdev == child)
                        break;
        }
-       KASSERT(i < sc->sc_ubsa.sc_numif);
-       sc->sc_ubsa.sc_subdevs[i] = NULL;
+       KASSERT(i < sc->sc_numif);
+       iface->sc_subdev = NULL;
 }
 
 USB_DETACH(uhmodem)
 {
        USB_DETACH_START(uhmodem, sc);
+       struct uhmodem_iface *iface;
        int i;
        int rv = 0;
 
        DPRINTF(("uhmodem_detach: sc = %p\n", sc));
 
-       if (sc->sc_ubsa.sc_intr_pipe != NULL) {
-               usbd_abort_pipe(sc->sc_ubsa.sc_intr_pipe);
-               usbd_close_pipe(sc->sc_ubsa.sc_intr_pipe);
-               free(sc->sc_ubsa.sc_intr_buf, M_USBDEV);
-               sc->sc_ubsa.sc_intr_pipe = NULL;
+       /* close all interrupt endpoints */
+       for (i = 0; i < sc->sc_numif; i++) {
+               iface = &sc->sc_iface[i];
+               if (iface->sc_intr_pipe != NULL) {
+                       usbd_abort_pipe(iface->sc_intr_pipe);
+                       usbd_close_pipe(iface->sc_intr_pipe);
+                       free(iface->sc_intr_buf, M_USBDEV);
+                       iface->sc_intr_pipe = NULL;
+               }
        }
 
-       sc->sc_ubsa.sc_dying = 1;
-       for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-               if (sc->sc_ubsa.sc_subdevs[i] != NULL)
-                       rv |= config_detach(sc->sc_ubsa.sc_subdevs[i], flags);
+       sc->sc_dying = 1;
+       /* detach all sub devices */
+       for (i = 0; i < sc->sc_numif; i++) {
+               iface = &sc->sc_iface[i];
+               if (iface->sc_subdev != NULL)
+                       rv |= config_detach(iface->sc_subdev, flags);
        }
 
-       usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_ubsa.sc_udev,
-                          USBDEV(sc->sc_ubsa.sc_dev));
+       usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+                          USBDEV(sc->sc_dev));
 
        return (rv);
 }
@@ -420,6 +466,7 @@ int
 uhmodem_activate(device_t self, enum devact act)
 {
        struct uhmodem_softc *sc = device_private(self);
+       struct uhmodem_iface *iface;
        int rv = 0;
        int i;
 
@@ -428,66 +475,145 @@ uhmodem_activate(device_t self, enum dev
                return (EOPNOTSUPP);
 
        case DVACT_DEACTIVATE:
-               for (i = 0; i < sc->sc_ubsa.sc_numif; i++) {
-                       if (sc->sc_ubsa.sc_subdevs[i] != NULL)
-                               rv |= 
config_deactivate(sc->sc_ubsa.sc_subdevs[i]);
+               for (i = 0; i < sc->sc_numif; i++) {
+                       iface = &sc->sc_iface[i];
+                       if (iface->sc_subdev != NULL)
+                               rv |= config_deactivate(iface->sc_subdev);
                }
-               sc->sc_ubsa.sc_dying = 1;
+               sc->sc_dying = 1;
                break;
        }
        return (rv);
 }
 
-Static int
+void
+uhmodem_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
+{
+       struct uhmodem_softc *sc = addr;
+       struct uhmodem_iface *iface = &sc->sc_iface[portno];
+
+       if (lsr != NULL)
+               *lsr = iface->sc_lsr;
+       if (msr != NULL)
+               *msr = iface->sc_msr;
+}
+
+int
+uhmodem_param(void *addr, int portno, struct termios *t)
+{
+       struct uhmodem_softc *sc = addr;
+       usbd_status err;
+       usb_cdc_line_state_t ls;
+       
+       DPRINTF(("%s: called with speed:%d, flag:0x%02x\n", __func__,
+           t->c_ospeed, t->c_cflag));
+
+       USETDW(ls.dwDTERate, t->c_ospeed);
+       if (ISSET(t->c_cflag, CSTOPB))
+               ls.bCharFormat = UCDC_STOP_BIT_2;
+       else
+               ls.bCharFormat = UCDC_STOP_BIT_1;
+       if (ISSET(t->c_cflag, PARENB)) {
+               if (ISSET(t->c_cflag, PARODD))
+                       ls.bParityType = UCDC_PARITY_ODD;
+               else
+                       ls.bParityType = UCDC_PARITY_EVEN;
+       } else
+               ls.bParityType = UCDC_PARITY_NONE;
+       switch (ISSET(t->c_cflag, CSIZE)) {
+       case CS5:
+               ls.bDataBits = 5;
+               break;
+       case CS6:
+               ls.bDataBits = 6;
+               break;
+       case CS7:
+               ls.bDataBits = 7;
+               break;
+       case CS8:
+               ls.bDataBits = 8;
+               break;
+       }
+
+       err = uhmodem_set_line_coding(sc, portno, &ls);
+       if (err) {
+               DPRINTF(("%s: err=%s\n", __func__, usbd_errstr(err)));
+               return (EPASSTHROUGH);
+       }
+               
+       return (0);
+}
+
+void
+uhmodem_set(void *addr, int portno, int reg, int onoff)
+{
+       struct uhmodem_softc *sc = addr;
+       struct uhmodem_iface *iface = &sc->sc_iface[portno];
+       usb_device_request_t req;
+       int ls;
+
+       switch (reg) {
+       case UCOM_SET_DTR:
+               if (iface->sc_dtr == onoff)
+                       return;
+               iface->sc_dtr = onoff;
+               break;
+       case UCOM_SET_RTS:
+               if (iface->sc_rts == onoff)
+                       return;
+               iface->sc_rts = onoff;
+               break;
+       default:
+               return;
+       }
+
+       /* build a usb request */
+       ls = (iface->sc_dtr ? UCDC_LINE_DTR : 0) |
+            (iface->sc_rts ? UCDC_LINE_RTS : 0);
+       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+       req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+       USETW(req.wValue, ls);
+       USETW(req.wIndex, iface->sc_iface_number);
+       USETW(req.wLength, 0);
+
+       (void)usbd_do_request(sc->sc_udev, &req, 0);
+}
+
+int
 uhmodem_open(void *addr, int portno)
 {
-       struct ubsa_softc *sc = addr;
+       struct uhmodem_softc *sc = addr;
+       struct uhmodem_iface *iface = &sc->sc_iface[portno];
        usbd_status err;
 
        if (sc->sc_dying)
                return (ENXIO);
 
-       DPRINTF(("%s: sc = %p\n", __func__, sc));
-
-       err = uhmodem_endpointhalt(sc, 0);
-       if (err) 
-               aprint_error("%s: endpointhalt fail\n", __func__);
-       else
-               usbd_delay_ms(sc->sc_udev, 50);
-
-       if (sc->sc_devflags & A2502) {
-               err = a2502_init(sc->sc_udev);
+#if 0 /* XXX: windows driver issues following request but currently disabled */
+       if (sc->sc_devflags & DEV_E220) {
+               err = e220_init(sc, portno);
                if (err)
-                       aprint_error("%s: a2502init fail\n", __func__);
+                       aprint_error("%s: e220 init fail\n", __func__);
                else
-                       usbd_delay_ms(sc->sc_udev, 50);
-       }
-#if 0 /* currently disabled */
-       if (sc->sc_devflags & E220) {
-               err = e220_init(sc->sc_udev);
-               if (err)
-                       aprint_error("%s: e220init fail\n", __func__);
-               else
-                       usbd_delay_ms(sc->sc_udev, 50);
+                       usbd_delay_ms(sc->sc_udev, 100);
        }
 #endif
-       if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
-               sc->sc_intr_buf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
-               /* XXX only iface# = 0 has intr line */
-               /* XXX E220 specific? need to check */
-               err = usbd_open_pipe_intr(sc->sc_iface[0],
-                   sc->sc_intr_number,
+
+       if (iface->sc_intr_number != -1 && iface->sc_intr_pipe == NULL) {
+               iface->sc_intr_buf = malloc(iface->sc_isize, M_USBDEV, 
M_WAITOK);
+               err = usbd_open_pipe_intr(iface->sc_ifaceh,
+                   iface->sc_intr_number,
                    USBD_SHORT_XFER_OK,
-                   &sc->sc_intr_pipe,
-                   sc,
-                   sc->sc_intr_buf,
-                   sc->sc_isize,
-                   ubsa_intr,
-                   UBSA_INTR_INTERVAL);
+                   &iface->sc_intr_pipe,
+                   iface,
+                   iface->sc_intr_buf,
+                   iface->sc_isize,
+                   uhmodem_intr,
+                   UHMODEM_INTR_INTERVAL);
                if (err) {
                        aprint_error_dev(sc->sc_dev,
                            "cannot open interrupt pipe (addr %d)\n",
-                           sc->sc_intr_number);
+                           iface->sc_intr_number);
                        return (EIO);
                }
        }
@@ -495,6 +621,85 @@ uhmodem_open(void *addr, int portno)
        return (0);
 }
 
+void
+uhmodem_close(void *addr, int portno)
+{
+       struct uhmodem_softc *sc = addr;
+       struct uhmodem_iface *iface = &sc->sc_iface[portno];
+       int err;
+
+       if (sc->sc_dying)
+               return;
+
+       if (iface->sc_intr_pipe != NULL) {
+               err = usbd_abort_pipe(iface->sc_intr_pipe);
+               if (err)
+                       aprint_error_dev(sc->sc_dev, 
+                           "abort interrupt pipe failed: %s\n", 
+                           usbd_errstr(err));
+               err = usbd_close_pipe(iface->sc_intr_pipe);
+               if (err)
+                       aprint_error_dev(sc->sc_dev, 
+                           "close interrupt pipe failed: %s\n", 
+                           usbd_errstr(err));
+               free(iface->sc_intr_buf, M_USBDEV);
+               iface->sc_intr_pipe = NULL;
+       }
+}
+
+void
+uhmodem_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
+       usbd_status status)
+{
+       struct uhmodem_iface *iface = priv;
+       struct uhmodem_softc *sc = iface->parent;
+       usb_cdc_notification_t *buf;
+       u_char mstatus;
+
+
+       buf = (usb_cdc_notification_t *)iface->sc_intr_buf;
+       if (sc->sc_dying)
+               return;
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+                       return;
+               aprint_error_dev(sc->sc_dev, 
+                   "uhmodem interrupt abnormal status: %s\n", 
+                   usbd_errstr(status));
+               usbd_clear_endpoint_stall_async(iface->sc_intr_pipe);
+               return;
+       }
+
+       if (buf->bmRequestType != UCDC_NOTIFICATION) {
+               aprint_error_dev(sc->sc_dev, 
+                   "uhmodem_intr: unknown message type(0x%02x)\n",
+                   buf->bmRequestType);
+               return;
+       }
+       if (buf->bNotification == UCDC_N_SERIAL_STATE) {
+               /* invalid message length, discard it */
+               if (UGETW(buf->wLength) != 2)
+                       return;
+               /* XXX: sc_lsr is always 0 */
+               iface->sc_lsr = iface->sc_msr = 0;
+               mstatus = buf->data[0];
+               if (ISSET(mstatus, UCDC_N_SERIAL_RI))
+                       iface->sc_msr |= UMSR_RI;
+               if (ISSET(mstatus, UCDC_N_SERIAL_DSR))
+                       iface->sc_msr |= UMSR_DSR;
+               if (ISSET(mstatus, UCDC_N_SERIAL_DCD))
+                       iface->sc_msr |= UMSR_DCD;
+       } else if (buf->bNotification != UCDC_N_CONNECTION_SPEED_CHANGE) {
+               aprint_error_dev(sc->sc_dev, 
+                   "uhmodem_intr: unknown notify message (0x%02x)\n",
+                    buf->bNotification);
+               return;
+       }
+
+       ucom_status_change(device_private(iface->sc_subdev));
+}
+
 /*
  * Hauwei E220 needs special request to enable modem function.
  * -- DEVICE_REMOTE_WAKEUP ruquest to endpoint 2.
@@ -502,7 +707,6 @@ uhmodem_open(void *addr, int portno)
 Static  usbd_status 
 e220_modechange_request(usbd_device_handle dev)
 {
-#define E220_MODE_CHANGE_REQUEST 0x2
        usb_device_request_t req;
        usbd_status err;
 
@@ -520,28 +724,30 @@ e220_modechange_request(usbd_device_hand
        }
 
        return (0);
-#undef E220_MODE_CHANGE_REQUEST
 }
 
 Static  usbd_status 
-uhmodem_endpointhalt(struct ubsa_softc *sc, int iface)
+uhmodem_endpointhalt(struct uhmodem_softc *sc, int port)
 {
        usb_device_request_t req;
        usb_endpoint_descriptor_t *ed;
        usb_interface_descriptor_t *id;
+       struct uhmodem_iface *iface;
        usbd_status err;
        int i;
 
-       /* Find the endpoints */
-       id = usbd_get_interface_descriptor(sc->sc_iface[iface]);
+       iface = &sc->sc_iface[port];
+       id = usbd_get_interface_descriptor(iface->sc_ifaceh);
 
        for (i = 0; i < id->bNumEndpoints; i++) {
-               ed = usbd_interface2endpoint_descriptor(sc->sc_iface[iface], i);
+               ed = usbd_interface2endpoint_descriptor(iface->sc_ifaceh, i);
                if (ed == NULL) 
                        return (EIO);
 
                if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
                        /* issue ENDPOINT_HALT request */
+                       DPRINTF(("%s: endpoint halt to EP:0x%.2x\n", 
+                               __func__, ed->bEndpointAddress));
                        req.bmRequestType = UT_WRITE_ENDPOINT;
                        req.bRequest = UR_CLEAR_FEATURE;
                        USETW(req.wValue, UF_ENDPOINT_HALT);
@@ -553,7 +759,6 @@ uhmodem_endpointhalt(struct ubsa_softc *
                                        __func__, ed->bEndpointAddress));
                                return (EIO);
                        }
-
                }
        } /* end of Endpoint loop */
 
@@ -561,147 +766,105 @@ uhmodem_endpointhalt(struct ubsa_softc *
 }
 
 Static usbd_status
-uhmodem_regwrite(usbd_device_handle dev, uint8_t *data, size_t len)
+uhmodem_set_line_coding(struct uhmodem_softc *sc, int portno,
+    usb_cdc_line_state_t *state)
 {
        usb_device_request_t req;
        usbd_status err;
+       int iface = sc->sc_iface[portno].sc_iface_number;
 
        req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
-       req.bRequest = UHMODEM_REGWRITE;
-       USETW(req.wValue, 0x0000);
-       USETW(req.wIndex, 0x0000);
-       USETW(req.wLength, len);
-       err = usbd_do_request(dev, &req, data);
+       req.bRequest = UCDC_SET_LINE_CODING;
+       USETW(req.wValue, 0);    /* zero */
+       USETW(req.wIndex, iface);        /* iface */
+       USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+       err = usbd_do_request(sc->sc_udev, &req, state);
        if (err) 
                return err;
 
-       return 0;
+       return (USBD_NORMAL_COMPLETION);
 }
 
 Static usbd_status
-uhmodem_regread(usbd_device_handle dev, uint8_t *data, size_t len)
+uhmodem_get_line_coding(struct uhmodem_softc *sc, int portno, 
+    usb_cdc_line_state_t *state)
 {
        usb_device_request_t req;
        usbd_status err;
+       int iface = sc->sc_iface[portno].sc_iface_number;
 
        req.bmRequestType = UT_READ_CLASS_INTERFACE;
-       req.bRequest = UHMODEM_REGREAD;
-       USETW(req.wValue, 0x0000);
-       USETW(req.wIndex, 0x0000);
-       USETW(req.wLength, len);
-       err = usbd_do_request(dev, &req, data);
+       req.bRequest = UCDC_GET_LINE_CODING;
+       USETW(req.wValue, 0);           /* zero */
+       USETW(req.wIndex, iface);       /* iface */
+       USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+       err = usbd_do_request(sc->sc_udev, &req, state);
 
        if (err)
                return err;
 
-       return 0;
+       return (USBD_NORMAL_COMPLETION);
 }
 
-#if 0
 Static usbd_status
-uhmodem_regsetup(usbd_device_handle dev, uint16_t cmd)
+uhmodem_set_ctrl_line_state(struct uhmodem_softc *sc, int portno, uint16_t cmd)
 {
        usb_device_request_t req;
        usbd_status err;
+       int iface = sc->sc_iface[portno].sc_iface_number;
 
        req.bmRequestType = UT_READ_CLASS_INTERFACE;
-       req.bRequest = UHMODEM_SETUP;
-       USETW(req.wValue, cmd);
-       USETW(req.wIndex, 0x0000);
-       USETW(req.wLength, 0);
-       err = usbd_do_request(dev, &req, 0);
+       req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+       USETW(req.wValue, cmd);         /* control signal bitmap */
+       USETW(req.wIndex, iface);       /* iface */
+       USETW(req.wLength, 0);          /* zero */
+       err = usbd_do_request(sc->sc_udev, &req, 0);
 
        if (err)
                return err;
 
-       return 0;
+       return (USBD_NORMAL_COMPLETION);
 }
-#endif
 
-Static  usbd_status 
-a2502_init(usbd_device_handle dev)
-{
-       uint8_t data[8];
-       static uint8_t init_cmd[] = {0x00, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x08};
-#ifdef UHMODEM_DEBUG
-       int i;
-#endif
-       if (uhmodem_regread(dev, data, 7)) {
-               DPRINTF(("%s: read fail\n", __func__));
-               return EIO;
-       }
-#ifdef UHMODEM_DEBUG
-       printf("%s: readdata: ", __func__);
-       for (i = 0; i < 7; i++)
-               printf("0x%x ", data[i]);
-#endif
-       if (uhmodem_regwrite(dev, init_cmd, sizeof(init_cmd)) ) {
-               DPRINTF(("%s: write fail\n", __func__));
-               return EIO;
-       }
-
-       if (uhmodem_regread(dev, data, 7)) { 
-               DPRINTF(("%s: read fail\n", __func__));
-               return EIO;
-       }
-#ifdef UHMODEM_DEBUG
-       printf("%s: readdata: ", __func__);
-       printf(" => ");
-       for (i = 0; i < 7; i++)
-               printf("0x%x ", data[i]);
-       printf("\n");
-#endif
-       return 0;
-}
-
-
-#if 0
-/* 
- * Windows device driver send these sequens of USB requests.
- * However currently I can't understand what the messege is,
- * disable this code when I get more information about it.
- */ 
-Static  usbd_status 
-e220_init(usbd_device_handle dev)
+Static usbd_status
+uhmodem_set_comm_feature(struct uhmodem_softc *sc, int portno, 
+    usb_cdc_abstract_state_t *as)
 {
-       uint8_t data[8];
        usb_device_request_t req;
-       int i;
+       usbd_status err;
+       int iface = sc->sc_iface[portno].sc_iface_number;
 
-       /* vendor specific unknown request */
        req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
-       req.bRequest = 0x02;
-       USETW(req.wValue, 0x0001);
-       USETW(req.wIndex, 0x0000);
-       USETW(req.wLength, 2);
-       data[0] = 0x0;
-       data[1] = 0x0;
-       if (usbd_do_request(dev, &req, data))
-               goto error;
+       req.bRequest = UCDC_SET_COMM_FEATURE;
+       USETW(req.wValue, UCDC_ABSTRACT_STATE); /* feature selector */
+       USETW(req.wIndex, iface); /* iface */
+       USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); 
 
-       /* vendor specific unknown sequence */
-       if(uhmodem_regsetup(dev, 0x1)) 
-               goto error;
+       err = usbd_do_request(sc->sc_udev, &req, as);
+       if (err)
+               return err;
 
-       if (uhmodem_regread(dev, data, 7))
-               goto error;
+       return (USBD_NORMAL_COMPLETION);
+}
 
-       data[1] = 0x8;
-       data[2] = 0x7;
-       if (uhmodem_regwrite(dev, data, sizeof(data)) )
-               goto error;
+#if 0
+Static  usbd_status 
+e220_init(struct uhmodem_softc *sc, int portno)
+{
+       usb_cdc_abstract_state_t as;
 
-       if (uhmodem_regread(dev, data, 7))
+       USETW(as.wState, 0); /* state no multiplex, no idle */
+       if (uhmodem_set_comm_feature(sc, portno, &as))
                goto error;
-               /* XXX should verify the read data ? */
 
-       if (uhmodem_regsetup(dev, 0x3))
+       if (uhmodem_set_ctrl_line_state(sc, portno, 
+           UCDC_LINE_DTR | UCDC_LINE_RTS))
                goto error;
 
        return (0);
 error:
        DPRINTF(("%s: E220 init request fail\n", __func__));
-       return (EIO);
+       return (USBD_IOERROR);
 }
 #endif
 
Index: sys/dev/usb/files.usb
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/files.usb,v
retrieving revision 1.84
diff -u -p -r1.84 files.usb
--- sys/dev/usb/files.usb       26 May 2008 00:23:05 -0000      1.84
+++ sys/dev/usb/files.usb       2 Jul 2008 01:14:32 -0000
@@ -242,7 +242,7 @@ attach      ubsa at usbifif
 file   dev/usb/ubsa.c                  ubsa
 
 # Huawei E220 3G/HSDPA modem (ubsa)
-device uhmodem: ucombus, ubsa_common
+device uhmodem: ucombus
 attach uhmodem at usbifif
 file   dev/usb/uhmodem.c               uhmodem
 




Home | Main Index | Thread Index | Old Index