diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2003-10-26 15:34:17 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2003-10-26 15:34:17 +0000 |
commit | 43782ab58acc9f9d055d1fc33d7069fe85857d90 (patch) | |
tree | 75ff040ecf49a4340cd89b95a47ff537576652da /sys/dev/usb | |
parent | b62dde670d7c7927ce0448b2c885cb9fdd70e437 (diff) |
Driver for PRISM 2.5/3 based (wifi) USB adapters. This is a work in progress,
it does not yet handle bulk data copies or hostap mode. Only one model
currently supported, however driver may support other PRISM based adapters.
ok millert@ fgsch@
Diffstat (limited to 'sys/dev/usb')
-rw-r--r-- | sys/dev/usb/files.usb | 6 | ||||
-rw-r--r-- | sys/dev/usb/if_wi_usb.c | 1941 | ||||
-rw-r--r-- | sys/dev/usb/if_wi_usb.h | 161 |
3 files changed, 2107 insertions, 1 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb index c3bbcfa5697..7ac467f9fe3 100644 --- a/sys/dev/usb/files.usb +++ b/sys/dev/usb/files.usb @@ -1,4 +1,4 @@ -# $OpenBSD: files.usb,v 1.32 2003/05/17 06:07:57 nate Exp $ +# $OpenBSD: files.usb,v 1.33 2003/10/26 15:34:16 drahn Exp $ # $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $ # # Config file and device description for machine-independent USB code. @@ -187,3 +187,7 @@ file dev/usb/uscanner.c uscanner needs-flag device usscanner: scsi attach usscanner at uhub file dev/usb/usscanner.c usscanner + +# Prism3 WI @ USB +attach wi at uhub with wi_usb +file dev/usb/if_wi_usb.c wi_usb needs-flag diff --git a/sys/dev/usb/if_wi_usb.c b/sys/dev/usb/if_wi_usb.c new file mode 100644 index 00000000000..1d48681a158 --- /dev/null +++ b/sys/dev/usb/if_wi_usb.c @@ -0,0 +1,1941 @@ +/* $OpenBSD: if_wi_usb.c,v 1.1 2003/10/26 15:34:16 drahn Exp $ */ + +/* + * Copyright (c) 2003 Dale Rahn. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + */ +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/socket.h> +#include <sys/device.h> +#include <sys/kthread.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> + +#define ROUNDUP64(x) (((x)+63) & ~63) + +#include <net/if_ieee80211.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <machine/bus.h> + +#include <dev/rndvar.h> + +#include <dev/ic/if_wireg.h> +#include <dev/ic/if_wi_ieee.h> +#include <dev/ic/if_wivar.h> + +#include <wi_usb.h> +#include <dev/usb/if_wi_usb.h> + +int wi_usb_do_transmit_sync(struct wi_usb_softc *wsc, struct wi_usb_chain *c, + void *ident); +void wi_usb_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status); +void wi_usb_txeof_frm(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status); +void wi_usb_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status); +void wi_usb_intr(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status); +void wi_usb_stop(struct wi_usb_softc *usc); +int wi_usb_tx_list_init(struct wi_usb_softc *usc); +int wi_usb_rx_list_init(struct wi_usb_softc *usc); +int wi_usb_open_pipes(struct wi_usb_softc *usc); +void wi_usb_cmdresp(struct wi_usb_chain *c); +void wi_usb_rridresp(struct wi_usb_chain *c); +void wi_usb_wridresp(struct wi_usb_chain *c); +void wi_usb_infofrm(struct wi_usb_chain *c, int len); +int wi_send_packet(struct wi_usb_softc *sc, int id); +void wi_usb_rxfrm(struct wi_usb_softc *usc, wi_usb_usbin *uin, int total_len); +void wi_usb_txfrm(struct wi_usb_softc *usc, wi_usb_usbin *uin, int total_len); +void wi_usb_start_thread(void *); + +int wi_usb_tx_lock_try(struct wi_usb_softc *sc); +void wi_usb_tx_lock(struct wi_usb_softc *usc); +void wi_usb_tx_unlock(struct wi_usb_softc *usc); +void wi_usb_ctl_lock(struct wi_usb_softc *usc); +void wi_usb_ctl_unlock(struct wi_usb_softc *usc); + +void wi_dump_data(void *buffer, int len); + +void wi_usb_thread(void *arg); + +#define WI_USB_DEBUG +#ifdef WI_USB_DEBUG +#define DPRINTF(x) if (wi_usbdebug) logprintf x +#define DPRINTFN(n,x) if (wi_usbdebug >= (n)) logprintf x +int wi_usbdebug = 1; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +struct wi_usb_thread_info { + int status; + int dying; + int idle; +}; + +/* thread status flags */ +#define WI_START 0x01 +#define WI_DYING 0x02 +#define WI_INQUIRE 0x04 +#define WI_WATCHDOG 0x08 + + +struct wi_usb_softc { + struct wi_softc sc_wi; +#define wi_usb_dev sc_wi.sc_dev + + usb_callout_t wi_usb_stat_ch; + + usbd_device_handle wi_usb_udev; + usbd_interface_handle wi_usb_iface; + u_int16_t wi_usb_vendor; + u_int16_t wi_usb_product; + int wi_usb_ed[WI_USB_ENDPT_MAX]; + usbd_pipe_handle wi_usb_ep[WI_USB_ENDPT_MAX]; + + struct wi_usb_chain wi_usb_tx_chain[WI_USB_TX_LIST_CNT]; + struct wi_usb_chain wi_usb_rx_chain[WI_USB_RX_LIST_CNT]; + + int wi_usb_refcnt; + char wi_usb_dying; + char wi_usb_attached; + int wi_usb_intr_errs; + struct timeval wi_usb_rx_notice; + + int wi_usb_pollpending; + + wi_usb_usbin wi_usb_ibuf; + int wi_usb_tx_prod; + int wi_usb_tx_cons; + int wi_usb_tx_cnt; + int wi_usb_rx_prod; + + struct wi_ltv_gen *ridltv; + int ridresperr; + + int cmdresp; + int cmdresperr; + int txresp; + int txresperr; + + /* nummem (tx/mgmt) */ + int wi_usb_nummem; +#define MAX_WI_NMEM 3 + void *wi_usb_txmem[MAX_WI_NMEM]; + int wi_usb_txmemsize[MAX_WI_NMEM]; + void *wi_usb_rxmem; + int wi_usb_rxmemsize; + + void *wi_info; + void *wi_rxframe; + + /* prevent multpile outstanding USB requests */ + int wi_lock; + int wi_lockwait; + + /* prevent multiple command requests */ + int wi_ctllock; + int wi_ctllockwait; + struct proc *wi_curproc; + + /* kthread */ + struct wi_usb_thread_info *wi_thread_info; + int wi_resetonce; +}; + +struct wi_funcs wi_func_usb = { + wi_cmd_usb, + wi_read_record_usb, + wi_write_record_usb, + wi_alloc_nicmem_usb, + wi_read_data_usb, + wi_write_data_usb, + wi_get_fid_usb, + wi_init_usb, + + wi_start_usb, + wi_ioctl_usb, + wi_watchdog_usb, + wi_inquire_usb, +}; + +/* + * Various supported device vendors/products. + */ +const struct wi_usb_type { + struct usb_devno wi_usb_device; + u_int16_t wi_usb_flags; + /* XXX */ +} wi_usb_devs[] = { + {{ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_MA111NA }, 0 } +}; +#define wi_usb_lookup(v, p) ((struct wi_usb_type *)usb_lookup(wi_usb_devs, v, p)) + +USB_DECLARE_DRIVER(wi_usb); + +USB_MATCH(wi_usb) +{ + USB_MATCH_START(wi_usb, uaa); + + if (uaa->iface != NULL) + return (UMATCH_NONE); + + return (wi_usb_lookup(uaa->vendor, uaa->product) != NULL ? + UMATCH_VENDOR_PRODUCT : UMATCH_NONE); +} + + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +USB_ATTACH(wi_usb) +{ + USB_ATTACH_START(wi_usb, sc, uaa); + char devinfo[1024]; +/* int s; */ + usbd_device_handle dev = uaa->device; + usbd_interface_handle iface; + usbd_status err; + usb_interface_descriptor_t *id; + usb_endpoint_descriptor_t *ed; + int i; + + DPRINTFN(5,(" : wi_usb_attach: sc=%p", sc)); + + err = usbd_set_config_no(dev, WI_USB_CONFIG_NO, 1); + if (err) { + printf("%s: setting config no failed\n", + USBDEVNAME(sc->wi_usb_dev)); + USB_ATTACH_ERROR_RETURN; + } + + usbd_devinfo(dev, 0, devinfo, sizeof devinfo); + USB_ATTACH_SETUP; + printf("%s: %s\n", USBDEVNAME(sc->wi_usb_dev), devinfo); + + /* XXX - any tasks? */ + + err = usbd_device2interface_handle(dev, WI_USB_IFACE_IDX, &iface); + if (err) { + printf("%s: getting interface handle failed\n", + USBDEVNAME(sc->wi_usb_dev)); + USB_ATTACH_ERROR_RETURN; + } + + /* XXX - flags? */ + + sc->wi_usb_udev = dev; + sc->wi_usb_iface = iface; + sc->wi_usb_product = uaa->product; + sc->wi_usb_vendor = uaa->vendor; + + sc->sc_wi.wi_usb_cdata = sc; + sc->sc_wi.wi_flags |= WI_FLAGS_BUS_USB; + + sc->wi_lock = 0; + sc->wi_lockwait = 0; + sc->wi_resetonce = 0; + + id = usbd_get_interface_descriptor(iface); + + /* Find endpoints. */ + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) { + printf("%s: couldn't get endpoint descriptor %d\n", + USBDEVNAME(sc->wi_usb_dev), i); + USB_ATTACH_ERROR_RETURN; + } + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + sc->wi_usb_ed[WI_USB_ENDPT_RX] = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { + sc->wi_usb_ed[WI_USB_ENDPT_TX] = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) { + sc->wi_usb_ed[WI_USB_ENDPT_INTR] = ed->bEndpointAddress; + } + } + + sc->wi_usb_nummem = 0; + + /* attach wi device */ + + if (wi_usb_rx_list_init(sc)) { + printf("%s: rx list init failed\n", + USBDEVNAME(sc->wi_usb_dev)); + USB_ATTACH_ERROR_RETURN; + } + if (wi_usb_tx_list_init(sc)) { + printf("%s: tx list init failed\n", + USBDEVNAME(sc->wi_usb_dev)); + USB_ATTACH_ERROR_RETURN; + } + + if (wi_usb_open_pipes(sc)){ + printf("%s: open pipes failed\n", + USBDEVNAME(sc->wi_usb_dev)); + USB_ATTACH_ERROR_RETURN; + } + + sc->wi_usb_attached = 1; + + if (cold) + kthread_create_deferred(wi_usb_start_thread, sc); + else + wi_usb_start_thread(sc); + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->wi_usb_udev, + USBDEV(sc->wi_usb_dev)); + + + USB_ATTACH_SUCCESS_RETURN; +} + +USB_DETACH(wi_usb) +{ + USB_DETACH_START(wi_usb, sc); + struct ifnet *ifp = WI_GET_IFP(sc); + struct wi_softc *wsc = &sc->sc_wi; + int s; + + sc->wi_usb_dying = 1; + if (sc->wi_thread_info != NULL) { + sc->wi_thread_info->dying = 1; + + sc->wi_thread_info->status |= WI_DYING; + if (sc->wi_thread_info->idle) + wakeup (sc->wi_thread_info); + } + + if (!sc->wi_usb_attached) { + /* Detached before attach finished, so just bail out. */ + return (0); + } + /* tasks? */ + + s = splusb(); + /* detatch wi */ + + if (!(wsc->wi_flags & WI_FLAGS_ATTACHED)) { + printf("%s: already detached\n", USBDEVNAME(sc->wi_usb_dev)); + splx(s); + return (0); + } + + wi_detach(&sc->sc_wi); + + wsc->wi_flags = 0; + + ether_ifdetach(ifp); + if_detach(ifp); + + sc->wi_usb_attached = 0; + + if (--sc->wi_usb_refcnt >= 0) { + /* Wait for processes to go away. */ + usb_detach_wait(USBDEV(sc->wi_usb_dev)); + } + + while (sc->wi_usb_nummem) { + sc->wi_usb_nummem--; + if (sc->wi_usb_txmem[sc->wi_usb_nummem] != NULL) + free(sc->wi_usb_txmem[sc->wi_usb_nummem], M_DEVBUF); + sc->wi_usb_txmem[sc->wi_usb_nummem] = NULL; + } + + if (sc->wi_usb_ep[WI_USB_ENDPT_INTR] != NULL); + usbd_abort_pipe(sc->wi_usb_ep[WI_USB_ENDPT_INTR]); + if (sc->wi_usb_ep[WI_USB_ENDPT_TX] != NULL); + usbd_abort_pipe(sc->wi_usb_ep[WI_USB_ENDPT_TX]); + if (sc->wi_usb_ep[WI_USB_ENDPT_RX] != NULL); + usbd_abort_pipe(sc->wi_usb_ep[WI_USB_ENDPT_RX]); + + splx(s); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->wi_usb_udev, + USBDEV(sc->wi_usb_dev)); + return (0); +} + +int +wi_send_packet(struct wi_usb_softc *sc, int id) +{ + struct wi_usb_chain *c; + struct wi_frame *wibuf; + int total_len, rnd_len; + int err; + + c = &sc->wi_usb_tx_chain[0]; + + DPRINTFN(10,("%s: %s: id=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, id)); + + /* assemble packet from write_data buffer */ + if (id == 0 || id == 1) { + /* tx_lock acquired before wi_start() */ + wibuf = sc->wi_usb_txmem[id]; + + total_len = sizeof (struct wi_frame) + + letoh16(wibuf->wi_dat_len); + rnd_len = ROUNDUP64(total_len); + if ((total_len > sc->wi_usb_txmemsize[id]) || + (rnd_len > WI_USB_BUFSZ )){ + printf("invalid packet len: %x memsz %x max %x\n", + total_len, sc->wi_usb_txmemsize[id], WI_USB_BUFSZ); + total_len = sc->wi_usb_txmemsize[id]; + + err = EIO; + goto err_ret; + } + + sc->txresp = WI_CMD_TX; + sc->txresperr = 0; + + bcopy(wibuf, c->wi_usb_buf, total_len); + + bzero(((char *)c->wi_usb_buf)+total_len, + rnd_len - total_len); + + total_len = rnd_len; + + DPRINTFN(5,("%s: %s: id=%x len=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, id, total_len)); + + usbd_setup_xfer(c->wi_usb_xfer, sc->wi_usb_ep[WI_USB_ENDPT_TX], + c, c->wi_usb_buf, rnd_len, + USBD_FORCE_SHORT_XFER | USBD_NO_COPY, + WI_USB_TX_TIMEOUT, wi_usb_txeof_frm); + + err = usbd_transfer(c->wi_usb_xfer); + if (err != USBD_IN_PROGRESS && err != USBD_NORMAL_COMPLETION) { + printf("%s: wi_usb_send error=%s\n", + USBDEVNAME(sc->wi_usb_dev), usbd_errstr(err)); + /* Stop the interface from process context. */ + wi_usb_stop(sc); + err = EIO; + } else { + err = 0; + } + + DPRINTFN(5,("%s: %s: exit err=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, err)); +err_ret: + return err; + } + printf("%s:%s: invalid packet id sent %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, id); + return 0; +} + +int +wi_cmd_usb(struct wi_softc *wsc, int cmd, int val0, int val1, int val2) +{ + struct wi_usb_chain *c; + struct wi_usb_softc *sc = wsc->wi_usb_cdata; + struct wi_cmdreq *pcmd; + int total_len, rnd_len; + int err; + + DPRINTFN(5,("%s: %s: enter cmd=%x %x %x %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, cmd, val0, val1, val2)); + + if ((cmd & WI_CMD_CODE_MASK) == WI_CMD_TX) { + return wi_send_packet(sc, val0); + } + + + if ((cmd & WI_CMD_CODE_MASK) == WI_CMD_INI) { + /* free alloc_nicmem regions */ + while (sc->wi_usb_nummem) { + sc->wi_usb_nummem--; + free(sc->wi_usb_txmem[sc->wi_usb_nummem], M_DEVBUF); + sc->wi_usb_txmem[sc->wi_usb_nummem] = NULL; + } + +#if 0 + /* if this is the first time, init, otherwise do not?? */ + if (sc->wi_resetonce) { + return 0; + } else + sc->wi_resetonce = 1; +#endif + } + + wi_usb_ctl_lock(sc); + + wi_usb_tx_lock(sc); + + c = &sc->wi_usb_tx_chain[0]; + pcmd = c->wi_usb_buf; + + + total_len = sizeof (struct wi_cmdreq); + rnd_len = ROUNDUP64(total_len); + if (rnd_len > WI_USB_BUFSZ) { + printf("read_record buf size err %x %x\n", + rnd_len, WI_USB_BUFSZ); + err = EIO; + goto err_ret; + } + + sc->cmdresp = cmd; + sc->cmdresperr = 0; + + pcmd->type = htole16(WI_USB_CMDREQ); + pcmd->cmd = htole16(cmd); + pcmd->param0 = htole16(val0); + pcmd->param1 = htole16(val1); + pcmd->param2 = htole16(val2); + + bzero(((char*)pcmd)+total_len, rnd_len - total_len); + + total_len = rnd_len; + + usbd_setup_xfer(c->wi_usb_xfer, sc->wi_usb_ep[WI_USB_ENDPT_TX], + c, c->wi_usb_buf, rnd_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, + WI_USB_TX_TIMEOUT, wi_usb_txeof); + + err = wi_usb_do_transmit_sync(sc, c, &sc->cmdresperr); + + if (err == 0) + err = sc->cmdresperr; + + sc->cmdresperr = 0; + +err_ret: + wi_usb_tx_unlock(sc); + + wi_usb_ctl_unlock(sc); + + DPRINTFN(5,("%s: %s: exit err=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, err)); + return err; +} + + +int +wi_read_record_usb(struct wi_softc *wsc, struct wi_ltv_gen *ltv) +{ + struct wi_usb_chain *c; + struct wi_usb_softc *sc = wsc->wi_usb_cdata; + struct wi_rridreq *prid; + int total_len, rnd_len; + int err; + struct wi_ltv_gen *oltv, p2ltv; + + DPRINTFN(5,("%s: %s: enter rid=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, ltv->wi_type)); + + /* Do we need to deal with these here, as in _io version? + * WI_RID_ENCRYPTION -> WI_RID_P2_ENCRYPTION + * WI_RID_TX_CRYPT_KEY -> WI_RID_P2_TX_CRYPT_KEY + */ + if (wsc->sc_firmware_type != WI_LUCENT) { + oltv = ltv; + switch (ltv->wi_type) { + case WI_RID_ENCRYPTION: + p2ltv.wi_type = WI_RID_P2_ENCRYPTION; + p2ltv.wi_len = 2; + ltv = &p2ltv; + break; + case WI_RID_TX_CRYPT_KEY: + if (ltv->wi_val > WI_NLTV_KEYS) + return (EINVAL); + p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; + p2ltv.wi_len = 2; + ltv = &p2ltv; + break; + } + } + + wi_usb_tx_lock(sc); + + c = &sc->wi_usb_tx_chain[0]; + prid = c->wi_usb_buf; + + total_len = sizeof(struct wi_rridreq); + rnd_len = ROUNDUP64(total_len); + + if (rnd_len > WI_USB_BUFSZ) { + printf("read_record buf size err %x %x\n", + rnd_len, WI_USB_BUFSZ); + return EIO; + } + + sc->ridltv = ltv; + sc->ridresperr = 0; + + prid->type = htole16(WI_USB_RRIDREQ); + prid->frmlen = htole16(2); /* variable size? */ + prid->rid = htole16(ltv->wi_type); + + bzero(((char*)prid)+total_len, rnd_len - total_len); + + usbd_setup_xfer(c->wi_usb_xfer, sc->wi_usb_ep[WI_USB_ENDPT_TX], + c, c->wi_usb_buf, rnd_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, + WI_USB_TX_TIMEOUT, wi_usb_txeof); + + DPRINTFN(10,("%s: %s: total_len=%x, wilen %d\n", + USBDEVNAME(sc->wi_usb_dev), __func__, total_len, ltv->wi_len)); + + err = wi_usb_do_transmit_sync(sc, c, &sc->ridresperr); + + /* Do we need to deal with these here, as in _io version? + * + * WI_RID_TX_RATE + * WI_RID_CUR_TX_RATE + * WI_RID_ENCRYPTION + * WI_RID_TX_CRYPT_KEY + * WI_RID_CNFAUTHMODE + */ + if (ltv->wi_type == WI_RID_PORTTYPE && wsc->wi_ptype == WI_PORTTYPE_IBSS + && ltv->wi_val == wsc->wi_ibss_port) { + /* + * Convert vendor IBSS port type to WI_PORTTYPE_IBSS. + * Since Lucent uses port type 1 for BSS *and* IBSS we + * have to rely on wi_ptype to distinguish this for us. + */ + ltv->wi_val = htole16(WI_PORTTYPE_IBSS); + } else if (wsc->sc_firmware_type != WI_LUCENT) { + int v; + + switch (oltv->wi_type) { + case WI_RID_TX_RATE: + case WI_RID_CUR_TX_RATE: + switch (letoh16(ltv->wi_val)) { + case 1: v = 1; break; + case 2: v = 2; break; + case 3: v = 6; break; + case 4: v = 5; break; + case 7: v = 7; break; + case 8: v = 11; break; + case 15: v = 3; break; + default: v = 0x100 + letoh16(ltv->wi_val); break; + } + oltv->wi_val = htole16(v); + break; + case WI_RID_ENCRYPTION: + oltv->wi_len = 2; + if (ltv->wi_val & htole16(0x01)) + oltv->wi_val = htole16(1); + else + oltv->wi_val = htole16(0); + break; + case WI_RID_TX_CRYPT_KEY: + case WI_RID_CNFAUTHMODE: + oltv->wi_len = 2; + oltv->wi_val = ltv->wi_val; + break; + } + } + + if (err == 0) + err = sc->ridresperr; + + sc->ridresperr = 0; + + wi_usb_tx_unlock(sc); + + DPRINTFN(5,("%s: %s: exit err=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, err)); + return err; +} + +int +wi_write_record_usb(struct wi_softc *wsc, struct wi_ltv_gen *ltv) +{ + struct wi_usb_chain *c; + struct wi_usb_softc *sc = wsc->wi_usb_cdata; + struct wi_wridreq *prid; + int total_len, rnd_len; + int err; + struct wi_ltv_gen p2ltv; + u_int16_t val = 0; + int i; + + DPRINTFN(5,("%s: %s: enter rid=%x wi_len %d copying %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, ltv->wi_type, ltv->wi_len, + (ltv->wi_len-1)*2 )); + + /* Do we need to deal with these here, as in _io version? + * WI_PORTTYPE_IBSS -> WI_RID_PORTTYPE + * RID_TX_RATE munging + * RID_ENCRYPTION + * WI_RID_TX_CRYPT_KEY + * WI_RID_DEFLT_CRYPT_KEYS + */ + if (ltv->wi_type == WI_RID_PORTTYPE && + letoh16(ltv->wi_val) == WI_PORTTYPE_IBSS) { + /* Convert WI_PORTTYPE_IBSS to vendor IBSS port type. */ + p2ltv.wi_type = WI_RID_PORTTYPE; + p2ltv.wi_len = 2; + p2ltv.wi_val = wsc->wi_ibss_port; + ltv = &p2ltv; + } else if (wsc->sc_firmware_type != WI_LUCENT) { + int v; + + switch (ltv->wi_type) { + case WI_RID_TX_RATE: + p2ltv.wi_type = WI_RID_TX_RATE; + p2ltv.wi_len = 2; + switch (letoh16(ltv->wi_val)) { + case 1: v = 1; break; + case 2: v = 2; break; + case 3: v = 15; break; + case 5: v = 4; break; + case 6: v = 3; break; + case 7: v = 7; break; + case 11: v = 8; break; + default: return EINVAL; + } + p2ltv.wi_val = htole16(v); + ltv = &p2ltv; + break; + case WI_RID_ENCRYPTION: + p2ltv.wi_type = WI_RID_P2_ENCRYPTION; + p2ltv.wi_len = 2; + if (ltv->wi_val & htole16(0x01)) { + val = PRIVACY_INVOKED; + /* + * If using shared key WEP we must set the + * EXCLUDE_UNENCRYPTED bit. Symbol cards + * need this bit set even when not using + * shared key. We can't just test for + * IEEE80211_AUTH_SHARED since Symbol cards + * have 2 shared key modes. + */ + if (wsc->wi_authtype != IEEE80211_AUTH_OPEN || + wsc->sc_firmware_type == WI_SYMBOL) + val |= EXCLUDE_UNENCRYPTED; + + switch (wsc->wi_crypto_algorithm) { + case WI_CRYPTO_FIRMWARE_WEP: + /* + * TX encryption is broken in + * Host AP mode. + */ + if (wsc->wi_ptype == WI_PORTTYPE_HOSTAP) + val |= HOST_ENCRYPT; + break; + case WI_CRYPTO_SOFTWARE_WEP: + val |= HOST_ENCRYPT|HOST_DECRYPT; + break; + } + p2ltv.wi_val = htole16(val); + } else + p2ltv.wi_val = htole16(HOST_ENCRYPT | HOST_DECRYPT); + ltv = &p2ltv; + break; + case WI_RID_TX_CRYPT_KEY: + if (ltv->wi_val > WI_NLTV_KEYS) + return (EINVAL); + p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; + p2ltv.wi_len = 2; + p2ltv.wi_val = ltv->wi_val; + ltv = &p2ltv; + break; + case WI_RID_DEFLT_CRYPT_KEYS: { + int error; + int keylen; + struct wi_ltv_str ws; + struct wi_ltv_keys *wk = (struct wi_ltv_keys *)ltv; + keylen = wk->wi_keys[wsc->wi_tx_key].wi_keylen; + + for (i = 0; i < 4; i++) { + bzero(&ws, sizeof(ws)); + ws.wi_len = (keylen > 5) ? 8 : 4; + ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i; + bcopy(&wk->wi_keys[i].wi_keydat, + ws.wi_str, keylen); + error = wi_write_record_usb(wsc, + (struct wi_ltv_gen *)&ws); + if (error) + return (error); + } + } + return (0); + } + } + + wi_usb_tx_lock(sc); + + c = &sc->wi_usb_tx_chain[0]; + + prid = c->wi_usb_buf; + + total_len = sizeof(prid->type) + sizeof(prid->frmlen) + + sizeof(prid->rid) + (ltv->wi_len-1)*2; + rnd_len = ROUNDUP64(total_len); + if (rnd_len > WI_USB_BUFSZ) { + printf("write_record buf size err %x %x\n", + rnd_len, WI_USB_BUFSZ); + return EIO; + } + + prid->type = htole16(WI_USB_WRIDREQ); + prid->frmlen = htole16(ltv->wi_len); + prid->rid = htole16(ltv->wi_type); + if (ltv->wi_len > 1) + bcopy((u_int8_t *)<v->wi_val, (u_int8_t *)&prid->data[0], + (ltv->wi_len-1)*2); + + bzero(((char*)prid)+total_len, rnd_len - total_len); + + usbd_setup_xfer(c->wi_usb_xfer, sc->wi_usb_ep[WI_USB_ENDPT_TX], + c, c->wi_usb_buf, rnd_len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, + WI_USB_TX_TIMEOUT, wi_usb_txeof); + + err = wi_usb_do_transmit_sync(sc, c, &sc->ridresperr); + + if (err == 0) + err = sc->ridresperr; + + sc->ridresperr = 0; + + wi_usb_tx_unlock(sc); + + DPRINTFN(5,("%s: %s: exit err=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, err)); + return err; +} + +/* + * This is an ugly compat portion to emulate the I/O which writes + * a packet or management information + * The data is copied into local memory for the requested + * 'id' then on the wi_cmd WI_CMD_TX, the id argument + * will identify which buffer to use + */ +int +wi_alloc_nicmem_usb(struct wi_softc *wsc, int len, int *id) +{ + int nmem; + struct wi_usb_softc *sc = wsc->wi_usb_cdata; + + DPRINTFN(10,("%s: %s: enter len=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, len)); + + /* + * NOTE THIS IS A USB DEVICE WHICH WILL LIKELY HAVE MANY + * CONNECTS/DISCONNECTS, FREE THIS MEMORY XXX XXX XXX !!! !!! + */ + nmem = sc->wi_usb_nummem++; + + if (nmem >= MAX_WI_NMEM) { + sc->wi_usb_nummem--; + return ENOMEM; + } + + sc->wi_usb_txmem[nmem] = malloc(len, M_DEVBUF, M_WAITOK); + if (sc->wi_usb_txmem[nmem] == NULL) { + sc->wi_usb_nummem--; + return ENOMEM; + } + sc->wi_usb_txmemsize[nmem] = len; + + *id = nmem; + return 0; +} + +/* + * this is crazy, we skip the first 16 bits of the buf so that it + * can be used as the 'type' of the usb transfer. + */ + + +int +wi_write_data_usb(struct wi_softc *wsc, int id, int off, caddr_t buf, int len) +{ + u_int8_t *ptr; + struct wi_usb_softc *sc = wsc->wi_usb_cdata; + + DPRINTFN(10,("%s: %s: id %x off %x len %d\n", + USBDEVNAME(sc->wi_usb_dev), __func__, id, off, len)); + + if (id < 0 && id >= sc->wi_usb_nummem) + return EIO; + + ptr = (u_int8_t *)(sc->wi_usb_txmem[id]) + off; + + if (len + off > sc->wi_usb_txmemsize[id]) + return EIO; + DPRINTFN(10,("%s: %s: completed \n", + USBDEVNAME(sc->wi_usb_dev), __func__)); + + bcopy(buf, ptr, len); + return 0; +} + +/* + * On the prism I/O, this read_data points to the hardware buffer + * which contains the + */ +int +wi_read_data_usb(struct wi_softc *wsc, int id, int off, caddr_t buf, int len) +{ + u_int8_t *ptr; + struct wi_usb_softc *sc = wsc->wi_usb_cdata; + + DPRINTFN(10,("%s: %s: id %x off %x len %d\n", + USBDEVNAME(sc->wi_usb_dev), __func__, id, off, len)); + + if (id == 0x1001 && sc->wi_info != NULL) + ptr = (u_int8_t *)sc->wi_info + off; + else if (id == 0x1000 && sc->wi_rxframe != NULL) + ptr = (u_int8_t *)sc->wi_rxframe + off; + else if (id >= 0 && id < sc->wi_usb_nummem) { + + if (sc->wi_usb_txmem[id] == NULL) + return EIO; + if (len + off > sc->wi_usb_txmemsize[id]) + return EIO; + + ptr = (u_int8_t *)(sc->wi_usb_txmem[id]) + off; + } else + return EIO; + + if (id < sc->wi_usb_nummem) { + ptr = (u_int8_t *)(sc->wi_usb_txmem[id]) + off; + + if (len + off > sc->wi_usb_txmemsize[id]) + return EIO; + } + + bcopy(ptr, buf, len); + return 0; +} + +void +wi_usb_stop(struct wi_usb_softc *sc) +{ + DPRINTFN(1,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev),__func__)); + /* XXX */ + + /* Stop transfers */ +} + +int +wi_usb_do_transmit_sync(struct wi_usb_softc *sc, struct wi_usb_chain *c, + void *ident) +{ + usbd_status err; + + DPRINTFN(10,("%s: %s:\n", + USBDEVNAME(sc->wi_usb_dev), __func__)); + + sc->wi_usb_refcnt++; + err = usbd_transfer(c->wi_usb_xfer); + if (err != USBD_IN_PROGRESS && err != USBD_NORMAL_COMPLETION) { + printf("%s: wi_usb_send error=%s\n", + USBDEVNAME(sc->wi_usb_dev), usbd_errstr(err)); + /* Stop the interface from process context. */ + wi_usb_stop(sc); + err = EIO; + goto done; + } + err = tsleep(ident, PRIBIO, "wiTXsync", hz*1); + if (err) { + DPRINTFN(1,("%s: %s: err %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, err)); + err = ETIMEDOUT; + } +done: + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + return err; +} + + +/* + * A command/rrid/wrid was sent to the chip. It's safe for us to clean up + * the list buffers. + */ + +void +wi_usb_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + struct wi_usb_chain *c = priv; + struct wi_usb_softc *sc = c->wi_usb_sc; + + int s; + + if (sc->wi_usb_dying) + return; + + s = splnet(); + + DPRINTFN(10,("%s: %s: enter status=%d\n", USBDEVNAME(sc->wi_usb_dev), + __func__, status)); + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + splx(s); + return; + } + printf("%s: usb error on tx: %s\n", USBDEVNAME(sc->wi_usb_dev), + usbd_errstr(status)); + if (status == USBD_STALLED) { + sc->wi_usb_refcnt++; + usbd_clear_endpoint_stall( + sc->wi_usb_ep[WI_USB_ENDPT_TX]); + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + } + splx(s); + return; + } + + splx(s); +} + +/* + * A packet was sent to the chip. It's safe for us to clean up + * the list buffers. + */ + +void +wi_usb_txeof_frm(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + struct wi_usb_chain *c = priv; + struct wi_usb_softc *sc = c->wi_usb_sc; + struct wi_softc *wsc = &sc->sc_wi; + struct ifnet *ifp = &wsc->sc_arpcom.ac_if; + + int s; + int err = 0; + + if (sc->wi_usb_dying) + return; + + s = splnet(); + + DPRINTFN(10,("%s: %s: enter status=%d\n", USBDEVNAME(sc->wi_usb_dev), + __func__, status)); + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) { + splx(s); + return; + } + printf("%s: usb error on tx: %s\n", USBDEVNAME(sc->wi_usb_dev), + usbd_errstr(status)); + if (status == USBD_STALLED) { + sc->wi_usb_refcnt++; + usbd_clear_endpoint_stall( + sc->wi_usb_ep[WI_USB_ENDPT_TX]); + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + } + splx(s); + return; + } + + if (status) + err = WI_EV_TX_EXC; + + wi_txeof(wsc, err); + + wi_usb_tx_unlock(sc); + + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + wi_start_usb(ifp); + + splx(s); +} + +int +wi_usb_rx_list_init(struct wi_usb_softc *sc) +{ + struct wi_usb_chain *c; + int i; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + for (i = 0; i < WI_USB_RX_LIST_CNT; i++) { + c = &sc->wi_usb_rx_chain[i]; + c->wi_usb_sc = sc; + c->wi_usb_idx = i; + if (c->wi_usb_xfer != NULL) { + printf("UGH RX\n"); + } + if (c->wi_usb_xfer == NULL) { + c->wi_usb_xfer = usbd_alloc_xfer(sc->wi_usb_udev); + if (c->wi_usb_xfer == NULL) + return (ENOBUFS); + c->wi_usb_buf = usbd_alloc_buffer(c->wi_usb_xfer, + WI_USB_BUFSZ); + if (c->wi_usb_buf == NULL) + return (ENOBUFS); /* XXX free xfer */ + } + } + + return (0); +} + +int +wi_usb_tx_list_init(struct wi_usb_softc *sc) +{ + struct wi_usb_chain *c; + int i; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + for (i = 0; i < WI_USB_TX_LIST_CNT; i++) { + c = &sc->wi_usb_tx_chain[i]; + c->wi_usb_sc = sc; + c->wi_usb_idx = i; + c->wi_usb_mbuf = NULL; + if (c->wi_usb_xfer != NULL) { + printf("UGH TX\n"); + } + if (c->wi_usb_xfer == NULL) { + c->wi_usb_xfer = usbd_alloc_xfer(sc->wi_usb_udev); + if (c->wi_usb_xfer == NULL) + return (ENOBUFS); + c->wi_usb_buf = usbd_alloc_buffer(c->wi_usb_xfer, + WI_USB_BUFSZ); + if (c->wi_usb_buf == NULL) + return (ENOBUFS); + } + } + + return (0); +} + +int +wi_usb_open_pipes(struct wi_usb_softc *sc) +{ + usbd_status err; + int error = 0; + struct wi_usb_chain *c; + int i; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev),__func__)); + + sc->wi_usb_refcnt++; + + /* Open RX and TX pipes. */ + err = usbd_open_pipe(sc->wi_usb_iface, sc->wi_usb_ed[WI_USB_ENDPT_RX], + USBD_EXCLUSIVE_USE, &sc->wi_usb_ep[WI_USB_ENDPT_RX]); + if (err) { + printf("%s: open rx pipe failed: %s\n", + USBDEVNAME(sc->wi_usb_dev), usbd_errstr(err)); + error = EIO; + goto done; + } + + err = usbd_open_pipe(sc->wi_usb_iface, sc->wi_usb_ed[WI_USB_ENDPT_TX], + USBD_EXCLUSIVE_USE, &sc->wi_usb_ep[WI_USB_ENDPT_TX]); + if (err) { + printf("%s: open tx pipe failed: %s\n", + USBDEVNAME(sc->wi_usb_dev), usbd_errstr(err)); + error = EIO; + goto done; + } + + /* is this used? */ + err = usbd_open_pipe_intr(sc->wi_usb_iface, + sc->wi_usb_ed[WI_USB_ENDPT_INTR], USBD_EXCLUSIVE_USE, + &sc->wi_usb_ep[WI_USB_ENDPT_INTR], sc, &sc->wi_usb_ibuf, + WI_USB_INTR_PKTLEN, wi_usb_intr, WI_USB_INTR_INTERVAL); + if (err) { + printf("%s: open intr pipe failed: %s\n", + USBDEVNAME(sc->wi_usb_dev), usbd_errstr(err)); + error = EIO; + goto done; + } + + /* Start up the receive pipe. */ + for (i = 0; i < WI_USB_RX_LIST_CNT; i++) { + c = &sc->wi_usb_rx_chain[i]; + usbd_setup_xfer(c->wi_usb_xfer, sc->wi_usb_ep[WI_USB_ENDPT_RX], + c, c->wi_usb_buf, WI_USB_BUFSZ, + USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, + wi_usb_rxeof); + DPRINTFN(10,("%s: %s: start read\n", USBDEVNAME(sc->wi_usb_dev), + __func__)); + usbd_transfer(c->wi_usb_xfer); + } + +done: + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + + return (error); +} + +/* + * This is a bit of a kludge, however wi_rxeof and wi_update_stats + * call wi_get_fid to determine where the data associated with + * the transaction is located, the returned id is then used to + * wi_read_data the information out. + * + * This code returns which 'fid' should be used. The results are only valid + * during a wi_usb_rxeof because the data is recieved packet is 'held' + * an a variable for reading by wi_read_data_usb for that period. + * + * for magic numbers this uses 0x1000, 0x1001 for rx/info + */ + +int +wi_get_fid_usb(struct wi_softc *sc, int fid) +{ + switch (fid) { + case WI_RX_FID: + return 0x1000; + case WI_INFO_FID: + return 0x1001; + default: + return 0x1111; + } + +} + +int +wi_usb_activate(device_ptr_t self, enum devact act) +{ + struct wi_usb_softc *sc = (struct wi_usb_softc *)self; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + if_deactivate(&sc->sc_wi.wi_ec.ec_if); + sc->wi_usb_dying = 1; + sc->wi_thread_info->dying = 1; + break; + } + return (0); +} + +#if 0 +void +wi_dump_data(void *buffer, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (((i) % 16) == 0) + printf("\n %02x:", i); + printf(" %02x", + ((uint8_t *)(buffer))[i]); + + } + printf("\n"); + +} +#endif + +/* + * A frame has been recieved. + */ +void +wi_usb_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct wi_usb_chain *c = priv; + struct wi_usb_softc *sc = c->wi_usb_sc; + wi_usb_usbin *uin; + int total_len = 0; + u_int16_t rtype; + + if (sc->wi_usb_dying) + return; + + DPRINTFN(10,("%s: %s: enter status=%d\n", USBDEVNAME(sc->wi_usb_dev), + __func__, status)); + + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_IOERROR + || status == USBD_CANCELLED) { + printf("%s: %u usb errors on rx: %s\n", + USBDEVNAME(sc->wi_usb_dev), 1, + /* sc->wi_usb_rx_errs, */ + usbd_errstr(status)); + return; + } +#if 0 + sc->wi_usb_rx_errs++; + if (usbd_ratecheck(&sc->wi_usb_rx_notice)) { + printf("%s: %u usb errors on rx: %s\n", + USBDEVNAME(sc->wi_usb_dev), sc->wi_usb_rx_errs, + usbd_errstr(status)); + sc->wi_usb_rx_errs = 0; + } +#endif + if (status == USBD_STALLED) { + sc->wi_usb_refcnt++; + usbd_clear_endpoint_stall( + sc->wi_usb_ep[WI_USB_ENDPT_RX]); + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + } + goto done; + } + + usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL); + + if (total_len < 6) /* short XXX */ + goto done; + + uin = (wi_usb_usbin *)(c->wi_usb_buf); + + rtype = letoh16(uin->type); + + +#if 0 + wi_dump_data(c->wi_usb_buf, total_len); +#endif + + if (WI_USB_ISRXFRM(rtype)) { + wi_usb_rxfrm(sc, uin, total_len); + goto done; + } + if (WI_USB_ISTXFRM(rtype)) { + DPRINTFN(2,("%s: %s: txfrm type %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, rtype)); + wi_usb_txfrm(sc, uin, total_len); + goto done; + } + + switch (rtype) { + case WI_USB_INFOFRM: + /* info packet, INFO_FID hmm */ + DPRINTFN(10,("%s: %s: infofrm type %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, rtype)); + wi_usb_infofrm(c, total_len); + break; + case WI_USB_CMDRESP: + wi_usb_cmdresp(c); + break; + case WI_USB_WRIDRESP: + wi_usb_wridresp(c); + break; + case WI_USB_RRIDRESP: + wi_usb_rridresp(c); + break; + case WI_USB_WMEMRESP: + /* Not currently used */ + DPRINTFN(2,("%s: %s: wmemresp type %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, rtype)); + break; + case WI_USB_RMEMRESP: + /* Not currently used */ + DPRINTFN(2,("%s: %s: rmemresp type %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, rtype)); + break; + case WI_USB_BUFAVAIL: + printf("wi_usb: recieved USB_BUFAVAIL packet\n"); /* XXX */ + break; + case WI_USB_ERROR: + printf("wi_usb: recieved USB_ERROR packet\n"); /* XXX */ + break; + default: +#if 0 + printf("wi_usb: recieved Unknown packet 0x%x len %x\n", + rtype, total_len); + wi_dump_data(c->wi_usb_buf, total_len); +#endif + } + + done: + /* Setup new transfer. */ + usbd_setup_xfer(c->wi_usb_xfer, sc->wi_usb_ep[WI_USB_ENDPT_RX], + c, c->wi_usb_buf, WI_USB_BUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, + USBD_NO_TIMEOUT, wi_usb_rxeof); + sc->wi_usb_refcnt++; + usbd_transfer(c->wi_usb_xfer); + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + + DPRINTFN(10,("%s: %s: start rx\n", USBDEVNAME(sc->wi_usb_dev), + __func__)); +} + +void +wi_usb_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct wi_usb_softc *sc = priv; + + DPRINTFN(2,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + if (sc->wi_usb_dying) + return; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) + return; + + if (status == USBD_STALLED) { + sc->wi_usb_refcnt++; + usbd_clear_endpoint_stall( + sc->wi_usb_ep[WI_USB_ENDPT_RX]); + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + } + return; + } + /* XXX oerrors or collisions? */ +} +void +wi_usb_cmdresp(struct wi_usb_chain *c) +{ + struct wi_cmdresp *presp = (struct wi_cmdresp *)(c->wi_usb_buf); + u_int16_t status = letoh16(presp->status); + struct wi_usb_softc *sc = c->wi_usb_sc; + uint16_t type; + uint16_t cmdresperr; + + type = htole16(presp->type); + cmdresperr = letoh16(presp->resp0); + DPRINTFN(10,("%s: %s: enter rid=%x status %x %x %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, type, status, sc->cmdresp, + cmdresperr)); + + if (sc->cmdresp != status) { + DPRINTFN(1,("%s:cmd ty %x st %x cmd %x failed %x\n", + USBDEVNAME(sc->wi_usb_dev), + type, status, sc->cmdresp, cmdresperr)); + return; + } + + if ((cmdresperr != 0) && ((sc->cmdresp == WI_CMD_INQUIRE) || + (sc->cmdresp == WI_CMD_DISABLE)) ) { + /* + * For some reason MA111 does not like info frame requests, + * or some DISABLES + * It responds to the request with the info + * but it claims the request failed + * reset the error code. + */ + cmdresperr = 0; + } + + if (cmdresperr != 0) { + DPRINTFN(1,("%s:cmd ty %x st %x cmd %x failed %x\n", + USBDEVNAME(sc->wi_usb_dev), + type, status, sc->cmdresp, cmdresperr)); + } + + sc->cmdresperr = cmdresperr; + + sc->cmdresp = 0; /* good value for idle == INI ?? XXX */ + + wakeup(&sc->cmdresperr); +} +void +wi_usb_rridresp(struct wi_usb_chain *c) +{ + struct wi_rridresp *presp = (struct wi_rridresp *)(c->wi_usb_buf); + u_int16_t frmlen = letoh16(presp->frmlen); + struct wi_usb_softc *sc = c->wi_usb_sc; + struct wi_ltv_gen *ltv; + uint16_t rid; + + rid = letoh16(presp->rid); + ltv = sc->ridltv; + + if (ltv == 0) { + DPRINTFN(5,("%s: %s: enter ltv = 0 rid=%x len %d\n", + USBDEVNAME(sc->wi_usb_dev), __func__, rid, + frmlen)); + return; + } + + DPRINTFN(5,("%s: %s: enter rid=%x expecting %x len %d exptlen %d\n", + USBDEVNAME(sc->wi_usb_dev), __func__, rid, ltv->wi_type, + frmlen, ltv->wi_len)); + + rid = letoh16(presp->rid); + + if (rid != ltv->wi_type) { + sc->ridresperr = EIO; + return; + } + + /* XXX */ + if (rid == WI_RID_DATA_RATES) + frmlen = 2; + + if (frmlen > ltv->wi_len) { + sc->ridresperr = ENOSPC; + sc->ridltv = 0; + wakeup(&sc->ridresperr); + return; + } + + ltv->wi_len = frmlen; + + DPRINTFN(10,("%s: %s: copying %x frmlen %d s %x d %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, (ltv->wi_len-1)*2, + frmlen)); + + if (ltv->wi_len > 1) + bcopy(&presp->data[0], (u_int8_t *)<v->wi_val, + (ltv->wi_len-1)*2); + + sc->ridresperr = 0; + sc->ridltv = 0; + wakeup(&sc->ridresperr); + +} + +void +wi_usb_wridresp(struct wi_usb_chain *c) +{ + struct wi_wridresp *presp = (struct wi_wridresp *)(c->wi_usb_buf); + struct wi_usb_softc *sc = c->wi_usb_sc; + uint16_t status; + + status = letoh16(presp->status); + + DPRINTFN(10,("%s: %s: enter status=%x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, status)); + + sc->ridresperr = status; + sc->ridltv = 0; + wakeup(&sc->ridresperr); +} + +void +wi_usb_infofrm(struct wi_usb_chain *c, int len) +{ + struct wi_usb_softc *sc = c->wi_usb_sc; + + DPRINTFN(10,("%s: %s: enter\n", + USBDEVNAME(sc->wi_usb_dev), __func__)); + + sc->wi_info = ((char *)c->wi_usb_buf) + 2; + wi_update_stats(&sc->sc_wi); + sc->wi_info = NULL; +} + +void +wi_usb_txfrm(struct wi_usb_softc *sc, wi_usb_usbin *uin, int total_len) +{ + u_int16_t status; + int s; + struct wi_softc *wsc = &sc->sc_wi; + struct ifnet *ifp = &wsc->sc_arpcom.ac_if; + + s = splnet(); + status = letoh16(uin->type); /* XXX -- type == status */ + + + DPRINTFN(2,("%s: %s: enter status=%d\n", + USBDEVNAME(sc->wi_usb_dev), __func__, status)); + + if (sc->txresp == WI_CMD_TX) { + sc->txresperr=status; + sc->txresp = 0; + wakeup(&sc->txresperr); + } else { + if (status != 0) /* XXX */ + wi_watchdog_usb(ifp); + DPRINTFN(1,("%s: %s: txresp not expected status=%d \n", + USBDEVNAME(sc->wi_usb_dev), __func__, status)); + } + + splx(s); +} +void +wi_usb_rxfrm(struct wi_usb_softc *sc, wi_usb_usbin *uin, int total_len) +{ + int s; + + DPRINTFN(5,("%s: %s: enter len=%d\n", + USBDEVNAME(sc->wi_usb_dev), __func__, total_len)); + + s = splnet(); + + sc->wi_rxframe = (void *)uin; + + wi_rxeof(&sc->sc_wi); + + sc->wi_rxframe = NULL; + + splx(s); + +} + + +void +wi_usb_start_thread(void *arg) +{ + struct wi_usb_softc *sc = arg; + kthread_create (wi_usb_thread, arg, NULL, USBDEVNAME(sc->wi_usb_dev)); +} + +void +wi_start_usb(struct ifnet *ifp) +{ + struct wi_softc *wsc; + struct wi_usb_softc *sc; + int s; + + wsc = ifp->if_softc; + sc = wsc->wi_usb_cdata; + + s = splimp(); + + DPRINTFN(5,("%s: %s:\n", + USBDEVNAME(sc->wi_usb_dev), __func__)); + + if (wi_usb_tx_lock_try(sc)) { + /* lock acquired do start now */ + wi_func_io.f_start(ifp); + } else { + sc->wi_thread_info->status |= WI_START; + if (sc->wi_thread_info->idle) + wakeup (sc->wi_thread_info); + } + + splx(s); +} + +/* + * inquire is called from interrupt context (timeout) + * It is not possible to sleep in interrupt context so it is necessary + * to signal the kernel thread to perform the action. + */ +void +wi_init_usb(struct wi_softc *wsc) +{ + DPRINTFN(5,("%s: %s:\n", WI_PRT_ARG(wsc), __func__)); + + wi_usb_ctl_lock(wsc->wi_usb_cdata); + wi_func_io.f_init(wsc); + wi_usb_ctl_unlock(wsc->wi_usb_cdata); +} + + +/* + * inquire is called from interrupt context (timeout) + * It is not possible to sleep in interrupt context so it is necessary + * to signal the kernel thread to perform the action. + */ +void +wi_inquire_usb(void *xsc) +{ + struct wi_softc *wsc = xsc; + struct wi_usb_softc *sc = wsc->wi_usb_cdata; + int s; + + + s = splimp(); + + DPRINTFN(2,("%s: %s:\n", + USBDEVNAME(sc->wi_usb_dev), __func__)); + + sc->wi_thread_info->status |= WI_INQUIRE; + + if (sc->wi_thread_info->idle) + wakeup (sc->wi_thread_info); + splx(s); +} + +/* + * Watchdog is normally called from interrupt context (timeout) + * It is not possible to sleep in interrupt context so it is necessary + * to signal the kernel thread to perform the action. + */ +void +wi_watchdog_usb(struct ifnet *ifp) +{ + struct wi_softc *wsc; + struct wi_usb_softc *sc; + int s; + + wsc = ifp->if_softc; + sc = wsc->wi_usb_cdata; + + s = splimp(); + + DPRINTFN(5,("%s: %s: ifp %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, ifp)); + + sc->wi_thread_info->status |= WI_WATCHDOG; + + if (sc->wi_thread_info->idle) + wakeup (sc->wi_thread_info); + splx(s); +} + +/* + * ioctl will always be called from a user context, + * therefore is is possible to sleep in the calling context + * acquire the lock and call the real ioctl fucntion directly + */ +int +wi_ioctl_usb(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct wi_softc *wsc; + int err; + + wsc = ifp->if_softc; + + wi_usb_ctl_lock(wsc->wi_usb_cdata); + err = wi_func_io.f_ioctl(ifp, command, data); + wi_usb_ctl_unlock(wsc->wi_usb_cdata); + return err; +} + +void +wi_usb_thread(void *arg) +{ + struct wi_usb_softc *sc = arg; + struct wi_usb_thread_info *wi_thread_info; + int s; + + wi_thread_info = malloc(sizeof(*wi_thread_info), M_DEVBUF, M_WAITOK); + + /* + * is there a remote possibility that the device could + * be removed before the kernel thread starts up? + */ + + sc->wi_usb_refcnt++; + + sc->wi_thread_info = wi_thread_info; + wi_thread_info->dying = 0; + wi_thread_info->status = 0; + + wi_usb_ctl_lock(sc); + + wi_attach(&sc->sc_wi, &wi_func_usb); + + wi_usb_ctl_unlock(sc); + + for(;;) { + if (wi_thread_info->dying) { + if (--sc->wi_usb_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->wi_usb_dev)); + kthread_exit(0); + } + + DPRINTFN(5,("%s: %s: dying %x status %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, + wi_thread_info->dying, wi_thread_info->status)); + + wi_usb_ctl_lock(sc); + + DPRINTFN(5,("%s: %s: starting %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, + wi_thread_info->status)); + + s = splusb(); + if (wi_thread_info->status & WI_START) { + wi_thread_info->status &= ~WI_START; + wi_usb_tx_lock(sc); + wi_func_io.f_start(&sc->sc_wi.sc_arpcom.ac_if); + /* + * tx_unlock is explictly missing here + * is is done in txeof_frm + */ + } else if (wi_thread_info->status & WI_INQUIRE) { + wi_thread_info->status &= ~WI_INQUIRE; + wi_func_io.f_inquire(&sc->sc_wi); + } else if (wi_thread_info->status & WI_WATCHDOG) { + wi_thread_info->status &= ~WI_WATCHDOG; + wi_func_io.f_watchdog( &sc->sc_wi.sc_arpcom.ac_if); + } + splx(s); + + DPRINTFN(5,("%s: %s: ending %x\n", + USBDEVNAME(sc->wi_usb_dev), __func__, + wi_thread_info->status)); + wi_usb_ctl_unlock(sc); + + if (wi_thread_info->status == 0) { + s = splimp(); + wi_thread_info->idle = 1; + tsleep(wi_thread_info, PRIBIO, "wiIDL", 0); + wi_thread_info->idle = 0; + splx(s); + } + } +} + +int +wi_usb_tx_lock_try(struct wi_usb_softc *sc) +{ + int s; + + s = splimp(); /* right priority? */ + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + if (sc->wi_lock != 0) { + return 0; /* failed to aquire lock */ + } + + sc->wi_lock = 1; + + splx(s); + + return 1; +} +void +wi_usb_tx_lock(struct wi_usb_softc *sc) +{ + int s; + + s = splimp(); /* right priority? */ + + again: + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + if (sc->wi_lock != 0) { + sc->wi_lockwait++; + DPRINTFN(10,("%s: %s: busy %d\n", USBDEVNAME(sc->wi_usb_dev), + __func__, sc->wi_lockwait )); + tsleep(&sc->wi_lock, PRIBIO, "witxl", 0); + } + + if (sc->wi_lock != 0) + goto again; + sc->wi_lock = 1; + + splx(s); + + return; + +} + +void +wi_usb_tx_unlock(struct wi_usb_softc *sc) +{ + int s; + s = splimp(); /* right priority? */ + + sc->wi_lock = 0; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + if (sc->wi_lockwait) { + DPRINTFN(10,("%s: %s: waking\n", + USBDEVNAME(sc->wi_usb_dev), __func__)); + sc->wi_lockwait = 0; + wakeup(&sc->wi_lock); + } + + splx(s); +} + +void +wi_usb_ctl_lock(struct wi_usb_softc *sc) +{ + int s; + + s = splimp(); /* right priority? */ + + again: + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), + __func__)); + + if (sc->wi_ctllock != 0) { + if (curproc == sc->wi_curproc) { + /* allow recursion */ + sc->wi_ctllock++; + splx(s); + return; + } + sc->wi_ctllockwait++; + DPRINTFN(10,("%s: %s: busy %d\n", USBDEVNAME(sc->wi_usb_dev), + __func__, sc->wi_ctllockwait )); + tsleep(&sc->wi_ctllock, PRIBIO, "wiusbthr", 0); + } + + if (sc->wi_ctllock != 0) + goto again; + sc->wi_ctllock++; + sc->wi_curproc = curproc; + + splx(s); + + return; + +} + +void +wi_usb_ctl_unlock(struct wi_usb_softc *sc) +{ + int s; + + s = splimp(); /* right priority? */ + + sc->wi_ctllock--; + + DPRINTFN(10,("%s: %s: enter\n", USBDEVNAME(sc->wi_usb_dev), __func__)); + + if (sc->wi_ctllock == 0 && sc->wi_ctllockwait) { + DPRINTFN(10,("%s: %s: waking\n", + USBDEVNAME(sc->wi_usb_dev), __func__)); + sc->wi_ctllockwait = 0; + sc->wi_curproc = 0; + wakeup(&sc->wi_ctllock); + } + + splx(s); +} diff --git a/sys/dev/usb/if_wi_usb.h b/sys/dev/usb/if_wi_usb.h new file mode 100644 index 00000000000..41ca871b350 --- /dev/null +++ b/sys/dev/usb/if_wi_usb.h @@ -0,0 +1,161 @@ +/* $OpenBSD: if_wi_usb.h,v 1.1 2003/10/26 15:34:16 drahn Exp $ */ + +/* + * Copyright (c) 2003 Dale Rahn. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + */ + +#define WI_USB_CONFIG_NO 1 + +#define WI_USB_ENDPT_TX 1 +#define WI_USB_ENDPT_RX 2 +#define WI_USB_ENDPT_INTR 3 +#define WI_USB_ENDPT_MAX 4 + +#define WI_USB_IFACE_IDX 0 + + +/* XXX */ +#define WI_USB_DATA_MAXLEN WI_DEFAULT_DATALEN +#define WI_USB_BUFSZ 2368 /* MAX PACKET LEN ??? n%64 == 0 */ +#define WI_USB_INTR_INTERVAL 100 /* ms */ + +struct wi_usb_softc; + +struct wi_usb_chain { + struct wi_usb_softc *wi_usb_sc; + struct usbd_xfer *wi_usb_xfer; + void *wi_usb_buf; + struct mbuf *wi_usb_mbuf; + int wi_usb_idx; +}; +#define WI_USB_TX_LIST_CNT 1 +#define WI_USB_RX_LIST_CNT 1 + +struct wi_rridreq { + u_int16_t type; /* 0x00 */ + u_int16_t frmlen; /* 0x02 */ + u_int16_t rid; /* 0x04 */ + u_int8_t pad[58]; /* 0x06 + sizeof(.) == 64 */ +}; +struct wi_rridresp { + u_int16_t type; /* 0x00 */ + u_int16_t frmlen; /* 0x02 */ + u_int16_t rid; /* 0x04 */ + u_int8_t data[1658]; /* 0x06 */ + /* sizeof(struct wi_rridresp) == WI_USB_BUFSZ */ +}; +struct wi_wridreq { + u_int16_t type; /* 0x00 */ + u_int16_t frmlen; /* 0x02 */ + u_int16_t rid; /* 0x04 */ + u_int8_t data[2048]; /* 0x06 */ +}; +struct wi_wridresp { + u_int16_t type; + u_int16_t status; + u_int16_t resp0; + u_int16_t resp1; + u_int16_t resp2; +}; +struct wi_info { + u_int16_t type; + u_int16_t info; +}; + + +#define WI_USB_CMD_INIT 0x0 +#define WI_USB_CMD_ENABLE 0x1 +#define WI_USB_CMD_DISABLE 0x2 +#define WI_USB_CMD_DIAG 0x3 + +struct wi_cmdreq { + u_int16_t type; + u_int16_t cmd; + u_int16_t param0; + u_int16_t param1; + u_int16_t param2; + u_int8_t pad[54]; +}; +struct wi_cmdresp { + u_int16_t type; + u_int16_t status; + u_int16_t resp0; + u_int16_t resp1; + u_int16_t resp2; +}; + +typedef union { + u_int16_t type; + struct wi_rridreq rridreq; + struct wi_rridresp rridresp; + struct wi_cmdreq cmdreq; + struct wi_cmdresp cmdresp; +} wi_usb_usbin; +#define WI_USB_INTR_PKTLEN 8 + +#define WI_USB_TX_TIMEOUT 10000 /* ms */ + + +/* Should be sent to the bulkout endpoint */ +#define WI_USB_TXFRM 0 +#define WI_USB_CMDREQ 1 +#define WI_USB_WRIDREQ 2 +#define WI_USB_RRIDREQ 3 +#define WI_USB_WMEMREQ 4 +#define WI_USB_RMEMREQ 5 + +/* Received from the bulkin endpoint */ +#define WI_USB_ISTXFRM(a) (((a) & 0xf000) == 0x0000) +#define WI_USB_ISRXFRM(a) (((a) & 0xf000) == 0x2000) + +#define WI_USB_INFOFRM 0x8000 +#define WI_USB_CMDRESP 0x8001 +#define WI_USB_WRIDRESP 0x8002 +#define WI_USB_RRIDRESP 0x8003 +#define WI_USB_WMEMRESP 0x8004 +#define WI_USB_RMEMRESP 0x8005 +#define WI_USB_BUFAVAIL 0x8006 +#define WI_USB_ERROR 0x8007 + +#define WI_GET_IFP(sc) &(sc)->sc_wi.sc_arpcom.ac_if + +/* USB */ +int wi_cmd_usb(struct wi_softc *sc, int cmd, int val0, int val1, int val2); +int wi_read_record_usb(struct wi_softc *sc, struct wi_ltv_gen *ltv); +int wi_write_record_usb(struct wi_softc *sc, struct wi_ltv_gen *ltv); +int wi_read_data_usb(struct wi_softc *sc, int id, int off, caddr_t buf, + int len); +int wi_write_data_usb(struct wi_softc *sc, int id, int off, caddr_t buf, + int len); +int wi_alloc_nicmem_usb(struct wi_softc *sc, int len, int *id); +int wi_get_fid_usb(struct wi_softc *sc, int fid); +void wi_init_usb(struct wi_softc *sc); + +void wi_start_usb(struct ifnet *ifp); +int wi_ioctl_usb(struct ifnet *, u_long, caddr_t); +void wi_inquire_usb(void *xsc); +void wi_watchdog_usb(struct ifnet *ifp); |