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