1*81508fe3Sjsg /* $OpenBSD: if_bwfm_usb.c,v 1.21 2024/05/23 03:21:08 jsg Exp $ */
232b2494eSpatrick /*
332b2494eSpatrick * Copyright (c) 2010-2016 Broadcom Corporation
432b2494eSpatrick * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
532b2494eSpatrick *
632b2494eSpatrick * Permission to use, copy, modify, and/or distribute this software for any
732b2494eSpatrick * purpose with or without fee is hereby granted, provided that the above
832b2494eSpatrick * copyright notice and this permission notice appear in all copies.
932b2494eSpatrick *
1032b2494eSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1132b2494eSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1232b2494eSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1332b2494eSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1432b2494eSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1532b2494eSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1632b2494eSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1732b2494eSpatrick */
1832b2494eSpatrick
1932b2494eSpatrick #include <sys/param.h>
2032b2494eSpatrick #include <sys/systm.h>
2132b2494eSpatrick #include <sys/malloc.h>
2232b2494eSpatrick #include <sys/device.h>
2332b2494eSpatrick #include <sys/queue.h>
2432b2494eSpatrick
2532b2494eSpatrick #include <net/if.h>
2632b2494eSpatrick #include <net/if_media.h>
2732b2494eSpatrick
2832b2494eSpatrick #include <netinet/in.h>
2932b2494eSpatrick #include <netinet/if_ether.h>
3032b2494eSpatrick
3132b2494eSpatrick #include <net80211/ieee80211_var.h>
3232b2494eSpatrick
3332b2494eSpatrick #include <machine/bus.h>
3432b2494eSpatrick
3532b2494eSpatrick #include <dev/usb/usb.h>
3632b2494eSpatrick #include <dev/usb/usbdi.h>
3732b2494eSpatrick #include <dev/usb/usbdivar.h>
38029d6dd5Spatrick #include <dev/usb/usb_mem.h>
3932b2494eSpatrick #include <dev/usb/usbdevs.h>
4032b2494eSpatrick
4132b2494eSpatrick #include <dev/ic/bwfmvar.h>
4232b2494eSpatrick #include <dev/ic/bwfmreg.h>
4332b2494eSpatrick
4432b2494eSpatrick /*
4532b2494eSpatrick * Various supported device vendors/products.
4632b2494eSpatrick */
4732b2494eSpatrick static const struct usb_devno bwfm_usbdevs[] = {
4832b2494eSpatrick { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43143 },
4932b2494eSpatrick { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43236 },
5032b2494eSpatrick { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43242 },
5132b2494eSpatrick { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43569 },
5232b2494eSpatrick { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCMFW },
5332b2494eSpatrick };
5432b2494eSpatrick
5532b2494eSpatrick #ifdef BWFM_DEBUG
5632b2494eSpatrick #define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0)
5732b2494eSpatrick #define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0)
5832b2494eSpatrick static int bwfm_debug = 2;
5932b2494eSpatrick #else
6032b2494eSpatrick #define DPRINTF(x) do { ; } while (0)
6132b2494eSpatrick #define DPRINTFN(n, x) do { ; } while (0)
6232b2494eSpatrick #endif
6332b2494eSpatrick
6432b2494eSpatrick #define DEVNAME(sc) ((sc)->sc_sc.sc_dev.dv_xname)
6532b2494eSpatrick
6632b2494eSpatrick #define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
6732b2494eSpatrick * has boot up
6832b2494eSpatrick */
6932b2494eSpatrick
7032b2494eSpatrick #define TRX_MAGIC 0x30524448 /* "HDR0" */
7132b2494eSpatrick #define TRX_MAX_OFFSET 3 /* Max number of file offsets */
7232b2494eSpatrick #define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */
7332b2494eSpatrick #define TRX_RDL_CHUNK 1500 /* size of each dl transfer */
7432b2494eSpatrick #define TRX_OFFSETS_DLFWLEN_IDX 0
7532b2494eSpatrick
7632b2494eSpatrick /* Control messages: bRequest values */
7732b2494eSpatrick #define DL_GETSTATE 0 /* returns the rdl_state_t struct */
7832b2494eSpatrick #define DL_CHECK_CRC 1 /* currently unused */
7932b2494eSpatrick #define DL_GO 2 /* execute downloaded image */
8032b2494eSpatrick #define DL_START 3 /* initialize dl state */
8132b2494eSpatrick #define DL_REBOOT 4 /* reboot the device in 2 seconds */
8232b2494eSpatrick #define DL_GETVER 5 /* returns the bootrom_id_t struct */
8332b2494eSpatrick #define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset
8432b2494eSpatrick * event to occur in 2 seconds. It is the
8532b2494eSpatrick * responsibility of the downloaded code to
8632b2494eSpatrick * clear this event
8732b2494eSpatrick */
8832b2494eSpatrick #define DL_EXEC 7 /* jump to a supplied address */
8932b2494eSpatrick #define DL_RESETCFG 8 /* To support single enum on dongle
9032b2494eSpatrick * - Not used by bootloader
9132b2494eSpatrick */
9232b2494eSpatrick #define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
9332b2494eSpatrick * if resp unavailable
9432b2494eSpatrick */
9532b2494eSpatrick
9632b2494eSpatrick /* states */
9732b2494eSpatrick #define DL_WAITING 0 /* waiting to rx first pkt */
9832b2494eSpatrick #define DL_READY 1 /* hdr was good, waiting for more of the
9932b2494eSpatrick * compressed image
10032b2494eSpatrick */
10132b2494eSpatrick #define DL_BAD_HDR 2 /* hdr was corrupted */
10232b2494eSpatrick #define DL_BAD_CRC 3 /* compressed image was corrupted */
10332b2494eSpatrick #define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */
10432b2494eSpatrick #define DL_START_FAIL 5 /* failed to initialize correctly */
10532b2494eSpatrick #define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM
10632b2494eSpatrick * value
10732b2494eSpatrick */
10832b2494eSpatrick #define DL_IMAGE_TOOBIG 7 /* firmware image too big */
10932b2494eSpatrick
11032b2494eSpatrick
11132b2494eSpatrick struct trx_header {
11232b2494eSpatrick uint32_t magic; /* "HDR0" */
11332b2494eSpatrick uint32_t len; /* Length of file including header */
11432b2494eSpatrick uint32_t crc32; /* CRC from flag_version to end of file */
11532b2494eSpatrick uint32_t flag_version; /* 0:15 flags, 16:31 version */
11632b2494eSpatrick uint32_t offsets[TRX_MAX_OFFSET];/* Offsets of partitions from start of
11732b2494eSpatrick * header
11832b2494eSpatrick */
11932b2494eSpatrick };
12032b2494eSpatrick
12132b2494eSpatrick struct rdl_state {
12232b2494eSpatrick uint32_t state;
12332b2494eSpatrick uint32_t bytes;
12432b2494eSpatrick };
12532b2494eSpatrick
12632b2494eSpatrick struct bootrom_id {
12732b2494eSpatrick uint32_t chip; /* Chip id */
12832b2494eSpatrick uint32_t chiprev; /* Chip rev */
12932b2494eSpatrick uint32_t ramsize; /* Size of RAM */
13032b2494eSpatrick uint32_t remapbase; /* Current remap base address */
13132b2494eSpatrick uint32_t boardtype; /* Type of board */
13232b2494eSpatrick uint32_t boardrev; /* Board revision */
13332b2494eSpatrick };
13432b2494eSpatrick
135483142cfSpatrick struct bwfm_usb_rx_data {
136483142cfSpatrick struct bwfm_usb_softc *sc;
137483142cfSpatrick struct usbd_xfer *xfer;
138483142cfSpatrick uint8_t *buf;
139483142cfSpatrick };
140483142cfSpatrick
141483142cfSpatrick struct bwfm_usb_tx_data {
142483142cfSpatrick struct bwfm_usb_softc *sc;
143483142cfSpatrick struct usbd_xfer *xfer;
144483142cfSpatrick uint8_t *buf;
145483142cfSpatrick struct mbuf *mbuf;
146483142cfSpatrick TAILQ_ENTRY(bwfm_usb_tx_data) next;
147483142cfSpatrick };
148483142cfSpatrick
149483142cfSpatrick #define BWFM_RX_LIST_COUNT 50
150483142cfSpatrick #define BWFM_TX_LIST_COUNT 50
15132b2494eSpatrick #define BWFM_RXBUFSZ 1600
15232b2494eSpatrick #define BWFM_TXBUFSZ 1600
15332b2494eSpatrick struct bwfm_usb_softc {
15432b2494eSpatrick struct bwfm_softc sc_sc;
15532b2494eSpatrick struct usbd_device *sc_udev;
15632b2494eSpatrick struct usbd_interface *sc_iface;
15732b2494eSpatrick uint8_t sc_ifaceno;
15832b2494eSpatrick
159972218f3Spatrick int sc_initialized;
160972218f3Spatrick
16132b2494eSpatrick uint16_t sc_vendor;
16232b2494eSpatrick uint16_t sc_product;
16332b2494eSpatrick
16432b2494eSpatrick uint32_t sc_chip;
16532b2494eSpatrick uint32_t sc_chiprev;
16632b2494eSpatrick
16732b2494eSpatrick int sc_rx_no;
16832b2494eSpatrick int sc_tx_no;
16932b2494eSpatrick
17032b2494eSpatrick struct usbd_pipe *sc_rx_pipeh;
17132b2494eSpatrick struct usbd_pipe *sc_tx_pipeh;
17232b2494eSpatrick
173483142cfSpatrick struct bwfm_usb_rx_data sc_rx_data[BWFM_RX_LIST_COUNT];
174483142cfSpatrick struct bwfm_usb_tx_data sc_tx_data[BWFM_TX_LIST_COUNT];
175483142cfSpatrick TAILQ_HEAD(, bwfm_usb_tx_data) sc_tx_free_list;
17632b2494eSpatrick };
17732b2494eSpatrick
17832b2494eSpatrick int bwfm_usb_match(struct device *, void *, void *);
17932b2494eSpatrick void bwfm_usb_attach(struct device *, struct device *, void *);
18032b2494eSpatrick int bwfm_usb_detach(struct device *, int);
18132b2494eSpatrick
18232b2494eSpatrick int bwfm_usb_dl_cmd(struct bwfm_usb_softc *, uint8_t, void *, int);
18332b2494eSpatrick int bwfm_usb_load_microcode(struct bwfm_usb_softc *, const u_char *,
18432b2494eSpatrick size_t);
18532b2494eSpatrick
186483142cfSpatrick int bwfm_usb_alloc_rx_list(struct bwfm_usb_softc *);
187483142cfSpatrick void bwfm_usb_free_rx_list(struct bwfm_usb_softc *);
188483142cfSpatrick int bwfm_usb_alloc_tx_list(struct bwfm_usb_softc *);
189483142cfSpatrick void bwfm_usb_free_tx_list(struct bwfm_usb_softc *);
190483142cfSpatrick
191972218f3Spatrick int bwfm_usb_preinit(struct bwfm_softc *);
19202ee7d07Spatrick int bwfm_usb_txcheck(struct bwfm_softc *);
19332b2494eSpatrick int bwfm_usb_txdata(struct bwfm_softc *, struct mbuf *);
1942802c178Spatrick int bwfm_usb_txctl(struct bwfm_softc *, void *);
195029d6dd5Spatrick void bwfm_usb_txctl_cb(struct usbd_xfer *, void *, usbd_status);
19632b2494eSpatrick
197f37fc236Spatrick struct mbuf * bwfm_usb_newbuf(void);
19832b2494eSpatrick void bwfm_usb_rxeof(struct usbd_xfer *, void *, usbd_status);
19932b2494eSpatrick void bwfm_usb_txeof(struct usbd_xfer *, void *, usbd_status);
20032b2494eSpatrick
20132b2494eSpatrick struct bwfm_bus_ops bwfm_usb_bus_ops = {
202972218f3Spatrick .bs_preinit = bwfm_usb_preinit,
20332b2494eSpatrick .bs_stop = NULL,
20402ee7d07Spatrick .bs_txcheck = bwfm_usb_txcheck,
20532b2494eSpatrick .bs_txdata = bwfm_usb_txdata,
20632b2494eSpatrick .bs_txctl = bwfm_usb_txctl,
20732b2494eSpatrick };
20832b2494eSpatrick
209990261acSmpi const struct cfattach bwfm_usb_ca = {
21032b2494eSpatrick sizeof(struct bwfm_usb_softc),
21132b2494eSpatrick bwfm_usb_match,
21232b2494eSpatrick bwfm_usb_attach,
21332b2494eSpatrick bwfm_usb_detach,
21432b2494eSpatrick };
21532b2494eSpatrick
21632b2494eSpatrick int
bwfm_usb_match(struct device * parent,void * match,void * aux)21732b2494eSpatrick bwfm_usb_match(struct device *parent, void *match, void *aux)
21832b2494eSpatrick {
21932b2494eSpatrick struct usb_attach_arg *uaa = aux;
22032b2494eSpatrick
22132b2494eSpatrick if (uaa->iface == NULL || uaa->configno != 1)
22232b2494eSpatrick return UMATCH_NONE;
22332b2494eSpatrick
22432b2494eSpatrick return (usb_lookup(bwfm_usbdevs, uaa->vendor, uaa->product) != NULL) ?
22532b2494eSpatrick UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE;
22632b2494eSpatrick }
22732b2494eSpatrick
22832b2494eSpatrick void
bwfm_usb_attach(struct device * parent,struct device * self,void * aux)22932b2494eSpatrick bwfm_usb_attach(struct device *parent, struct device *self, void *aux)
23032b2494eSpatrick {
23132b2494eSpatrick struct bwfm_usb_softc *sc = (struct bwfm_usb_softc *)self;
23232b2494eSpatrick struct usb_attach_arg *uaa = aux;
23332b2494eSpatrick usb_device_descriptor_t *dd;
23432b2494eSpatrick usb_interface_descriptor_t *id;
23532b2494eSpatrick usb_endpoint_descriptor_t *ed;
23632b2494eSpatrick int i;
23732b2494eSpatrick
23832b2494eSpatrick sc->sc_udev = uaa->device;
23932b2494eSpatrick sc->sc_iface = uaa->iface;
24032b2494eSpatrick sc->sc_ifaceno = uaa->ifaceno;
24132b2494eSpatrick sc->sc_vendor = uaa->vendor;
24232b2494eSpatrick sc->sc_product = uaa->product;
24332b2494eSpatrick sc->sc_sc.sc_bus_ops = &bwfm_usb_bus_ops;
24432b2494eSpatrick sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops;
24532b2494eSpatrick
24632b2494eSpatrick /* Check number of configurations. */
24732b2494eSpatrick dd = usbd_get_device_descriptor(sc->sc_udev);
24832b2494eSpatrick if (dd->bNumConfigurations != 1) {
24932b2494eSpatrick printf("%s: number of configurations not supported\n",
25032b2494eSpatrick DEVNAME(sc));
25132b2494eSpatrick return;
25232b2494eSpatrick }
25332b2494eSpatrick
25432b2494eSpatrick /* Get endpoints. */
25532b2494eSpatrick id = usbd_get_interface_descriptor(sc->sc_iface);
25632b2494eSpatrick
25732b2494eSpatrick sc->sc_rx_no = sc->sc_tx_no = -1;
25832b2494eSpatrick for (i = 0; i < id->bNumEndpoints; i++) {
25932b2494eSpatrick ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
26032b2494eSpatrick if (ed == NULL) {
26132b2494eSpatrick printf("%s: no endpoint descriptor for iface %d\n",
26232b2494eSpatrick DEVNAME(sc), i);
26332b2494eSpatrick return;
26432b2494eSpatrick }
26532b2494eSpatrick
26632b2494eSpatrick if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
26732b2494eSpatrick UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
26832b2494eSpatrick sc->sc_rx_no == -1)
26932b2494eSpatrick sc->sc_rx_no = ed->bEndpointAddress;
27032b2494eSpatrick else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
27132b2494eSpatrick UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
27232b2494eSpatrick sc->sc_tx_no == -1)
27332b2494eSpatrick sc->sc_tx_no = ed->bEndpointAddress;
27432b2494eSpatrick }
27532b2494eSpatrick if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
27632b2494eSpatrick printf("%s: missing endpoint\n", DEVNAME(sc));
27732b2494eSpatrick return;
27832b2494eSpatrick }
27932b2494eSpatrick
280972218f3Spatrick bwfm_attach(&sc->sc_sc);
281972218f3Spatrick config_mountroot(self, bwfm_attachhook);
28232b2494eSpatrick }
28332b2494eSpatrick
284972218f3Spatrick int
bwfm_usb_preinit(struct bwfm_softc * bwfm)285972218f3Spatrick bwfm_usb_preinit(struct bwfm_softc *bwfm)
28632b2494eSpatrick {
287972218f3Spatrick struct bwfm_usb_softc *sc = (void *)bwfm;
288483142cfSpatrick struct bwfm_usb_rx_data *data;
28932b2494eSpatrick const char *name = NULL;
29032b2494eSpatrick struct bootrom_id brom;
29132b2494eSpatrick usbd_status error;
29232b2494eSpatrick u_char *ucode;
29332b2494eSpatrick size_t size;
29432b2494eSpatrick int i;
29532b2494eSpatrick
296972218f3Spatrick if (sc->sc_initialized)
297972218f3Spatrick return 0;
298972218f3Spatrick
29932b2494eSpatrick /* Read chip id and chip rev to check the firmware. */
30032b2494eSpatrick memset(&brom, 0, sizeof(brom));
30132b2494eSpatrick bwfm_usb_dl_cmd(sc, DL_GETVER, &brom, sizeof(brom));
30232b2494eSpatrick sc->sc_chip = letoh32(brom.chip);
30332b2494eSpatrick sc->sc_chiprev = letoh32(brom.chiprev);
30432b2494eSpatrick
30532b2494eSpatrick /* Setup data pipes */
30632b2494eSpatrick error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
30732b2494eSpatrick &sc->sc_rx_pipeh);
30832b2494eSpatrick if (error != 0) {
30932b2494eSpatrick printf("%s: could not open rx pipe: %s\n",
31032b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
311972218f3Spatrick return 1;
31232b2494eSpatrick }
31332b2494eSpatrick error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
31432b2494eSpatrick &sc->sc_tx_pipeh);
31532b2494eSpatrick if (error != 0) {
31632b2494eSpatrick printf("%s: could not open tx pipe: %s\n",
31732b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
3189817a909Spatrick goto cleanup;
31932b2494eSpatrick }
32032b2494eSpatrick
32132b2494eSpatrick /* Firmware not yet loaded? */
32232b2494eSpatrick if (sc->sc_chip != BRCMF_POSTBOOT_ID) {
32332b2494eSpatrick switch (sc->sc_chip)
32432b2494eSpatrick {
32532b2494eSpatrick case BRCM_CC_43143_CHIP_ID:
32632b2494eSpatrick name = "brcmfmac43143.bin";
32732b2494eSpatrick break;
32832b2494eSpatrick case BRCM_CC_43235_CHIP_ID:
32932b2494eSpatrick case BRCM_CC_43236_CHIP_ID:
33032b2494eSpatrick case BRCM_CC_43238_CHIP_ID:
33132b2494eSpatrick if (sc->sc_chiprev == 3)
33232b2494eSpatrick name = "brcmfmac43236b.bin";
33332b2494eSpatrick break;
33432b2494eSpatrick case BRCM_CC_43242_CHIP_ID:
33532b2494eSpatrick name = "brcmfmac43242a.bin";
33632b2494eSpatrick break;
33732b2494eSpatrick case BRCM_CC_43566_CHIP_ID:
33832b2494eSpatrick case BRCM_CC_43569_CHIP_ID:
33932b2494eSpatrick name = "brcmfmac43569.bin";
34032b2494eSpatrick break;
34132b2494eSpatrick default:
34232b2494eSpatrick break;
34332b2494eSpatrick }
34432b2494eSpatrick
34532b2494eSpatrick if (name == NULL) {
34632b2494eSpatrick printf("%s: unknown firmware\n", DEVNAME(sc));
347972218f3Spatrick goto cleanup;
34832b2494eSpatrick }
34932b2494eSpatrick
35032b2494eSpatrick if (loadfirmware(name, &ucode, &size) != 0) {
35132b2494eSpatrick printf("%s: failed loadfirmware of file %s\n",
35232b2494eSpatrick DEVNAME(sc), name);
353972218f3Spatrick goto cleanup;
35432b2494eSpatrick }
35532b2494eSpatrick
35632b2494eSpatrick if (bwfm_usb_load_microcode(sc, ucode, size) != 0) {
35732b2494eSpatrick printf("%s: could not load microcode\n",
35832b2494eSpatrick DEVNAME(sc));
3596a67147aSpatrick free(ucode, M_DEVBUF, size);
360972218f3Spatrick goto cleanup;
36132b2494eSpatrick }
36232b2494eSpatrick
3636a67147aSpatrick free(ucode, M_DEVBUF, size);
36432b2494eSpatrick
36532b2494eSpatrick for (i = 0; i < 10; i++) {
36632b2494eSpatrick delay(100 * 1000);
36732b2494eSpatrick memset(&brom, 0, sizeof(brom));
36832b2494eSpatrick bwfm_usb_dl_cmd(sc, DL_GETVER, &brom, sizeof(brom));
36932b2494eSpatrick if (letoh32(brom.chip) == BRCMF_POSTBOOT_ID)
37032b2494eSpatrick break;
37132b2494eSpatrick }
37232b2494eSpatrick
37332b2494eSpatrick if (letoh32(brom.chip) != BRCMF_POSTBOOT_ID) {
37432b2494eSpatrick printf("%s: firmware did not start up\n",
37532b2494eSpatrick DEVNAME(sc));
376972218f3Spatrick goto cleanup;
37732b2494eSpatrick }
37832b2494eSpatrick
37932b2494eSpatrick sc->sc_chip = letoh32(brom.chip);
38032b2494eSpatrick sc->sc_chiprev = letoh32(brom.chiprev);
38132b2494eSpatrick }
38232b2494eSpatrick
38332b2494eSpatrick bwfm_usb_dl_cmd(sc, DL_RESETCFG, &brom, sizeof(brom));
38432b2494eSpatrick
385483142cfSpatrick if (bwfm_usb_alloc_rx_list(sc) || bwfm_usb_alloc_tx_list(sc)) {
386483142cfSpatrick printf("%s: cannot allocate rx/tx lists\n", DEVNAME(sc));
387972218f3Spatrick goto cleanup;
38832b2494eSpatrick }
38932b2494eSpatrick
390483142cfSpatrick for (i = 0; i < BWFM_RX_LIST_COUNT; i++) {
391483142cfSpatrick data = &sc->sc_rx_data[i];
392483142cfSpatrick
393483142cfSpatrick usbd_setup_xfer(data->xfer, sc->sc_rx_pipeh, data, data->buf,
39432b2494eSpatrick BWFM_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
39532b2494eSpatrick bwfm_usb_rxeof);
396483142cfSpatrick error = usbd_transfer(data->xfer);
39732b2494eSpatrick if (error != 0 && error != USBD_IN_PROGRESS)
39832b2494eSpatrick printf("%s: could not set up new transfer: %s\n",
39932b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
40032b2494eSpatrick }
40132b2494eSpatrick
402972218f3Spatrick sc->sc_initialized = 1;
403972218f3Spatrick return 0;
404972218f3Spatrick
405972218f3Spatrick cleanup:
406972218f3Spatrick if (sc->sc_rx_pipeh) {
407972218f3Spatrick usbd_close_pipe(sc->sc_rx_pipeh);
408972218f3Spatrick sc->sc_rx_pipeh = NULL;
409972218f3Spatrick }
410972218f3Spatrick if (sc->sc_tx_pipeh) {
411972218f3Spatrick usbd_close_pipe(sc->sc_tx_pipeh);
412972218f3Spatrick sc->sc_tx_pipeh = NULL;
413972218f3Spatrick }
414972218f3Spatrick bwfm_usb_free_rx_list(sc);
415972218f3Spatrick bwfm_usb_free_tx_list(sc);
416972218f3Spatrick return 1;
41732b2494eSpatrick }
41832b2494eSpatrick
419f37fc236Spatrick struct mbuf *
bwfm_usb_newbuf(void)420f37fc236Spatrick bwfm_usb_newbuf(void)
421f37fc236Spatrick {
422f37fc236Spatrick struct mbuf *m;
423f37fc236Spatrick
424f37fc236Spatrick MGETHDR(m, M_DONTWAIT, MT_DATA);
425f37fc236Spatrick if (m == NULL)
426f37fc236Spatrick return (NULL);
427f37fc236Spatrick
428f37fc236Spatrick MCLGET(m, M_DONTWAIT);
429f37fc236Spatrick if (!(m->m_flags & M_EXT)) {
430f37fc236Spatrick m_freem(m);
431f37fc236Spatrick return (NULL);
432f37fc236Spatrick }
433f37fc236Spatrick
434f37fc236Spatrick m->m_len = m->m_pkthdr.len = MCLBYTES;
435f37fc236Spatrick
436f37fc236Spatrick return (m);
437f37fc236Spatrick }
438f37fc236Spatrick
43932b2494eSpatrick void
bwfm_usb_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)44032b2494eSpatrick bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
44132b2494eSpatrick {
442483142cfSpatrick struct bwfm_usb_rx_data *data = priv;
443483142cfSpatrick struct bwfm_usb_softc *sc = data->sc;
4446f241297Spatrick struct mbuf_list ml = MBUF_LIST_INITIALIZER();
4456f241297Spatrick struct ifnet *ifp = &sc->sc_sc.sc_ic.ic_if;
44632b2494eSpatrick usbd_status error;
447f37fc236Spatrick struct mbuf *m;
4487dfc0b73Spatrick uint32_t len;
44932b2494eSpatrick
45032b2494eSpatrick DPRINTFN(2, ("%s: %s status %s\n", DEVNAME(sc), __func__,
45132b2494eSpatrick usbd_errstr(status)));
45232b2494eSpatrick
45332b2494eSpatrick if (usbd_is_dying(sc->sc_udev))
45432b2494eSpatrick return;
45532b2494eSpatrick
45632b2494eSpatrick if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
45732b2494eSpatrick usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
45832b2494eSpatrick if (status != USBD_CANCELLED)
45932b2494eSpatrick goto resubmit;
46032b2494eSpatrick return;
46132b2494eSpatrick }
46232b2494eSpatrick usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
46332b2494eSpatrick
464f37fc236Spatrick m = bwfm_usb_newbuf();
465f37fc236Spatrick if (m == NULL)
466f37fc236Spatrick goto resubmit;
467f37fc236Spatrick
4687dfc0b73Spatrick memcpy(mtod(m, char *), data->buf, len);
469f37fc236Spatrick m->m_len = m->m_pkthdr.len = len;
4706f241297Spatrick sc->sc_sc.sc_proto_ops->proto_rx(&sc->sc_sc, m, &ml);
4716f241297Spatrick if_input(ifp, &ml);
47232b2494eSpatrick
473b0aafb6dSpatrick resubmit:
474483142cfSpatrick usbd_setup_xfer(data->xfer, sc->sc_rx_pipeh, data, data->buf,
47532b2494eSpatrick BWFM_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
47632b2494eSpatrick bwfm_usb_rxeof);
477483142cfSpatrick error = usbd_transfer(data->xfer);
47832b2494eSpatrick if (error != 0 && error != USBD_IN_PROGRESS)
47932b2494eSpatrick printf("%s: could not set up new transfer: %s\n",
48032b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
48132b2494eSpatrick }
48232b2494eSpatrick
483483142cfSpatrick int
bwfm_usb_alloc_rx_list(struct bwfm_usb_softc * sc)484483142cfSpatrick bwfm_usb_alloc_rx_list(struct bwfm_usb_softc *sc)
485483142cfSpatrick {
486483142cfSpatrick struct bwfm_usb_rx_data *data;
487483142cfSpatrick int i, error = 0;
488483142cfSpatrick
489483142cfSpatrick for (i = 0; i < BWFM_RX_LIST_COUNT; i++) {
490483142cfSpatrick data = &sc->sc_rx_data[i];
491483142cfSpatrick
492483142cfSpatrick data->sc = sc; /* Backpointer for callbacks. */
493483142cfSpatrick
494483142cfSpatrick data->xfer = usbd_alloc_xfer(sc->sc_udev);
495483142cfSpatrick if (data->xfer == NULL) {
496483142cfSpatrick printf("%s: could not allocate xfer\n",
497483142cfSpatrick DEVNAME(sc));
498483142cfSpatrick error = ENOMEM;
499483142cfSpatrick break;
500483142cfSpatrick }
501483142cfSpatrick data->buf = usbd_alloc_buffer(data->xfer, BWFM_RXBUFSZ);
502483142cfSpatrick if (data->buf == NULL) {
503483142cfSpatrick printf("%s: could not allocate xfer buffer\n",
504483142cfSpatrick DEVNAME(sc));
505483142cfSpatrick error = ENOMEM;
506483142cfSpatrick break;
507483142cfSpatrick }
508483142cfSpatrick }
509483142cfSpatrick if (error != 0)
510483142cfSpatrick bwfm_usb_free_rx_list(sc);
511483142cfSpatrick return (error);
512483142cfSpatrick }
513483142cfSpatrick
514483142cfSpatrick void
bwfm_usb_free_rx_list(struct bwfm_usb_softc * sc)515483142cfSpatrick bwfm_usb_free_rx_list(struct bwfm_usb_softc *sc)
516483142cfSpatrick {
517483142cfSpatrick int i;
518483142cfSpatrick
519483142cfSpatrick /* NB: Caller must abort pipe first. */
520483142cfSpatrick for (i = 0; i < BWFM_RX_LIST_COUNT; i++) {
521483142cfSpatrick if (sc->sc_rx_data[i].xfer != NULL)
522483142cfSpatrick usbd_free_xfer(sc->sc_rx_data[i].xfer);
523483142cfSpatrick sc->sc_rx_data[i].xfer = NULL;
524483142cfSpatrick }
525483142cfSpatrick }
526483142cfSpatrick
527483142cfSpatrick int
bwfm_usb_alloc_tx_list(struct bwfm_usb_softc * sc)528483142cfSpatrick bwfm_usb_alloc_tx_list(struct bwfm_usb_softc *sc)
529483142cfSpatrick {
530483142cfSpatrick struct bwfm_usb_tx_data *data;
531483142cfSpatrick int i, error = 0;
532483142cfSpatrick
533483142cfSpatrick TAILQ_INIT(&sc->sc_tx_free_list);
534483142cfSpatrick for (i = 0; i < BWFM_TX_LIST_COUNT; i++) {
535483142cfSpatrick data = &sc->sc_tx_data[i];
536483142cfSpatrick
537483142cfSpatrick data->sc = sc; /* Backpointer for callbacks. */
538483142cfSpatrick
539483142cfSpatrick data->xfer = usbd_alloc_xfer(sc->sc_udev);
540483142cfSpatrick if (data->xfer == NULL) {
541483142cfSpatrick printf("%s: could not allocate xfer\n",
542483142cfSpatrick DEVNAME(sc));
543483142cfSpatrick error = ENOMEM;
544483142cfSpatrick break;
545483142cfSpatrick }
546483142cfSpatrick data->buf = usbd_alloc_buffer(data->xfer, BWFM_TXBUFSZ);
547483142cfSpatrick if (data->buf == NULL) {
548483142cfSpatrick printf("%s: could not allocate xfer buffer\n",
549483142cfSpatrick DEVNAME(sc));
550483142cfSpatrick error = ENOMEM;
551483142cfSpatrick break;
552483142cfSpatrick }
553483142cfSpatrick /* Append this Tx buffer to our free list. */
554483142cfSpatrick TAILQ_INSERT_TAIL(&sc->sc_tx_free_list, data, next);
555483142cfSpatrick }
556483142cfSpatrick if (error != 0)
557483142cfSpatrick bwfm_usb_free_tx_list(sc);
558483142cfSpatrick return (error);
559483142cfSpatrick }
560483142cfSpatrick
561483142cfSpatrick void
bwfm_usb_free_tx_list(struct bwfm_usb_softc * sc)562483142cfSpatrick bwfm_usb_free_tx_list(struct bwfm_usb_softc *sc)
563483142cfSpatrick {
564483142cfSpatrick int i;
565483142cfSpatrick
566483142cfSpatrick /* NB: Caller must abort pipe first. */
567483142cfSpatrick for (i = 0; i < BWFM_TX_LIST_COUNT; i++) {
568483142cfSpatrick if (sc->sc_tx_data[i].xfer != NULL)
569483142cfSpatrick usbd_free_xfer(sc->sc_tx_data[i].xfer);
570483142cfSpatrick sc->sc_tx_data[i].xfer = NULL;
571483142cfSpatrick }
572483142cfSpatrick }
573483142cfSpatrick
57432b2494eSpatrick void
bwfm_usb_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)57532b2494eSpatrick bwfm_usb_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
57632b2494eSpatrick {
577483142cfSpatrick struct bwfm_usb_tx_data *data = priv;
578483142cfSpatrick struct bwfm_usb_softc *sc = data->sc;
579483142cfSpatrick struct ifnet *ifp = &sc->sc_sc.sc_ic.ic_if;
580483142cfSpatrick int s;
58132b2494eSpatrick
58232b2494eSpatrick DPRINTFN(2, ("%s: %s status %s\n", DEVNAME(sc), __func__,
58332b2494eSpatrick usbd_errstr(status)));
58432b2494eSpatrick
58532b2494eSpatrick if (usbd_is_dying(sc->sc_udev))
58632b2494eSpatrick return;
58732b2494eSpatrick
588483142cfSpatrick s = splnet();
589483142cfSpatrick /* Put this Tx buffer back to our free list. */
590483142cfSpatrick TAILQ_INSERT_TAIL(&sc->sc_tx_free_list, data, next);
591483142cfSpatrick
59232b2494eSpatrick if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
593483142cfSpatrick if (status == USBD_CANCELLED)
59432b2494eSpatrick usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
595483142cfSpatrick ifp->if_oerrors++;
596483142cfSpatrick splx(s);
59732b2494eSpatrick return;
59832b2494eSpatrick }
59932b2494eSpatrick
600483142cfSpatrick m_freem(data->mbuf);
601483142cfSpatrick data->mbuf = NULL;
60232b2494eSpatrick
603483142cfSpatrick /* We just released a Tx buffer, notify Tx. */
604483142cfSpatrick if (ifq_is_oactive(&ifp->if_snd)) {
60502ee7d07Spatrick ifq_restart(&ifp->if_snd);
606483142cfSpatrick }
607483142cfSpatrick splx(s);
60832b2494eSpatrick }
60932b2494eSpatrick
61032b2494eSpatrick int
bwfm_usb_detach(struct device * self,int flags)61132b2494eSpatrick bwfm_usb_detach(struct device *self, int flags)
61232b2494eSpatrick {
61332b2494eSpatrick struct bwfm_usb_softc *sc = (struct bwfm_usb_softc *)self;
61432b2494eSpatrick
61532b2494eSpatrick bwfm_detach(&sc->sc_sc, flags);
61632b2494eSpatrick
617f88cb03eSmglocker if (sc->sc_rx_pipeh != NULL)
61832b2494eSpatrick usbd_close_pipe(sc->sc_rx_pipeh);
619f88cb03eSmglocker if (sc->sc_tx_pipeh != NULL)
62032b2494eSpatrick usbd_close_pipe(sc->sc_tx_pipeh);
62132b2494eSpatrick
622483142cfSpatrick bwfm_usb_free_rx_list(sc);
623483142cfSpatrick bwfm_usb_free_tx_list(sc);
624483142cfSpatrick
62532b2494eSpatrick return 0;
62632b2494eSpatrick }
62732b2494eSpatrick
62832b2494eSpatrick int
bwfm_usb_dl_cmd(struct bwfm_usb_softc * sc,uByte cmd,void * buf,int len)62932b2494eSpatrick bwfm_usb_dl_cmd(struct bwfm_usb_softc *sc, uByte cmd, void *buf, int len)
63032b2494eSpatrick {
63132b2494eSpatrick usb_device_request_t req;
63232b2494eSpatrick usbd_status error;
63332b2494eSpatrick
63432b2494eSpatrick req.bmRequestType = UT_READ_VENDOR_INTERFACE;
63532b2494eSpatrick req.bRequest = cmd;
63632b2494eSpatrick
63732b2494eSpatrick USETW(req.wValue, 0);
63832b2494eSpatrick USETW(req.wIndex, sc->sc_ifaceno);
63932b2494eSpatrick USETW(req.wLength, len);
64032b2494eSpatrick
64132b2494eSpatrick error = usbd_do_request(sc->sc_udev, &req, buf);
64232b2494eSpatrick if (error != 0) {
64332b2494eSpatrick printf("%s: could not read register: %s\n",
64432b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
64532b2494eSpatrick }
64632b2494eSpatrick return error;
64732b2494eSpatrick }
64832b2494eSpatrick
64932b2494eSpatrick int
bwfm_usb_load_microcode(struct bwfm_usb_softc * sc,const u_char * ucode,size_t size)65032b2494eSpatrick bwfm_usb_load_microcode(struct bwfm_usb_softc *sc, const u_char *ucode, size_t size)
65132b2494eSpatrick {
65232b2494eSpatrick struct trx_header *trx = (struct trx_header *)ucode;
65332b2494eSpatrick struct rdl_state state;
65432b2494eSpatrick uint32_t rdlstate, rdlbytes, sent = 0, sendlen = 0;
65532b2494eSpatrick struct usbd_xfer *xfer;
65632b2494eSpatrick usbd_status error;
65732b2494eSpatrick char *buf;
65832b2494eSpatrick
65932b2494eSpatrick if (letoh32(trx->magic) != TRX_MAGIC ||
66032b2494eSpatrick (letoh32(trx->flag_version) & TRX_UNCOMP_IMAGE) == 0) {
66132b2494eSpatrick printf("%s: invalid firmware\n", DEVNAME(sc));
66232b2494eSpatrick return 1;
66332b2494eSpatrick }
66432b2494eSpatrick
66532b2494eSpatrick bwfm_usb_dl_cmd(sc, DL_START, &state, sizeof(state));
66632b2494eSpatrick rdlstate = letoh32(state.state);
66732b2494eSpatrick rdlbytes = letoh32(state.bytes);
66832b2494eSpatrick
66932b2494eSpatrick if (rdlstate != DL_WAITING) {
67032b2494eSpatrick printf("%s: cannot start fw download\n", DEVNAME(sc));
67132b2494eSpatrick return 1;
67232b2494eSpatrick }
67332b2494eSpatrick
67432b2494eSpatrick xfer = usbd_alloc_xfer(sc->sc_udev);
67532b2494eSpatrick if (xfer == NULL) {
67632b2494eSpatrick printf("%s: cannot alloc xfer\n", DEVNAME(sc));
67732b2494eSpatrick goto err;
67832b2494eSpatrick }
67932b2494eSpatrick
68032b2494eSpatrick buf = usbd_alloc_buffer(xfer, TRX_RDL_CHUNK);
68132b2494eSpatrick if (buf == NULL) {
68232b2494eSpatrick printf("%s: cannot alloc buf\n", DEVNAME(sc));
68332b2494eSpatrick goto err;
68432b2494eSpatrick }
68532b2494eSpatrick
68632b2494eSpatrick while (rdlbytes != size) {
68732b2494eSpatrick sendlen = MIN(size - sent, TRX_RDL_CHUNK);
68832b2494eSpatrick memcpy(buf, ucode + sent, sendlen);
68932b2494eSpatrick
69032b2494eSpatrick usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, buf, sendlen,
69132b2494eSpatrick USBD_SYNCHRONOUS | USBD_NO_COPY, USBD_NO_TIMEOUT, NULL);
69232b2494eSpatrick error = usbd_transfer(xfer);
69332b2494eSpatrick if (error != 0 && error != USBD_IN_PROGRESS) {
69432b2494eSpatrick printf("%s: transfer error\n", DEVNAME(sc));
69532b2494eSpatrick goto err;
69632b2494eSpatrick }
69732b2494eSpatrick sent += sendlen;
69832b2494eSpatrick
69932b2494eSpatrick bwfm_usb_dl_cmd(sc, DL_GETSTATE, &state, sizeof(state));
70032b2494eSpatrick rdlstate = letoh32(state.state);
70132b2494eSpatrick rdlbytes = letoh32(state.bytes);
70232b2494eSpatrick
70332b2494eSpatrick if (rdlbytes != sent) {
70432b2494eSpatrick printf("%s: device reported different size\n",
70532b2494eSpatrick DEVNAME(sc));
70632b2494eSpatrick goto err;
70732b2494eSpatrick }
70832b2494eSpatrick
70932b2494eSpatrick if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) {
71032b2494eSpatrick printf("%s: device reported bad hdr/crc\n",
71132b2494eSpatrick DEVNAME(sc));
71232b2494eSpatrick goto err;
71332b2494eSpatrick }
71432b2494eSpatrick }
71532b2494eSpatrick
71632b2494eSpatrick bwfm_usb_dl_cmd(sc, DL_GETSTATE, &state, sizeof(state));
71732b2494eSpatrick rdlstate = letoh32(state.state);
71832b2494eSpatrick rdlbytes = letoh32(state.bytes);
71932b2494eSpatrick
72032b2494eSpatrick if (rdlstate != DL_RUNNABLE) {
72132b2494eSpatrick printf("%s: dongle not runnable\n", DEVNAME(sc));
72232b2494eSpatrick goto err;
72332b2494eSpatrick }
72432b2494eSpatrick
72532b2494eSpatrick bwfm_usb_dl_cmd(sc, DL_GO, &state, sizeof(state));
72632b2494eSpatrick
72732b2494eSpatrick return 0;
72832b2494eSpatrick err:
72932b2494eSpatrick if (sc->sc_tx_pipeh != NULL) {
73032b2494eSpatrick usbd_close_pipe(sc->sc_tx_pipeh);
73132b2494eSpatrick sc->sc_tx_pipeh = NULL;
73232b2494eSpatrick }
73332b2494eSpatrick if (xfer != NULL)
73432b2494eSpatrick usbd_free_xfer(xfer);
73532b2494eSpatrick return 1;
73632b2494eSpatrick }
73732b2494eSpatrick
73832b2494eSpatrick int
bwfm_usb_txcheck(struct bwfm_softc * bwfm)73902ee7d07Spatrick bwfm_usb_txcheck(struct bwfm_softc *bwfm)
74002ee7d07Spatrick {
74102ee7d07Spatrick struct bwfm_usb_softc *sc = (void *)bwfm;
74202ee7d07Spatrick
74302ee7d07Spatrick if (TAILQ_EMPTY(&sc->sc_tx_free_list))
74402ee7d07Spatrick return ENOBUFS;
74502ee7d07Spatrick
74602ee7d07Spatrick return 0;
74702ee7d07Spatrick }
74802ee7d07Spatrick
74902ee7d07Spatrick int
bwfm_usb_txdata(struct bwfm_softc * bwfm,struct mbuf * m)75032b2494eSpatrick bwfm_usb_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
75132b2494eSpatrick {
75232b2494eSpatrick struct bwfm_usb_softc *sc = (void *)bwfm;
75332b2494eSpatrick struct bwfm_proto_bcdc_hdr *hdr;
754483142cfSpatrick struct bwfm_usb_tx_data *data;
75532b2494eSpatrick uint32_t len = 0;
75632b2494eSpatrick int error;
75732b2494eSpatrick
75832b2494eSpatrick DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
75932b2494eSpatrick
760483142cfSpatrick if (TAILQ_EMPTY(&sc->sc_tx_free_list))
761483142cfSpatrick return ENOBUFS;
762483142cfSpatrick
763483142cfSpatrick /* Grab a Tx buffer from our free list. */
764483142cfSpatrick data = TAILQ_FIRST(&sc->sc_tx_free_list);
765483142cfSpatrick TAILQ_REMOVE(&sc->sc_tx_free_list, data, next);
766483142cfSpatrick
767483142cfSpatrick hdr = (void *)&data->buf[len];
76832b2494eSpatrick hdr->data_offset = 0;
76987e99414Spatrick hdr->priority = ieee80211_classify(&sc->sc_sc.sc_ic, m);
77032b2494eSpatrick hdr->flags = BWFM_BCDC_FLAG_VER(BWFM_BCDC_FLAG_PROTO_VER);
77187e99414Spatrick hdr->flags2 = 0;
77232b2494eSpatrick len += sizeof(*hdr);
77332b2494eSpatrick
774483142cfSpatrick m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&data->buf[len]);
77532b2494eSpatrick len += m->m_pkthdr.len;
77632b2494eSpatrick
777483142cfSpatrick data->mbuf = m;
778483142cfSpatrick
779483142cfSpatrick usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf,
78032b2494eSpatrick len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, USBD_NO_TIMEOUT,
78132b2494eSpatrick bwfm_usb_txeof);
782483142cfSpatrick error = usbd_transfer(data->xfer);
78332b2494eSpatrick if (error != 0 && error != USBD_IN_PROGRESS)
78432b2494eSpatrick printf("%s: could not set up new transfer: %s\n",
78532b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
78632b2494eSpatrick return 0;
78732b2494eSpatrick }
78832b2494eSpatrick
78932b2494eSpatrick int
bwfm_usb_txctl(struct bwfm_softc * bwfm,void * arg)7902802c178Spatrick bwfm_usb_txctl(struct bwfm_softc *bwfm, void *arg)
79132b2494eSpatrick {
79232b2494eSpatrick struct bwfm_usb_softc *sc = (void *)bwfm;
7932802c178Spatrick struct bwfm_proto_bcdc_ctl *ctl = arg;
79432b2494eSpatrick usb_device_request_t req;
795029d6dd5Spatrick struct usbd_xfer *xfer;
79632b2494eSpatrick usbd_status error;
797029d6dd5Spatrick char *buf;
79832b2494eSpatrick
79932b2494eSpatrick DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
80032b2494eSpatrick
801029d6dd5Spatrick /* Send out control packet. */
80232b2494eSpatrick req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
80332b2494eSpatrick req.bRequest = 0;
80432b2494eSpatrick USETW(req.wValue, 0);
80532b2494eSpatrick USETW(req.wIndex, sc->sc_ifaceno);
806029d6dd5Spatrick USETW(req.wLength, ctl->len);
80732b2494eSpatrick
808029d6dd5Spatrick error = usbd_do_request(sc->sc_udev, &req, ctl->buf);
80932b2494eSpatrick if (error != 0) {
810029d6dd5Spatrick printf("%s: could not write ctl packet: %s\n",
81132b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
812029d6dd5Spatrick free(ctl->buf, M_TEMP, ctl->len);
813029d6dd5Spatrick free(ctl, M_TEMP, sizeof(*ctl));
814029d6dd5Spatrick return 1;
81532b2494eSpatrick }
81632b2494eSpatrick
817029d6dd5Spatrick /* Setup asynchronous receive. */
818029d6dd5Spatrick if ((xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
819029d6dd5Spatrick free(ctl->buf, M_TEMP, ctl->len);
820029d6dd5Spatrick free(ctl, M_TEMP, sizeof(*ctl));
821029d6dd5Spatrick return 1;
822029d6dd5Spatrick }
823029d6dd5Spatrick if ((buf = usbd_alloc_buffer(xfer, ctl->len)) == NULL) {
824029d6dd5Spatrick free(ctl->buf, M_TEMP, ctl->len);
825aa67b630Spatrick free(ctl, M_TEMP, sizeof(*ctl));
826029d6dd5Spatrick usbd_free_xfer(xfer);
827029d6dd5Spatrick return 1;
82832b2494eSpatrick }
82932b2494eSpatrick
830029d6dd5Spatrick memset(buf, 0, ctl->len);
83132b2494eSpatrick req.bmRequestType = UT_READ_CLASS_INTERFACE;
83232b2494eSpatrick req.bRequest = 1;
83332b2494eSpatrick USETW(req.wValue, 0);
83432b2494eSpatrick USETW(req.wIndex, sc->sc_ifaceno);
835029d6dd5Spatrick USETW(req.wLength, ctl->len);
83632b2494eSpatrick
837029d6dd5Spatrick error = usbd_request_async(xfer, &req, sc, bwfm_usb_txctl_cb);
83832b2494eSpatrick if (error != 0) {
83932b2494eSpatrick printf("%s: could not read ctl packet: %s\n",
84032b2494eSpatrick DEVNAME(sc), usbd_errstr(error));
841029d6dd5Spatrick free(ctl->buf, M_TEMP, ctl->len);
842029d6dd5Spatrick free(ctl, M_TEMP, sizeof(*ctl));
843029d6dd5Spatrick return 1;
84432b2494eSpatrick }
84532b2494eSpatrick
846029d6dd5Spatrick TAILQ_INSERT_TAIL(&sc->sc_sc.sc_bcdc_rxctlq, ctl, next);
84732b2494eSpatrick
848029d6dd5Spatrick return 0;
849029d6dd5Spatrick }
850029d6dd5Spatrick
851029d6dd5Spatrick void
bwfm_usb_txctl_cb(struct usbd_xfer * xfer,void * priv,usbd_status err)852029d6dd5Spatrick bwfm_usb_txctl_cb(struct usbd_xfer *xfer, void *priv, usbd_status err)
853029d6dd5Spatrick {
854029d6dd5Spatrick struct bwfm_usb_softc *sc = priv;
855029d6dd5Spatrick
856029d6dd5Spatrick if (usbd_is_dying(xfer->pipe->device))
857029d6dd5Spatrick goto err;
858029d6dd5Spatrick
859029d6dd5Spatrick if (err == USBD_NORMAL_COMPLETION || err == USBD_SHORT_XFER) {
860029d6dd5Spatrick sc->sc_sc.sc_proto_ops->proto_rxctl(&sc->sc_sc,
861029d6dd5Spatrick KERNADDR(&xfer->dmabuf, 0), xfer->actlen);
862029d6dd5Spatrick }
863029d6dd5Spatrick
86432b2494eSpatrick err:
865029d6dd5Spatrick usbd_free_xfer(xfer);
86632b2494eSpatrick }
867