From owner-svn-src-stable@FreeBSD.ORG Thu Oct 25 21:22:05 2012 Return-Path: Delivered-To: svn-src-stable@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id DF49CF3A; Thu, 25 Oct 2012 21:22:05 +0000 (UTC) (envelope-from n_hibma@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id C54358FC14; Thu, 25 Oct 2012 21:22:05 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q9PLM5CJ048930; Thu, 25 Oct 2012 21:22:05 GMT (envelope-from n_hibma@svn.freebsd.org) Received: (from n_hibma@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q9PLM5O4048928; Thu, 25 Oct 2012 21:22:05 GMT (envelope-from n_hibma@svn.freebsd.org) Message-Id: <201210252122.q9PLM5O4048928@svn.freebsd.org> From: Nick Hibma Date: Thu, 25 Oct 2012 21:22:05 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r242100 - stable/9/sys/dev/usb/serial X-SVN-Group: stable-9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for all the -stable branches of the src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 25 Oct 2012 21:22:06 -0000 Author: n_hibma Date: Thu Oct 25 21:22:05 2012 New Revision: 242100 URL: http://svn.freebsd.org/changeset/base/242100 Log: MFC 241555: Implement modem control in u3g. Tested on Option GTM382W, Huawei E220, and Sierra Wireless MC8790V. Also implement the .ucom_poll method. Note: It resolves ppp hanging during the PPp> phase. Modified: stable/9/sys/dev/usb/serial/u3g.c Directory Properties: stable/9/sys/ (props changed) stable/9/sys/dev/ (props changed) Modified: stable/9/sys/dev/usb/serial/u3g.c ============================================================================== --- stable/9/sys/dev/usb/serial/u3g.c Thu Oct 25 21:09:42 2012 (r242099) +++ stable/9/sys/dev/usb/serial/u3g.c Thu Oct 25 21:22:05 2012 (r242100) @@ -53,6 +53,7 @@ #include #include #include +#include #include "usbdevs.h" #define USB_DEBUG_VAR u3g_debug @@ -99,6 +100,7 @@ SYSCTL_INT(_hw_usb_u3g, OID_AUTO, debug, enum { U3G_BULK_WR, U3G_BULK_RD, + U3G_INTR, U3G_N_TRANSFER, }; @@ -107,12 +109,15 @@ struct u3g_softc { struct ucom_softc sc_ucom[U3G_MAXPORTS]; struct usb_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER]; + uint8_t sc_iface[U3G_MAXPORTS]; /* local status register */ + uint8_t sc_lsr[U3G_MAXPORTS]; /* local status register */ + uint8_t sc_msr[U3G_MAXPORTS]; /* u3g status register */ + uint16_t sc_line[U3G_MAXPORTS]; /* line status */ + struct usb_device *sc_udev; struct mtx sc_mtx; - uint8_t sc_lsr; /* local status register */ - uint8_t sc_msr; /* U3G status register */ - uint8_t sc_numports; + uint8_t sc_numports; }; static device_probe_t u3g_probe; @@ -122,12 +127,17 @@ static void u3g_free_softc(struct u3g_so static usb_callback_t u3g_write_callback; static usb_callback_t u3g_read_callback; +static usb_callback_t u3g_intr_callback; -static void u3g_free(struct ucom_softc *ucom); +static void u3g_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); +static void u3g_cfg_set_dtr(struct ucom_softc *, uint8_t); +static void u3g_cfg_set_rts(struct ucom_softc *, uint8_t); static void u3g_start_read(struct ucom_softc *ucom); static void u3g_stop_read(struct ucom_softc *ucom); static void u3g_start_write(struct ucom_softc *ucom); static void u3g_stop_write(struct ucom_softc *ucom); +static void u3g_poll(struct ucom_softc *ucom); +static void u3g_free(struct ucom_softc *ucom); static void u3g_test_autoinst(void *, struct usb_device *, @@ -155,13 +165,26 @@ static const struct usb_config u3g_confi .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &u3g_read_callback, }, + + [U3G_INTR] = { + .type = UE_INTERRUPT, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, + .bufsize = 0, /* use wMaxPacketSize */ + .callback = &u3g_intr_callback, + }, }; static const struct ucom_callback u3g_callback = { + .ucom_cfg_get_status = &u3g_cfg_get_status, + .ucom_cfg_set_dtr = &u3g_cfg_set_dtr, + .ucom_cfg_set_rts = &u3g_cfg_set_rts, .ucom_start_read = &u3g_start_read, .ucom_stop_read = &u3g_stop_read, .ucom_start_write = &u3g_start_write, .ucom_stop_write = &u3g_stop_write, + .ucom_poll = &u3g_poll, .ucom_free = &u3g_free, }; @@ -849,6 +872,15 @@ u3g_attach(device_t dev) continue; } + iface = usbd_get_iface(uaa->device, i); + id = usbd_get_interface_descriptor(iface); + sc->sc_iface[nports] = id->bInterfaceNumber; + + if (bootverbose && sc->sc_xfer[nports][U3G_INTR]) { + device_printf(dev, "port %d supports modem control", + nports); + } + /* set stall by default */ mtx_lock(&sc->sc_mtx); usbd_xfer_set_stall(sc->sc_xfer[nports][U3G_BULK_WR]); @@ -926,6 +958,9 @@ u3g_start_read(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; + /* start interrupt endpoint (if configured) */ + usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_INTR]); + /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_RD]); } @@ -935,6 +970,9 @@ u3g_stop_read(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; + /* stop interrupt endpoint (if configured) */ + usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_INTR]); + /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_RD]); } @@ -1012,3 +1050,134 @@ tr_setup: break; } } + +static void +u3g_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) +{ + struct u3g_softc *sc = ucom->sc_parent; + + *lsr = sc->sc_lsr[ucom->sc_subunit]; + *msr = sc->sc_msr[ucom->sc_subunit]; +} + +static void +u3g_cfg_set_line(struct ucom_softc *ucom) +{ + struct u3g_softc *sc = ucom->sc_parent; + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bRequest = UCDC_SET_CONTROL_LINE_STATE; + USETW(req.wValue, sc->sc_line[ucom->sc_subunit]); + req.wIndex[0] = sc->sc_iface[ucom->sc_subunit]; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + ucom_cfg_do_request(sc->sc_udev, ucom, + &req, NULL, 0, 1000); +} + +static void +u3g_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) +{ + struct u3g_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + sc->sc_line[ucom->sc_subunit] |= UCDC_LINE_DTR; + else + sc->sc_line[ucom->sc_subunit] &= ~UCDC_LINE_DTR; + + u3g_cfg_set_line(ucom); +} + +static void +u3g_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) +{ + struct u3g_softc *sc = ucom->sc_parent; + + DPRINTF("onoff = %d\n", onoff); + + if (onoff) + sc->sc_line[ucom->sc_subunit] |= UCDC_LINE_RTS; + else + sc->sc_line[ucom->sc_subunit] &= ~UCDC_LINE_RTS; + + u3g_cfg_set_line(ucom); +} + +static void +u3g_intr_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ucom_softc *ucom = usbd_xfer_softc(xfer); + struct u3g_softc *sc = ucom->sc_parent; + struct usb_page_cache *pc; + struct usb_cdc_notification pkt; + int actlen; + uint16_t wLen; + uint8_t mstatus; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + if (actlen < 8) { /* usb_cdc_notification with 2 data bytes */ + DPRINTF("message too short (expected 8, received %d)\n", actlen); + goto tr_setup; + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &pkt, actlen); + + wLen = UGETW(pkt.wLength); + if (wLen < 2) { + DPRINTF("message too short (expected 2 data bytes, received %d)\n", wLen); + goto tr_setup; + } + + if (pkt.bmRequestType == UCDC_NOTIFICATION + && pkt.bNotification == UCDC_N_SERIAL_STATE) { + /* + * Set the serial state in ucom driver based on + * the bits from the notify message + */ + DPRINTF("notify bytes = 0x%02x, 0x%02x\n", + pkt.data[0], pkt.data[1]); + + /* currently, lsr is always zero. */ + sc->sc_lsr[ucom->sc_subunit] = 0; + sc->sc_msr[ucom->sc_subunit] = 0; + + mstatus = pkt.data[0]; + + if (mstatus & UCDC_N_SERIAL_RI) + sc->sc_msr[ucom->sc_subunit] |= SER_RI; + if (mstatus & UCDC_N_SERIAL_DSR) + sc->sc_msr[ucom->sc_subunit] |= SER_DSR; + if (mstatus & UCDC_N_SERIAL_DCD) + sc->sc_msr[ucom->sc_subunit] |= SER_DCD; + ucom_status_change(ucom); + } + + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static void +u3g_poll(struct ucom_softc *ucom) +{ + struct u3g_softc *sc = ucom->sc_parent; + usbd_transfer_poll(sc->sc_xfer[ucom->sc_subunit], U3G_N_TRANSFER); +}