149901133SHans Petter Selasky /*- 249901133SHans Petter Selasky * Copyright (c) 2011 Anybots Inc 349901133SHans Petter Selasky * written by Akinori Furukoshi <moonlightakkiy@yahoo.ca> 449901133SHans Petter Selasky * - ucom part is based on u3g.c 549901133SHans Petter Selasky * 649901133SHans Petter Selasky * Redistribution and use in source and binary forms, with or without 749901133SHans Petter Selasky * modification, are permitted provided that the following conditions 849901133SHans Petter Selasky * are met: 949901133SHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 1049901133SHans Petter Selasky * notice, this list of conditions and the following disclaimer. 1149901133SHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 1249901133SHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 1349901133SHans Petter Selasky * documentation and/or other materials provided with the distribution. 1449901133SHans Petter Selasky * 1549901133SHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1649901133SHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1749901133SHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1849901133SHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1949901133SHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2049901133SHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2149901133SHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2249901133SHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2349901133SHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2449901133SHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2549901133SHans Petter Selasky * SUCH DAMAGE. 2649901133SHans Petter Selasky */ 2749901133SHans Petter Selasky 2849901133SHans Petter Selasky #include <sys/cdefs.h> 2949901133SHans Petter Selasky __FBSDID("$FreeBSD$"); 3049901133SHans Petter Selasky 3149901133SHans Petter Selasky #include <sys/param.h> 3249901133SHans Petter Selasky #include <sys/systm.h> 3349901133SHans Petter Selasky #include <sys/queue.h> 3449901133SHans Petter Selasky #include <sys/systm.h> 3576039bc8SGleb Smirnoff #include <sys/socket.h> 3649901133SHans Petter Selasky #include <sys/kernel.h> 3749901133SHans Petter Selasky #include <sys/bus.h> 3849901133SHans Petter Selasky #include <sys/module.h> 3949901133SHans Petter Selasky #include <sys/sockio.h> 4049901133SHans Petter Selasky #include <sys/socket.h> 4149901133SHans Petter Selasky #include <sys/lock.h> 4249901133SHans Petter Selasky #include <sys/mutex.h> 4349901133SHans Petter Selasky #include <sys/condvar.h> 4449901133SHans Petter Selasky #include <sys/sysctl.h> 4549901133SHans Petter Selasky #include <sys/malloc.h> 4649901133SHans Petter Selasky #include <sys/taskqueue.h> 4749901133SHans Petter Selasky 4876039bc8SGleb Smirnoff #include <net/if.h> 4976039bc8SGleb Smirnoff #include <net/if_var.h> 5076039bc8SGleb Smirnoff 5149901133SHans Petter Selasky #include <machine/bus.h> 5249901133SHans Petter Selasky 5349901133SHans Petter Selasky #include <net/if.h> 5449901133SHans Petter Selasky #include <net/if_types.h> 5549901133SHans Petter Selasky #include <net/netisr.h> 5649901133SHans Petter Selasky #include <net/bpf.h> 5749901133SHans Petter Selasky #include <net/ethernet.h> 5849901133SHans Petter Selasky 5949901133SHans Petter Selasky #include <netinet/in.h> 6049901133SHans Petter Selasky #include <netinet/ip.h> 6149901133SHans Petter Selasky #include <netinet/ip6.h> 6249901133SHans Petter Selasky #include <netinet/udp.h> 6349901133SHans Petter Selasky 6449901133SHans Petter Selasky #include <net80211/ieee80211_ioctl.h> 6549901133SHans Petter Selasky 6649901133SHans Petter Selasky #include <dev/usb/usb.h> 6749901133SHans Petter Selasky #include <dev/usb/usbdi.h> 6849901133SHans Petter Selasky #include <dev/usb/usbdi_util.h> 6949901133SHans Petter Selasky #include <dev/usb/usb_cdc.h> 7049901133SHans Petter Selasky #include "usbdevs.h" 7149901133SHans Petter Selasky 7249901133SHans Petter Selasky #define USB_DEBUG_VAR usie_debug 7349901133SHans Petter Selasky #include <dev/usb/usb_debug.h> 7449901133SHans Petter Selasky #include <dev/usb/usb_process.h> 7549901133SHans Petter Selasky #include <dev/usb/usb_msctest.h> 7649901133SHans Petter Selasky 7749901133SHans Petter Selasky #include <dev/usb/serial/usb_serial.h> 7849901133SHans Petter Selasky 7949901133SHans Petter Selasky #include <dev/usb/net/if_usievar.h> 8049901133SHans Petter Selasky 8149901133SHans Petter Selasky #ifdef USB_DEBUG 8249901133SHans Petter Selasky static int usie_debug = 0; 8349901133SHans Petter Selasky 846472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, usie, CTLFLAG_RW, 0, "sierra USB modem"); 85*ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_usie, OID_AUTO, debug, CTLFLAG_RWTUN, &usie_debug, 0, 8649901133SHans Petter Selasky "usie debug level"); 8749901133SHans Petter Selasky #endif 8849901133SHans Petter Selasky 8949901133SHans Petter Selasky /* Sierra Wireless Direct IP modems */ 9049901133SHans Petter Selasky static const STRUCT_USB_HOST_ID usie_devs[] = { 9149901133SHans Petter Selasky #define USIE_DEV(v, d) { \ 9249901133SHans Petter Selasky USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##d) } 9349901133SHans Petter Selasky USIE_DEV(SIERRA, MC8700), 9449901133SHans Petter Selasky USIE_DEV(SIERRA, TRUINSTALL), 9549901133SHans Petter Selasky USIE_DEV(AIRPRIME, USB308), 9649901133SHans Petter Selasky #undef USIE_DEV 9749901133SHans Petter Selasky }; 9849901133SHans Petter Selasky 9949901133SHans Petter Selasky static device_probe_t usie_probe; 10049901133SHans Petter Selasky static device_attach_t usie_attach; 10149901133SHans Petter Selasky static device_detach_t usie_detach; 102c01fc06eSHans Petter Selasky static void usie_free_softc(struct usie_softc *); 10349901133SHans Petter Selasky 1045805d178SHans Petter Selasky static void usie_free(struct ucom_softc *); 10549901133SHans Petter Selasky static void usie_uc_update_line_state(struct ucom_softc *, uint8_t); 10649901133SHans Petter Selasky static void usie_uc_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); 10749901133SHans Petter Selasky static void usie_uc_cfg_set_dtr(struct ucom_softc *, uint8_t); 10849901133SHans Petter Selasky static void usie_uc_cfg_set_rts(struct ucom_softc *, uint8_t); 10949901133SHans Petter Selasky static void usie_uc_cfg_open(struct ucom_softc *); 11049901133SHans Petter Selasky static void usie_uc_cfg_close(struct ucom_softc *); 11149901133SHans Petter Selasky static void usie_uc_start_read(struct ucom_softc *); 11249901133SHans Petter Selasky static void usie_uc_stop_read(struct ucom_softc *); 11349901133SHans Petter Selasky static void usie_uc_start_write(struct ucom_softc *); 11449901133SHans Petter Selasky static void usie_uc_stop_write(struct ucom_softc *); 11549901133SHans Petter Selasky 11649901133SHans Petter Selasky static usb_callback_t usie_uc_tx_callback; 11749901133SHans Petter Selasky static usb_callback_t usie_uc_rx_callback; 11849901133SHans Petter Selasky static usb_callback_t usie_uc_status_callback; 11949901133SHans Petter Selasky static usb_callback_t usie_if_tx_callback; 12049901133SHans Petter Selasky static usb_callback_t usie_if_rx_callback; 12149901133SHans Petter Selasky static usb_callback_t usie_if_status_callback; 12249901133SHans Petter Selasky 12349901133SHans Petter Selasky static void usie_if_sync_to(void *); 12449901133SHans Petter Selasky static void usie_if_sync_cb(void *, int); 12549901133SHans Petter Selasky static void usie_if_status_cb(void *, int); 12649901133SHans Petter Selasky 12749901133SHans Petter Selasky static void usie_if_start(struct ifnet *); 12847e8d432SGleb Smirnoff static int usie_if_output(struct ifnet *, struct mbuf *, 12947e8d432SGleb Smirnoff const struct sockaddr *, struct route *); 13049901133SHans Petter Selasky static void usie_if_init(void *); 13149901133SHans Petter Selasky static void usie_if_stop(struct usie_softc *); 13249901133SHans Petter Selasky static int usie_if_ioctl(struct ifnet *, u_long, caddr_t); 13349901133SHans Petter Selasky 13449901133SHans Petter Selasky static int usie_do_request(struct usie_softc *, struct usb_device_request *, void *); 13549901133SHans Petter Selasky static int usie_if_cmd(struct usie_softc *, uint8_t); 13649901133SHans Petter Selasky static void usie_cns_req(struct usie_softc *, uint32_t, uint16_t); 13749901133SHans Petter Selasky static void usie_cns_rsp(struct usie_softc *, struct usie_cns *); 13849901133SHans Petter Selasky static void usie_hip_rsp(struct usie_softc *, uint8_t *, uint32_t); 13949901133SHans Petter Selasky static int usie_driver_loaded(struct module *, int, void *); 14049901133SHans Petter Selasky 14149901133SHans Petter Selasky static const struct usb_config usie_uc_config[USIE_UC_N_XFER] = { 14249901133SHans Petter Selasky [USIE_UC_STATUS] = { 14349901133SHans Petter Selasky .type = UE_INTERRUPT, 14449901133SHans Petter Selasky .endpoint = UE_ADDR_ANY, 14549901133SHans Petter Selasky .direction = UE_DIR_IN, 14649901133SHans Petter Selasky .bufsize = 0, /* use wMaxPacketSize */ 14749901133SHans Petter Selasky .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 14849901133SHans Petter Selasky .callback = &usie_uc_status_callback, 14949901133SHans Petter Selasky }, 15049901133SHans Petter Selasky [USIE_UC_RX] = { 15149901133SHans Petter Selasky .type = UE_BULK, 15249901133SHans Petter Selasky .endpoint = UE_ADDR_ANY, 15349901133SHans Petter Selasky .direction = UE_DIR_IN, 15449901133SHans Petter Selasky .bufsize = USIE_BUFSIZE, 15549901133SHans Petter Selasky .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,}, 15649901133SHans Petter Selasky .callback = &usie_uc_rx_callback, 15749901133SHans Petter Selasky }, 15849901133SHans Petter Selasky [USIE_UC_TX] = { 15949901133SHans Petter Selasky .type = UE_BULK, 16049901133SHans Petter Selasky .endpoint = UE_ADDR_ANY, 16149901133SHans Petter Selasky .direction = UE_DIR_OUT, 16249901133SHans Petter Selasky .bufsize = USIE_BUFSIZE, 16349901133SHans Petter Selasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 16449901133SHans Petter Selasky .callback = &usie_uc_tx_callback, 16549901133SHans Petter Selasky } 16649901133SHans Petter Selasky }; 16749901133SHans Petter Selasky 16849901133SHans Petter Selasky static const struct usb_config usie_if_config[USIE_IF_N_XFER] = { 16949901133SHans Petter Selasky [USIE_IF_STATUS] = { 17049901133SHans Petter Selasky .type = UE_INTERRUPT, 17149901133SHans Petter Selasky .endpoint = UE_ADDR_ANY, 17249901133SHans Petter Selasky .direction = UE_DIR_IN, 17349901133SHans Petter Selasky .bufsize = 0, /* use wMaxPacketSize */ 17449901133SHans Petter Selasky .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 17549901133SHans Petter Selasky .callback = &usie_if_status_callback, 17649901133SHans Petter Selasky }, 17749901133SHans Petter Selasky [USIE_IF_RX] = { 17849901133SHans Petter Selasky .type = UE_BULK, 17949901133SHans Petter Selasky .endpoint = UE_ADDR_ANY, 18049901133SHans Petter Selasky .direction = UE_DIR_IN, 18149901133SHans Petter Selasky .bufsize = USIE_BUFSIZE, 18249901133SHans Petter Selasky .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, 18349901133SHans Petter Selasky .callback = &usie_if_rx_callback, 18449901133SHans Petter Selasky }, 18549901133SHans Petter Selasky [USIE_IF_TX] = { 18649901133SHans Petter Selasky .type = UE_BULK, 18749901133SHans Petter Selasky .endpoint = UE_ADDR_ANY, 18849901133SHans Petter Selasky .direction = UE_DIR_OUT, 18949901133SHans Petter Selasky .bufsize = MAX(USIE_BUFSIZE, MCLBYTES), 19049901133SHans Petter Selasky .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, 19149901133SHans Petter Selasky .callback = &usie_if_tx_callback, 19249901133SHans Petter Selasky } 19349901133SHans Petter Selasky }; 19449901133SHans Petter Selasky 19549901133SHans Petter Selasky static device_method_t usie_methods[] = { 19649901133SHans Petter Selasky DEVMETHOD(device_probe, usie_probe), 19749901133SHans Petter Selasky DEVMETHOD(device_attach, usie_attach), 19849901133SHans Petter Selasky DEVMETHOD(device_detach, usie_detach), 1995805d178SHans Petter Selasky DEVMETHOD_END 20049901133SHans Petter Selasky }; 20149901133SHans Petter Selasky 20249901133SHans Petter Selasky static driver_t usie_driver = { 20349901133SHans Petter Selasky .name = "usie", 20449901133SHans Petter Selasky .methods = usie_methods, 20549901133SHans Petter Selasky .size = sizeof(struct usie_softc), 20649901133SHans Petter Selasky }; 20749901133SHans Petter Selasky 20849901133SHans Petter Selasky static devclass_t usie_devclass; 20949901133SHans Petter Selasky static eventhandler_tag usie_etag; 21049901133SHans Petter Selasky 21149901133SHans Petter Selasky DRIVER_MODULE(usie, uhub, usie_driver, usie_devclass, usie_driver_loaded, 0); 21249901133SHans Petter Selasky MODULE_DEPEND(usie, ucom, 1, 1, 1); 21349901133SHans Petter Selasky MODULE_DEPEND(usie, usb, 1, 1, 1); 21449901133SHans Petter Selasky MODULE_VERSION(usie, 1); 21549901133SHans Petter Selasky 21649901133SHans Petter Selasky static const struct ucom_callback usie_uc_callback = { 21749901133SHans Petter Selasky .ucom_cfg_get_status = &usie_uc_cfg_get_status, 21849901133SHans Petter Selasky .ucom_cfg_set_dtr = &usie_uc_cfg_set_dtr, 21949901133SHans Petter Selasky .ucom_cfg_set_rts = &usie_uc_cfg_set_rts, 22049901133SHans Petter Selasky .ucom_cfg_open = &usie_uc_cfg_open, 22149901133SHans Petter Selasky .ucom_cfg_close = &usie_uc_cfg_close, 22249901133SHans Petter Selasky .ucom_start_read = &usie_uc_start_read, 22349901133SHans Petter Selasky .ucom_stop_read = &usie_uc_stop_read, 22449901133SHans Petter Selasky .ucom_start_write = &usie_uc_start_write, 22549901133SHans Petter Selasky .ucom_stop_write = &usie_uc_stop_write, 2265805d178SHans Petter Selasky .ucom_free = &usie_free, 22749901133SHans Petter Selasky }; 22849901133SHans Petter Selasky 22949901133SHans Petter Selasky static void 23049901133SHans Petter Selasky usie_autoinst(void *arg, struct usb_device *udev, 23149901133SHans Petter Selasky struct usb_attach_arg *uaa) 23249901133SHans Petter Selasky { 23349901133SHans Petter Selasky struct usb_interface *iface; 23449901133SHans Petter Selasky struct usb_interface_descriptor *id; 23549901133SHans Petter Selasky struct usb_device_request req; 23649901133SHans Petter Selasky int err; 23749901133SHans Petter Selasky 23849901133SHans Petter Selasky if (uaa->dev_state != UAA_DEV_READY) 23949901133SHans Petter Selasky return; 24049901133SHans Petter Selasky 24149901133SHans Petter Selasky iface = usbd_get_iface(udev, 0); 24249901133SHans Petter Selasky if (iface == NULL) 24349901133SHans Petter Selasky return; 24449901133SHans Petter Selasky 24549901133SHans Petter Selasky id = iface->idesc; 24649901133SHans Petter Selasky if (id == NULL || id->bInterfaceClass != UICLASS_MASS) 24749901133SHans Petter Selasky return; 24849901133SHans Petter Selasky 24949901133SHans Petter Selasky if (usbd_lookup_id_by_uaa(usie_devs, sizeof(usie_devs), uaa) != 0) 25049901133SHans Petter Selasky return; /* no device match */ 25149901133SHans Petter Selasky 25249901133SHans Petter Selasky if (bootverbose) { 25349901133SHans Petter Selasky DPRINTF("Ejecting %s %s\n", 25449901133SHans Petter Selasky usb_get_manufacturer(udev), 25549901133SHans Petter Selasky usb_get_product(udev)); 25649901133SHans Petter Selasky } 25749901133SHans Petter Selasky req.bmRequestType = UT_VENDOR; 25849901133SHans Petter Selasky req.bRequest = UR_SET_INTERFACE; 25949901133SHans Petter Selasky USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); 26049901133SHans Petter Selasky USETW(req.wIndex, UHF_PORT_CONNECTION); 26149901133SHans Petter Selasky USETW(req.wLength, 0); 26249901133SHans Petter Selasky 26349901133SHans Petter Selasky /* at this moment there is no mutex */ 26449901133SHans Petter Selasky err = usbd_do_request_flags(udev, NULL, &req, 26549901133SHans Petter Selasky NULL, 0, NULL, 250 /* ms */ ); 26649901133SHans Petter Selasky 26749901133SHans Petter Selasky /* success, mark the udev as disappearing */ 26849901133SHans Petter Selasky if (err == 0) 26949901133SHans Petter Selasky uaa->dev_state = UAA_DEV_EJECTING; 27049901133SHans Petter Selasky } 27149901133SHans Petter Selasky 27249901133SHans Petter Selasky static int 27349901133SHans Petter Selasky usie_probe(device_t self) 27449901133SHans Petter Selasky { 27549901133SHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(self); 27649901133SHans Petter Selasky 27749901133SHans Petter Selasky if (uaa->usb_mode != USB_MODE_HOST) 27849901133SHans Petter Selasky return (ENXIO); 27949901133SHans Petter Selasky if (uaa->info.bConfigIndex != USIE_CNFG_INDEX) 28049901133SHans Petter Selasky return (ENXIO); 28149901133SHans Petter Selasky if (uaa->info.bIfaceIndex != USIE_IFACE_INDEX) 28249901133SHans Petter Selasky return (ENXIO); 28349901133SHans Petter Selasky if (uaa->info.bInterfaceClass != UICLASS_VENDOR) 28449901133SHans Petter Selasky return (ENXIO); 28549901133SHans Petter Selasky 28649901133SHans Petter Selasky return (usbd_lookup_id_by_uaa(usie_devs, sizeof(usie_devs), uaa)); 28749901133SHans Petter Selasky } 28849901133SHans Petter Selasky 28949901133SHans Petter Selasky static int 29049901133SHans Petter Selasky usie_attach(device_t self) 29149901133SHans Petter Selasky { 29249901133SHans Petter Selasky struct usie_softc *sc = device_get_softc(self); 29349901133SHans Petter Selasky struct usb_attach_arg *uaa = device_get_ivars(self); 29449901133SHans Petter Selasky struct ifnet *ifp; 29549901133SHans Petter Selasky struct usb_interface *iface; 29649901133SHans Petter Selasky struct usb_interface_descriptor *id; 29749901133SHans Petter Selasky struct usb_device_request req; 29849901133SHans Petter Selasky int err; 29949901133SHans Petter Selasky uint16_t fwattr; 30049901133SHans Petter Selasky uint8_t iface_index; 30149901133SHans Petter Selasky uint8_t ifidx; 30249901133SHans Petter Selasky uint8_t start; 30349901133SHans Petter Selasky 30449901133SHans Petter Selasky device_set_usb_desc(self); 30549901133SHans Petter Selasky sc->sc_udev = uaa->device; 30649901133SHans Petter Selasky sc->sc_dev = self; 30749901133SHans Petter Selasky 30849901133SHans Petter Selasky mtx_init(&sc->sc_mtx, "usie", MTX_NETWORK_LOCK, MTX_DEF); 3095805d178SHans Petter Selasky ucom_ref(&sc->sc_super_ucom); 31049901133SHans Petter Selasky 31149901133SHans Petter Selasky TASK_INIT(&sc->sc_if_status_task, 0, usie_if_status_cb, sc); 31249901133SHans Petter Selasky TASK_INIT(&sc->sc_if_sync_task, 0, usie_if_sync_cb, sc); 31349901133SHans Petter Selasky 31449901133SHans Petter Selasky usb_callout_init_mtx(&sc->sc_if_sync_ch, &sc->sc_mtx, 0); 31549901133SHans Petter Selasky 31649901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 31749901133SHans Petter Selasky 31849901133SHans Petter Selasky /* set power mode to D0 */ 31949901133SHans Petter Selasky req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 32049901133SHans Petter Selasky req.bRequest = USIE_POWER; 32149901133SHans Petter Selasky USETW(req.wValue, 0); 32249901133SHans Petter Selasky USETW(req.wIndex, 0); 32349901133SHans Petter Selasky USETW(req.wLength, 0); 32449901133SHans Petter Selasky if (usie_do_request(sc, &req, NULL)) { 32549901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 32649901133SHans Petter Selasky goto detach; 32749901133SHans Petter Selasky } 32849901133SHans Petter Selasky /* read fw attr */ 32949901133SHans Petter Selasky fwattr = 0; 33049901133SHans Petter Selasky req.bmRequestType = UT_READ_VENDOR_DEVICE; 33149901133SHans Petter Selasky req.bRequest = USIE_FW_ATTR; 33249901133SHans Petter Selasky USETW(req.wValue, 0); 33349901133SHans Petter Selasky USETW(req.wIndex, 0); 33449901133SHans Petter Selasky USETW(req.wLength, sizeof(fwattr)); 33549901133SHans Petter Selasky if (usie_do_request(sc, &req, &fwattr)) { 33649901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 33749901133SHans Petter Selasky goto detach; 33849901133SHans Petter Selasky } 33949901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 34049901133SHans Petter Selasky 34149901133SHans Petter Selasky /* check DHCP supports */ 34249901133SHans Petter Selasky DPRINTF("fwattr=%x\n", fwattr); 34349901133SHans Petter Selasky if (!(fwattr & USIE_FW_DHCP)) { 34449901133SHans Petter Selasky device_printf(self, "DHCP is not supported. A firmware upgrade might be needed.\n"); 34549901133SHans Petter Selasky } 34649901133SHans Petter Selasky 34749901133SHans Petter Selasky /* find available interfaces */ 34849901133SHans Petter Selasky sc->sc_nucom = 0; 34949901133SHans Petter Selasky for (ifidx = 0; ifidx < USIE_IFACE_MAX; ifidx++) { 35049901133SHans Petter Selasky iface = usbd_get_iface(uaa->device, ifidx); 35149901133SHans Petter Selasky if (iface == NULL) 35249901133SHans Petter Selasky break; 35349901133SHans Petter Selasky 35449901133SHans Petter Selasky id = usbd_get_interface_descriptor(iface); 35549901133SHans Petter Selasky if ((id == NULL) || (id->bInterfaceClass != UICLASS_VENDOR)) 35649901133SHans Petter Selasky continue; 35749901133SHans Petter Selasky 35849901133SHans Petter Selasky /* setup Direct IP transfer */ 35949901133SHans Petter Selasky if (id->bInterfaceNumber >= 7 && id->bNumEndpoints == 3) { 36049901133SHans Petter Selasky sc->sc_if_ifnum = id->bInterfaceNumber; 36149901133SHans Petter Selasky iface_index = ifidx; 36249901133SHans Petter Selasky 36349901133SHans Petter Selasky DPRINTF("ifnum=%d, ifidx=%d\n", 36449901133SHans Petter Selasky sc->sc_if_ifnum, ifidx); 36549901133SHans Petter Selasky 36649901133SHans Petter Selasky err = usbd_transfer_setup(uaa->device, 36749901133SHans Petter Selasky &iface_index, sc->sc_if_xfer, usie_if_config, 36849901133SHans Petter Selasky USIE_IF_N_XFER, sc, &sc->sc_mtx); 36949901133SHans Petter Selasky 37049901133SHans Petter Selasky if (err == 0) 37149901133SHans Petter Selasky continue; 37249901133SHans Petter Selasky 37349901133SHans Petter Selasky device_printf(self, 37449901133SHans Petter Selasky "could not allocate USB transfers on " 37549901133SHans Petter Selasky "iface_index=%d, err=%s\n", 37649901133SHans Petter Selasky iface_index, usbd_errstr(err)); 37749901133SHans Petter Selasky goto detach; 37849901133SHans Petter Selasky } 37949901133SHans Petter Selasky 38049901133SHans Petter Selasky /* setup ucom */ 38149901133SHans Petter Selasky if (sc->sc_nucom >= USIE_UCOM_MAX) 38249901133SHans Petter Selasky continue; 38349901133SHans Petter Selasky 38449901133SHans Petter Selasky usbd_set_parent_iface(uaa->device, ifidx, 38549901133SHans Petter Selasky uaa->info.bIfaceIndex); 38649901133SHans Petter Selasky 38749901133SHans Petter Selasky DPRINTF("NumEndpoints=%d bInterfaceNumber=%d\n", 38849901133SHans Petter Selasky id->bNumEndpoints, id->bInterfaceNumber); 38949901133SHans Petter Selasky 39049901133SHans Petter Selasky if (id->bNumEndpoints == 2) { 39149901133SHans Petter Selasky sc->sc_uc_xfer[sc->sc_nucom][0] = NULL; 39249901133SHans Petter Selasky start = 1; 39349901133SHans Petter Selasky } else 39449901133SHans Petter Selasky start = 0; 39549901133SHans Petter Selasky 39649901133SHans Petter Selasky err = usbd_transfer_setup(uaa->device, &ifidx, 39749901133SHans Petter Selasky sc->sc_uc_xfer[sc->sc_nucom] + start, 39849901133SHans Petter Selasky usie_uc_config + start, USIE_UC_N_XFER - start, 39949901133SHans Petter Selasky &sc->sc_ucom[sc->sc_nucom], &sc->sc_mtx); 40049901133SHans Petter Selasky 40149901133SHans Petter Selasky if (err != 0) { 40249901133SHans Petter Selasky DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(err)); 40349901133SHans Petter Selasky continue; 40449901133SHans Petter Selasky } 40549901133SHans Petter Selasky 40649901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 40749901133SHans Petter Selasky for (; start < USIE_UC_N_XFER; start++) 40849901133SHans Petter Selasky usbd_xfer_set_stall(sc->sc_uc_xfer[sc->sc_nucom][start]); 40949901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 41049901133SHans Petter Selasky 41149901133SHans Petter Selasky sc->sc_uc_ifnum[sc->sc_nucom] = id->bInterfaceNumber; 41249901133SHans Petter Selasky 41349901133SHans Petter Selasky sc->sc_nucom++; /* found a port */ 41449901133SHans Petter Selasky } 41549901133SHans Petter Selasky 41649901133SHans Petter Selasky if (sc->sc_nucom == 0) { 41749901133SHans Petter Selasky device_printf(self, "no comports found\n"); 41849901133SHans Petter Selasky goto detach; 41949901133SHans Petter Selasky } 42049901133SHans Petter Selasky 42149901133SHans Petter Selasky err = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, 42249901133SHans Petter Selasky sc->sc_nucom, sc, &usie_uc_callback, &sc->sc_mtx); 42349901133SHans Petter Selasky 42449901133SHans Petter Selasky if (err != 0) { 42549901133SHans Petter Selasky DPRINTF("ucom_attach failed\n"); 42649901133SHans Petter Selasky goto detach; 42749901133SHans Petter Selasky } 42849901133SHans Petter Selasky DPRINTF("Found %d interfaces.\n", sc->sc_nucom); 42949901133SHans Petter Selasky 43049901133SHans Petter Selasky /* setup ifnet (Direct IP) */ 43149901133SHans Petter Selasky sc->sc_ifp = ifp = if_alloc(IFT_OTHER); 43249901133SHans Petter Selasky 43349901133SHans Petter Selasky if (ifp == NULL) { 43449901133SHans Petter Selasky device_printf(self, "Could not allocate a network interface\n"); 43549901133SHans Petter Selasky goto detach; 43649901133SHans Petter Selasky } 43749901133SHans Petter Selasky if_initname(ifp, "usie", device_get_unit(self)); 43849901133SHans Petter Selasky 43949901133SHans Petter Selasky ifp->if_softc = sc; 44049901133SHans Petter Selasky ifp->if_mtu = USIE_MTU_MAX; 44149901133SHans Petter Selasky ifp->if_flags |= IFF_NOARP; 44249901133SHans Petter Selasky ifp->if_init = usie_if_init; 44349901133SHans Petter Selasky ifp->if_ioctl = usie_if_ioctl; 44449901133SHans Petter Selasky ifp->if_start = usie_if_start; 44549901133SHans Petter Selasky ifp->if_output = usie_if_output; 44649901133SHans Petter Selasky IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 44749901133SHans Petter Selasky ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 44849901133SHans Petter Selasky IFQ_SET_READY(&ifp->if_snd); 44949901133SHans Petter Selasky 45049901133SHans Petter Selasky if_attach(ifp); 45149901133SHans Petter Selasky bpfattach(ifp, DLT_RAW, 0); 45249901133SHans Petter Selasky 45349901133SHans Petter Selasky if (fwattr & USIE_PM_AUTO) { 45449901133SHans Petter Selasky usbd_set_power_mode(uaa->device, USB_POWER_MODE_SAVE); 45549901133SHans Petter Selasky DPRINTF("enabling automatic suspend and resume\n"); 45649901133SHans Petter Selasky } else { 45749901133SHans Petter Selasky usbd_set_power_mode(uaa->device, USB_POWER_MODE_ON); 45849901133SHans Petter Selasky DPRINTF("USB power is always ON\n"); 45949901133SHans Petter Selasky } 46049901133SHans Petter Selasky 46149901133SHans Petter Selasky DPRINTF("device attached\n"); 46249901133SHans Petter Selasky return (0); 46349901133SHans Petter Selasky 46449901133SHans Petter Selasky detach: 46549901133SHans Petter Selasky usie_detach(self); 46649901133SHans Petter Selasky return (ENOMEM); 46749901133SHans Petter Selasky } 46849901133SHans Petter Selasky 46949901133SHans Petter Selasky static int 47049901133SHans Petter Selasky usie_detach(device_t self) 47149901133SHans Petter Selasky { 47249901133SHans Petter Selasky struct usie_softc *sc = device_get_softc(self); 47349901133SHans Petter Selasky uint8_t x; 47449901133SHans Petter Selasky 47549901133SHans Petter Selasky /* detach ifnet */ 47649901133SHans Petter Selasky if (sc->sc_ifp != NULL) { 47749901133SHans Petter Selasky usie_if_stop(sc); 47849901133SHans Petter Selasky usbd_transfer_unsetup(sc->sc_if_xfer, USIE_IF_N_XFER); 47949901133SHans Petter Selasky bpfdetach(sc->sc_ifp); 48049901133SHans Petter Selasky if_detach(sc->sc_ifp); 48149901133SHans Petter Selasky if_free(sc->sc_ifp); 48249901133SHans Petter Selasky sc->sc_ifp = NULL; 48349901133SHans Petter Selasky } 48449901133SHans Petter Selasky /* detach ucom */ 48549901133SHans Petter Selasky if (sc->sc_nucom > 0) 48649901133SHans Petter Selasky ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); 48749901133SHans Petter Selasky 48849901133SHans Petter Selasky /* stop all USB transfers */ 48949901133SHans Petter Selasky usbd_transfer_unsetup(sc->sc_if_xfer, USIE_IF_N_XFER); 49049901133SHans Petter Selasky 49149901133SHans Petter Selasky for (x = 0; x != USIE_UCOM_MAX; x++) 49249901133SHans Petter Selasky usbd_transfer_unsetup(sc->sc_uc_xfer[x], USIE_UC_N_XFER); 49349901133SHans Petter Selasky 49449901133SHans Petter Selasky 495c01fc06eSHans Petter Selasky device_claim_softc(self); 496c01fc06eSHans Petter Selasky 497c01fc06eSHans Petter Selasky usie_free_softc(sc); 498c01fc06eSHans Petter Selasky 49949901133SHans Petter Selasky return (0); 50049901133SHans Petter Selasky } 50149901133SHans Petter Selasky 5025805d178SHans Petter Selasky UCOM_UNLOAD_DRAIN(usie); 5035805d178SHans Petter Selasky 5045805d178SHans Petter Selasky static void 505c01fc06eSHans Petter Selasky usie_free_softc(struct usie_softc *sc) 5065805d178SHans Petter Selasky { 5075805d178SHans Petter Selasky if (ucom_unref(&sc->sc_super_ucom)) { 5085805d178SHans Petter Selasky mtx_destroy(&sc->sc_mtx); 509c01fc06eSHans Petter Selasky device_free_softc(sc); 5105805d178SHans Petter Selasky } 5115805d178SHans Petter Selasky } 5125805d178SHans Petter Selasky 5135805d178SHans Petter Selasky static void 5145805d178SHans Petter Selasky usie_free(struct ucom_softc *ucom) 5155805d178SHans Petter Selasky { 516c01fc06eSHans Petter Selasky usie_free_softc(ucom->sc_parent); 5175805d178SHans Petter Selasky } 5185805d178SHans Petter Selasky 51949901133SHans Petter Selasky static void 52049901133SHans Petter Selasky usie_uc_update_line_state(struct ucom_softc *ucom, uint8_t ls) 52149901133SHans Petter Selasky { 52249901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 52349901133SHans Petter Selasky struct usb_device_request req; 52449901133SHans Petter Selasky 52549901133SHans Petter Selasky if (sc->sc_uc_xfer[ucom->sc_subunit][USIE_UC_STATUS] == NULL) 52649901133SHans Petter Selasky return; 52749901133SHans Petter Selasky 52849901133SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 52949901133SHans Petter Selasky req.bRequest = USIE_LINK_STATE; 53049901133SHans Petter Selasky USETW(req.wValue, ls); 53149901133SHans Petter Selasky USETW(req.wIndex, sc->sc_uc_ifnum[ucom->sc_subunit]); 53249901133SHans Petter Selasky USETW(req.wLength, 0); 53349901133SHans Petter Selasky 53449901133SHans Petter Selasky DPRINTF("sc_uc_ifnum=%d\n", sc->sc_uc_ifnum[ucom->sc_subunit]); 53549901133SHans Petter Selasky 53649901133SHans Petter Selasky usie_do_request(sc, &req, NULL); 53749901133SHans Petter Selasky } 53849901133SHans Petter Selasky 53949901133SHans Petter Selasky static void 54049901133SHans Petter Selasky usie_uc_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) 54149901133SHans Petter Selasky { 54249901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 54349901133SHans Petter Selasky 54449901133SHans Petter Selasky *msr = sc->sc_msr; 54549901133SHans Petter Selasky *lsr = sc->sc_lsr; 54649901133SHans Petter Selasky } 54749901133SHans Petter Selasky 54849901133SHans Petter Selasky static void 54949901133SHans Petter Selasky usie_uc_cfg_set_dtr(struct ucom_softc *ucom, uint8_t flag) 55049901133SHans Petter Selasky { 55149901133SHans Petter Selasky uint8_t dtr; 55249901133SHans Petter Selasky 55349901133SHans Petter Selasky dtr = flag ? USIE_LS_DTR : 0; 55449901133SHans Petter Selasky usie_uc_update_line_state(ucom, dtr); 55549901133SHans Petter Selasky } 55649901133SHans Petter Selasky 55749901133SHans Petter Selasky static void 55849901133SHans Petter Selasky usie_uc_cfg_set_rts(struct ucom_softc *ucom, uint8_t flag) 55949901133SHans Petter Selasky { 56049901133SHans Petter Selasky uint8_t rts; 56149901133SHans Petter Selasky 56249901133SHans Petter Selasky rts = flag ? USIE_LS_RTS : 0; 56349901133SHans Petter Selasky usie_uc_update_line_state(ucom, rts); 56449901133SHans Petter Selasky } 56549901133SHans Petter Selasky 56649901133SHans Petter Selasky static void 56749901133SHans Petter Selasky usie_uc_cfg_open(struct ucom_softc *ucom) 56849901133SHans Petter Selasky { 56949901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 57049901133SHans Petter Selasky 57149901133SHans Petter Selasky /* usbd_transfer_start() is NULL safe */ 57249901133SHans Petter Selasky 57349901133SHans Petter Selasky usbd_transfer_start(sc->sc_uc_xfer[ucom->sc_subunit][USIE_UC_STATUS]); 57449901133SHans Petter Selasky } 57549901133SHans Petter Selasky 57649901133SHans Petter Selasky static void 57749901133SHans Petter Selasky usie_uc_cfg_close(struct ucom_softc *ucom) 57849901133SHans Petter Selasky { 57949901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 58049901133SHans Petter Selasky 58149901133SHans Petter Selasky usbd_transfer_stop(sc->sc_uc_xfer[ucom->sc_subunit][USIE_UC_STATUS]); 58249901133SHans Petter Selasky } 58349901133SHans Petter Selasky 58449901133SHans Petter Selasky static void 58549901133SHans Petter Selasky usie_uc_start_read(struct ucom_softc *ucom) 58649901133SHans Petter Selasky { 58749901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 58849901133SHans Petter Selasky 58949901133SHans Petter Selasky usbd_transfer_start(sc->sc_uc_xfer[ucom->sc_subunit][USIE_UC_RX]); 59049901133SHans Petter Selasky } 59149901133SHans Petter Selasky 59249901133SHans Petter Selasky static void 59349901133SHans Petter Selasky usie_uc_stop_read(struct ucom_softc *ucom) 59449901133SHans Petter Selasky { 59549901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 59649901133SHans Petter Selasky 59749901133SHans Petter Selasky usbd_transfer_stop(sc->sc_uc_xfer[ucom->sc_subunit][USIE_UC_RX]); 59849901133SHans Petter Selasky } 59949901133SHans Petter Selasky 60049901133SHans Petter Selasky static void 60149901133SHans Petter Selasky usie_uc_start_write(struct ucom_softc *ucom) 60249901133SHans Petter Selasky { 60349901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 60449901133SHans Petter Selasky 60549901133SHans Petter Selasky usbd_transfer_start(sc->sc_uc_xfer[ucom->sc_subunit][USIE_UC_TX]); 60649901133SHans Petter Selasky } 60749901133SHans Petter Selasky 60849901133SHans Petter Selasky static void 60949901133SHans Petter Selasky usie_uc_stop_write(struct ucom_softc *ucom) 61049901133SHans Petter Selasky { 61149901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 61249901133SHans Petter Selasky 61349901133SHans Petter Selasky usbd_transfer_stop(sc->sc_uc_xfer[ucom->sc_subunit][USIE_UC_TX]); 61449901133SHans Petter Selasky } 61549901133SHans Petter Selasky 61649901133SHans Petter Selasky static void 61749901133SHans Petter Selasky usie_uc_rx_callback(struct usb_xfer *xfer, usb_error_t error) 61849901133SHans Petter Selasky { 61949901133SHans Petter Selasky struct ucom_softc *ucom = usbd_xfer_softc(xfer); 62049901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 62149901133SHans Petter Selasky struct usb_page_cache *pc; 62249901133SHans Petter Selasky uint32_t actlen; 62349901133SHans Petter Selasky 62449901133SHans Petter Selasky usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 62549901133SHans Petter Selasky 62649901133SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 62749901133SHans Petter Selasky case USB_ST_TRANSFERRED: 62849901133SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 62949901133SHans Petter Selasky 63049901133SHans Petter Selasky /* handle CnS response */ 63149901133SHans Petter Selasky if (ucom == sc->sc_ucom && actlen >= USIE_HIPCNS_MIN) { 63249901133SHans Petter Selasky 63349901133SHans Petter Selasky DPRINTF("transferred=%u\n", actlen); 63449901133SHans Petter Selasky 63549901133SHans Petter Selasky /* check if it is really CnS reply */ 63649901133SHans Petter Selasky usbd_copy_out(pc, 0, sc->sc_resp_temp, 1); 63749901133SHans Petter Selasky 63849901133SHans Petter Selasky if (sc->sc_resp_temp[0] == USIE_HIP_FRM_CHR) { 63949901133SHans Petter Selasky 64049901133SHans Petter Selasky /* verify actlen */ 64149901133SHans Petter Selasky if (actlen > USIE_BUFSIZE) 64249901133SHans Petter Selasky actlen = USIE_BUFSIZE; 64349901133SHans Petter Selasky 64449901133SHans Petter Selasky /* get complete message */ 64549901133SHans Petter Selasky usbd_copy_out(pc, 0, sc->sc_resp_temp, actlen); 64649901133SHans Petter Selasky usie_hip_rsp(sc, sc->sc_resp_temp, actlen); 64749901133SHans Petter Selasky 64849901133SHans Petter Selasky /* need to fall though */ 64949901133SHans Petter Selasky goto tr_setup; 65049901133SHans Petter Selasky } 65149901133SHans Petter Selasky /* else call ucom_put_data() */ 65249901133SHans Petter Selasky } 65349901133SHans Petter Selasky /* standard ucom transfer */ 65449901133SHans Petter Selasky ucom_put_data(ucom, pc, 0, actlen); 65549901133SHans Petter Selasky 65649901133SHans Petter Selasky /* fall though */ 65749901133SHans Petter Selasky case USB_ST_SETUP: 65849901133SHans Petter Selasky tr_setup: 65949901133SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 66049901133SHans Petter Selasky usbd_transfer_submit(xfer); 66149901133SHans Petter Selasky break; 66249901133SHans Petter Selasky 66349901133SHans Petter Selasky default: /* Error */ 66449901133SHans Petter Selasky if (error != USB_ERR_CANCELLED) { 66549901133SHans Petter Selasky usbd_xfer_set_stall(xfer); 66649901133SHans Petter Selasky goto tr_setup; 66749901133SHans Petter Selasky } 66849901133SHans Petter Selasky break; 66949901133SHans Petter Selasky } 67049901133SHans Petter Selasky } 67149901133SHans Petter Selasky 67249901133SHans Petter Selasky static void 67349901133SHans Petter Selasky usie_uc_tx_callback(struct usb_xfer *xfer, usb_error_t error) 67449901133SHans Petter Selasky { 67549901133SHans Petter Selasky struct ucom_softc *ucom = usbd_xfer_softc(xfer); 67649901133SHans Petter Selasky struct usb_page_cache *pc; 67749901133SHans Petter Selasky uint32_t actlen; 67849901133SHans Petter Selasky 67949901133SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 68049901133SHans Petter Selasky case USB_ST_TRANSFERRED: 68149901133SHans Petter Selasky case USB_ST_SETUP: 68249901133SHans Petter Selasky tr_setup: 68349901133SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 68449901133SHans Petter Selasky 68549901133SHans Petter Selasky /* handle CnS request */ 68649901133SHans Petter Selasky struct mbuf *m = usbd_xfer_get_priv(xfer); 68749901133SHans Petter Selasky 68849901133SHans Petter Selasky if (m != NULL) { 68949901133SHans Petter Selasky usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len); 69049901133SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len); 69149901133SHans Petter Selasky usbd_xfer_set_priv(xfer, NULL); 69249901133SHans Petter Selasky usbd_transfer_submit(xfer); 69349901133SHans Petter Selasky m_freem(m); 69449901133SHans Petter Selasky break; 69549901133SHans Petter Selasky } 69649901133SHans Petter Selasky /* standard ucom transfer */ 69749901133SHans Petter Selasky if (ucom_get_data(ucom, pc, 0, USIE_BUFSIZE, &actlen)) { 69849901133SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, actlen); 69949901133SHans Petter Selasky usbd_transfer_submit(xfer); 70049901133SHans Petter Selasky } 70149901133SHans Petter Selasky break; 70249901133SHans Petter Selasky 70349901133SHans Petter Selasky default: /* Error */ 70449901133SHans Petter Selasky if (error != USB_ERR_CANCELLED) { 70549901133SHans Petter Selasky usbd_xfer_set_stall(xfer); 70649901133SHans Petter Selasky goto tr_setup; 70749901133SHans Petter Selasky } 70849901133SHans Petter Selasky break; 70949901133SHans Petter Selasky } 71049901133SHans Petter Selasky } 71149901133SHans Petter Selasky 71249901133SHans Petter Selasky static void 71349901133SHans Petter Selasky usie_uc_status_callback(struct usb_xfer *xfer, usb_error_t error) 71449901133SHans Petter Selasky { 71549901133SHans Petter Selasky struct usb_page_cache *pc; 71649901133SHans Petter Selasky struct { 71749901133SHans Petter Selasky struct usb_device_request req; 71849901133SHans Petter Selasky uint16_t param; 71949901133SHans Petter Selasky } st; 72049901133SHans Petter Selasky uint32_t actlen; 72149901133SHans Petter Selasky uint16_t param; 72249901133SHans Petter Selasky 72349901133SHans Petter Selasky usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 72449901133SHans Petter Selasky 72549901133SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 72649901133SHans Petter Selasky case USB_ST_TRANSFERRED: 72749901133SHans Petter Selasky DPRINTFN(4, "info received, actlen=%u\n", actlen); 72849901133SHans Petter Selasky 72949901133SHans Petter Selasky if (actlen < sizeof(st)) { 73049901133SHans Petter Selasky DPRINTF("data too short actlen=%u\n", actlen); 73149901133SHans Petter Selasky goto tr_setup; 73249901133SHans Petter Selasky } 73349901133SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 73449901133SHans Petter Selasky usbd_copy_out(pc, 0, &st, sizeof(st)); 73549901133SHans Petter Selasky 73649901133SHans Petter Selasky if (st.req.bmRequestType == 0xa1 && st.req.bRequest == 0x20) { 73749901133SHans Petter Selasky struct ucom_softc *ucom = usbd_xfer_softc(xfer); 73849901133SHans Petter Selasky struct usie_softc *sc = ucom->sc_parent; 73949901133SHans Petter Selasky 74049901133SHans Petter Selasky param = le16toh(st.param); 74149901133SHans Petter Selasky DPRINTF("param=%x\n", param); 74249901133SHans Petter Selasky sc->sc_msr = sc->sc_lsr = 0; 74349901133SHans Petter Selasky sc->sc_msr |= (param & USIE_DCD) ? SER_DCD : 0; 74449901133SHans Petter Selasky sc->sc_msr |= (param & USIE_DSR) ? SER_DSR : 0; 74549901133SHans Petter Selasky sc->sc_msr |= (param & USIE_RI) ? SER_RI : 0; 74649901133SHans Petter Selasky sc->sc_msr |= (param & USIE_CTS) ? 0 : SER_CTS; 74749901133SHans Petter Selasky sc->sc_msr |= (param & USIE_RTS) ? SER_RTS : 0; 74849901133SHans Petter Selasky sc->sc_msr |= (param & USIE_DTR) ? SER_DTR : 0; 74949901133SHans Petter Selasky } 75049901133SHans Petter Selasky /* fall though */ 75149901133SHans Petter Selasky case USB_ST_SETUP: 75249901133SHans Petter Selasky tr_setup: 75349901133SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 75449901133SHans Petter Selasky usbd_transfer_submit(xfer); 75549901133SHans Petter Selasky break; 75649901133SHans Petter Selasky 75749901133SHans Petter Selasky default: /* Error */ 75849901133SHans Petter Selasky DPRINTF("USB transfer error, %s\n", 75949901133SHans Petter Selasky usbd_errstr(error)); 76049901133SHans Petter Selasky 76149901133SHans Petter Selasky if (error != USB_ERR_CANCELLED) { 76249901133SHans Petter Selasky usbd_xfer_set_stall(xfer); 76349901133SHans Petter Selasky goto tr_setup; 76449901133SHans Petter Selasky } 76549901133SHans Petter Selasky break; 76649901133SHans Petter Selasky } 76749901133SHans Petter Selasky } 76849901133SHans Petter Selasky 76949901133SHans Petter Selasky static void 77049901133SHans Petter Selasky usie_if_rx_callback(struct usb_xfer *xfer, usb_error_t error) 77149901133SHans Petter Selasky { 77249901133SHans Petter Selasky struct usie_softc *sc = usbd_xfer_softc(xfer); 77349901133SHans Petter Selasky struct ifnet *ifp = sc->sc_ifp; 77449901133SHans Petter Selasky struct mbuf *m0; 77549901133SHans Petter Selasky struct mbuf *m = NULL; 77649901133SHans Petter Selasky struct usie_desc *rxd; 77749901133SHans Petter Selasky uint32_t actlen; 77849901133SHans Petter Selasky uint16_t err; 77949901133SHans Petter Selasky uint16_t pkt; 78049901133SHans Petter Selasky uint16_t ipl; 78149901133SHans Petter Selasky uint16_t len; 78249901133SHans Petter Selasky uint16_t diff; 78349901133SHans Petter Selasky uint8_t pad; 78449901133SHans Petter Selasky uint8_t ipv; 78549901133SHans Petter Selasky 78649901133SHans Petter Selasky usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 78749901133SHans Petter Selasky 78849901133SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 78949901133SHans Petter Selasky case USB_ST_TRANSFERRED: 79049901133SHans Petter Selasky DPRINTFN(15, "rx done, actlen=%u\n", actlen); 79149901133SHans Petter Selasky 79249901133SHans Petter Selasky if (actlen < sizeof(struct usie_hip)) { 79349901133SHans Petter Selasky DPRINTF("data too short %u\n", actlen); 79449901133SHans Petter Selasky goto tr_setup; 79549901133SHans Petter Selasky } 79649901133SHans Petter Selasky m = sc->sc_rxm; 79749901133SHans Petter Selasky sc->sc_rxm = NULL; 79849901133SHans Petter Selasky 79949901133SHans Petter Selasky /* fall though */ 80049901133SHans Petter Selasky case USB_ST_SETUP: 80149901133SHans Petter Selasky tr_setup: 80249901133SHans Petter Selasky 80349901133SHans Petter Selasky if (sc->sc_rxm == NULL) { 804c6499eccSGleb Smirnoff sc->sc_rxm = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, 80549901133SHans Petter Selasky MJUMPAGESIZE /* could be bigger than MCLBYTES */ ); 80649901133SHans Petter Selasky } 80749901133SHans Petter Selasky if (sc->sc_rxm == NULL) { 80849901133SHans Petter Selasky DPRINTF("could not allocate Rx mbuf\n"); 809ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 81049901133SHans Petter Selasky usbd_xfer_set_stall(xfer); 81149901133SHans Petter Selasky usbd_xfer_set_frames(xfer, 0); 81249901133SHans Petter Selasky } else { 81349901133SHans Petter Selasky /* 81449901133SHans Petter Selasky * Directly loading a mbuf cluster into DMA to 81549901133SHans Petter Selasky * save some data copying. This works because 81649901133SHans Petter Selasky * there is only one cluster. 81749901133SHans Petter Selasky */ 81849901133SHans Petter Selasky usbd_xfer_set_frame_data(xfer, 0, 81949901133SHans Petter Selasky mtod(sc->sc_rxm, caddr_t), MIN(MJUMPAGESIZE, USIE_RXSZ_MAX)); 82049901133SHans Petter Selasky usbd_xfer_set_frames(xfer, 1); 82149901133SHans Petter Selasky } 82249901133SHans Petter Selasky usbd_transfer_submit(xfer); 82349901133SHans Petter Selasky break; 82449901133SHans Petter Selasky 82549901133SHans Petter Selasky default: /* Error */ 82649901133SHans Petter Selasky DPRINTF("USB transfer error, %s\n", usbd_errstr(error)); 82749901133SHans Petter Selasky 82849901133SHans Petter Selasky if (error != USB_ERR_CANCELLED) { 82949901133SHans Petter Selasky /* try to clear stall first */ 83049901133SHans Petter Selasky usbd_xfer_set_stall(xfer); 831ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 83249901133SHans Petter Selasky goto tr_setup; 83349901133SHans Petter Selasky } 83449901133SHans Petter Selasky if (sc->sc_rxm != NULL) { 83549901133SHans Petter Selasky m_freem(sc->sc_rxm); 83649901133SHans Petter Selasky sc->sc_rxm = NULL; 83749901133SHans Petter Selasky } 83849901133SHans Petter Selasky break; 83949901133SHans Petter Selasky } 84049901133SHans Petter Selasky 84149901133SHans Petter Selasky if (m == NULL) 84249901133SHans Petter Selasky return; 84349901133SHans Petter Selasky 84449901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 84549901133SHans Petter Selasky 84649901133SHans Petter Selasky m->m_pkthdr.len = m->m_len = actlen; 84749901133SHans Petter Selasky 84849901133SHans Petter Selasky err = pkt = 0; 84949901133SHans Petter Selasky 85049901133SHans Petter Selasky /* HW can aggregate multiple frames in a single USB xfer */ 85149901133SHans Petter Selasky for (;;) { 85249901133SHans Petter Selasky rxd = mtod(m, struct usie_desc *); 85349901133SHans Petter Selasky 85449901133SHans Petter Selasky len = be16toh(rxd->hip.len) & USIE_HIP_IP_LEN_MASK; 85549901133SHans Petter Selasky pad = (rxd->hip.id & USIE_HIP_PAD) ? 1 : 0; 85649901133SHans Petter Selasky ipl = (len - pad - ETHER_HDR_LEN); 85749901133SHans Petter Selasky if (ipl >= len) { 85849901133SHans Petter Selasky DPRINTF("Corrupt frame\n"); 85949901133SHans Petter Selasky m_freem(m); 86049901133SHans Petter Selasky break; 86149901133SHans Petter Selasky } 86249901133SHans Petter Selasky diff = sizeof(struct usie_desc) + ipl + pad; 86349901133SHans Petter Selasky 86449901133SHans Petter Selasky if (((rxd->hip.id & USIE_HIP_MASK) != USIE_HIP_IP) || 86549901133SHans Petter Selasky (be16toh(rxd->desc_type) & USIE_TYPE_MASK) != USIE_IP_RX) { 86649901133SHans Petter Selasky DPRINTF("received wrong type of packet\n"); 86749901133SHans Petter Selasky m->m_data += diff; 86849901133SHans Petter Selasky m->m_pkthdr.len = (m->m_len -= diff); 86949901133SHans Petter Selasky err++; 87049901133SHans Petter Selasky if (m->m_pkthdr.len > 0) 87149901133SHans Petter Selasky continue; 87249901133SHans Petter Selasky m_freem(m); 87349901133SHans Petter Selasky break; 87449901133SHans Petter Selasky } 87549901133SHans Petter Selasky switch (be16toh(rxd->ethhdr.ether_type)) { 87649901133SHans Petter Selasky case ETHERTYPE_IP: 87749901133SHans Petter Selasky ipv = NETISR_IP; 87849901133SHans Petter Selasky break; 87949901133SHans Petter Selasky #ifdef INET6 88049901133SHans Petter Selasky case ETHERTYPE_IPV6: 88149901133SHans Petter Selasky ipv = NETISR_IPV6; 88249901133SHans Petter Selasky break; 88349901133SHans Petter Selasky #endif 88449901133SHans Petter Selasky default: 88549901133SHans Petter Selasky DPRINTF("unsupported ether type\n"); 88649901133SHans Petter Selasky err++; 88749901133SHans Petter Selasky break; 88849901133SHans Petter Selasky } 88949901133SHans Petter Selasky 89049901133SHans Petter Selasky /* the last packet */ 89149901133SHans Petter Selasky if (m->m_pkthdr.len <= diff) { 89249901133SHans Petter Selasky m->m_data += (sizeof(struct usie_desc) + pad); 89349901133SHans Petter Selasky m->m_pkthdr.len = m->m_len = ipl; 89449901133SHans Petter Selasky m->m_pkthdr.rcvif = ifp; 89549901133SHans Petter Selasky BPF_MTAP(sc->sc_ifp, m); 89649901133SHans Petter Selasky netisr_dispatch(ipv, m); 89749901133SHans Petter Selasky break; 89849901133SHans Petter Selasky } 89949901133SHans Petter Selasky /* copy aggregated frames to another mbuf */ 900c6499eccSGleb Smirnoff m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 90149901133SHans Petter Selasky if (__predict_false(m0 == NULL)) { 90249901133SHans Petter Selasky DPRINTF("could not allocate mbuf\n"); 90349901133SHans Petter Selasky err++; 90449901133SHans Petter Selasky m_freem(m); 90549901133SHans Petter Selasky break; 90649901133SHans Petter Selasky } 90749901133SHans Petter Selasky m_copydata(m, sizeof(struct usie_desc) + pad, ipl, mtod(m0, caddr_t)); 90849901133SHans Petter Selasky m0->m_pkthdr.rcvif = ifp; 90949901133SHans Petter Selasky m0->m_pkthdr.len = m0->m_len = ipl; 91049901133SHans Petter Selasky 91149901133SHans Petter Selasky BPF_MTAP(sc->sc_ifp, m0); 91249901133SHans Petter Selasky netisr_dispatch(ipv, m0); 91349901133SHans Petter Selasky 91449901133SHans Petter Selasky m->m_data += diff; 91549901133SHans Petter Selasky m->m_pkthdr.len = (m->m_len -= diff); 91649901133SHans Petter Selasky } 91749901133SHans Petter Selasky 91849901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 91949901133SHans Petter Selasky 920ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, err); 921ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, pkt); 92249901133SHans Petter Selasky } 92349901133SHans Petter Selasky 92449901133SHans Petter Selasky static void 92549901133SHans Petter Selasky usie_if_tx_callback(struct usb_xfer *xfer, usb_error_t error) 92649901133SHans Petter Selasky { 92749901133SHans Petter Selasky struct usie_softc *sc = usbd_xfer_softc(xfer); 92849901133SHans Petter Selasky struct usb_page_cache *pc; 92949901133SHans Petter Selasky struct ifnet *ifp = sc->sc_ifp; 93049901133SHans Petter Selasky struct mbuf *m; 93149901133SHans Petter Selasky uint16_t size; 93249901133SHans Petter Selasky 93349901133SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 93449901133SHans Petter Selasky case USB_ST_TRANSFERRED: 93549901133SHans Petter Selasky DPRINTFN(11, "transfer complete\n"); 93649901133SHans Petter Selasky ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 937ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 93849901133SHans Petter Selasky 93949901133SHans Petter Selasky /* fall though */ 94049901133SHans Petter Selasky case USB_ST_SETUP: 94149901133SHans Petter Selasky tr_setup: 94249901133SHans Petter Selasky 94349901133SHans Petter Selasky if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 94449901133SHans Petter Selasky break; 94549901133SHans Petter Selasky 94649901133SHans Petter Selasky IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 94749901133SHans Petter Selasky if (m == NULL) 94849901133SHans Petter Selasky break; 94949901133SHans Petter Selasky 9506d917491SHans Petter Selasky if (m->m_pkthdr.len > (int)(MCLBYTES - ETHER_HDR_LEN + 95149901133SHans Petter Selasky ETHER_CRC_LEN - sizeof(sc->sc_txd))) { 95249901133SHans Petter Selasky DPRINTF("packet len is too big: %d\n", 95349901133SHans Petter Selasky m->m_pkthdr.len); 95449901133SHans Petter Selasky break; 95549901133SHans Petter Selasky } 95649901133SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 95749901133SHans Petter Selasky 95849901133SHans Petter Selasky sc->sc_txd.hip.len = htobe16(m->m_pkthdr.len + 95949901133SHans Petter Selasky ETHER_HDR_LEN + ETHER_CRC_LEN); 96049901133SHans Petter Selasky size = sizeof(sc->sc_txd); 96149901133SHans Petter Selasky 96249901133SHans Petter Selasky usbd_copy_in(pc, 0, &sc->sc_txd, size); 96349901133SHans Petter Selasky usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len); 96449901133SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, m->m_pkthdr.len + 96549901133SHans Petter Selasky size + ETHER_CRC_LEN); 96649901133SHans Petter Selasky 96749901133SHans Petter Selasky BPF_MTAP(ifp, m); 96849901133SHans Petter Selasky 96949901133SHans Petter Selasky m_freem(m); 97049901133SHans Petter Selasky 97149901133SHans Petter Selasky usbd_transfer_submit(xfer); 97249901133SHans Petter Selasky break; 97349901133SHans Petter Selasky 97449901133SHans Petter Selasky default: /* Error */ 97549901133SHans Petter Selasky DPRINTF("USB transfer error, %s\n", 97649901133SHans Petter Selasky usbd_errstr(error)); 977ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 97849901133SHans Petter Selasky 97949901133SHans Petter Selasky if (error != USB_ERR_CANCELLED) { 98049901133SHans Petter Selasky usbd_xfer_set_stall(xfer); 981ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 98249901133SHans Petter Selasky goto tr_setup; 98349901133SHans Petter Selasky } 98449901133SHans Petter Selasky break; 98549901133SHans Petter Selasky } 98649901133SHans Petter Selasky } 98749901133SHans Petter Selasky 98849901133SHans Petter Selasky static void 98949901133SHans Petter Selasky usie_if_status_callback(struct usb_xfer *xfer, usb_error_t error) 99049901133SHans Petter Selasky { 99149901133SHans Petter Selasky struct usie_softc *sc = usbd_xfer_softc(xfer); 99249901133SHans Petter Selasky struct usb_page_cache *pc; 99349901133SHans Petter Selasky struct usb_cdc_notification cdc; 99449901133SHans Petter Selasky uint32_t actlen; 99549901133SHans Petter Selasky 99649901133SHans Petter Selasky usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 99749901133SHans Petter Selasky 99849901133SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 99949901133SHans Petter Selasky case USB_ST_TRANSFERRED: 100049901133SHans Petter Selasky DPRINTFN(4, "info received, actlen=%d\n", actlen); 100149901133SHans Petter Selasky 100249901133SHans Petter Selasky /* usb_cdc_notification - .data[16] */ 100349901133SHans Petter Selasky if (actlen < (sizeof(cdc) - 16)) { 100449901133SHans Petter Selasky DPRINTF("data too short %d\n", actlen); 100549901133SHans Petter Selasky goto tr_setup; 100649901133SHans Petter Selasky } 100749901133SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 100849901133SHans Petter Selasky usbd_copy_out(pc, 0, &cdc, (sizeof(cdc) - 16)); 100949901133SHans Petter Selasky 101049901133SHans Petter Selasky DPRINTFN(4, "bNotification=%x\n", cdc.bNotification); 101149901133SHans Petter Selasky 101249901133SHans Petter Selasky if (cdc.bNotification & UCDC_N_RESPONSE_AVAILABLE) { 101349901133SHans Petter Selasky taskqueue_enqueue(taskqueue_thread, 101449901133SHans Petter Selasky &sc->sc_if_status_task); 101549901133SHans Petter Selasky } 101649901133SHans Petter Selasky /* fall though */ 101749901133SHans Petter Selasky case USB_ST_SETUP: 101849901133SHans Petter Selasky tr_setup: 101949901133SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 102049901133SHans Petter Selasky usbd_transfer_submit(xfer); 102149901133SHans Petter Selasky break; 102249901133SHans Petter Selasky 102349901133SHans Petter Selasky default: /* Error */ 102449901133SHans Petter Selasky DPRINTF("USB transfer error, %s\n", 102549901133SHans Petter Selasky usbd_errstr(error)); 102649901133SHans Petter Selasky 102749901133SHans Petter Selasky if (error != USB_ERR_CANCELLED) { 102849901133SHans Petter Selasky usbd_xfer_set_stall(xfer); 102949901133SHans Petter Selasky goto tr_setup; 103049901133SHans Petter Selasky } 103149901133SHans Petter Selasky break; 103249901133SHans Petter Selasky } 103349901133SHans Petter Selasky } 103449901133SHans Petter Selasky 103549901133SHans Petter Selasky static void 103649901133SHans Petter Selasky usie_if_sync_to(void *arg) 103749901133SHans Petter Selasky { 103849901133SHans Petter Selasky struct usie_softc *sc = arg; 103949901133SHans Petter Selasky 104049901133SHans Petter Selasky taskqueue_enqueue(taskqueue_thread, &sc->sc_if_sync_task); 104149901133SHans Petter Selasky } 104249901133SHans Petter Selasky 104349901133SHans Petter Selasky static void 104449901133SHans Petter Selasky usie_if_sync_cb(void *arg, int pending) 104549901133SHans Petter Selasky { 104649901133SHans Petter Selasky struct usie_softc *sc = arg; 104749901133SHans Petter Selasky 104849901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 104949901133SHans Petter Selasky 105049901133SHans Petter Selasky /* call twice */ 105149901133SHans Petter Selasky usie_if_cmd(sc, USIE_HIP_SYNC2M); 105249901133SHans Petter Selasky usie_if_cmd(sc, USIE_HIP_SYNC2M); 105349901133SHans Petter Selasky 105449901133SHans Petter Selasky usb_callout_reset(&sc->sc_if_sync_ch, 2 * hz, usie_if_sync_to, sc); 105549901133SHans Petter Selasky 105649901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 105749901133SHans Petter Selasky } 105849901133SHans Petter Selasky 105949901133SHans Petter Selasky static void 106049901133SHans Petter Selasky usie_if_status_cb(void *arg, int pending) 106149901133SHans Petter Selasky { 106249901133SHans Petter Selasky struct usie_softc *sc = arg; 106349901133SHans Petter Selasky struct ifnet *ifp = sc->sc_ifp; 106449901133SHans Petter Selasky struct usb_device_request req; 106549901133SHans Petter Selasky struct usie_hip *hip; 106649901133SHans Petter Selasky struct usie_lsi *lsi; 106749901133SHans Petter Selasky uint16_t actlen; 106849901133SHans Petter Selasky uint8_t ntries; 106949901133SHans Petter Selasky uint8_t pad; 107049901133SHans Petter Selasky 107149901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 107249901133SHans Petter Selasky 107349901133SHans Petter Selasky req.bmRequestType = UT_READ_CLASS_INTERFACE; 107449901133SHans Petter Selasky req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE; 107549901133SHans Petter Selasky USETW(req.wValue, 0); 107649901133SHans Petter Selasky USETW(req.wIndex, sc->sc_if_ifnum); 107749901133SHans Petter Selasky USETW(req.wLength, sizeof(sc->sc_status_temp)); 107849901133SHans Petter Selasky 107949901133SHans Petter Selasky for (ntries = 0; ntries != 10; ntries++) { 108049901133SHans Petter Selasky int err; 108149901133SHans Petter Selasky 108249901133SHans Petter Selasky err = usbd_do_request_flags(sc->sc_udev, 108349901133SHans Petter Selasky &sc->sc_mtx, &req, sc->sc_status_temp, USB_SHORT_XFER_OK, 108449901133SHans Petter Selasky &actlen, USB_DEFAULT_TIMEOUT); 108549901133SHans Petter Selasky 108649901133SHans Petter Selasky if (err == 0) 108749901133SHans Petter Selasky break; 108849901133SHans Petter Selasky 108949901133SHans Petter Selasky DPRINTF("Control request failed: %s %d/10\n", 109049901133SHans Petter Selasky usbd_errstr(err), ntries); 109149901133SHans Petter Selasky 109249901133SHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10)); 109349901133SHans Petter Selasky } 109449901133SHans Petter Selasky 109549901133SHans Petter Selasky if (ntries == 10) { 109649901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 109749901133SHans Petter Selasky DPRINTF("Timeout\n"); 109849901133SHans Petter Selasky return; 109949901133SHans Petter Selasky } 110049901133SHans Petter Selasky 110149901133SHans Petter Selasky hip = (struct usie_hip *)sc->sc_status_temp; 110249901133SHans Petter Selasky 110349901133SHans Petter Selasky pad = (hip->id & USIE_HIP_PAD) ? 1 : 0; 110449901133SHans Petter Selasky 110549901133SHans Petter Selasky DPRINTF("hip.id=%x hip.len=%d actlen=%u pad=%d\n", 110649901133SHans Petter Selasky hip->id, be16toh(hip->len), actlen, pad); 110749901133SHans Petter Selasky 110849901133SHans Petter Selasky switch (hip->id & USIE_HIP_MASK) { 110949901133SHans Petter Selasky case USIE_HIP_SYNC2H: 111049901133SHans Petter Selasky usie_if_cmd(sc, USIE_HIP_SYNC2M); 111149901133SHans Petter Selasky break; 111249901133SHans Petter Selasky case USIE_HIP_RESTR: 111349901133SHans Petter Selasky usb_callout_stop(&sc->sc_if_sync_ch); 111449901133SHans Petter Selasky break; 111549901133SHans Petter Selasky case USIE_HIP_UMTS: 111649901133SHans Petter Selasky lsi = (struct usie_lsi *)( 111749901133SHans Petter Selasky sc->sc_status_temp + sizeof(struct usie_hip) + pad); 111849901133SHans Petter Selasky 111949901133SHans Petter Selasky DPRINTF("lsi.proto=%x lsi.len=%d\n", lsi->proto, 112049901133SHans Petter Selasky be16toh(lsi->len)); 112149901133SHans Petter Selasky 112249901133SHans Petter Selasky if (lsi->proto != USIE_LSI_UMTS) 112349901133SHans Petter Selasky break; 112449901133SHans Petter Selasky 112549901133SHans Petter Selasky if (lsi->area == USIE_LSI_AREA_NO || 112649901133SHans Petter Selasky lsi->area == USIE_LSI_AREA_NODATA) { 112749901133SHans Petter Selasky device_printf(sc->sc_dev, "no service available\n"); 112849901133SHans Petter Selasky break; 112949901133SHans Petter Selasky } 113049901133SHans Petter Selasky if (lsi->state == USIE_LSI_STATE_IDLE) { 113149901133SHans Petter Selasky DPRINTF("lsi.state=%x\n", lsi->state); 113249901133SHans Petter Selasky break; 113349901133SHans Petter Selasky } 113449901133SHans Petter Selasky DPRINTF("ctx=%x\n", hip->param); 113549901133SHans Petter Selasky sc->sc_txd.hip.param = hip->param; 113649901133SHans Petter Selasky 113749901133SHans Petter Selasky sc->sc_net.addr_len = lsi->pdp_addr_len; 113849901133SHans Petter Selasky memcpy(&sc->sc_net.dns1_addr, &lsi->dns1_addr, 16); 113949901133SHans Petter Selasky memcpy(&sc->sc_net.dns2_addr, &lsi->dns2_addr, 16); 114049901133SHans Petter Selasky memcpy(sc->sc_net.pdp_addr, lsi->pdp_addr, 16); 114149901133SHans Petter Selasky memcpy(sc->sc_net.gw_addr, lsi->gw_addr, 16); 114249901133SHans Petter Selasky ifp->if_flags |= IFF_UP; 114349901133SHans Petter Selasky ifp->if_drv_flags |= IFF_DRV_RUNNING; 114449901133SHans Petter Selasky 114549901133SHans Petter Selasky device_printf(sc->sc_dev, "IP Addr=%d.%d.%d.%d\n", 114649901133SHans Petter Selasky *lsi->pdp_addr, *(lsi->pdp_addr + 1), 114749901133SHans Petter Selasky *(lsi->pdp_addr + 2), *(lsi->pdp_addr + 3)); 114849901133SHans Petter Selasky device_printf(sc->sc_dev, "Gateway Addr=%d.%d.%d.%d\n", 114949901133SHans Petter Selasky *lsi->gw_addr, *(lsi->gw_addr + 1), 115049901133SHans Petter Selasky *(lsi->gw_addr + 2), *(lsi->gw_addr + 3)); 115149901133SHans Petter Selasky device_printf(sc->sc_dev, "Prim NS Addr=%d.%d.%d.%d\n", 115249901133SHans Petter Selasky *lsi->dns1_addr, *(lsi->dns1_addr + 1), 115349901133SHans Petter Selasky *(lsi->dns1_addr + 2), *(lsi->dns1_addr + 3)); 115449901133SHans Petter Selasky device_printf(sc->sc_dev, "Scnd NS Addr=%d.%d.%d.%d\n", 115549901133SHans Petter Selasky *lsi->dns2_addr, *(lsi->dns2_addr + 1), 115649901133SHans Petter Selasky *(lsi->dns2_addr + 2), *(lsi->dns2_addr + 3)); 115749901133SHans Petter Selasky 115849901133SHans Petter Selasky usie_cns_req(sc, USIE_CNS_ID_RSSI, USIE_CNS_OB_RSSI); 115949901133SHans Petter Selasky break; 116049901133SHans Petter Selasky 116149901133SHans Petter Selasky case USIE_HIP_RCGI: 116249901133SHans Petter Selasky /* ignore, workaround for sloppy windows */ 116349901133SHans Petter Selasky break; 116449901133SHans Petter Selasky default: 116549901133SHans Petter Selasky DPRINTF("undefined msgid: %x\n", hip->id); 116649901133SHans Petter Selasky break; 116749901133SHans Petter Selasky } 116849901133SHans Petter Selasky 116949901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 117049901133SHans Petter Selasky } 117149901133SHans Petter Selasky 117249901133SHans Petter Selasky static void 117349901133SHans Petter Selasky usie_if_start(struct ifnet *ifp) 117449901133SHans Petter Selasky { 117549901133SHans Petter Selasky struct usie_softc *sc = ifp->if_softc; 117649901133SHans Petter Selasky 117749901133SHans Petter Selasky if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 117849901133SHans Petter Selasky DPRINTF("Not running\n"); 117949901133SHans Petter Selasky return; 118049901133SHans Petter Selasky } 118149901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 118249901133SHans Petter Selasky usbd_transfer_start(sc->sc_if_xfer[USIE_IF_TX]); 118349901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 118449901133SHans Petter Selasky 118549901133SHans Petter Selasky DPRINTFN(3, "interface started\n"); 118649901133SHans Petter Selasky } 118749901133SHans Petter Selasky 118849901133SHans Petter Selasky static int 118947e8d432SGleb Smirnoff usie_if_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 119049901133SHans Petter Selasky struct route *ro) 119149901133SHans Petter Selasky { 119249901133SHans Petter Selasky int err; 119349901133SHans Petter Selasky 119449901133SHans Petter Selasky DPRINTF("proto=%x\n", dst->sa_family); 119549901133SHans Petter Selasky 119649901133SHans Petter Selasky switch (dst->sa_family) { 119749901133SHans Petter Selasky #ifdef INET6 119849901133SHans Petter Selasky case AF_INET6; 119949901133SHans Petter Selasky /* fall though */ 120049901133SHans Petter Selasky #endif 120149901133SHans Petter Selasky case AF_INET: 120249901133SHans Petter Selasky break; 120349901133SHans Petter Selasky 120449901133SHans Petter Selasky /* silently drop dhclient packets */ 120549901133SHans Petter Selasky case AF_UNSPEC: 120649901133SHans Petter Selasky m_freem(m); 120749901133SHans Petter Selasky return (0); 120849901133SHans Petter Selasky 120949901133SHans Petter Selasky /* drop other packet types */ 121049901133SHans Petter Selasky default: 121149901133SHans Petter Selasky m_freem(m); 121249901133SHans Petter Selasky return (EAFNOSUPPORT); 121349901133SHans Petter Selasky } 121449901133SHans Petter Selasky 121549901133SHans Petter Selasky err = (ifp->if_transmit)(ifp, m); 121649901133SHans Petter Selasky if (err) { 1217ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 121849901133SHans Petter Selasky return (ENOBUFS); 121949901133SHans Petter Selasky } 1220ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 122149901133SHans Petter Selasky 122249901133SHans Petter Selasky return (0); 122349901133SHans Petter Selasky } 122449901133SHans Petter Selasky 122549901133SHans Petter Selasky static void 122649901133SHans Petter Selasky usie_if_init(void *arg) 122749901133SHans Petter Selasky { 122849901133SHans Petter Selasky struct usie_softc *sc = arg; 122949901133SHans Petter Selasky struct ifnet *ifp = sc->sc_ifp; 123049901133SHans Petter Selasky uint8_t i; 123149901133SHans Petter Selasky 123249901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 123349901133SHans Petter Selasky 123449901133SHans Petter Selasky /* write tx descriptor */ 123549901133SHans Petter Selasky sc->sc_txd.hip.id = USIE_HIP_CTX; 123649901133SHans Petter Selasky sc->sc_txd.hip.param = 0; /* init value */ 123749901133SHans Petter Selasky sc->sc_txd.desc_type = htobe16(USIE_IP_TX); 123849901133SHans Petter Selasky 123949901133SHans Petter Selasky for (i = 0; i != USIE_IF_N_XFER; i++) 124049901133SHans Petter Selasky usbd_xfer_set_stall(sc->sc_if_xfer[i]); 124149901133SHans Petter Selasky 124249901133SHans Petter Selasky usbd_transfer_start(sc->sc_uc_xfer[USIE_HIP_IF][USIE_UC_RX]); 124349901133SHans Petter Selasky usbd_transfer_start(sc->sc_if_xfer[USIE_IF_STATUS]); 124449901133SHans Petter Selasky usbd_transfer_start(sc->sc_if_xfer[USIE_IF_RX]); 124549901133SHans Petter Selasky 124649901133SHans Petter Selasky /* if not running, initiate the modem */ 124749901133SHans Petter Selasky if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 124849901133SHans Petter Selasky usie_cns_req(sc, USIE_CNS_ID_INIT, USIE_CNS_OB_LINK_UPDATE); 124949901133SHans Petter Selasky 125049901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 125149901133SHans Petter Selasky 125249901133SHans Petter Selasky DPRINTF("ifnet initialized\n"); 125349901133SHans Petter Selasky } 125449901133SHans Petter Selasky 125549901133SHans Petter Selasky static void 125649901133SHans Petter Selasky usie_if_stop(struct usie_softc *sc) 125749901133SHans Petter Selasky { 125849901133SHans Petter Selasky usb_callout_drain(&sc->sc_if_sync_ch); 125949901133SHans Petter Selasky 126049901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 126149901133SHans Petter Selasky 126249901133SHans Petter Selasky /* usie_cns_req() clears IFF_* flags */ 126349901133SHans Petter Selasky usie_cns_req(sc, USIE_CNS_ID_STOP, USIE_CNS_OB_LINK_UPDATE); 126449901133SHans Petter Selasky 126549901133SHans Petter Selasky usbd_transfer_stop(sc->sc_if_xfer[USIE_IF_TX]); 126649901133SHans Petter Selasky usbd_transfer_stop(sc->sc_if_xfer[USIE_IF_RX]); 126749901133SHans Petter Selasky usbd_transfer_stop(sc->sc_if_xfer[USIE_IF_STATUS]); 126849901133SHans Petter Selasky 126949901133SHans Petter Selasky /* shutdown device */ 127049901133SHans Petter Selasky usie_if_cmd(sc, USIE_HIP_DOWN); 127149901133SHans Petter Selasky 127249901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 127349901133SHans Petter Selasky } 127449901133SHans Petter Selasky 127549901133SHans Petter Selasky static int 127649901133SHans Petter Selasky usie_if_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 127749901133SHans Petter Selasky { 127849901133SHans Petter Selasky struct usie_softc *sc = ifp->if_softc; 127949901133SHans Petter Selasky struct ieee80211req *ireq; 128049901133SHans Petter Selasky struct ieee80211req_sta_info si; 128149901133SHans Petter Selasky struct ifmediareq *ifmr; 128249901133SHans Petter Selasky 128349901133SHans Petter Selasky switch (cmd) { 128449901133SHans Petter Selasky case SIOCSIFFLAGS: 128549901133SHans Petter Selasky if (ifp->if_flags & IFF_UP) { 128649901133SHans Petter Selasky if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 128749901133SHans Petter Selasky usie_if_init(sc); 128849901133SHans Petter Selasky } else { 128949901133SHans Petter Selasky if (ifp->if_drv_flags & IFF_DRV_RUNNING) 129049901133SHans Petter Selasky usie_if_stop(sc); 129149901133SHans Petter Selasky } 129249901133SHans Petter Selasky break; 129349901133SHans Petter Selasky 129449901133SHans Petter Selasky case SIOCSIFCAP: 129549901133SHans Petter Selasky if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 129649901133SHans Petter Selasky device_printf(sc->sc_dev, 129749901133SHans Petter Selasky "Connect to the network first.\n"); 129849901133SHans Petter Selasky break; 129949901133SHans Petter Selasky } 130049901133SHans Petter Selasky mtx_lock(&sc->sc_mtx); 130149901133SHans Petter Selasky usie_cns_req(sc, USIE_CNS_ID_RSSI, USIE_CNS_OB_RSSI); 130249901133SHans Petter Selasky mtx_unlock(&sc->sc_mtx); 130349901133SHans Petter Selasky break; 130449901133SHans Petter Selasky 130549901133SHans Petter Selasky case SIOCG80211: 130649901133SHans Petter Selasky ireq = (struct ieee80211req *)data; 130749901133SHans Petter Selasky 130849901133SHans Petter Selasky if (ireq->i_type != IEEE80211_IOC_STA_INFO) 130949901133SHans Petter Selasky break; 131049901133SHans Petter Selasky 131149901133SHans Petter Selasky memset(&si, 0, sizeof(si)); 131249901133SHans Petter Selasky si.isi_len = sizeof(si); 131349901133SHans Petter Selasky /* 131449901133SHans Petter Selasky * ifconfig expects RSSI in 0.5dBm units 131549901133SHans Petter Selasky * relative to the noise floor. 131649901133SHans Petter Selasky */ 131749901133SHans Petter Selasky si.isi_rssi = 2 * sc->sc_rssi; 131849901133SHans Petter Selasky if (copyout(&si, (uint8_t *)ireq->i_data + 8, 131949901133SHans Petter Selasky sizeof(struct ieee80211req_sta_info))) 132049901133SHans Petter Selasky DPRINTF("copyout failed\n"); 132149901133SHans Petter Selasky DPRINTF("80211\n"); 132249901133SHans Petter Selasky break; 132349901133SHans Petter Selasky 132449901133SHans Petter Selasky case SIOCGIFMEDIA: /* to fool ifconfig */ 132549901133SHans Petter Selasky ifmr = (struct ifmediareq *)data; 132649901133SHans Petter Selasky ifmr->ifm_count = 1; 132749901133SHans Petter Selasky DPRINTF("media\n"); 132849901133SHans Petter Selasky break; 132949901133SHans Petter Selasky 133049901133SHans Petter Selasky case SIOCSIFADDR: 133149901133SHans Petter Selasky break; 133249901133SHans Petter Selasky 133349901133SHans Petter Selasky default: 133449901133SHans Petter Selasky return (EINVAL); 133549901133SHans Petter Selasky } 133649901133SHans Petter Selasky return (0); 133749901133SHans Petter Selasky } 133849901133SHans Petter Selasky 133949901133SHans Petter Selasky static int 134049901133SHans Petter Selasky usie_do_request(struct usie_softc *sc, struct usb_device_request *req, 134149901133SHans Petter Selasky void *data) 134249901133SHans Petter Selasky { 134349901133SHans Petter Selasky int err = 0; 134449901133SHans Petter Selasky int ntries; 134549901133SHans Petter Selasky 134649901133SHans Petter Selasky mtx_assert(&sc->sc_mtx, MA_OWNED); 134749901133SHans Petter Selasky 134849901133SHans Petter Selasky for (ntries = 0; ntries != 10; ntries++) { 134949901133SHans Petter Selasky err = usbd_do_request(sc->sc_udev, 135049901133SHans Petter Selasky &sc->sc_mtx, req, data); 135149901133SHans Petter Selasky if (err == 0) 135249901133SHans Petter Selasky break; 135349901133SHans Petter Selasky 135449901133SHans Petter Selasky DPRINTF("Control request failed: %s %d/10\n", 135549901133SHans Petter Selasky usbd_errstr(err), ntries); 135649901133SHans Petter Selasky 135749901133SHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10)); 135849901133SHans Petter Selasky } 135949901133SHans Petter Selasky return (err); 136049901133SHans Petter Selasky } 136149901133SHans Petter Selasky 136249901133SHans Petter Selasky static int 136349901133SHans Petter Selasky usie_if_cmd(struct usie_softc *sc, uint8_t cmd) 136449901133SHans Petter Selasky { 136549901133SHans Petter Selasky struct usb_device_request req; 136649901133SHans Petter Selasky struct usie_hip msg; 136749901133SHans Petter Selasky 136849901133SHans Petter Selasky msg.len = 0; 136949901133SHans Petter Selasky msg.id = cmd; 137049901133SHans Petter Selasky msg.param = 0; 137149901133SHans Petter Selasky 137249901133SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 137349901133SHans Petter Selasky req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; 137449901133SHans Petter Selasky USETW(req.wValue, 0); 137549901133SHans Petter Selasky USETW(req.wIndex, sc->sc_if_ifnum); 137649901133SHans Petter Selasky USETW(req.wLength, sizeof(msg)); 137749901133SHans Petter Selasky 137849901133SHans Petter Selasky DPRINTF("cmd=%x\n", cmd); 137949901133SHans Petter Selasky 138049901133SHans Petter Selasky return (usie_do_request(sc, &req, &msg)); 138149901133SHans Petter Selasky } 138249901133SHans Petter Selasky 138349901133SHans Petter Selasky static void 138449901133SHans Petter Selasky usie_cns_req(struct usie_softc *sc, uint32_t id, uint16_t obj) 138549901133SHans Petter Selasky { 138649901133SHans Petter Selasky struct ifnet *ifp = sc->sc_ifp; 138749901133SHans Petter Selasky struct mbuf *m; 138849901133SHans Petter Selasky struct usb_xfer *xfer; 138949901133SHans Petter Selasky struct usie_hip *hip; 139049901133SHans Petter Selasky struct usie_cns *cns; 139149901133SHans Petter Selasky uint8_t *param; 139249901133SHans Petter Selasky uint8_t *tmp; 139349901133SHans Petter Selasky uint8_t cns_len; 139449901133SHans Petter Selasky 1395c6499eccSGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 139649901133SHans Petter Selasky if (__predict_false(m == NULL)) { 139749901133SHans Petter Selasky DPRINTF("could not allocate mbuf\n"); 1398ecc70d3fSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 139949901133SHans Petter Selasky return; 140049901133SHans Petter Selasky } 140149901133SHans Petter Selasky /* to align usie_hip{} on 32 bit */ 140249901133SHans Petter Selasky m->m_data += 3; 140349901133SHans Petter Selasky param = mtod(m, uint8_t *); 140449901133SHans Petter Selasky *param++ = USIE_HIP_FRM_CHR; 140549901133SHans Petter Selasky hip = (struct usie_hip *)param; 140649901133SHans Petter Selasky cns = (struct usie_cns *)(hip + 1); 140749901133SHans Petter Selasky 140849901133SHans Petter Selasky tmp = param + USIE_HIPCNS_MIN - 2; 140949901133SHans Petter Selasky 141049901133SHans Petter Selasky switch (obj) { 141149901133SHans Petter Selasky case USIE_CNS_OB_LINK_UPDATE: 141249901133SHans Petter Selasky cns_len = 2; 141349901133SHans Petter Selasky cns->op = USIE_CNS_OP_SET; 141449901133SHans Petter Selasky *tmp++ = 1; /* profile ID, always use 1 for now */ 141549901133SHans Petter Selasky *tmp++ = id == USIE_CNS_ID_INIT ? 1 : 0; 141649901133SHans Petter Selasky break; 141749901133SHans Petter Selasky 141849901133SHans Petter Selasky case USIE_CNS_OB_PROF_WRITE: 141949901133SHans Petter Selasky cns_len = 245; 142049901133SHans Petter Selasky cns->op = USIE_CNS_OP_SET; 142149901133SHans Petter Selasky *tmp++ = 1; /* profile ID, always use 1 for now */ 142249901133SHans Petter Selasky *tmp++ = 2; 142349901133SHans Petter Selasky memcpy(tmp, &sc->sc_net, 34); 142449901133SHans Petter Selasky memset(tmp + 35, 0, 245 - 36); 142549901133SHans Petter Selasky tmp += 243; 142649901133SHans Petter Selasky break; 142749901133SHans Petter Selasky 142849901133SHans Petter Selasky case USIE_CNS_OB_RSSI: 142949901133SHans Petter Selasky cns_len = 0; 143049901133SHans Petter Selasky cns->op = USIE_CNS_OP_REQ; 143149901133SHans Petter Selasky break; 143249901133SHans Petter Selasky 143349901133SHans Petter Selasky default: 143449901133SHans Petter Selasky DPRINTF("unsupported CnS object type\n"); 143549901133SHans Petter Selasky return; 143649901133SHans Petter Selasky } 143749901133SHans Petter Selasky *tmp = USIE_HIP_FRM_CHR; 143849901133SHans Petter Selasky 143949901133SHans Petter Selasky hip->len = htobe16(sizeof(struct usie_cns) + cns_len); 144049901133SHans Petter Selasky hip->id = USIE_HIP_CNS2M; 144149901133SHans Petter Selasky hip->param = 0; /* none for CnS */ 144249901133SHans Petter Selasky 144349901133SHans Petter Selasky cns->obj = htobe16(obj); 144449901133SHans Petter Selasky cns->id = htobe32(id); 144549901133SHans Petter Selasky cns->len = cns_len; 144649901133SHans Petter Selasky cns->rsv0 = cns->rsv1 = 0; /* always '0' */ 144749901133SHans Petter Selasky 144849901133SHans Petter Selasky param = (uint8_t *)(cns + 1); 144949901133SHans Petter Selasky 145049901133SHans Petter Selasky DPRINTF("param: %16D\n", param, ":"); 145149901133SHans Petter Selasky 145249901133SHans Petter Selasky m->m_pkthdr.len = m->m_len = USIE_HIPCNS_MIN + cns_len + 2; 145349901133SHans Petter Selasky 145449901133SHans Petter Selasky xfer = sc->sc_uc_xfer[USIE_HIP_IF][USIE_UC_TX]; 145549901133SHans Petter Selasky 145649901133SHans Petter Selasky if (usbd_xfer_get_priv(xfer) == NULL) { 145749901133SHans Petter Selasky usbd_xfer_set_priv(xfer, m); 145849901133SHans Petter Selasky usbd_transfer_start(xfer); 145949901133SHans Petter Selasky } else { 146049901133SHans Petter Selasky DPRINTF("Dropped CNS event\n"); 146149901133SHans Petter Selasky m_freem(m); 146249901133SHans Petter Selasky } 146349901133SHans Petter Selasky } 146449901133SHans Petter Selasky 146549901133SHans Petter Selasky static void 146649901133SHans Petter Selasky usie_cns_rsp(struct usie_softc *sc, struct usie_cns *cns) 146749901133SHans Petter Selasky { 146849901133SHans Petter Selasky struct ifnet *ifp = sc->sc_ifp; 146949901133SHans Petter Selasky 147049901133SHans Petter Selasky DPRINTF("received CnS\n"); 147149901133SHans Petter Selasky 147249901133SHans Petter Selasky switch (be16toh(cns->obj)) { 147349901133SHans Petter Selasky case USIE_CNS_OB_LINK_UPDATE: 147449901133SHans Petter Selasky if (be32toh(cns->id) & USIE_CNS_ID_INIT) 147549901133SHans Petter Selasky usie_if_sync_to(sc); 147649901133SHans Petter Selasky else if (be32toh(cns->id) & USIE_CNS_ID_STOP) { 147749901133SHans Petter Selasky ifp->if_flags &= ~IFF_UP; 147849901133SHans Petter Selasky ifp->if_drv_flags &= 147949901133SHans Petter Selasky ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 148049901133SHans Petter Selasky } else 148149901133SHans Petter Selasky DPRINTF("undefined link update\n"); 148249901133SHans Petter Selasky break; 148349901133SHans Petter Selasky 148449901133SHans Petter Selasky case USIE_CNS_OB_RSSI: 148549901133SHans Petter Selasky sc->sc_rssi = be16toh(*(int16_t *)(cns + 1)); 148649901133SHans Petter Selasky if (sc->sc_rssi <= 0) 148749901133SHans Petter Selasky device_printf(sc->sc_dev, "No signal\n"); 148849901133SHans Petter Selasky else { 148949901133SHans Petter Selasky device_printf(sc->sc_dev, "RSSI=%ddBm\n", 149049901133SHans Petter Selasky sc->sc_rssi - 110); 149149901133SHans Petter Selasky } 149249901133SHans Petter Selasky break; 149349901133SHans Petter Selasky 149449901133SHans Petter Selasky case USIE_CNS_OB_PROF_WRITE: 149549901133SHans Petter Selasky break; 149649901133SHans Petter Selasky 149749901133SHans Petter Selasky case USIE_CNS_OB_PDP_READ: 149849901133SHans Petter Selasky break; 149949901133SHans Petter Selasky 150049901133SHans Petter Selasky default: 150149901133SHans Petter Selasky DPRINTF("undefined CnS\n"); 150249901133SHans Petter Selasky break; 150349901133SHans Petter Selasky } 150449901133SHans Petter Selasky } 150549901133SHans Petter Selasky 150649901133SHans Petter Selasky static void 150749901133SHans Petter Selasky usie_hip_rsp(struct usie_softc *sc, uint8_t *rsp, uint32_t len) 150849901133SHans Petter Selasky { 150949901133SHans Petter Selasky struct usie_hip *hip; 151049901133SHans Petter Selasky struct usie_cns *cns; 151149901133SHans Petter Selasky uint32_t i; 151249901133SHans Petter Selasky uint32_t j; 151349901133SHans Petter Selasky uint32_t off; 151449901133SHans Petter Selasky uint8_t tmp[USIE_HIPCNS_MAX] __aligned(4); 151549901133SHans Petter Selasky 151649901133SHans Petter Selasky for (off = 0; (off + USIE_HIPCNS_MIN) <= len; off++) { 151749901133SHans Petter Selasky 151849901133SHans Petter Selasky uint8_t pad; 151949901133SHans Petter Selasky 152049901133SHans Petter Selasky while ((off < len) && (rsp[off] == USIE_HIP_FRM_CHR)) 152149901133SHans Petter Selasky off++; 152249901133SHans Petter Selasky 152349901133SHans Petter Selasky /* Unstuff the bytes */ 152449901133SHans Petter Selasky for (i = j = 0; ((i + off) < len) && 152549901133SHans Petter Selasky (j < USIE_HIPCNS_MAX); i++) { 152649901133SHans Petter Selasky 152749901133SHans Petter Selasky if (rsp[i + off] == USIE_HIP_FRM_CHR) 152849901133SHans Petter Selasky break; 152949901133SHans Petter Selasky 153049901133SHans Petter Selasky if (rsp[i + off] == USIE_HIP_ESC_CHR) { 153149901133SHans Petter Selasky if ((i + off + 1) >= len) 153249901133SHans Petter Selasky break; 153349901133SHans Petter Selasky tmp[j++] = rsp[i++ + off + 1] ^ 0x20; 153449901133SHans Petter Selasky } else { 153549901133SHans Petter Selasky tmp[j++] = rsp[i + off]; 153649901133SHans Petter Selasky } 153749901133SHans Petter Selasky } 153849901133SHans Petter Selasky 153949901133SHans Petter Selasky off += i; 154049901133SHans Petter Selasky 154149901133SHans Petter Selasky DPRINTF("frame len=%d\n", j); 154249901133SHans Petter Selasky 154349901133SHans Petter Selasky if (j < sizeof(struct usie_hip)) { 154449901133SHans Petter Selasky DPRINTF("too little data\n"); 154549901133SHans Petter Selasky break; 154649901133SHans Petter Selasky } 154749901133SHans Petter Selasky /* 154849901133SHans Petter Selasky * Make sure we are not reading the stack if something 154949901133SHans Petter Selasky * is wrong. 155049901133SHans Petter Selasky */ 155149901133SHans Petter Selasky memset(tmp + j, 0, sizeof(tmp) - j); 155249901133SHans Petter Selasky 155349901133SHans Petter Selasky hip = (struct usie_hip *)tmp; 155449901133SHans Petter Selasky 155549901133SHans Petter Selasky DPRINTF("hip: len=%d msgID=%02x, param=%02x\n", 155649901133SHans Petter Selasky be16toh(hip->len), hip->id, hip->param); 155749901133SHans Petter Selasky 155849901133SHans Petter Selasky pad = (hip->id & USIE_HIP_PAD) ? 1 : 0; 155949901133SHans Petter Selasky 156049901133SHans Petter Selasky if ((hip->id & USIE_HIP_MASK) == USIE_HIP_CNS2H) { 156149901133SHans Petter Selasky cns = (struct usie_cns *)(((uint8_t *)(hip + 1)) + pad); 156249901133SHans Petter Selasky 156349901133SHans Petter Selasky if (j < (sizeof(struct usie_cns) + 156449901133SHans Petter Selasky sizeof(struct usie_hip) + pad)) { 156549901133SHans Petter Selasky DPRINTF("too little data\n"); 156649901133SHans Petter Selasky break; 156749901133SHans Petter Selasky } 156849901133SHans Petter Selasky DPRINTF("cns: obj=%04x, op=%02x, rsv0=%02x, " 156949901133SHans Petter Selasky "app=%08x, rsv1=%02x, len=%d\n", 157049901133SHans Petter Selasky be16toh(cns->obj), cns->op, cns->rsv0, 157149901133SHans Petter Selasky be32toh(cns->id), cns->rsv1, cns->len); 157249901133SHans Petter Selasky 157349901133SHans Petter Selasky if (cns->op & USIE_CNS_OP_ERR) 157449901133SHans Petter Selasky DPRINTF("CnS error response\n"); 157549901133SHans Petter Selasky else 157649901133SHans Petter Selasky usie_cns_rsp(sc, cns); 157749901133SHans Petter Selasky 157849901133SHans Petter Selasky i = sizeof(struct usie_hip) + pad + sizeof(struct usie_cns); 157949901133SHans Petter Selasky j = cns->len; 158049901133SHans Petter Selasky } else { 158149901133SHans Petter Selasky i = sizeof(struct usie_hip) + pad; 158249901133SHans Petter Selasky j = be16toh(hip->len); 158349901133SHans Petter Selasky } 158449901133SHans Petter Selasky #ifdef USB_DEBUG 158549901133SHans Petter Selasky if (usie_debug == 0) 158649901133SHans Petter Selasky continue; 158749901133SHans Petter Selasky 158849901133SHans Petter Selasky while (i < USIE_HIPCNS_MAX && j > 0) { 158949901133SHans Petter Selasky DPRINTF("param[0x%02x] = 0x%02x\n", i, tmp[i]); 159049901133SHans Petter Selasky i++; 159149901133SHans Petter Selasky j--; 159249901133SHans Petter Selasky } 159349901133SHans Petter Selasky #endif 159449901133SHans Petter Selasky } 159549901133SHans Petter Selasky } 159649901133SHans Petter Selasky 159749901133SHans Petter Selasky static int 159849901133SHans Petter Selasky usie_driver_loaded(struct module *mod, int what, void *arg) 159949901133SHans Petter Selasky { 160049901133SHans Petter Selasky switch (what) { 160149901133SHans Petter Selasky case MOD_LOAD: 160249901133SHans Petter Selasky /* register autoinstall handler */ 160349901133SHans Petter Selasky usie_etag = EVENTHANDLER_REGISTER(usb_dev_configured, 160449901133SHans Petter Selasky usie_autoinst, NULL, EVENTHANDLER_PRI_ANY); 160549901133SHans Petter Selasky break; 160649901133SHans Petter Selasky case MOD_UNLOAD: 160749901133SHans Petter Selasky EVENTHANDLER_DEREGISTER(usb_dev_configured, usie_etag); 160849901133SHans Petter Selasky break; 160949901133SHans Petter Selasky default: 161049901133SHans Petter Selasky return (EOPNOTSUPP); 161149901133SHans Petter Selasky } 161249901133SHans Petter Selasky return (0); 161349901133SHans Petter Selasky } 161449901133SHans Petter Selasky 1615