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