Module Name: src Committed By: christos Date: Thu Nov 1 00:38:43 UTC 2012
Modified Files: src/sys/dev/usb: u3g.c Log Message: Match all pairs of bulkin-bulkout interfaces to ucom's, configuring multiple ucoms per modem as they are available, instead of picking up the last bulkin bulkout pair. This makes my Sierra U250 3G portion of the modem to work. To generate a diff of this commit: cvs rdiff -u -r1.26 -r1.27 src/sys/dev/usb/u3g.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/u3g.c diff -u src/sys/dev/usb/u3g.c:1.26 src/sys/dev/usb/u3g.c:1.27 --- src/sys/dev/usb/u3g.c:1.26 Fri Oct 5 18:04:56 2012 +++ src/sys/dev/usb/u3g.c Wed Oct 31 20:38:43 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: u3g.c,v 1.26 2012/10/05 22:04:56 khorben Exp $ */ +/* $NetBSD: u3g.c,v 1.27 2012/11/01 00:38:43 christos Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. @@ -50,7 +50,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.26 2012/10/05 22:04:56 khorben Exp $"); +__KERNEL_RCSID(0, "$NetBSD: u3g.c,v 1.27 2012/11/01 00:38:43 christos Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -113,16 +113,19 @@ struct u3g_softc { device_t sc_dev; usbd_device_handle sc_udev; bool sc_dying; /* We're going away */ - - device_t sc_ucom; /* Child ucom(4) handle */ int sc_ifaceno; /* Device interface number */ - bool sc_open; /* Device is in use */ - bool sc_purging; /* Purging stale data */ - struct timeval sc_purge_start; /* Control duration of purge */ + struct u3g_com { + device_t c_dev; /* Child ucom(4) handle */ - u_char sc_msr; /* Emulated 'msr' */ - uint16_t sc_outpins; /* Output pin state */ + bool c_open; /* Device is in use */ + bool c_purging; /* Purging stale data */ + struct timeval c_purge_start; /* Control duration of purge */ + + u_char c_msr; /* Emulated 'msr' */ + uint16_t c_outpins; /* Output pin state */ + } sc_com[10]; + size_t sc_ncom; usbd_pipe_handle sc_intr_pipe; /* Interrupt pipe */ u_char *sc_intr_buff; /* Interrupt buffer */ @@ -246,7 +249,7 @@ static const struct usb_devno u3g_devs[] { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781 }, { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725 }, { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_USB305 }, - + { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_250U }, /* Toshiba */ { USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_HSDPA_MODEM_EU870DT1 }, @@ -629,20 +632,16 @@ u3g_attach(device_t parent, device_t sel uca.ibufsize = U3G_BUFF_SIZE; uca.obufsize = U3G_BUFF_SIZE; uca.ibufsizepad = U3G_BUFF_SIZE; - uca.portno = uaa->ifaceno; uca.opkthdrlen = 0; uca.device = dev; uca.iface = iface; uca.methods = &u3g_methods; uca.arg = sc; + uca.portno = -1; uca.bulkin = uca.bulkout = -1; - sc->sc_outpins = 0; - sc->sc_msr = UMSR_DSR | UMSR_CTS | UMSR_DCD; - sc->sc_ifaceno = uaa->ifaceno; - sc->sc_open = false; - sc->sc_purging = false; + sc->sc_ifaceno = uaa->ifaceno; intr_address = -1; intr_size = 0; @@ -668,25 +667,33 @@ u3g_attach(device_t parent, device_t sel UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { uca.bulkout = ed->bEndpointAddress; } + if (uca.bulkin != -1 && uca.bulkout != -1) { + struct u3g_com *com; + if (sc->sc_ncom == __arraycount(sc->sc_com)) { + aprint_error_dev(self, "Need to configure " + "more than %zu ttys", sc->sc_ncom); + continue; + } + uca.portno = sc->sc_ncom++; + com = &sc->sc_com[uca.portno]; + com->c_outpins = 0; + com->c_msr = UMSR_DSR | UMSR_CTS | UMSR_DCD; + com->c_open = false; + com->c_purging = false; + com->c_dev = config_found_sm_loc(self, "ucombus", + NULL, &uca, ucomprint, ucomsubmatch); + uca.bulkin = -1; + uca.bulkout = -1; + } } - if (uca.bulkin == -1) { - aprint_error_dev(self, "Missing bulk in for interface %d\n", - sc->sc_ifaceno); - sc->sc_dying = true; - return; - } - - if (uca.bulkout == -1) { - aprint_error_dev(self, "Missing bulk out for interface %d\n", + if (sc->sc_ncom == 0) { + aprint_error_dev(self, "Missing bulk in/out for interface %d\n", sc->sc_ifaceno); sc->sc_dying = true; return; } - sc->sc_ucom = config_found_sm_loc(self, "ucombus", - NULL, &uca, ucomprint, ucomsubmatch); - /* * If the interface has an interrupt pipe, open it immediately so * that we can track input pin state changes regardless of whether @@ -722,13 +729,14 @@ u3g_detach(device_t self, int flags) pmf_device_deregister(self); - if (sc->sc_ucom != NULL) { - rv = config_detach(sc->sc_ucom, flags); - if (rv != 0) { - aprint_verbose_dev(self, "Can't deallocate " - "port (%d)", rv); + for (size_t i = 0; i < sc->sc_ncom; i++) + if (sc->sc_com[i].c_dev != NULL) { + rv = config_detach(sc->sc_com[i].c_dev, flags); + if (rv != 0) { + aprint_verbose_dev(self, "Can't deallocate " + "port (%d)", rv); + } } - } if (sc->sc_intr_pipe != NULL) { (void) usbd_abort_pipe(sc->sc_intr_pipe); @@ -748,30 +756,32 @@ u3g_childdet(device_t self, device_t chi { struct u3g_softc *sc = device_private(self); - if (sc->sc_ucom == child) - sc->sc_ucom = NULL; + for (size_t i = 0; i < sc->sc_ncom; i++) + if (sc->sc_com[i].c_dev == child) + sc->sc_com[i].c_dev = NULL; } static int u3g_activate(device_t self, enum devact act) { struct u3g_softc *sc = device_private(self); - int rv; + int rv = 0; switch (act) { case DVACT_DEACTIVATE: - if (sc->sc_ucom != NULL && config_deactivate(sc->sc_ucom)) + for (size_t i = 0; i < sc->sc_ncom; i++) + if (sc->sc_com[i].c_dev != NULL && + config_deactivate(sc->sc_com[i].c_dev) && rv == 0) rv = -1; else rv = 0; break; default: - rv = 0; break; } - return (rv); + return rv; } static void @@ -779,6 +789,8 @@ u3g_intr(usbd_xfer_handle xfer, usbd_pri { struct u3g_softc *sc = (struct u3g_softc *)priv; u_char *buf; + int portno = 0; /* XXX */ + struct u3g_com *com = &sc->sc_com[portno]; if (sc->sc_dying) return; @@ -794,7 +806,7 @@ u3g_intr(usbd_xfer_handle xfer, usbd_pri if (buf[0] == 0xa1 && buf[1] == 0x20) { u_char msr; - msr = sc->sc_msr & ~(UMSR_DCD | UMSR_DSR | UMSR_RI); + msr = com->c_msr & ~(UMSR_DCD | UMSR_DSR | UMSR_RI); if (buf[8] & U3G_INPIN_DCD) msr |= UMSR_DCD; @@ -805,10 +817,10 @@ u3g_intr(usbd_xfer_handle xfer, usbd_pri if (buf[8] & U3G_INPIN_RI) msr |= UMSR_RI; - if (msr != sc->sc_msr) { - sc->sc_msr = msr; - if (sc->sc_open) - ucom_status_change(device_private(sc->sc_ucom)); + if (msr != com->c_msr) { + com->c_msr = msr; + if (com->c_open) + ucom_status_change(device_private(com->c_dev)); } } } @@ -822,7 +834,7 @@ u3g_get_status(void *arg, int portno, u_ if (lsr != NULL) *lsr = 0; /* LSR isn't supported */ if (msr != NULL) - *msr = sc->sc_msr; + *msr = sc->sc_com[portno].c_msr; } /*ARGSUSED*/ @@ -833,6 +845,7 @@ u3g_set(void *arg, int portno, int reg, usb_device_request_t req; uint16_t mask, new_state; usbd_status err; + struct u3g_com *com = &sc->sc_com[portno]; if (sc->sc_dying) return; @@ -848,14 +861,14 @@ u3g_set(void *arg, int portno, int reg, return; } - new_state = sc->sc_outpins & ~mask; + new_state = com->c_outpins & ~mask; if (onoff) new_state |= mask; - if (new_state == sc->sc_outpins) + if (new_state == com->c_outpins) return; - sc->sc_outpins = new_state; + com->c_outpins = new_state; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = U3G_SET_PIN; @@ -878,23 +891,26 @@ u3g_open(void *arg, int portno) usb_interface_descriptor_t *id; usbd_interface_handle ih; usbd_status err; - int i; + struct u3g_com *com = &sc->sc_com[portno]; + int i, nin; if (sc->sc_dying) return (0); - err = usbd_device2interface_handle(sc->sc_udev, portno, &ih); + err = usbd_device2interface_handle(sc->sc_udev, sc->sc_ifaceno, &ih); if (err) return (EIO); id = usbd_get_interface_descriptor(ih); - for (i = 0; i < id->bNumEndpoints; i++) { + for (nin = i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(ih, i); if (ed == NULL) return (EIO); - if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK && + nin++ == portno) { /* Issue ENDPOINT_HALT request */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; @@ -907,9 +923,9 @@ u3g_open(void *arg, int portno) } } - sc->sc_open = true; - sc->sc_purging = true; - getmicrotime(&sc->sc_purge_start); + com->c_open = true; + com->c_purging = true; + getmicrotime(&com->c_purge_start); return (0); } @@ -919,8 +935,9 @@ static void u3g_close(void *arg, int portno) { struct u3g_softc *sc = arg; + struct u3g_com *com = &sc->sc_com[portno]; - sc->sc_open = false; + com->c_open = false; } /*ARGSUSED*/ @@ -929,22 +946,23 @@ u3g_read(void *arg, int portno, u_char * { struct u3g_softc *sc = arg; struct timeval curr_tv, diff_tv; + struct u3g_com *com = &sc->sc_com[portno]; /* * If we're not purging input data following first open, do nothing. */ - if (sc->sc_purging == false) + if (com->c_purging == false) return; /* * Otherwise check if the purge timeout has expired */ getmicrotime(&curr_tv); - timersub(&curr_tv, &sc->sc_purge_start, &diff_tv); + timersub(&curr_tv, &com->c_purge_start, &diff_tv); if (diff_tv.tv_sec >= U3G_PURGE_SECS) { /* Timeout expired. */ - sc->sc_purging = false; + com->c_purging = false; } else { /* Still purging. Adjust the caller's byte count. */ *ccp = 0; @@ -956,10 +974,11 @@ static void u3g_write(void *arg, int portno, u_char *to, u_char *from, u_int32_t *count) { struct u3g_softc *sc = arg; + struct u3g_com *com = &sc->sc_com[portno]; /* * Stop purging as soon as the first data is written to the device. */ - sc->sc_purging = false; + com->c_purging = false; memcpy(to, from, *count); }