xref: /openbsd-src/sys/dev/usb/if_bwfm_usb.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
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