xref: /openbsd-src/sys/dev/usb/if_upgt.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: if_upgt.c,v 1.90 2024/05/23 03:21:09 jsg Exp $ */
21d97e97dSmglocker 
31d97e97dSmglocker /*
41d97e97dSmglocker  * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
51d97e97dSmglocker  *
61d97e97dSmglocker  * Permission to use, copy, modify, and distribute this software for any
71d97e97dSmglocker  * purpose with or without fee is hereby granted, provided that the above
81d97e97dSmglocker  * copyright notice and this permission notice appear in all copies.
91d97e97dSmglocker  *
101d97e97dSmglocker  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111d97e97dSmglocker  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121d97e97dSmglocker  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131d97e97dSmglocker  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141d97e97dSmglocker  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151d97e97dSmglocker  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161d97e97dSmglocker  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171d97e97dSmglocker  */
181d97e97dSmglocker 
191d97e97dSmglocker #include "bpfilter.h"
201d97e97dSmglocker 
211d97e97dSmglocker #include <sys/param.h>
221d97e97dSmglocker #include <sys/sockio.h>
231d97e97dSmglocker #include <sys/mbuf.h>
241d97e97dSmglocker #include <sys/systm.h>
251d97e97dSmglocker #include <sys/timeout.h>
261d97e97dSmglocker #include <sys/device.h>
279b18ffb8Sguenther #include <sys/endian.h>
281d97e97dSmglocker 
291d97e97dSmglocker #include <machine/intr.h>
301d97e97dSmglocker 
311d97e97dSmglocker #if NBPFILTER > 0
321d97e97dSmglocker #include <net/bpf.h>
331d97e97dSmglocker #endif
341d97e97dSmglocker #include <net/if.h>
351d97e97dSmglocker #include <net/if_dl.h>
361d97e97dSmglocker #include <net/if_media.h>
371d97e97dSmglocker 
381d97e97dSmglocker #include <netinet/in.h>
391d97e97dSmglocker #include <netinet/if_ether.h>
401d97e97dSmglocker 
411d97e97dSmglocker #include <net80211/ieee80211_var.h>
421d97e97dSmglocker #include <net80211/ieee80211_radiotap.h>
431d97e97dSmglocker 
441d97e97dSmglocker #include <dev/usb/usb.h>
451d97e97dSmglocker #include <dev/usb/usbdi.h>
461d97e97dSmglocker #include <dev/usb/usbdi_util.h>
471d97e97dSmglocker #include <dev/usb/usbdevs.h>
481d97e97dSmglocker 
491d97e97dSmglocker #include <dev/usb/if_upgtvar.h>
501d97e97dSmglocker 
511d97e97dSmglocker /*
521d97e97dSmglocker  * Driver for the USB PrismGT devices.
531d97e97dSmglocker  *
541d97e97dSmglocker  * For now just USB 2.0 devices with the GW3887 chipset are supported.
55b71d8b8eSmglocker  * The driver has been written based on the firmware version 2.13.1.0_LM87.
56b71d8b8eSmglocker  *
57b71d8b8eSmglocker  * TODO's:
58b71d8b8eSmglocker  * - Fix MONITOR mode (MAC filter).
59b71d8b8eSmglocker  * - Add HOSTAP mode.
60b71d8b8eSmglocker  * - Add IBSS mode.
61b71d8b8eSmglocker  * - Support the USB 1.0 devices (NET2280, ISL3880, ISL3886 chipsets).
6201b06dc6Smglocker  *
6301b06dc6Smglocker  * Parts of this driver has been influenced by reading the p54u driver
6401b06dc6Smglocker  * written by Jean-Baptiste Note <jean-baptiste.note@m4x.org> and
6501b06dc6Smglocker  * Sebastien Bourdeauducq <lekernel@prism54.org>.
661d97e97dSmglocker  */
671d97e97dSmglocker 
681d97e97dSmglocker #ifdef UPGT_DEBUG
691d97e97dSmglocker int upgt_debug = 2;
701d97e97dSmglocker #define DPRINTF(l, x...) do { if ((l) <= upgt_debug) printf(x); } while (0)
711d97e97dSmglocker #else
721d97e97dSmglocker #define DPRINTF(l, x...)
731d97e97dSmglocker #endif
741d97e97dSmglocker 
751d97e97dSmglocker /*
761d97e97dSmglocker  * Prototypes.
771d97e97dSmglocker  */
781d97e97dSmglocker int		upgt_match(struct device *, void *, void *);
791d97e97dSmglocker void		upgt_attach(struct device *, struct device *, void *);
80ef89f9e6Smpi void		upgt_attach_hook(struct device *);
811d97e97dSmglocker int		upgt_detach(struct device *, int);
821d97e97dSmglocker 
836abbc5b4Smglocker int		upgt_device_type(struct upgt_softc *, uint16_t, uint16_t);
841d97e97dSmglocker int		upgt_device_init(struct upgt_softc *);
851d97e97dSmglocker int		upgt_mem_init(struct upgt_softc *);
861d97e97dSmglocker uint32_t	upgt_mem_alloc(struct upgt_softc *);
871d97e97dSmglocker void		upgt_mem_free(struct upgt_softc *, uint32_t);
881d97e97dSmglocker int		upgt_fw_alloc(struct upgt_softc *);
891d97e97dSmglocker void		upgt_fw_free(struct upgt_softc *);
901d97e97dSmglocker int		upgt_fw_verify(struct upgt_softc *);
911d97e97dSmglocker int		upgt_fw_load(struct upgt_softc *);
921d97e97dSmglocker int		upgt_fw_copy(char *, char *, int);
931d97e97dSmglocker int		upgt_eeprom_read(struct upgt_softc *);
941d97e97dSmglocker int		upgt_eeprom_parse(struct upgt_softc *);
951d97e97dSmglocker void		upgt_eeprom_parse_hwrx(struct upgt_softc *, uint8_t *);
961d97e97dSmglocker void		upgt_eeprom_parse_freq3(struct upgt_softc *, uint8_t *, int);
971d97e97dSmglocker void		upgt_eeprom_parse_freq4(struct upgt_softc *, uint8_t *, int);
981d97e97dSmglocker void		upgt_eeprom_parse_freq6(struct upgt_softc *, uint8_t *, int);
991d97e97dSmglocker 
1001d97e97dSmglocker int		upgt_ioctl(struct ifnet *, u_long, caddr_t);
1011d97e97dSmglocker int		upgt_init(struct ifnet *);
1021d97e97dSmglocker void		upgt_stop(struct upgt_softc *);
1031d97e97dSmglocker int		upgt_media_change(struct ifnet *);
1041d97e97dSmglocker void		upgt_newassoc(struct ieee80211com *, struct ieee80211_node *,
1051d97e97dSmglocker 		    int);
1061d97e97dSmglocker int		upgt_newstate(struct ieee80211com *, enum ieee80211_state, int);
1071d97e97dSmglocker void		upgt_newstate_task(void *);
1081d97e97dSmglocker void		upgt_next_scan(void *);
1091d97e97dSmglocker void		upgt_start(struct ifnet *);
1101d97e97dSmglocker void		upgt_watchdog(struct ifnet *);
1111d97e97dSmglocker void		upgt_tx_task(void *);
1121d97e97dSmglocker void		upgt_tx_done(struct upgt_softc *, uint8_t *);
113ab0b1be7Smglocker void		upgt_rx_cb(struct usbd_xfer *, void *, usbd_status);
1141d97e97dSmglocker void		upgt_rx(struct upgt_softc *, uint8_t *, int);
1156b9aaf9aSmglocker void		upgt_setup_rates(struct upgt_softc *);
11625e71d99Smglocker uint8_t		upgt_rx_rate(struct upgt_softc *, const int);
1171d97e97dSmglocker int		upgt_set_macfilter(struct upgt_softc *, uint8_t state);
1181d97e97dSmglocker int		upgt_set_channel(struct upgt_softc *, unsigned);
11902f087d8Smglocker void		upgt_set_led(struct upgt_softc *, int);
12002f087d8Smglocker void		upgt_set_led_blink(void *);
121ec72f164Smglocker int		upgt_get_stats(struct upgt_softc *);
1221d97e97dSmglocker 
1231d97e97dSmglocker int		upgt_alloc_tx(struct upgt_softc *);
1241d97e97dSmglocker int		upgt_alloc_rx(struct upgt_softc *);
1251d97e97dSmglocker int		upgt_alloc_cmd(struct upgt_softc *);
1261d97e97dSmglocker void		upgt_free_tx(struct upgt_softc *);
1271d97e97dSmglocker void		upgt_free_rx(struct upgt_softc *);
1281d97e97dSmglocker void		upgt_free_cmd(struct upgt_softc *);
1291d97e97dSmglocker int		upgt_bulk_xmit(struct upgt_softc *, struct upgt_data *,
130ab0b1be7Smglocker 		    struct usbd_pipe *, uint32_t *, int);
1311d97e97dSmglocker 
1321d97e97dSmglocker void		upgt_hexdump(void *, int);
133f3a6740aSmglocker uint32_t	upgt_crc32_le(const void *, size_t);
134f3a6740aSmglocker uint32_t	upgt_chksum_le(const uint32_t *, size_t);
1351d97e97dSmglocker 
1361d97e97dSmglocker struct cfdriver upgt_cd = {
1371d97e97dSmglocker 	NULL, "upgt", DV_IFNET
1381d97e97dSmglocker };
1391d97e97dSmglocker 
1401d97e97dSmglocker const struct cfattach upgt_ca = {
14153c6612dSmpi 	sizeof(struct upgt_softc), upgt_match, upgt_attach, upgt_detach
1421d97e97dSmglocker };
1431d97e97dSmglocker 
1446abbc5b4Smglocker static const struct usb_devno upgt_devs_1[] = {
1456abbc5b4Smglocker 	/* version 1 devices */
1466abbc5b4Smglocker 	{ USB_VENDOR_ALCATELT,		USB_PRODUCT_ALCATELT_ST120G }
1476abbc5b4Smglocker };
1486abbc5b4Smglocker 
1496abbc5b4Smglocker static const struct usb_devno upgt_devs_2[] = {
1506abbc5b4Smglocker 	/* version 2 devices */
1510560509aSmglocker 	{ USB_VENDOR_ACCTON,		USB_PRODUCT_ACCTON_PRISM_GT },
152bdcdfdd1Sjsg 	{ USB_VENDOR_ALCATELT,		USB_PRODUCT_ALCATELT_ST121G },
1530560509aSmglocker 	{ USB_VENDOR_BELKIN,		USB_PRODUCT_BELKIN_F5D7050 },
154bdcdfdd1Sjsg 	{ USB_VENDOR_CISCOLINKSYS,	USB_PRODUCT_CISCOLINKSYS_WUSB54AG },
155bdcdfdd1Sjsg 	{ USB_VENDOR_CISCOLINKSYS,	USB_PRODUCT_CISCOLINKSYS_WUSB54GV2 },
1560560509aSmglocker 	{ USB_VENDOR_CONCEPTRONIC,	USB_PRODUCT_CONCEPTRONIC_PRISM_GT },
1570560509aSmglocker 	{ USB_VENDOR_DELL,		USB_PRODUCT_DELL_PRISM_GT_1 },
1580560509aSmglocker 	{ USB_VENDOR_DELL,		USB_PRODUCT_DELL_PRISM_GT_2 },
159bdcdfdd1Sjsg 	{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DWLG122A2 },
1603dd872c7Smglocker 	{ USB_VENDOR_FSC,		USB_PRODUCT_FSC_E5400 },
1610560509aSmglocker 	{ USB_VENDOR_GLOBESPAN,		USB_PRODUCT_GLOBESPAN_PRISM_GT_1 },
1620560509aSmglocker 	{ USB_VENDOR_GLOBESPAN,		USB_PRODUCT_GLOBESPAN_PRISM_GT_2 },
1630560509aSmglocker 	{ USB_VENDOR_INTERSIL,		USB_PRODUCT_INTERSIL_PRISM_GT },
164bdcdfdd1Sjsg 	{ USB_VENDOR_PHEENET,		USB_PRODUCT_PHEENET_GWU513 },
165bdcdfdd1Sjsg 	{ USB_VENDOR_PHILIPS,		USB_PRODUCT_PHILIPS_CPWUA054 },
1660560509aSmglocker 	{ USB_VENDOR_SMC,		USB_PRODUCT_SMC_2862WG },
167bdcdfdd1Sjsg 	{ USB_VENDOR_USR,		USB_PRODUCT_USR_USR5422 },
1680560509aSmglocker 	{ USB_VENDOR_WISTRONNEWEB,	USB_PRODUCT_WISTRONNEWEB_UR045G },
1690560509aSmglocker 	{ USB_VENDOR_XYRATEX,		USB_PRODUCT_XYRATEX_PRISM_GT_1 },
1700560509aSmglocker 	{ USB_VENDOR_XYRATEX,		USB_PRODUCT_XYRATEX_PRISM_GT_2 },
171bdcdfdd1Sjsg 	{ USB_VENDOR_ZCOM,		USB_PRODUCT_ZCOM_MD40900 },
1720560509aSmglocker 	{ USB_VENDOR_ZCOM,		USB_PRODUCT_ZCOM_XG703A }
1731d97e97dSmglocker };
1741d97e97dSmglocker 
1751d97e97dSmglocker int
upgt_match(struct device * parent,void * match,void * aux)1761d97e97dSmglocker upgt_match(struct device *parent, void *match, void *aux)
1771d97e97dSmglocker {
1781d97e97dSmglocker 	struct usb_attach_arg *uaa = aux;
1791d97e97dSmglocker 
180f4b7d08eSmpi 	if (uaa->iface == NULL || uaa->configno != UPGT_CONFIG_NO)
1811d97e97dSmglocker 		return (UMATCH_NONE);
1821d97e97dSmglocker 
1836abbc5b4Smglocker 	if (usb_lookup(upgt_devs_1, uaa->vendor, uaa->product) != NULL)
1846abbc5b4Smglocker 		return (UMATCH_VENDOR_PRODUCT);
1856abbc5b4Smglocker 
1866abbc5b4Smglocker 	if (usb_lookup(upgt_devs_2, uaa->vendor, uaa->product) != NULL)
1876abbc5b4Smglocker 		return (UMATCH_VENDOR_PRODUCT);
1886abbc5b4Smglocker 
1896abbc5b4Smglocker 	return (UMATCH_NONE);
1901d97e97dSmglocker }
1911d97e97dSmglocker 
1921d97e97dSmglocker void
upgt_attach(struct device * parent,struct device * self,void * aux)1931d97e97dSmglocker upgt_attach(struct device *parent, struct device *self, void *aux)
1941d97e97dSmglocker {
1951d97e97dSmglocker 	struct upgt_softc *sc = (struct upgt_softc *)self;
1961d97e97dSmglocker 	struct usb_attach_arg *uaa = aux;
1971d97e97dSmglocker 	usb_interface_descriptor_t *id;
1981d97e97dSmglocker 	usb_endpoint_descriptor_t *ed;
1991d97e97dSmglocker 	usbd_status error;
2001d97e97dSmglocker 	int i;
2011d97e97dSmglocker 
2021d97e97dSmglocker 	/*
2031d97e97dSmglocker 	 * Attach USB device.
2041d97e97dSmglocker 	 */
2051d97e97dSmglocker 	sc->sc_udev = uaa->device;
2061d97e97dSmglocker 
2076abbc5b4Smglocker 	/* check device type */
2086abbc5b4Smglocker 	if (upgt_device_type(sc, uaa->vendor, uaa->product) != 0)
2096abbc5b4Smglocker 		return;
2106abbc5b4Smglocker 
2111d97e97dSmglocker 	/* get the first interface handle */
2121d97e97dSmglocker 	error = usbd_device2interface_handle(sc->sc_udev, UPGT_IFACE_INDEX,
2131d97e97dSmglocker 	    &sc->sc_iface);
2141d97e97dSmglocker 	if (error != 0) {
2151d97e97dSmglocker 		printf("%s: could not get interface handle!\n",
2161d97e97dSmglocker 		    sc->sc_dev.dv_xname);
2171d97e97dSmglocker 		return;
2181d97e97dSmglocker 	}
2191d97e97dSmglocker 
2201d97e97dSmglocker 	/* find endpoints */
2211d97e97dSmglocker 	id = usbd_get_interface_descriptor(sc->sc_iface);
2221d97e97dSmglocker 	sc->sc_rx_no = sc->sc_tx_no = -1;
2231d97e97dSmglocker 	for (i = 0; i < id->bNumEndpoints; i++) {
2241d97e97dSmglocker 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
2251d97e97dSmglocker 		if (ed == NULL) {
2261d97e97dSmglocker 			printf("%s: no endpoint descriptor for iface %d!\n",
2271d97e97dSmglocker 			    sc->sc_dev.dv_xname, i);
2281d97e97dSmglocker 			return;
2291d97e97dSmglocker 		}
2301d97e97dSmglocker 
2311d97e97dSmglocker 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
2321d97e97dSmglocker 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
2331d97e97dSmglocker 			sc->sc_tx_no = ed->bEndpointAddress;
2341d97e97dSmglocker 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
2351d97e97dSmglocker 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
2361d97e97dSmglocker 			sc->sc_rx_no = ed->bEndpointAddress;
2371d97e97dSmglocker 
2381d97e97dSmglocker 		/*
2391d97e97dSmglocker 		 * 0x01 TX pipe
2401d97e97dSmglocker 		 * 0x81 RX pipe
2412fa849daSmglocker 		 *
2422fa849daSmglocker 		 * Deprecated scheme (not used with fw version >2.5.6.x):
2432fa849daSmglocker 		 * 0x02 TX MGMT pipe
2442fa849daSmglocker 		 * 0x82 TX MGMT pipe
2451d97e97dSmglocker 		 */
2461d97e97dSmglocker 		if (sc->sc_tx_no != -1 && sc->sc_rx_no != -1)
2471d97e97dSmglocker 			break;
2481d97e97dSmglocker 	}
2491d97e97dSmglocker 	if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
2501d97e97dSmglocker 		printf("%s: missing endpoint!\n", sc->sc_dev.dv_xname);
2511d97e97dSmglocker 		return;
2521d97e97dSmglocker 	}
2531d97e97dSmglocker 
25402f087d8Smglocker 	/* setup tasks and timeouts */
255c33449aaSjakemsr 	usb_init_task(&sc->sc_task_newstate, upgt_newstate_task, sc,
256c33449aaSjakemsr 	    USB_TASK_TYPE_GENERIC);
257c33449aaSjakemsr 	usb_init_task(&sc->sc_task_tx, upgt_tx_task, sc, USB_TASK_TYPE_GENERIC);
2581d97e97dSmglocker 	timeout_set(&sc->scan_to, upgt_next_scan, sc);
25902f087d8Smglocker 	timeout_set(&sc->led_to, upgt_set_led_blink, sc);
2601d97e97dSmglocker 
2611d97e97dSmglocker 	/*
2621d97e97dSmglocker 	 * Open TX and RX USB bulk pipes.
2631d97e97dSmglocker 	 */
2641d97e97dSmglocker 	error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
2651d97e97dSmglocker 	    &sc->sc_tx_pipeh);
2661d97e97dSmglocker 	if (error != 0) {
2671d97e97dSmglocker 		printf("%s: could not open TX pipe: %s!\n",
2681d97e97dSmglocker 		    sc->sc_dev.dv_xname, usbd_errstr(error));
2691d97e97dSmglocker 		goto fail;
2701d97e97dSmglocker 	}
2711d97e97dSmglocker 	error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
2721d97e97dSmglocker 	    &sc->sc_rx_pipeh);
2731d97e97dSmglocker 	if (error != 0) {
2741d97e97dSmglocker 		printf("%s: could not open RX pipe: %s!\n",
2751d97e97dSmglocker 		    sc->sc_dev.dv_xname, usbd_errstr(error));
2761d97e97dSmglocker 		goto fail;
2771d97e97dSmglocker 	}
2781d97e97dSmglocker 
2791d97e97dSmglocker 	/*
2801d97e97dSmglocker 	 * Allocate TX, RX, and CMD xfers.
2811d97e97dSmglocker 	 */
2821d97e97dSmglocker 	if (upgt_alloc_tx(sc) != 0)
2831d97e97dSmglocker 		goto fail;
2841d97e97dSmglocker 	if (upgt_alloc_rx(sc) != 0)
2851d97e97dSmglocker 		goto fail;
2861d97e97dSmglocker 	if (upgt_alloc_cmd(sc) != 0)
2871d97e97dSmglocker 		goto fail;
2881d97e97dSmglocker 
2891d97e97dSmglocker 	/*
2901d97e97dSmglocker 	 * We need the firmware loaded to complete the attach.
2911d97e97dSmglocker 	 */
292ef89f9e6Smpi 	config_mountroot(self, upgt_attach_hook);
2931d97e97dSmglocker 
2941d97e97dSmglocker 	return;
2951d97e97dSmglocker fail:
2961d97e97dSmglocker 	printf("%s: %s failed!\n", sc->sc_dev.dv_xname, __func__);
2971d97e97dSmglocker }
2981d97e97dSmglocker 
2991d97e97dSmglocker void
upgt_attach_hook(struct device * self)300ef89f9e6Smpi upgt_attach_hook(struct device *self)
3011d97e97dSmglocker {
302ef89f9e6Smpi 	struct upgt_softc *sc = (struct upgt_softc *)self;
3031d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
3041d97e97dSmglocker 	struct ifnet *ifp = &ic->ic_if;
3051d97e97dSmglocker 	usbd_status error;
3061d97e97dSmglocker 	int i;
3071d97e97dSmglocker 
3081d97e97dSmglocker 	/*
3091d97e97dSmglocker 	 * Load firmware file into memory.
3101d97e97dSmglocker 	 */
3111d97e97dSmglocker 	if (upgt_fw_alloc(sc) != 0)
3121d97e97dSmglocker 		goto fail;
3131d97e97dSmglocker 
3141d97e97dSmglocker 	/*
3151d97e97dSmglocker 	 * Initialize the device.
3161d97e97dSmglocker 	 */
3171d97e97dSmglocker 	if (upgt_device_init(sc) != 0)
3181d97e97dSmglocker 		goto fail;
3191d97e97dSmglocker 
3201d97e97dSmglocker 	/*
3211d97e97dSmglocker 	 * Verify the firmware.
3221d97e97dSmglocker 	 */
3231d97e97dSmglocker 	if (upgt_fw_verify(sc) != 0)
3241d97e97dSmglocker 		goto fail;
3251d97e97dSmglocker 
3261d97e97dSmglocker 	/*
3271d97e97dSmglocker 	 * Calculate device memory space.
3281d97e97dSmglocker 	 */
3291d97e97dSmglocker 	if (sc->sc_memaddr_frame_start == 0 || sc->sc_memaddr_frame_end == 0) {
3301d97e97dSmglocker 		printf("%s: could not find memory space addresses on FW!\n",
3311d97e97dSmglocker 		    sc->sc_dev.dv_xname);
3321d97e97dSmglocker 		goto fail;
3331d97e97dSmglocker 	}
3341d97e97dSmglocker 	sc->sc_memaddr_frame_end -= UPGT_MEMSIZE_RX + 1;
3351d97e97dSmglocker 	sc->sc_memaddr_rx_start = sc->sc_memaddr_frame_end + 1;
3361d97e97dSmglocker 
3371d97e97dSmglocker 	DPRINTF(1, "%s: memory address frame start=0x%08x\n",
3381d97e97dSmglocker 	    sc->sc_dev.dv_xname, sc->sc_memaddr_frame_start);
3391d97e97dSmglocker 	DPRINTF(1, "%s: memory address frame end=0x%08x\n",
3401d97e97dSmglocker 	    sc->sc_dev.dv_xname, sc->sc_memaddr_frame_end);
3411d97e97dSmglocker 	DPRINTF(1, "%s: memory address rx start=0x%08x\n",
3421d97e97dSmglocker 	    sc->sc_dev.dv_xname, sc->sc_memaddr_rx_start);
3431d97e97dSmglocker 
3441d97e97dSmglocker 	upgt_mem_init(sc);
3451d97e97dSmglocker 
3461d97e97dSmglocker 	/*
3471d97e97dSmglocker 	 * Load the firmware.
3481d97e97dSmglocker 	 */
3491d97e97dSmglocker 	if (upgt_fw_load(sc) != 0)
3501d97e97dSmglocker 		goto fail;
3511d97e97dSmglocker 
3521d97e97dSmglocker 	/*
3531d97e97dSmglocker 	 * Startup the RX pipe.
3541d97e97dSmglocker 	 */
3551d97e97dSmglocker 	struct upgt_data *data_rx = &sc->rx_data;
3561d97e97dSmglocker 
3571d97e97dSmglocker 	usbd_setup_xfer(data_rx->xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf,
3581d97e97dSmglocker 	    MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rx_cb);
3591d97e97dSmglocker 	error = usbd_transfer(data_rx->xfer);
3601d97e97dSmglocker 	if (error != 0 && error != USBD_IN_PROGRESS) {
3611d97e97dSmglocker 		printf("%s: could not queue RX transfer!\n",
3621d97e97dSmglocker 		    sc->sc_dev.dv_xname);
3631d97e97dSmglocker 		goto fail;
3641d97e97dSmglocker 	}
365836f404eSmglocker 	usbd_delay_ms(sc->sc_udev, 100);
3661d97e97dSmglocker 
3671d97e97dSmglocker 	/*
3681d97e97dSmglocker 	 * Read the whole EEPROM content and parse it.
3691d97e97dSmglocker 	 */
3701d97e97dSmglocker 	if (upgt_eeprom_read(sc) != 0)
3711d97e97dSmglocker 		goto fail;
3721d97e97dSmglocker 	if (upgt_eeprom_parse(sc) != 0)
3731d97e97dSmglocker 		goto fail;
3741d97e97dSmglocker 
3751d97e97dSmglocker 	/*
3761d97e97dSmglocker 	 * Setup the 802.11 device.
3771d97e97dSmglocker 	 */
3781d97e97dSmglocker 	ic->ic_phytype = IEEE80211_T_OFDM;
3791d97e97dSmglocker 	ic->ic_opmode = IEEE80211_M_STA;
3801d97e97dSmglocker 	ic->ic_state = IEEE80211_S_INIT;
3811d97e97dSmglocker 	ic->ic_caps =
3821d97e97dSmglocker 	    IEEE80211_C_MONITOR |
3831d97e97dSmglocker 	    IEEE80211_C_SHPREAMBLE |
3841d97e97dSmglocker 	    IEEE80211_C_SHSLOT |
385e03e709cSdamien 	    IEEE80211_C_WEP |
386e03e709cSdamien 	    IEEE80211_C_RSN;
3871d97e97dSmglocker 
3881d97e97dSmglocker 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
3891d97e97dSmglocker 	ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
3901d97e97dSmglocker 
3911d97e97dSmglocker 	for (i = 1; i <= 14; i++) {
3921d97e97dSmglocker 		ic->ic_channels[i].ic_freq =
3931d97e97dSmglocker 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
3941d97e97dSmglocker 		ic->ic_channels[i].ic_flags =
3951d97e97dSmglocker 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
3961d97e97dSmglocker 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
3971d97e97dSmglocker 	}
3981d97e97dSmglocker 
3991d97e97dSmglocker 	ifp->if_softc = sc;
4001d97e97dSmglocker 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
4011d97e97dSmglocker 	ifp->if_ioctl = upgt_ioctl;
4021d97e97dSmglocker 	ifp->if_start = upgt_start;
4031d97e97dSmglocker 	ifp->if_watchdog = upgt_watchdog;
4041d97e97dSmglocker 	memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
4051d97e97dSmglocker 
4061d97e97dSmglocker 	if_attach(ifp);
4071d97e97dSmglocker 	ieee80211_ifattach(ifp);
4081d97e97dSmglocker 	ic->ic_newassoc = upgt_newassoc;
4091d97e97dSmglocker 
4101d97e97dSmglocker 	sc->sc_newstate = ic->ic_newstate;
4111d97e97dSmglocker 	ic->ic_newstate = upgt_newstate;
4121d97e97dSmglocker 	ieee80211_media_init(ifp, upgt_media_change, ieee80211_media_status);
4131d97e97dSmglocker 
4141d97e97dSmglocker #if NBPFILTER > 0
4151d97e97dSmglocker 	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
4161d97e97dSmglocker 	    sizeof(struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
4171d97e97dSmglocker 
4181d97e97dSmglocker 	sc->sc_rxtap_len = sizeof(sc->sc_rxtapu);
4191d97e97dSmglocker 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
4201d97e97dSmglocker 	sc->sc_rxtap.wr_ihdr.it_present = htole32(UPGT_RX_RADIOTAP_PRESENT);
4211d97e97dSmglocker 
4221d97e97dSmglocker 	sc->sc_txtap_len = sizeof(sc->sc_txtapu);
4231d97e97dSmglocker 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
4241d97e97dSmglocker 	sc->sc_txtap.wt_ihdr.it_present = htole32(UPGT_TX_RADIOTAP_PRESENT);
4251d97e97dSmglocker #endif
4261d97e97dSmglocker 
4271d97e97dSmglocker 	printf("%s: address %s\n",
4281d97e97dSmglocker 	    sc->sc_dev.dv_xname, ether_sprintf(ic->ic_myaddr));
4291d97e97dSmglocker 
4301d97e97dSmglocker 	return;
4311d97e97dSmglocker fail:
4321d97e97dSmglocker 	printf("%s: %s failed!\n", sc->sc_dev.dv_xname, __func__);
4331d97e97dSmglocker }
4341d97e97dSmglocker 
4351d97e97dSmglocker int
upgt_detach(struct device * self,int flags)4361d97e97dSmglocker upgt_detach(struct device *self, int flags)
4371d97e97dSmglocker {
4381d97e97dSmglocker 	struct upgt_softc *sc = (struct upgt_softc *)self;
4391d97e97dSmglocker 	struct ifnet *ifp = &sc->sc_ic.ic_if;
4401d97e97dSmglocker 	int s;
4411d97e97dSmglocker 
442cec10f70Smglocker 	DPRINTF(1, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
443cec10f70Smglocker 
4441d97e97dSmglocker 	s = splusb();
4451d97e97dSmglocker 
4461d97e97dSmglocker 	/* abort and close TX / RX pipes */
447f88cb03eSmglocker 	if (sc->sc_tx_pipeh != NULL)
4481d97e97dSmglocker 		usbd_close_pipe(sc->sc_tx_pipeh);
449f88cb03eSmglocker 	if (sc->sc_rx_pipeh != NULL)
4501d97e97dSmglocker 		usbd_close_pipe(sc->sc_rx_pipeh);
4511d97e97dSmglocker 
4523c0b05beSmglocker 	/* remove tasks and timeouts */
4533c0b05beSmglocker 	usb_rem_task(sc->sc_udev, &sc->sc_task_newstate);
4543c0b05beSmglocker 	usb_rem_task(sc->sc_udev, &sc->sc_task_tx);
455c7438bddSjakemsr 	if (timeout_initialized(&sc->scan_to))
4563c0b05beSmglocker 		timeout_del(&sc->scan_to);
457c7438bddSjakemsr 	if (timeout_initialized(&sc->led_to))
45802f087d8Smglocker 		timeout_del(&sc->led_to);
4593c0b05beSmglocker 
4601d97e97dSmglocker 	/* free xfers */
4611d97e97dSmglocker 	upgt_free_tx(sc);
4621d97e97dSmglocker 	upgt_free_rx(sc);
4631d97e97dSmglocker 	upgt_free_cmd(sc);
4641d97e97dSmglocker 
4651d97e97dSmglocker 	/* free firmware */
4661d97e97dSmglocker 	upgt_fw_free(sc);
4671d97e97dSmglocker 
4685dd621eeSjakemsr 	if (ifp->if_softc != NULL) {
4691d97e97dSmglocker 		/* detach interface */
4701d97e97dSmglocker 		ieee80211_ifdetach(ifp);
4711d97e97dSmglocker 		if_detach(ifp);
4723c0b05beSmglocker 	}
4731d97e97dSmglocker 
4741d97e97dSmglocker 	splx(s);
4751d97e97dSmglocker 
4761d97e97dSmglocker 	return (0);
4771d97e97dSmglocker }
4781d97e97dSmglocker 
4791d97e97dSmglocker int
upgt_device_type(struct upgt_softc * sc,uint16_t vendor,uint16_t product)4806abbc5b4Smglocker upgt_device_type(struct upgt_softc *sc, uint16_t vendor, uint16_t product)
4816abbc5b4Smglocker {
4826abbc5b4Smglocker 	if (usb_lookup(upgt_devs_1, vendor, product) != NULL) {
4836abbc5b4Smglocker 		sc->sc_device_type = 1;
4846abbc5b4Smglocker 		/* XXX */
4856abbc5b4Smglocker 		printf("%s: version 1 devices not supported yet!\n",
4866abbc5b4Smglocker 		    sc->sc_dev.dv_xname);
4876abbc5b4Smglocker 		return (1);
4886abbc5b4Smglocker 	} else {
4896abbc5b4Smglocker 		sc->sc_device_type = 2;
4906abbc5b4Smglocker 	}
4916abbc5b4Smglocker 
4926abbc5b4Smglocker 	return (0);
4936abbc5b4Smglocker }
4946abbc5b4Smglocker 
4956abbc5b4Smglocker int
upgt_device_init(struct upgt_softc * sc)4961d97e97dSmglocker upgt_device_init(struct upgt_softc *sc)
4971d97e97dSmglocker {
4981d97e97dSmglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
4991d97e97dSmglocker 	char init_cmd[] = { 0x7e, 0x7e, 0x7e, 0x7e };
5001d97e97dSmglocker 	int len;
5011d97e97dSmglocker 
5021d97e97dSmglocker 	len = sizeof(init_cmd);
5031d97e97dSmglocker 	bcopy(init_cmd, data_cmd->buf, len);
5041d97e97dSmglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
5051d97e97dSmglocker 		printf("%s: could not send device init string!\n",
5061d97e97dSmglocker 		    sc->sc_dev.dv_xname);
5071d97e97dSmglocker 		return (EIO);
5081d97e97dSmglocker 	}
5091d97e97dSmglocker 	usbd_delay_ms(sc->sc_udev, 100);
5101d97e97dSmglocker 
5111d97e97dSmglocker 	DPRINTF(1, "%s: device initialized\n", sc->sc_dev.dv_xname);
5121d97e97dSmglocker 
5131d97e97dSmglocker 	return (0);
5141d97e97dSmglocker }
5151d97e97dSmglocker 
5161d97e97dSmglocker int
upgt_mem_init(struct upgt_softc * sc)5171d97e97dSmglocker upgt_mem_init(struct upgt_softc *sc)
5181d97e97dSmglocker {
5191d97e97dSmglocker 	int i;
5201d97e97dSmglocker 
5211d97e97dSmglocker 	for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) {
5221d97e97dSmglocker 		sc->sc_memory.page[i].used = 0;
5231d97e97dSmglocker 
5241d97e97dSmglocker 		if (i == 0) {
5251d97e97dSmglocker 			/*
5261d97e97dSmglocker 			 * The first memory page is always reserved for
5271d97e97dSmglocker 			 * command data.
5281d97e97dSmglocker 			 */
5291d97e97dSmglocker 			sc->sc_memory.page[i].addr =
5301d97e97dSmglocker 			    sc->sc_memaddr_frame_start + MCLBYTES;
5311d97e97dSmglocker 		} else {
5321d97e97dSmglocker 			sc->sc_memory.page[i].addr =
5331d97e97dSmglocker 			    sc->sc_memory.page[i - 1].addr + MCLBYTES;
5341d97e97dSmglocker 		}
5351d97e97dSmglocker 
5361d97e97dSmglocker 		if (sc->sc_memory.page[i].addr + MCLBYTES >=
5371d97e97dSmglocker 		    sc->sc_memaddr_frame_end)
5381d97e97dSmglocker 			break;
5391d97e97dSmglocker 
5401d97e97dSmglocker 		DPRINTF(2, "%s: memory address page %d=0x%08x\n",
5411d97e97dSmglocker 		    sc->sc_dev.dv_xname, i, sc->sc_memory.page[i].addr);
5421d97e97dSmglocker 	}
5431d97e97dSmglocker 
5441d97e97dSmglocker 	sc->sc_memory.pages = i;
5451d97e97dSmglocker 
5461d97e97dSmglocker 	DPRINTF(2, "%s: memory pages=%d\n",
5471d97e97dSmglocker 	    sc->sc_dev.dv_xname, sc->sc_memory.pages);
5481d97e97dSmglocker 
5491d97e97dSmglocker 	return (0);
5501d97e97dSmglocker }
5511d97e97dSmglocker 
5521d97e97dSmglocker uint32_t
upgt_mem_alloc(struct upgt_softc * sc)5531d97e97dSmglocker upgt_mem_alloc(struct upgt_softc *sc)
5541d97e97dSmglocker {
5551d97e97dSmglocker 	int i;
5561d97e97dSmglocker 
5571d97e97dSmglocker 	for (i = 0; i < sc->sc_memory.pages; i++) {
5581d97e97dSmglocker 		if (sc->sc_memory.page[i].used == 0) {
5591d97e97dSmglocker 			sc->sc_memory.page[i].used = 1;
5601d97e97dSmglocker 			return (sc->sc_memory.page[i].addr);
5611d97e97dSmglocker 		}
5621d97e97dSmglocker 	}
5631d97e97dSmglocker 
5641d97e97dSmglocker 	return (0);
5651d97e97dSmglocker }
5661d97e97dSmglocker 
5671d97e97dSmglocker void
upgt_mem_free(struct upgt_softc * sc,uint32_t addr)5681d97e97dSmglocker upgt_mem_free(struct upgt_softc *sc, uint32_t addr)
5691d97e97dSmglocker {
5701d97e97dSmglocker 	int i;
5711d97e97dSmglocker 
5721d97e97dSmglocker 	for (i = 0; i < sc->sc_memory.pages; i++) {
5731d97e97dSmglocker 		if (sc->sc_memory.page[i].addr == addr) {
5741d97e97dSmglocker 			sc->sc_memory.page[i].used = 0;
5751d97e97dSmglocker 			return;
5761d97e97dSmglocker 		}
5771d97e97dSmglocker 	}
5781d97e97dSmglocker 
5791d97e97dSmglocker 	printf("%s: could not free memory address 0x%08x!\n",
5801d97e97dSmglocker 	    sc->sc_dev.dv_xname, addr);
5811d97e97dSmglocker }
5821d97e97dSmglocker 
5831d97e97dSmglocker 
5841d97e97dSmglocker int
upgt_fw_alloc(struct upgt_softc * sc)5851d97e97dSmglocker upgt_fw_alloc(struct upgt_softc *sc)
5861d97e97dSmglocker {
5871d97e97dSmglocker 	const char *name = "upgt-gw3887";
5881d97e97dSmglocker 	int error;
5891d97e97dSmglocker 
5901d97e97dSmglocker 	if (sc->sc_fw == NULL) {
5911d97e97dSmglocker 		error = loadfirmware(name, &sc->sc_fw, &sc->sc_fw_size);
5921d97e97dSmglocker 		if (error != 0) {
5931d97e97dSmglocker 			printf("%s: error %d, could not read firmware %s!\n",
5941d97e97dSmglocker 			    sc->sc_dev.dv_xname, error, name);
5951d97e97dSmglocker 			return (EIO);
5961d97e97dSmglocker 		}
5971d97e97dSmglocker 	}
5981d97e97dSmglocker 
5991d97e97dSmglocker 	DPRINTF(1, "%s: firmware %s allocated\n", sc->sc_dev.dv_xname, name);
6001d97e97dSmglocker 
6011d97e97dSmglocker 	return (0);
6021d97e97dSmglocker }
6031d97e97dSmglocker 
6041d97e97dSmglocker void
upgt_fw_free(struct upgt_softc * sc)6051d97e97dSmglocker upgt_fw_free(struct upgt_softc *sc)
6061d97e97dSmglocker {
6071d97e97dSmglocker 	if (sc->sc_fw != NULL) {
608c9ee9455Sderaadt 		free(sc->sc_fw, M_DEVBUF, sc->sc_fw_size);
6091d97e97dSmglocker 		sc->sc_fw = NULL;
6101d97e97dSmglocker 		DPRINTF(1, "%s: firmware freed\n", sc->sc_dev.dv_xname);
6111d97e97dSmglocker 	}
6121d97e97dSmglocker }
6131d97e97dSmglocker 
6141d97e97dSmglocker int
upgt_fw_verify(struct upgt_softc * sc)6151d97e97dSmglocker upgt_fw_verify(struct upgt_softc *sc)
6161d97e97dSmglocker {
6171d97e97dSmglocker 	struct upgt_fw_bra_option *bra_option;
6181d97e97dSmglocker 	uint32_t bra_option_type, bra_option_len;
6191d97e97dSmglocker 	uint32_t *uc;
6201d97e97dSmglocker 	int offset, bra_end = 0;
6211d97e97dSmglocker 
6221d97e97dSmglocker 	/*
6231d97e97dSmglocker 	 * Seek to beginning of Boot Record Area (BRA).
6241d97e97dSmglocker 	 */
625b19eaa53Smglocker 	for (offset = 0; offset < sc->sc_fw_size; offset += sizeof(*uc)) {
6261d97e97dSmglocker 		uc = (uint32_t *)(sc->sc_fw + offset);
6271d97e97dSmglocker 		if (*uc == 0)
6281d97e97dSmglocker 			break;
6291d97e97dSmglocker 	}
630b19eaa53Smglocker 	for (; offset < sc->sc_fw_size; offset += sizeof(*uc)) {
6311d97e97dSmglocker 		uc = (uint32_t *)(sc->sc_fw + offset);
6321d97e97dSmglocker 		if (*uc != 0)
6331d97e97dSmglocker 			break;
6341d97e97dSmglocker 	}
6351d97e97dSmglocker 	if (offset == sc->sc_fw_size) {
6361d97e97dSmglocker 		printf("%s: firmware Boot Record Area not found!\n",
6371d97e97dSmglocker 		    sc->sc_dev.dv_xname);
6381d97e97dSmglocker 		return (EIO);
6391d97e97dSmglocker 	}
6401d97e97dSmglocker 	DPRINTF(1, "%s: firmware Boot Record Area found at offset %d\n",
6411d97e97dSmglocker 	    sc->sc_dev.dv_xname, offset);
6421d97e97dSmglocker 
6431d97e97dSmglocker 	/*
6441d97e97dSmglocker 	 * Parse Boot Record Area (BRA) options.
6451d97e97dSmglocker 	 */
6461d97e97dSmglocker 	while (offset < sc->sc_fw_size && bra_end == 0) {
6471d97e97dSmglocker 		/* get current BRA option */
6481d97e97dSmglocker 		bra_option = (struct upgt_fw_bra_option *)(sc->sc_fw + offset);
6491d97e97dSmglocker 		bra_option_type = letoh32(bra_option->type);
650b19eaa53Smglocker 		bra_option_len = letoh32(bra_option->len) * sizeof(*uc);
6511d97e97dSmglocker 
6521d97e97dSmglocker 		switch (bra_option_type) {
6531d97e97dSmglocker 		case UPGT_BRA_TYPE_FW:
6541d97e97dSmglocker 			DPRINTF(1, "%s: UPGT_BRA_TYPE_FW len=%d\n",
6551d97e97dSmglocker 			    sc->sc_dev.dv_xname, bra_option_len);
6561d97e97dSmglocker 
6571d97e97dSmglocker 			if (bra_option_len != UPGT_BRA_FWTYPE_SIZE) {
6581d97e97dSmglocker 				printf("%s: wrong UPGT_BRA_TYPE_FW len!\n",
6591d97e97dSmglocker 				    sc->sc_dev.dv_xname);
6601d97e97dSmglocker 				return (EIO);
6611d97e97dSmglocker 			}
6621d97e97dSmglocker 			if (memcmp(UPGT_BRA_FWTYPE_LM86, bra_option->data,
6631d97e97dSmglocker 			    bra_option_len) == 0) {
6641d97e97dSmglocker 				sc->sc_fw_type = UPGT_FWTYPE_LM86;
6651d97e97dSmglocker 				break;
6661d97e97dSmglocker 			}
6671d97e97dSmglocker 			if (memcmp(UPGT_BRA_FWTYPE_LM87, bra_option->data,
6681d97e97dSmglocker 			    bra_option_len) == 0) {
6691d97e97dSmglocker 				sc->sc_fw_type = UPGT_FWTYPE_LM87;
6701d97e97dSmglocker 				break;
6711d97e97dSmglocker 			}
6721d97e97dSmglocker 			if (memcmp(UPGT_BRA_FWTYPE_FMAC, bra_option->data,
6731d97e97dSmglocker 			    bra_option_len) == 0) {
6741d97e97dSmglocker 				sc->sc_fw_type = UPGT_FWTYPE_FMAC;
6751d97e97dSmglocker 				break;
6761d97e97dSmglocker 			}
6771d97e97dSmglocker 			printf("%s: unsupported firmware type!\n",
6781d97e97dSmglocker 			    sc->sc_dev.dv_xname);
6791d97e97dSmglocker 			return (EIO);
6801d97e97dSmglocker 		case UPGT_BRA_TYPE_VERSION:
6811d97e97dSmglocker 			DPRINTF(1, "%s: UPGT_BRA_TYPE_VERSION len=%d\n",
6821d97e97dSmglocker 			    sc->sc_dev.dv_xname, bra_option_len);
6831d97e97dSmglocker 			break;
6841d97e97dSmglocker 		case UPGT_BRA_TYPE_DEPIF:
6851d97e97dSmglocker 			DPRINTF(1, "%s: UPGT_BRA_TYPE_DEPIF len=%d\n",
6861d97e97dSmglocker 			    sc->sc_dev.dv_xname, bra_option_len);
6871d97e97dSmglocker 			break;
6881d97e97dSmglocker 		case UPGT_BRA_TYPE_EXPIF:
6891d97e97dSmglocker 			DPRINTF(1, "%s: UPGT_BRA_TYPE_EXPIF len=%d\n",
6901d97e97dSmglocker 			    sc->sc_dev.dv_xname, bra_option_len);
6911d97e97dSmglocker 			break;
6921d97e97dSmglocker 		case UPGT_BRA_TYPE_DESCR:
6931d97e97dSmglocker 			DPRINTF(1, "%s: UPGT_BRA_TYPE_DESCR len=%d\n",
6941d97e97dSmglocker 			    sc->sc_dev.dv_xname, bra_option_len);
6951d97e97dSmglocker 
6961d97e97dSmglocker 			struct upgt_fw_bra_descr *descr =
6971d97e97dSmglocker 				(struct upgt_fw_bra_descr *)bra_option->data;
6981d97e97dSmglocker 
6991d97e97dSmglocker 			sc->sc_memaddr_frame_start =
7001d97e97dSmglocker 			    letoh32(descr->memaddr_space_start);
7011d97e97dSmglocker 			sc->sc_memaddr_frame_end =
7021d97e97dSmglocker 			    letoh32(descr->memaddr_space_end);
7031d97e97dSmglocker 
7041d97e97dSmglocker 			DPRINTF(2, "%s: memory address space start=0x%08x\n",
7051d97e97dSmglocker 			    sc->sc_dev.dv_xname, sc->sc_memaddr_frame_start);
7061d97e97dSmglocker 			DPRINTF(2, "%s: memory address space end=0x%08x\n",
7071d97e97dSmglocker 			    sc->sc_dev.dv_xname, sc->sc_memaddr_frame_end);
7081d97e97dSmglocker 			break;
7091d97e97dSmglocker 		case UPGT_BRA_TYPE_END:
7101d97e97dSmglocker 			DPRINTF(1, "%s: UPGT_BRA_TYPE_END len=%d\n",
7111d97e97dSmglocker 			    sc->sc_dev.dv_xname, bra_option_len);
7121d97e97dSmglocker 			bra_end = 1;
7131d97e97dSmglocker 			break;
7141d97e97dSmglocker 		default:
7151d97e97dSmglocker 			DPRINTF(1, "%s: unknown BRA option len=%d\n",
7161d97e97dSmglocker 			    sc->sc_dev.dv_xname, bra_option_len);
7171d97e97dSmglocker 			return (EIO);
7181d97e97dSmglocker 		}
7191d97e97dSmglocker 
7201d97e97dSmglocker 		/* jump to next BRA option */
7211d97e97dSmglocker 		offset += sizeof(struct upgt_fw_bra_option) + bra_option_len;
7221d97e97dSmglocker 	}
7231d97e97dSmglocker 
7241d97e97dSmglocker 	DPRINTF(1, "%s: firmware verified\n", sc->sc_dev.dv_xname);
7251d97e97dSmglocker 
7261d97e97dSmglocker 	return (0);
7271d97e97dSmglocker }
7281d97e97dSmglocker 
7291d97e97dSmglocker int
upgt_fw_load(struct upgt_softc * sc)7301d97e97dSmglocker upgt_fw_load(struct upgt_softc *sc)
7311d97e97dSmglocker {
7321d97e97dSmglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
7331d97e97dSmglocker 	struct upgt_data *data_rx = &sc->rx_data;
7341d97e97dSmglocker 	char start_fwload_cmd[] = { 0x3c, 0x0d };
7351d97e97dSmglocker 	int offset, bsize, n, i, len;
7361d97e97dSmglocker 	uint32_t crc32;
7371d97e97dSmglocker 
7381d97e97dSmglocker 	/* send firmware start load command */
7391d97e97dSmglocker 	len = sizeof(start_fwload_cmd);
7401d97e97dSmglocker 	bcopy(start_fwload_cmd, data_cmd->buf, len);
7411d97e97dSmglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
7421d97e97dSmglocker 		printf("%s: could not send start_firmware_load command!\n",
7431d97e97dSmglocker 		    sc->sc_dev.dv_xname);
7441d97e97dSmglocker 		return (EIO);
7451d97e97dSmglocker 	}
7461d97e97dSmglocker 
7471d97e97dSmglocker 	/* send X2 header */
7481d97e97dSmglocker 	len = sizeof(struct upgt_fw_x2_header);
7491d97e97dSmglocker 	struct upgt_fw_x2_header *x2 = data_cmd->buf;
7501d97e97dSmglocker 	bcopy(UPGT_X2_SIGNATURE, x2->signature, UPGT_X2_SIGNATURE_SIZE);
7511d97e97dSmglocker 	x2->startaddr = htole32(UPGT_MEMADDR_FIRMWARE_START);
7521d97e97dSmglocker 	x2->len = htole32(sc->sc_fw_size);
753f3a6740aSmglocker 	x2->crc = upgt_crc32_le(data_cmd->buf + UPGT_X2_SIGNATURE_SIZE,
7541d97e97dSmglocker 	    sizeof(struct upgt_fw_x2_header) - UPGT_X2_SIGNATURE_SIZE -
755f3a6740aSmglocker 	    sizeof(uint32_t));
7561d97e97dSmglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
7571d97e97dSmglocker 		printf("%s: could not send firmware X2 header!\n",
7581d97e97dSmglocker 		    sc->sc_dev.dv_xname);
7591d97e97dSmglocker 		return (EIO);
7601d97e97dSmglocker 	}
7611d97e97dSmglocker 
7621d97e97dSmglocker 	/* download firmware */
7631d97e97dSmglocker 	for (offset = 0; offset < sc->sc_fw_size; offset += bsize) {
7641d97e97dSmglocker 		if (sc->sc_fw_size - offset > UPGT_FW_BLOCK_SIZE)
7651d97e97dSmglocker 			bsize = UPGT_FW_BLOCK_SIZE;
7661d97e97dSmglocker 		else
7671d97e97dSmglocker 			bsize = sc->sc_fw_size - offset;
7681d97e97dSmglocker 
7691d97e97dSmglocker 		n = upgt_fw_copy(sc->sc_fw + offset, data_cmd->buf, bsize);
7701d97e97dSmglocker 
7711d97e97dSmglocker 		DPRINTF(1, "%s: FW offset=%d, read=%d, sent=%d\n",
7721d97e97dSmglocker 		    sc->sc_dev.dv_xname, offset, n, bsize);
7731d97e97dSmglocker 
7741d97e97dSmglocker 		if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &bsize, 0)
7751d97e97dSmglocker 		    != 0) {
7761d97e97dSmglocker 			printf("%s: error while downloading firmware block!\n",
7771d97e97dSmglocker 			    sc->sc_dev.dv_xname);
7781d97e97dSmglocker 			return (EIO);
7791d97e97dSmglocker 		}
7801d97e97dSmglocker 
7811d97e97dSmglocker 		bsize = n;
7821d97e97dSmglocker 	}
7831d97e97dSmglocker 	DPRINTF(1, "%s: firmware downloaded\n", sc->sc_dev.dv_xname);
7841d97e97dSmglocker 
7851d97e97dSmglocker 	/* load firmware */
786f3a6740aSmglocker 	crc32 = upgt_crc32_le(sc->sc_fw, sc->sc_fw_size);
7871d97e97dSmglocker 	*((uint32_t *)(data_cmd->buf)    ) = crc32;
7881d97e97dSmglocker 	*((uint8_t  *)(data_cmd->buf) + 4) = 'g';
7891d97e97dSmglocker 	*((uint8_t  *)(data_cmd->buf) + 5) = '\r';
7901d97e97dSmglocker 	len = 6;
7911d97e97dSmglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
7921d97e97dSmglocker 		printf("%s: could not send load_firmware command!\n",
7931d97e97dSmglocker 		    sc->sc_dev.dv_xname);
7941d97e97dSmglocker 		return (EIO);
7951d97e97dSmglocker 	}
7961d97e97dSmglocker 
7971d97e97dSmglocker 	for (i = 0; i < UPGT_FIRMWARE_TIMEOUT; i++) {
7981d97e97dSmglocker 		len = UPGT_FW_BLOCK_SIZE;
7991d97e97dSmglocker 		bzero(data_rx->buf, MCLBYTES);
8001d97e97dSmglocker 		if (upgt_bulk_xmit(sc, data_rx, sc->sc_rx_pipeh, &len,
8011d97e97dSmglocker 		    USBD_SHORT_XFER_OK) != 0) {
8021d97e97dSmglocker 			printf("%s: could not read firmware response!\n",
8031d97e97dSmglocker 			    sc->sc_dev.dv_xname);
8041d97e97dSmglocker 			return (EIO);
8051d97e97dSmglocker 		}
8061d97e97dSmglocker 
8071d97e97dSmglocker 		if (memcmp(data_rx->buf, "OK", 2) == 0)
8081d97e97dSmglocker 			break;	/* firmware load was successful */
8091d97e97dSmglocker 	}
8101d97e97dSmglocker 	if (i == UPGT_FIRMWARE_TIMEOUT) {
8111d97e97dSmglocker 		printf("%s: firmware load failed!\n", sc->sc_dev.dv_xname);
8121d97e97dSmglocker 		return (EIO);
8131d97e97dSmglocker 	}
8141d97e97dSmglocker 	DPRINTF(1, "%s: firmware loaded\n", sc->sc_dev.dv_xname);
8151d97e97dSmglocker 
8161d97e97dSmglocker 	return (0);
8171d97e97dSmglocker }
8181d97e97dSmglocker 
8191d97e97dSmglocker /*
8201d97e97dSmglocker  * While copying the version 2 firmware, we need to replace two characters:
8211d97e97dSmglocker  *
8221d97e97dSmglocker  * 0x7e -> 0x7d 0x5e
8231d97e97dSmglocker  * 0x7d -> 0x7d 0x5d
8241d97e97dSmglocker  */
8251d97e97dSmglocker int
upgt_fw_copy(char * src,char * dst,int size)8261d97e97dSmglocker upgt_fw_copy(char *src, char *dst, int size)
8271d97e97dSmglocker {
8281d97e97dSmglocker 	int i, j;
8291d97e97dSmglocker 
8301d97e97dSmglocker 	for (i = 0, j = 0; i < size && j < size; i++) {
8311d97e97dSmglocker 		switch (src[i]) {
8321d97e97dSmglocker 		case 0x7e:
8331d97e97dSmglocker 			dst[j] = 0x7d;
8341d97e97dSmglocker 			j++;
8351d97e97dSmglocker 			dst[j] = 0x5e;
8361d97e97dSmglocker 			j++;
8371d97e97dSmglocker 			break;
8381d97e97dSmglocker 		case 0x7d:
8391d97e97dSmglocker 			dst[j] = 0x7d;
8401d97e97dSmglocker 			j++;
8411d97e97dSmglocker 			dst[j] = 0x5d;
8421d97e97dSmglocker 			j++;
8431d97e97dSmglocker 			break;
8441d97e97dSmglocker 		default:
8451d97e97dSmglocker 			dst[j] = src[i];
8461d97e97dSmglocker 			j++;
8471d97e97dSmglocker 			break;
8481d97e97dSmglocker 		}
8491d97e97dSmglocker 	}
8501d97e97dSmglocker 
8511d97e97dSmglocker 	return (i);
8521d97e97dSmglocker }
8531d97e97dSmglocker 
8541d97e97dSmglocker int
upgt_eeprom_read(struct upgt_softc * sc)8551d97e97dSmglocker upgt_eeprom_read(struct upgt_softc *sc)
8561d97e97dSmglocker {
8571d97e97dSmglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
858836f404eSmglocker 	struct upgt_lmac_mem *mem;
8591d97e97dSmglocker 	struct upgt_lmac_eeprom	*eeprom;
8601d97e97dSmglocker 	int offset, block, len;
8611d97e97dSmglocker 
862772b9d72Smglocker 	offset = 0;
8631d97e97dSmglocker 	block = UPGT_EEPROM_BLOCK_SIZE;
864772b9d72Smglocker 	while (offset < UPGT_EEPROM_SIZE) {
8651d97e97dSmglocker 		DPRINTF(1, "%s: request EEPROM block (offset=%d, len=%d)\n",
8661d97e97dSmglocker 		    sc->sc_dev.dv_xname, offset, block);
8671d97e97dSmglocker 
8681d97e97dSmglocker 		/*
869836f404eSmglocker 		 * Transmit the URB containing the CMD data.
8701d97e97dSmglocker 		 */
871836f404eSmglocker 		bzero(data_cmd->buf, MCLBYTES);
872836f404eSmglocker 
873836f404eSmglocker 		mem = (struct upgt_lmac_mem *)data_cmd->buf;
874836f404eSmglocker 		mem->addr = htole32(sc->sc_memaddr_frame_start +
8751d97e97dSmglocker 		    UPGT_MEMSIZE_FRAME_HEAD);
8761d97e97dSmglocker 
877836f404eSmglocker 		eeprom = (struct upgt_lmac_eeprom *)(mem + 1);
8781d97e97dSmglocker 		eeprom->header1.flags = 0;
8791d97e97dSmglocker 		eeprom->header1.type = UPGT_H1_TYPE_CTRL;
8801d97e97dSmglocker 		eeprom->header1.len = htole16((
8811d97e97dSmglocker 		    sizeof(struct upgt_lmac_eeprom) -
8821d97e97dSmglocker 		    sizeof(struct upgt_lmac_header)) + block);
8831d97e97dSmglocker 
8841d97e97dSmglocker 		eeprom->header2.reqid = htole32(sc->sc_memaddr_frame_start);
8851d97e97dSmglocker 		eeprom->header2.type = htole16(UPGT_H2_TYPE_EEPROM);
8861d97e97dSmglocker 		eeprom->header2.flags = 0;
8871d97e97dSmglocker 
8881d97e97dSmglocker 		eeprom->offset = htole16(offset);
8891d97e97dSmglocker 		eeprom->len = htole16(block);
8901d97e97dSmglocker 
891836f404eSmglocker 		len = sizeof(*mem) + sizeof(*eeprom) + block;
892836f404eSmglocker 
893f3a6740aSmglocker 		mem->chksum = upgt_chksum_le((uint32_t *)eeprom,
894f3a6740aSmglocker 		    len - sizeof(*mem));
895836f404eSmglocker 
896836f404eSmglocker 		if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len,
897836f404eSmglocker 		    USBD_FORCE_SHORT_XFER) != 0) {
8981d97e97dSmglocker 			printf("%s: could not transmit EEPROM data URB!\n",
8991d97e97dSmglocker 			    sc->sc_dev.dv_xname);
9001d97e97dSmglocker 			return (EIO);
9011d97e97dSmglocker 		}
902a94d7c77Smpi 		if (tsleep_nsec(sc, 0, "eeprom_request",
903a94d7c77Smpi 		    MSEC_TO_NSEC(UPGT_USB_TIMEOUT))) {
9041d97e97dSmglocker 			printf("%s: timeout while waiting for EEPROM data!\n",
9051d97e97dSmglocker 			    sc->sc_dev.dv_xname);
9061d97e97dSmglocker 			return (EIO);
9071d97e97dSmglocker 		}
9081d97e97dSmglocker 
909772b9d72Smglocker 		offset += block;
910772b9d72Smglocker 		if (UPGT_EEPROM_SIZE - offset < block)
911772b9d72Smglocker 			block = UPGT_EEPROM_SIZE - offset;
9121d97e97dSmglocker 	}
9131d97e97dSmglocker 
9141d97e97dSmglocker 	return (0);
9151d97e97dSmglocker }
9161d97e97dSmglocker 
9171d97e97dSmglocker int
upgt_eeprom_parse(struct upgt_softc * sc)9181d97e97dSmglocker upgt_eeprom_parse(struct upgt_softc *sc)
9191d97e97dSmglocker {
9201d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
9211d97e97dSmglocker 	struct upgt_eeprom_header *eeprom_header;
9221d97e97dSmglocker 	struct upgt_eeprom_option *eeprom_option;
9231d97e97dSmglocker 	uint16_t option_len;
9241d97e97dSmglocker 	uint16_t option_type;
9251d97e97dSmglocker 	uint16_t preamble_len;
9261d97e97dSmglocker 	int option_end = 0;
9271d97e97dSmglocker 
9281d97e97dSmglocker 	/* calculate eeprom options start offset */
9291d97e97dSmglocker 	eeprom_header = (struct upgt_eeprom_header *)sc->sc_eeprom;
9301d97e97dSmglocker 	preamble_len = letoh16(eeprom_header->preamble_len);
9311d97e97dSmglocker 	eeprom_option = (struct upgt_eeprom_option *)(sc->sc_eeprom +
9321d97e97dSmglocker 	    (sizeof(struct upgt_eeprom_header) + preamble_len));
9331d97e97dSmglocker 
9341d97e97dSmglocker 	while (!option_end) {
9351d97e97dSmglocker 		/* the eeprom option length is stored in words */
9361d97e97dSmglocker 		option_len =
9371d97e97dSmglocker 		    (letoh16(eeprom_option->len) - 1) * sizeof(uint16_t);
9381d97e97dSmglocker 		option_type =
9391d97e97dSmglocker 		    letoh16(eeprom_option->type);
9401d97e97dSmglocker 
9411d97e97dSmglocker 		switch (option_type) {
9421d97e97dSmglocker 		case UPGT_EEPROM_TYPE_NAME:
9431d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM name len=%d\n",
9441d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9451d97e97dSmglocker 			break;
9461d97e97dSmglocker 		case UPGT_EEPROM_TYPE_SERIAL:
9471d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM serial len=%d\n",
9481d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9491d97e97dSmglocker 			break;
9501d97e97dSmglocker 		case UPGT_EEPROM_TYPE_MAC:
9511d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM mac len=%d\n",
9521d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9531d97e97dSmglocker 
9541d97e97dSmglocker 			IEEE80211_ADDR_COPY(ic->ic_myaddr, eeprom_option->data);
9551d97e97dSmglocker 			break;
9561d97e97dSmglocker 		case UPGT_EEPROM_TYPE_HWRX:
9571d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM hwrx len=%d\n",
9581d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9591d97e97dSmglocker 
9601d97e97dSmglocker 			upgt_eeprom_parse_hwrx(sc, eeprom_option->data);
9611d97e97dSmglocker 			break;
9621d97e97dSmglocker 		case UPGT_EEPROM_TYPE_CHIP:
9631d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM chip len=%d\n",
9641d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9651d97e97dSmglocker 			break;
9661d97e97dSmglocker 		case UPGT_EEPROM_TYPE_FREQ3:
9671d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM freq3 len=%d\n",
9681d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9691d97e97dSmglocker 
9701d97e97dSmglocker 			upgt_eeprom_parse_freq3(sc, eeprom_option->data,
9711d97e97dSmglocker 			    option_len);
9721d97e97dSmglocker 			break;
9731d97e97dSmglocker 		case UPGT_EEPROM_TYPE_FREQ4:
9741d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM freq4 len=%d\n",
9751d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9761d97e97dSmglocker 
9771d97e97dSmglocker 			upgt_eeprom_parse_freq4(sc, eeprom_option->data,
9781d97e97dSmglocker 			    option_len);
9791d97e97dSmglocker 			break;
9801d97e97dSmglocker 		case UPGT_EEPROM_TYPE_FREQ5:
9811d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM freq5 len=%d\n",
9821d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9831d97e97dSmglocker 			break;
9841d97e97dSmglocker 		case UPGT_EEPROM_TYPE_FREQ6:
9851d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM freq6 len=%d\n",
9861d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9871d97e97dSmglocker 
9881d97e97dSmglocker 			upgt_eeprom_parse_freq6(sc, eeprom_option->data,
9891d97e97dSmglocker 			    option_len);
9901d97e97dSmglocker 			break;
9911d97e97dSmglocker 		case UPGT_EEPROM_TYPE_END:
9921d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM end len=%d\n",
9931d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_len);
9941d97e97dSmglocker 			option_end = 1;
9951d97e97dSmglocker 			break;
9961d97e97dSmglocker 		case UPGT_EEPROM_TYPE_OFF:
9971d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM off without end option!\n",
9981d97e97dSmglocker 			    sc->sc_dev.dv_xname);
9991d97e97dSmglocker 			return (EIO);
10001d97e97dSmglocker 		default:
10011d97e97dSmglocker 			DPRINTF(1, "%s: EEPROM unknown type 0x%04x len=%d\n",
10021d97e97dSmglocker 			    sc->sc_dev.dv_xname, option_type, option_len);
10031d97e97dSmglocker 			break;
10041d97e97dSmglocker 		}
10051d97e97dSmglocker 
10061d97e97dSmglocker 		/* jump to next EEPROM option */
10071d97e97dSmglocker 		eeprom_option = (struct upgt_eeprom_option *)
10081d97e97dSmglocker 		    (eeprom_option->data + option_len);
10091d97e97dSmglocker 	}
10101d97e97dSmglocker 
10111d97e97dSmglocker 	return (0);
10121d97e97dSmglocker }
10131d97e97dSmglocker 
10141d97e97dSmglocker void
upgt_eeprom_parse_hwrx(struct upgt_softc * sc,uint8_t * data)10151d97e97dSmglocker upgt_eeprom_parse_hwrx(struct upgt_softc *sc, uint8_t *data)
10161d97e97dSmglocker {
10171d97e97dSmglocker 	struct upgt_eeprom_option_hwrx *option_hwrx;
10181d97e97dSmglocker 
10191d97e97dSmglocker 	option_hwrx = (struct upgt_eeprom_option_hwrx *)data;
10201d97e97dSmglocker 
10211d97e97dSmglocker 	sc->sc_eeprom_hwrx = option_hwrx->rxfilter - UPGT_EEPROM_RX_CONST;
10221d97e97dSmglocker 
10231d97e97dSmglocker 	DPRINTF(2, "%s: hwrx option value=0x%04x\n",
10241d97e97dSmglocker 	    sc->sc_dev.dv_xname, sc->sc_eeprom_hwrx);
10251d97e97dSmglocker }
10261d97e97dSmglocker 
10271d97e97dSmglocker void
upgt_eeprom_parse_freq3(struct upgt_softc * sc,uint8_t * data,int len)10281d97e97dSmglocker upgt_eeprom_parse_freq3(struct upgt_softc *sc, uint8_t *data, int len)
10291d97e97dSmglocker {
10301d97e97dSmglocker 	struct upgt_eeprom_freq3_header *freq3_header;
10311d97e97dSmglocker 	struct upgt_lmac_freq3 *freq3;
10321d97e97dSmglocker 	int i, elements, flags;
10331d97e97dSmglocker 	unsigned channel;
10341d97e97dSmglocker 
10351d97e97dSmglocker 	freq3_header = (struct upgt_eeprom_freq3_header *)data;
10361d97e97dSmglocker 	freq3 = (struct upgt_lmac_freq3 *)(freq3_header + 1);
10371d97e97dSmglocker 
10381d97e97dSmglocker 	flags = freq3_header->flags;
10391d97e97dSmglocker 	elements = freq3_header->elements;
10401d97e97dSmglocker 
10411d97e97dSmglocker 	DPRINTF(2, "%s: flags=0x%02x\n", sc->sc_dev.dv_xname, flags);
10421d97e97dSmglocker 	DPRINTF(2, "%s: elements=%d\n", sc->sc_dev.dv_xname, elements);
10431d97e97dSmglocker 
10441d97e97dSmglocker 	for (i = 0; i < elements; i++) {
10451d97e97dSmglocker 		channel = ieee80211_mhz2ieee(letoh16(freq3[i].freq), 0);
10461d97e97dSmglocker 
10471d97e97dSmglocker 		sc->sc_eeprom_freq3[channel] = freq3[i];
10481d97e97dSmglocker 
10494b1a56afSjsg 		DPRINTF(2, "%s: frequency=%d, channel=%d\n",
10501d97e97dSmglocker 		    sc->sc_dev.dv_xname,
10511d97e97dSmglocker 		    letoh16(sc->sc_eeprom_freq3[channel].freq), channel);
10521d97e97dSmglocker 	}
10531d97e97dSmglocker }
10541d97e97dSmglocker 
10551d97e97dSmglocker void
upgt_eeprom_parse_freq4(struct upgt_softc * sc,uint8_t * data,int len)10561d97e97dSmglocker upgt_eeprom_parse_freq4(struct upgt_softc *sc, uint8_t *data, int len)
10571d97e97dSmglocker {
10581d97e97dSmglocker 	struct upgt_eeprom_freq4_header *freq4_header;
10591d97e97dSmglocker 	struct upgt_eeprom_freq4_1 *freq4_1;
10601d97e97dSmglocker 	struct upgt_eeprom_freq4_2 *freq4_2;
10611d97e97dSmglocker 	int i, j, elements, settings, flags;
10621d97e97dSmglocker 	unsigned channel;
10631d97e97dSmglocker 
10641d97e97dSmglocker 	freq4_header = (struct upgt_eeprom_freq4_header *)data;
10651d97e97dSmglocker 	freq4_1 = (struct upgt_eeprom_freq4_1 *)(freq4_header + 1);
10661d97e97dSmglocker 
10671d97e97dSmglocker 	flags = freq4_header->flags;
10681d97e97dSmglocker 	elements = freq4_header->elements;
10691d97e97dSmglocker 	settings = freq4_header->settings;
10701d97e97dSmglocker 
10711d97e97dSmglocker 	/* we need this value later */
10721d97e97dSmglocker 	sc->sc_eeprom_freq6_settings = freq4_header->settings;
10731d97e97dSmglocker 
10741d97e97dSmglocker 	DPRINTF(2, "%s: flags=0x%02x\n", sc->sc_dev.dv_xname, flags);
10751d97e97dSmglocker 	DPRINTF(2, "%s: elements=%d\n", sc->sc_dev.dv_xname, elements);
10761d97e97dSmglocker 	DPRINTF(2, "%s: settings=%d\n", sc->sc_dev.dv_xname, settings);
10771d97e97dSmglocker 
10781d97e97dSmglocker 	for (i = 0; i < elements; i++) {
10791d97e97dSmglocker 		channel = ieee80211_mhz2ieee(letoh16(freq4_1[i].freq), 0);
10801d97e97dSmglocker 
10811d97e97dSmglocker 		freq4_2 = (struct upgt_eeprom_freq4_2 *)freq4_1[i].data;
10821d97e97dSmglocker 
10831d97e97dSmglocker 		for (j = 0; j < settings; j++) {
10841d97e97dSmglocker 			sc->sc_eeprom_freq4[channel][j].cmd = freq4_2[j];
10851d97e97dSmglocker 			sc->sc_eeprom_freq4[channel][j].pad = 0;
10861d97e97dSmglocker 		}
10871d97e97dSmglocker 
10884b1a56afSjsg 		DPRINTF(2, "%s: frequency=%d, channel=%d\n",
10891d97e97dSmglocker 		    sc->sc_dev.dv_xname,
10901d97e97dSmglocker 		    letoh16(freq4_1[i].freq), channel);
10911d97e97dSmglocker 	}
10921d97e97dSmglocker }
10931d97e97dSmglocker 
10941d97e97dSmglocker void
upgt_eeprom_parse_freq6(struct upgt_softc * sc,uint8_t * data,int len)10951d97e97dSmglocker upgt_eeprom_parse_freq6(struct upgt_softc *sc, uint8_t *data, int len)
10961d97e97dSmglocker {
10971d97e97dSmglocker 	struct upgt_lmac_freq6 *freq6;
10981d97e97dSmglocker 	int i, elements;
10991d97e97dSmglocker 	unsigned channel;
11001d97e97dSmglocker 
11011d97e97dSmglocker 	freq6 = (struct upgt_lmac_freq6 *)data;
11021d97e97dSmglocker 
11031d97e97dSmglocker 	elements = len / sizeof(struct upgt_lmac_freq6);
11041d97e97dSmglocker 
11051d97e97dSmglocker 	DPRINTF(2, "%s: elements=%d\n", sc->sc_dev.dv_xname, elements);
11061d97e97dSmglocker 
11071d97e97dSmglocker 	for (i = 0; i < elements; i++) {
11081d97e97dSmglocker 		channel = ieee80211_mhz2ieee(letoh16(freq6[i].freq), 0);
11091d97e97dSmglocker 
11101d97e97dSmglocker 		sc->sc_eeprom_freq6[channel] = freq6[i];
11111d97e97dSmglocker 
11124b1a56afSjsg 		DPRINTF(2, "%s: frequency=%d, channel=%d\n",
11131d97e97dSmglocker 		    sc->sc_dev.dv_xname,
11141d97e97dSmglocker 		    letoh16(sc->sc_eeprom_freq6[channel].freq), channel);
11151d97e97dSmglocker 	}
11161d97e97dSmglocker }
11171d97e97dSmglocker 
11181d97e97dSmglocker int
upgt_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)11191d97e97dSmglocker upgt_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
11201d97e97dSmglocker {
11211d97e97dSmglocker 	struct upgt_softc *sc = ifp->if_softc;
11221d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
11231d97e97dSmglocker 	int s, error = 0;
112437df8618Smglocker 	uint8_t chan;
11251d97e97dSmglocker 
11261d97e97dSmglocker 	s = splnet();
11271d97e97dSmglocker 
11281d97e97dSmglocker 	switch (cmd) {
11291d97e97dSmglocker 	case SIOCSIFADDR:
11301d97e97dSmglocker 		ifp->if_flags |= IFF_UP;
11311d97e97dSmglocker 		/* FALLTHROUGH */
11321d97e97dSmglocker 	case SIOCSIFFLAGS:
11331d97e97dSmglocker 		if (ifp->if_flags & IFF_UP) {
11341d97e97dSmglocker 			if ((ifp->if_flags & IFF_RUNNING) == 0)
11351d97e97dSmglocker 				upgt_init(ifp);
11361d97e97dSmglocker 		} else {
11371d97e97dSmglocker 			if (ifp->if_flags & IFF_RUNNING)
11381d97e97dSmglocker 				upgt_stop(sc);
11391d97e97dSmglocker 		}
11401d97e97dSmglocker 		break;
114137df8618Smglocker 	case SIOCS80211CHANNEL:
114237df8618Smglocker 		/* allow fast channel switching in monitor mode */
114337df8618Smglocker 		error = ieee80211_ioctl(ifp, cmd, data);
114437df8618Smglocker 		if (error == ENETRESET &&
114537df8618Smglocker 		    ic->ic_opmode == IEEE80211_M_MONITOR) {
114637df8618Smglocker 			if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
114737df8618Smglocker 			    (IFF_UP | IFF_RUNNING)) {
114837df8618Smglocker 				ic->ic_bss->ni_chan = ic->ic_ibss_chan;
114937df8618Smglocker 				chan = ieee80211_chan2ieee(ic,
115037df8618Smglocker 				    ic->ic_bss->ni_chan);
115137df8618Smglocker 				upgt_set_channel(sc, chan);
115237df8618Smglocker 			}
115337df8618Smglocker 			error = 0;
115437df8618Smglocker 		}
115537df8618Smglocker 		break;
11561d97e97dSmglocker 	default:
11571d97e97dSmglocker 		error = ieee80211_ioctl(ifp, cmd, data);
11581d97e97dSmglocker 		break;
11591d97e97dSmglocker 	}
11601d97e97dSmglocker 
11611d97e97dSmglocker 	if (error == ENETRESET) {
11621d97e97dSmglocker 		if (ifp->if_flags & (IFF_UP | IFF_RUNNING))
11631d97e97dSmglocker 			upgt_init(ifp);
11641d97e97dSmglocker 		error = 0;
11651d97e97dSmglocker 	}
11661d97e97dSmglocker 
11671d97e97dSmglocker 	splx(s);
11681d97e97dSmglocker 
11691d97e97dSmglocker 	return (error);
11701d97e97dSmglocker }
11711d97e97dSmglocker 
11721d97e97dSmglocker int
upgt_init(struct ifnet * ifp)11731d97e97dSmglocker upgt_init(struct ifnet *ifp)
11741d97e97dSmglocker {
11751d97e97dSmglocker 	struct upgt_softc *sc = ifp->if_softc;
11761d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
11771d97e97dSmglocker 
11781d97e97dSmglocker 	DPRINTF(1, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
11791d97e97dSmglocker 
11801d97e97dSmglocker 	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
11811d97e97dSmglocker 
11821d97e97dSmglocker 	/* select default channel */
11831d97e97dSmglocker 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
11846b9aaf9aSmglocker 	sc->sc_cur_chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
11856b9aaf9aSmglocker 
11866b9aaf9aSmglocker 	/* setup device rates */
11876b9aaf9aSmglocker 	upgt_setup_rates(sc);
11881d97e97dSmglocker 
11891d97e97dSmglocker 	ifp->if_flags |= IFF_RUNNING;
1190de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
11911d97e97dSmglocker 
11924162887eSmglocker 	upgt_set_macfilter(sc, IEEE80211_S_SCAN);
119337df8618Smglocker 
119437df8618Smglocker 	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
119537df8618Smglocker 		upgt_set_channel(sc, sc->sc_cur_chan);
119637df8618Smglocker 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
119737df8618Smglocker 	} else
11981d97e97dSmglocker 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
11991d97e97dSmglocker 
12001d97e97dSmglocker 	return (0);
12011d97e97dSmglocker }
12021d97e97dSmglocker 
12031d97e97dSmglocker void
upgt_stop(struct upgt_softc * sc)12041d97e97dSmglocker upgt_stop(struct upgt_softc *sc)
12051d97e97dSmglocker {
12061d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
12071d97e97dSmglocker 	struct ifnet *ifp = &ic->ic_if;
12081d97e97dSmglocker 
1209cec10f70Smglocker 	DPRINTF(1, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
1210cec10f70Smglocker 
12111d97e97dSmglocker 	/* device down */
12121d97e97dSmglocker 	ifp->if_timer = 0;
1213de6cd8fbSdlg 	ifp->if_flags &= ~IFF_RUNNING;
1214de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
12151d97e97dSmglocker 
12164721c510Sderaadt 	upgt_set_led(sc, UPGT_LED_OFF);
12174721c510Sderaadt 
12181d97e97dSmglocker 	/* change device back to initial state */
12191d97e97dSmglocker 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
12201d97e97dSmglocker }
12211d97e97dSmglocker 
12221d97e97dSmglocker int
upgt_media_change(struct ifnet * ifp)12231d97e97dSmglocker upgt_media_change(struct ifnet *ifp)
12241d97e97dSmglocker {
122537df8618Smglocker 	struct upgt_softc *sc = ifp->if_softc;
12261d97e97dSmglocker 	int error;
12271d97e97dSmglocker 
122837df8618Smglocker 	DPRINTF(1, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
122937df8618Smglocker 
1230d5f45330Smestre 	if ((error = ieee80211_media_change(ifp)) != ENETRESET)
12311d97e97dSmglocker 		return (error);
12321d97e97dSmglocker 
123337df8618Smglocker 	if (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
123437df8618Smglocker 		/* give pending USB transfers a chance to finish */
123537df8618Smglocker 		usbd_delay_ms(sc->sc_udev, 100);
12361d97e97dSmglocker 		upgt_init(ifp);
123737df8618Smglocker 	}
12381d97e97dSmglocker 
12392e342c84Skevlo 	return (error);
12401d97e97dSmglocker }
12411d97e97dSmglocker 
12421d97e97dSmglocker void
upgt_newassoc(struct ieee80211com * ic,struct ieee80211_node * ni,int isnew)12431d97e97dSmglocker upgt_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
12441d97e97dSmglocker {
12451d97e97dSmglocker 	ni->ni_txrate = 0;
12461d97e97dSmglocker }
12471d97e97dSmglocker 
12481d97e97dSmglocker int
upgt_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)12491d97e97dSmglocker upgt_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
12501d97e97dSmglocker {
12511d97e97dSmglocker 	struct upgt_softc *sc = ic->ic_if.if_softc;
12521d97e97dSmglocker 
12531d97e97dSmglocker 	usb_rem_task(sc->sc_udev, &sc->sc_task_newstate);
12541d97e97dSmglocker 	timeout_del(&sc->scan_to);
12551d97e97dSmglocker 
12561d97e97dSmglocker 	/* do it in a process context */
12571d97e97dSmglocker 	sc->sc_state = nstate;
12581d97e97dSmglocker 	sc->sc_arg = arg;
12591d97e97dSmglocker 	usb_add_task(sc->sc_udev, &sc->sc_task_newstate);
12601d97e97dSmglocker 
12611d97e97dSmglocker 	return (0);
12621d97e97dSmglocker }
12631d97e97dSmglocker 
12641d97e97dSmglocker void
upgt_newstate_task(void * arg)12651d97e97dSmglocker upgt_newstate_task(void *arg)
12661d97e97dSmglocker {
12671d97e97dSmglocker 	struct upgt_softc *sc = arg;
12681d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
12691d97e97dSmglocker 	struct ieee80211_node *ni;
12701d97e97dSmglocker 	unsigned channel;
12711d97e97dSmglocker 
12721d97e97dSmglocker 	switch (sc->sc_state) {
12731d97e97dSmglocker 	case IEEE80211_S_INIT:
12741d97e97dSmglocker 		DPRINTF(1, "%s: newstate is IEEE80211_S_INIT\n",
12751d97e97dSmglocker 		    sc->sc_dev.dv_xname);
12764162887eSmglocker 
12774162887eSmglocker 		/* do not accept any frames if the device is down */
12784162887eSmglocker 		upgt_set_macfilter(sc, IEEE80211_S_INIT);
127902f087d8Smglocker 		upgt_set_led(sc, UPGT_LED_OFF);
12801d97e97dSmglocker 		break;
12811d97e97dSmglocker 	case IEEE80211_S_SCAN:
12821d97e97dSmglocker 		DPRINTF(1, "%s: newstate is IEEE80211_S_SCAN\n",
12831d97e97dSmglocker 		    sc->sc_dev.dv_xname);
12841d97e97dSmglocker 
12851d97e97dSmglocker 		channel = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
12861d97e97dSmglocker 		upgt_set_channel(sc, channel);
128736d45870Sblambert 		timeout_add_msec(&sc->scan_to, 200);
12881d97e97dSmglocker 		break;
12891d97e97dSmglocker 	case IEEE80211_S_AUTH:
12901d97e97dSmglocker 		DPRINTF(1, "%s: newstate is IEEE80211_S_AUTH\n",
12911d97e97dSmglocker 		    sc->sc_dev.dv_xname);
12921d97e97dSmglocker 
12931d97e97dSmglocker 		channel = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
12941d97e97dSmglocker 		upgt_set_channel(sc, channel);
12951d97e97dSmglocker 		break;
12961d97e97dSmglocker 	case IEEE80211_S_ASSOC:
12971d97e97dSmglocker 		DPRINTF(1, "%s: newstate is IEEE80211_S_ASSOC\n",
12981d97e97dSmglocker 		    sc->sc_dev.dv_xname);
12991d97e97dSmglocker 		break;
13001d97e97dSmglocker 	case IEEE80211_S_RUN:
13011d97e97dSmglocker 		DPRINTF(1, "%s: newstate is IEEE80211_S_RUN\n",
13021d97e97dSmglocker 		    sc->sc_dev.dv_xname);
13031d97e97dSmglocker 
13041d97e97dSmglocker 		ni = ic->ic_bss;
13051d97e97dSmglocker 
130656007171Smglocker 		/*
130756007171Smglocker 		 * TX rate control is done by the firmware.
130856007171Smglocker 		 * Report the maximum rate which is available therefore.
130956007171Smglocker 		 */
131056007171Smglocker 		ni->ni_txrate = ni->ni_rates.rs_nrates - 1;
131156007171Smglocker 
131237df8618Smglocker 		if (ic->ic_opmode != IEEE80211_M_MONITOR)
13131d97e97dSmglocker 			upgt_set_macfilter(sc, IEEE80211_S_RUN);
131402f087d8Smglocker 		upgt_set_led(sc, UPGT_LED_ON);
13151d97e97dSmglocker 		break;
13161d97e97dSmglocker 	}
13171d97e97dSmglocker 
13181d97e97dSmglocker 	sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
13191d97e97dSmglocker }
13201d97e97dSmglocker 
13211d97e97dSmglocker void
upgt_next_scan(void * arg)13221d97e97dSmglocker upgt_next_scan(void *arg)
13231d97e97dSmglocker {
13241d97e97dSmglocker 	struct upgt_softc *sc = arg;
13251d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
13261d97e97dSmglocker 	struct ifnet *ifp = &ic->ic_if;
13271d97e97dSmglocker 
13281d97e97dSmglocker 	DPRINTF(2, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
13291d97e97dSmglocker 
13301d97e97dSmglocker 	if (ic->ic_state == IEEE80211_S_SCAN)
13311d97e97dSmglocker 		ieee80211_next_scan(ifp);
13321d97e97dSmglocker }
13331d97e97dSmglocker 
13341d97e97dSmglocker void
upgt_start(struct ifnet * ifp)13351d97e97dSmglocker upgt_start(struct ifnet *ifp)
13361d97e97dSmglocker {
13371d97e97dSmglocker 	struct upgt_softc *sc = ifp->if_softc;
13381d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
13391d97e97dSmglocker 	struct ieee80211_node *ni;
13401d97e97dSmglocker 	struct mbuf *m;
13411d97e97dSmglocker 	int i;
13421d97e97dSmglocker 
13431d97e97dSmglocker 	/* don't transmit packets if interface is busy or down */
1344de6cd8fbSdlg 	if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
13451d97e97dSmglocker 		return;
13461d97e97dSmglocker 
13471d97e97dSmglocker 	DPRINTF(2, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
13481d97e97dSmglocker 
13491d97e97dSmglocker 	for (i = 0; i < UPGT_TX_COUNT; i++) {
13501d97e97dSmglocker 		struct upgt_data *data_tx = &sc->tx_data[i];
13511d97e97dSmglocker 
1352351e1934Sdlg 		m = mq_dequeue(&ic->ic_mgtq);
13531d97e97dSmglocker 		if (m != NULL) {
13541d97e97dSmglocker 			/* management frame */
13556da4b19dSmpi 			ni = m->m_pkthdr.ph_cookie;
13561d97e97dSmglocker #if NBPFILTER > 0
13571d97e97dSmglocker 			if (ic->ic_rawbpf != NULL)
13581d97e97dSmglocker 				bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
13591d97e97dSmglocker #endif
13601d97e97dSmglocker 			if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) {
13611d01a6f0Smglocker 				printf("%s: no free prism memory!\n",
13621d97e97dSmglocker 				    sc->sc_dev.dv_xname);
13631d97e97dSmglocker 				return;
13641d97e97dSmglocker 			}
13651d97e97dSmglocker 			data_tx->ni = ni;
13661d97e97dSmglocker 			data_tx->m = m;
13671d97e97dSmglocker 			sc->tx_queued++;
13681d97e97dSmglocker 		} else {
13691d97e97dSmglocker 			/* data frame */
13701d97e97dSmglocker 			if (ic->ic_state != IEEE80211_S_RUN)
13711d97e97dSmglocker 				break;
13721d97e97dSmglocker 
137363bcfa73Spatrick 			m = ifq_dequeue(&ifp->if_snd);
13741d97e97dSmglocker 			if (m == NULL)
13751d97e97dSmglocker 				break;
13761d97e97dSmglocker 
13771d97e97dSmglocker #if NBPFILTER > 0
13781d97e97dSmglocker 			if (ifp->if_bpf != NULL)
13791d97e97dSmglocker 				bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
13801d97e97dSmglocker #endif
13811d97e97dSmglocker 			m = ieee80211_encap(ifp, m, &ni);
13821d97e97dSmglocker 			if (m == NULL)
13831d97e97dSmglocker 				continue;
13841d97e97dSmglocker #if NBPFILTER > 0
13851d97e97dSmglocker 			if (ic->ic_rawbpf != NULL)
13861d97e97dSmglocker 				bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
13871d97e97dSmglocker #endif
13881d97e97dSmglocker 			if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) {
13891d01a6f0Smglocker 				printf("%s: no free prism memory!\n",
13901d97e97dSmglocker 				    sc->sc_dev.dv_xname);
13911d97e97dSmglocker 				return;
13921d97e97dSmglocker 			}
13931d97e97dSmglocker 			data_tx->ni = ni;
13941d97e97dSmglocker 			data_tx->m = m;
13951d97e97dSmglocker 			sc->tx_queued++;
13961d97e97dSmglocker 		}
13971d97e97dSmglocker 	}
13981d97e97dSmglocker 
13991d97e97dSmglocker 	if (sc->tx_queued > 0) {
14001d97e97dSmglocker 		DPRINTF(2, "%s: tx_queued=%d\n",
14011d97e97dSmglocker 		    sc->sc_dev.dv_xname, sc->tx_queued);
14021d97e97dSmglocker 		/* process the TX queue in process context */
14031d97e97dSmglocker 		ifp->if_timer = 5;
1404de6cd8fbSdlg 		ifq_set_oactive(&ifp->if_snd);
14051d01a6f0Smglocker 		usb_rem_task(sc->sc_udev, &sc->sc_task_tx);
14061d97e97dSmglocker 		usb_add_task(sc->sc_udev, &sc->sc_task_tx);
14071d97e97dSmglocker 	}
14081d97e97dSmglocker }
14091d97e97dSmglocker 
14101d97e97dSmglocker void
upgt_watchdog(struct ifnet * ifp)14111d97e97dSmglocker upgt_watchdog(struct ifnet *ifp)
14121d97e97dSmglocker {
141325999c83Smglocker 	struct upgt_softc *sc = ifp->if_softc;
14146eac90d1Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
14156eac90d1Smglocker 
14166eac90d1Smglocker 	if (ic->ic_state == IEEE80211_S_INIT)
14176eac90d1Smglocker 		return;
14181d97e97dSmglocker 
141925999c83Smglocker 	printf("%s: watchdog timeout!\n", sc->sc_dev.dv_xname);
14201d97e97dSmglocker 
14211d97e97dSmglocker 	/* TODO: what shall we do on TX timeout? */
14221d97e97dSmglocker 
14231d97e97dSmglocker 	ieee80211_watchdog(ifp);
14241d97e97dSmglocker }
14251d97e97dSmglocker 
14261d97e97dSmglocker void
upgt_tx_task(void * arg)14271d97e97dSmglocker upgt_tx_task(void *arg)
14281d97e97dSmglocker {
14291d97e97dSmglocker 	struct upgt_softc *sc = arg;
14301d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
14311d97e97dSmglocker 	struct ieee80211_frame *wh;
1432e03e709cSdamien 	struct ieee80211_key *k;
1433836f404eSmglocker 	struct upgt_lmac_mem *mem;
14341d97e97dSmglocker 	struct upgt_lmac_tx_desc *txdesc;
14351d97e97dSmglocker 	struct mbuf *m;
14361d97e97dSmglocker 	uint32_t addr;
1437836f404eSmglocker 	int len, i, s;
1438836f404eSmglocker 	usbd_status error;
1439836f404eSmglocker 
1440836f404eSmglocker 	s = splusb();
14411d97e97dSmglocker 
144202f087d8Smglocker 	upgt_set_led(sc, UPGT_LED_BLINK);
144302f087d8Smglocker 
14441d97e97dSmglocker 	for (i = 0; i < UPGT_TX_COUNT; i++) {
14451d97e97dSmglocker 		struct upgt_data *data_tx = &sc->tx_data[i];
14461d97e97dSmglocker 
14471d01a6f0Smglocker 		if (data_tx->m == NULL) {
1448ccdbc99cSmglocker 			DPRINTF(2, "%s: %d: m is NULL\n",
1449ccdbc99cSmglocker 			    sc->sc_dev.dv_xname, i);
14501d97e97dSmglocker 			continue;
14511d01a6f0Smglocker 		}
14521d97e97dSmglocker 
14531d97e97dSmglocker 		m = data_tx->m;
14541d97e97dSmglocker 		addr = data_tx->addr + UPGT_MEMSIZE_FRAME_HEAD;
14551d97e97dSmglocker 
14561d97e97dSmglocker 		/*
1457e03e709cSdamien 		 * Software crypto.
14581d97e97dSmglocker 		 */
14591d97e97dSmglocker 		wh = mtod(m, struct ieee80211_frame *);
14601d97e97dSmglocker 
1461e03e709cSdamien 		if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
1462e03e709cSdamien 			k = ieee80211_get_txkey(ic, wh, ic->ic_bss);
1463e03e709cSdamien 
1464a8359cd7Sjsg 			if ((m = ieee80211_encrypt(ic, m, k)) == NULL) {
1465a8359cd7Sjsg 				splx(s);
14661d97e97dSmglocker 				return;
1467a8359cd7Sjsg 			}
1468e03e709cSdamien 
14696b9aaf9aSmglocker 			/* in case packet header moved, reset pointer */
14706b9aaf9aSmglocker 			wh = mtod(m, struct ieee80211_frame *);
14711d97e97dSmglocker 		}
14721d97e97dSmglocker 
14731d97e97dSmglocker 		/*
1474836f404eSmglocker 		 * Transmit the URB containing the TX data.
14751d97e97dSmglocker 		 */
1476aeb546b6Smglocker 		bzero(data_tx->buf, MCLBYTES);
1477836f404eSmglocker 
1478836f404eSmglocker 		mem = (struct upgt_lmac_mem *)data_tx->buf;
1479836f404eSmglocker 		mem->addr = htole32(addr);
1480836f404eSmglocker 
1481836f404eSmglocker 		txdesc = (struct upgt_lmac_tx_desc *)(mem + 1);
14821d97e97dSmglocker 
14831d97e97dSmglocker 		/* XXX differ between data and mgmt frames? */
14841d97e97dSmglocker 		txdesc->header1.flags = UPGT_H1_FLAGS_TX_DATA;
14858880a392Smglocker 		txdesc->header1.type = UPGT_H1_TYPE_TX_DATA;
14861d97e97dSmglocker 		txdesc->header1.len = htole16(m->m_pkthdr.len);
14871d97e97dSmglocker 
14881d97e97dSmglocker 		txdesc->header2.reqid = htole32(data_tx->addr);
1489aeb546b6Smglocker 		txdesc->header2.type = htole16(UPGT_H2_TYPE_TX_ACK_YES);
1490aeb546b6Smglocker 		txdesc->header2.flags = htole16(UPGT_H2_FLAGS_TX_ACK_YES);
14911d97e97dSmglocker 
14926b9aaf9aSmglocker 		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
14936b9aaf9aSmglocker 		    IEEE80211_FC0_TYPE_MGT) {
14946b9aaf9aSmglocker 			/* always send mgmt frames at lowest rate (DS1) */
14956b9aaf9aSmglocker 			memset(txdesc->rates, 0x10, sizeof(txdesc->rates));
14966b9aaf9aSmglocker 		} else {
14976b9aaf9aSmglocker 			bcopy(sc->sc_cur_rateset, txdesc->rates,
14986b9aaf9aSmglocker 			    sizeof(txdesc->rates));
14996b9aaf9aSmglocker 		}
15001d97e97dSmglocker 		txdesc->type = htole32(UPGT_TX_DESC_TYPE_DATA);
150126068c4bSmglocker 		txdesc->pad3[0] = UPGT_TX_DESC_PAD3_SIZE;
15021d97e97dSmglocker 
15031d97e97dSmglocker #if NBPFILTER > 0
15041d97e97dSmglocker 		if (sc->sc_drvbpf != NULL) {
15051d97e97dSmglocker 			struct mbuf mb;
15061d97e97dSmglocker 			struct upgt_tx_radiotap_header *tap = &sc->sc_txtap;
15071d97e97dSmglocker 
15081d97e97dSmglocker 			tap->wt_flags = 0;
1509b71d8b8eSmglocker 			tap->wt_rate = 0;	/* TODO: where to get from? */
15101d97e97dSmglocker 			tap->wt_chan_freq =
15111d97e97dSmglocker 			    htole16(ic->ic_bss->ni_chan->ic_freq);
15121d97e97dSmglocker 			tap->wt_chan_flags =
15131d97e97dSmglocker 			    htole16(ic->ic_bss->ni_chan->ic_flags);
15141d97e97dSmglocker 
15151d97e97dSmglocker 			mb.m_data = (caddr_t)tap;
15161d97e97dSmglocker 			mb.m_len = sc->sc_txtap_len;
15171d97e97dSmglocker 			mb.m_next = m;
15181d97e97dSmglocker 			mb.m_nextpkt = NULL;
15191d97e97dSmglocker 			mb.m_type = 0;
15201d97e97dSmglocker 			mb.m_flags = 0;
15211d97e97dSmglocker 			bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
15221d97e97dSmglocker 		}
15231d97e97dSmglocker #endif
15241d97e97dSmglocker 		/* copy frame below our TX descriptor header */
15251d97e97dSmglocker 		m_copydata(m, 0, m->m_pkthdr.len,
1526836f404eSmglocker 		    data_tx->buf + (sizeof(*mem) + sizeof(*txdesc)));
15271d97e97dSmglocker 
1528836f404eSmglocker 		/* calculate frame size */
1529836f404eSmglocker 		len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len;
1530836f404eSmglocker 
1531836f404eSmglocker 		/* we need to align the frame to a 4 byte boundary */
1532836f404eSmglocker 		len = (len + 3) & ~3;
1533836f404eSmglocker 
1534836f404eSmglocker 		/* calculate frame checksum */
1535f3a6740aSmglocker 		mem->chksum = upgt_chksum_le((uint32_t *)txdesc,
1536f3a6740aSmglocker 		    len - sizeof(*mem));
1537ccdbc99cSmglocker 
1538ccdbc99cSmglocker 		/* we do not need the mbuf anymore */
1539ccdbc99cSmglocker 		m_freem(m);
1540ccdbc99cSmglocker 		data_tx->m = NULL;
1541ccdbc99cSmglocker 
1542ccdbc99cSmglocker 		DPRINTF(2, "%s: TX start data sending\n", sc->sc_dev.dv_xname);
1543ccdbc99cSmglocker 
1544836f404eSmglocker 		usbd_setup_xfer(data_tx->xfer, sc->sc_tx_pipeh, data_tx,
1545836f404eSmglocker 		    data_tx->buf, len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
1546836f404eSmglocker 		    UPGT_USB_TIMEOUT, NULL);
1547836f404eSmglocker 		error = usbd_transfer(data_tx->xfer);
1548836f404eSmglocker 		if (error != 0 && error != USBD_IN_PROGRESS) {
15491d97e97dSmglocker 			printf("%s: could not transmit TX data URB!\n",
15501d97e97dSmglocker 			    sc->sc_dev.dv_xname);
1551a8359cd7Sjsg 			splx(s);
15521d97e97dSmglocker 			return;
15531d97e97dSmglocker 		}
15541d97e97dSmglocker 
1555ccdbc99cSmglocker 		DPRINTF(2, "%s: TX sent (%d bytes)\n",
1556ccdbc99cSmglocker 		    sc->sc_dev.dv_xname, len);
15571d97e97dSmglocker 	}
1558836f404eSmglocker 
1559ec72f164Smglocker 	/*
15604b1a56afSjsg 	 * If we don't regularly read the device statistics, the RX queue
1561ec72f164Smglocker 	 * will stall.  It's strange, but it works, so we keep reading
1562ec72f164Smglocker 	 * the statistics here.  *shrug*
1563ec72f164Smglocker 	 */
1564ec72f164Smglocker 	upgt_get_stats(sc);
1565ec72f164Smglocker 
1566836f404eSmglocker 	splx(s);
15671d97e97dSmglocker }
15681d97e97dSmglocker 
15691d97e97dSmglocker void
upgt_tx_done(struct upgt_softc * sc,uint8_t * data)15701d97e97dSmglocker upgt_tx_done(struct upgt_softc *sc, uint8_t *data)
15711d97e97dSmglocker {
15721d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
15731d97e97dSmglocker 	struct ifnet *ifp = &ic->ic_if;
15741d97e97dSmglocker 	struct upgt_lmac_tx_done_desc *desc;
15751d97e97dSmglocker 	int i, s;
15761d97e97dSmglocker 
15771d97e97dSmglocker 	s = splnet();
15781d97e97dSmglocker 
15791d97e97dSmglocker 	desc = (struct upgt_lmac_tx_done_desc *)data;
15801d97e97dSmglocker 
15811d97e97dSmglocker 	for (i = 0; i < UPGT_TX_COUNT; i++) {
15821d97e97dSmglocker 		struct upgt_data *data_tx = &sc->tx_data[i];
15831d97e97dSmglocker 
15841d97e97dSmglocker 		if (data_tx->addr == letoh32(desc->header2.reqid)) {
15851d01a6f0Smglocker 			upgt_mem_free(sc, data_tx->addr);
15861d97e97dSmglocker 			ieee80211_release_node(ic, data_tx->ni);
15871d97e97dSmglocker 			data_tx->ni = NULL;
15881d97e97dSmglocker 			data_tx->addr = 0;
15891d97e97dSmglocker 
15901d97e97dSmglocker 			sc->tx_queued--;
15911d97e97dSmglocker 
15921d97e97dSmglocker 			DPRINTF(2, "%s: TX done: ", sc->sc_dev.dv_xname);
15931d97e97dSmglocker 			DPRINTF(2, "memaddr=0x%08x, status=0x%04x, rssi=%d, ",
15941d97e97dSmglocker 			    letoh32(desc->header2.reqid),
15951d97e97dSmglocker 			    letoh16(desc->status),
15961d97e97dSmglocker 			    letoh16(desc->rssi));
15971d97e97dSmglocker 			DPRINTF(2, "seq=%d\n", letoh16(desc->seq));
1598ccdbc99cSmglocker 			break;
15991d97e97dSmglocker 		}
16001d97e97dSmglocker 	}
16011d97e97dSmglocker 
16021d97e97dSmglocker 	if (sc->tx_queued == 0) {
16031d97e97dSmglocker 		/* TX queued was processed, continue */
16041d97e97dSmglocker 		ifp->if_timer = 0;
1605de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
16061d97e97dSmglocker 		upgt_start(ifp);
16071d97e97dSmglocker 	}
16081d97e97dSmglocker 
16091d97e97dSmglocker 	splx(s);
16101d97e97dSmglocker }
16111d97e97dSmglocker 
16121d97e97dSmglocker void
upgt_rx_cb(struct usbd_xfer * xfer,void * priv,usbd_status status)1613ab0b1be7Smglocker upgt_rx_cb(struct usbd_xfer *xfer, void *priv, usbd_status status)
16141d97e97dSmglocker {
16151d97e97dSmglocker 	struct upgt_data *data_rx = priv;
16161d97e97dSmglocker 	struct upgt_softc *sc = data_rx->sc;
16171d97e97dSmglocker 	int len;
16181d97e97dSmglocker 	struct upgt_lmac_header *header;
16191d97e97dSmglocker 	struct upgt_lmac_eeprom *eeprom;
16201d97e97dSmglocker 	uint8_t h1_type;
16211d97e97dSmglocker 	uint16_t h2_type;
16221d97e97dSmglocker 
16231d97e97dSmglocker 	DPRINTF(3, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
16241d97e97dSmglocker 
16251d97e97dSmglocker 	if (status != USBD_NORMAL_COMPLETION) {
16261d97e97dSmglocker 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
16271d97e97dSmglocker 			return;
16281d97e97dSmglocker 		if (status == USBD_STALLED)
16291d97e97dSmglocker 			usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
16301d97e97dSmglocker 		goto skip;
16311d97e97dSmglocker 	}
16321d97e97dSmglocker 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
16331d97e97dSmglocker 
16341d97e97dSmglocker 	/*
16351d97e97dSmglocker 	 * Check what type of frame came in.
16361d97e97dSmglocker 	 */
1637836f404eSmglocker 	header = (struct upgt_lmac_header *)(data_rx->buf + 4);
16381d97e97dSmglocker 
16391d97e97dSmglocker 	h1_type = header->header1.type;
16401d97e97dSmglocker 	h2_type = letoh16(header->header2.type);
16411d97e97dSmglocker 
16421d97e97dSmglocker 	if (h1_type == UPGT_H1_TYPE_CTRL &&
16431d97e97dSmglocker 	    h2_type == UPGT_H2_TYPE_EEPROM) {
1644836f404eSmglocker 		eeprom = (struct upgt_lmac_eeprom *)(data_rx->buf + 4);
16451d97e97dSmglocker 		uint16_t eeprom_offset = letoh16(eeprom->offset);
16461d97e97dSmglocker 		uint16_t eeprom_len = letoh16(eeprom->len);
16471d97e97dSmglocker 
16481d97e97dSmglocker 		DPRINTF(2, "%s: received EEPROM block (offset=%d, len=%d)\n",
16491d97e97dSmglocker 			sc->sc_dev.dv_xname, eeprom_offset, eeprom_len);
16501d97e97dSmglocker 
1651836f404eSmglocker 		bcopy(data_rx->buf + sizeof(struct upgt_lmac_eeprom) + 4,
16521d97e97dSmglocker 			sc->sc_eeprom + eeprom_offset, eeprom_len);
16531d97e97dSmglocker 
16541d97e97dSmglocker 		/* EEPROM data has arrived in time, wakeup tsleep() */
16551d97e97dSmglocker 		wakeup(sc);
16561d97e97dSmglocker 	} else
16571d97e97dSmglocker 	if (h1_type == UPGT_H1_TYPE_CTRL &&
16581d97e97dSmglocker 	    h2_type == UPGT_H2_TYPE_TX_DONE) {
16591d97e97dSmglocker 		DPRINTF(2, "%s: received 802.11 TX done\n",
16601d97e97dSmglocker 		    sc->sc_dev.dv_xname);
16611d97e97dSmglocker 
1662836f404eSmglocker 		upgt_tx_done(sc, data_rx->buf + 4);
16631d97e97dSmglocker 	} else
16648880a392Smglocker 	if (h1_type == UPGT_H1_TYPE_RX_DATA ||
16658880a392Smglocker 	    h1_type == UPGT_H1_TYPE_RX_DATA_MGMT) {
16661d97e97dSmglocker 		DPRINTF(3, "%s: received 802.11 RX data\n",
16671d97e97dSmglocker 		    sc->sc_dev.dv_xname);
16681d97e97dSmglocker 
1669836f404eSmglocker 		upgt_rx(sc, data_rx->buf + 4, letoh16(header->header1.len));
1670ec72f164Smglocker 	} else
1671ec72f164Smglocker 	if (h1_type == UPGT_H1_TYPE_CTRL &&
1672ec72f164Smglocker 	    h2_type == UPGT_H2_TYPE_STATS) {
1673ec72f164Smglocker 		DPRINTF(2, "%s: received statistic data\n",
1674ec72f164Smglocker 		    sc->sc_dev.dv_xname);
1675ec72f164Smglocker 
1676ec72f164Smglocker 		/* TODO: what could we do with the statistic data? */
16771d97e97dSmglocker 	} else {
16781d97e97dSmglocker 		/* ignore unknown frame types */
16791d97e97dSmglocker 		DPRINTF(1, "%s: received unknown frame type 0x%02x\n",
16801d97e97dSmglocker 		    sc->sc_dev.dv_xname, header->header1.type);
16811d97e97dSmglocker 	}
16821d97e97dSmglocker 
16831d97e97dSmglocker skip:	/* setup new transfer */
16841d97e97dSmglocker 	usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf, MCLBYTES,
16851d97e97dSmglocker 	    USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rx_cb);
16861d97e97dSmglocker 	(void)usbd_transfer(xfer);
16871d97e97dSmglocker }
16881d97e97dSmglocker 
16891d97e97dSmglocker void
upgt_rx(struct upgt_softc * sc,uint8_t * data,int pkglen)16901d97e97dSmglocker upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen)
16911d97e97dSmglocker {
16921d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
16931d97e97dSmglocker 	struct ifnet *ifp = &ic->ic_if;
16941d97e97dSmglocker 	struct upgt_lmac_rx_desc *rxdesc;
16951d97e97dSmglocker 	struct ieee80211_frame *wh;
169630d05aacSdamien 	struct ieee80211_rxinfo rxi;
16971d97e97dSmglocker 	struct ieee80211_node *ni;
16981d97e97dSmglocker 	struct mbuf *m;
16991d97e97dSmglocker 	int s;
17001d97e97dSmglocker 
17011d97e97dSmglocker 	/* access RX packet descriptor */
17021d97e97dSmglocker 	rxdesc = (struct upgt_lmac_rx_desc *)data;
17031d97e97dSmglocker 
17041d97e97dSmglocker 	/* create mbuf which is suitable for strict alignment archs */
1705c35f95b8Smpi 	m = m_devget(rxdesc->data, pkglen, ETHER_ALIGN);
17061d97e97dSmglocker 	if (m == NULL) {
1707255388bfSthib 		DPRINTF(1, "%s: could not create RX mbuf!\n", sc->sc_dev.dv_xname);
1708255388bfSthib 		ifp->if_ierrors++;
17091d97e97dSmglocker 		return;
17101d97e97dSmglocker 	}
17111d97e97dSmglocker 
17121d97e97dSmglocker 	s = splnet();
17131d97e97dSmglocker 
17141d97e97dSmglocker #if NBPFILTER > 0
17151d97e97dSmglocker 	if (sc->sc_drvbpf != NULL) {
17161d97e97dSmglocker 		struct mbuf mb;
17171d97e97dSmglocker 		struct upgt_rx_radiotap_header *tap = &sc->sc_rxtap;
17181d97e97dSmglocker 
171906375c52Sdamien 		tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
172025e71d99Smglocker 		tap->wr_rate = upgt_rx_rate(sc, rxdesc->rate);
17211d97e97dSmglocker 		tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
17221d97e97dSmglocker 		tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
17231d97e97dSmglocker 		tap->wr_antsignal = rxdesc->rssi;
17241d97e97dSmglocker 
17251d97e97dSmglocker 		mb.m_data = (caddr_t)tap;
17261d97e97dSmglocker 		mb.m_len = sc->sc_rxtap_len;
17271d97e97dSmglocker 		mb.m_next = m;
17281d97e97dSmglocker 		mb.m_nextpkt = NULL;
17291d97e97dSmglocker 		mb.m_type = 0;
17301d97e97dSmglocker 		mb.m_flags = 0;
17311d97e97dSmglocker 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
17321d97e97dSmglocker 	}
17331d97e97dSmglocker #endif
173406375c52Sdamien 	/* trim FCS */
173506375c52Sdamien 	m_adj(m, -IEEE80211_CRC_LEN);
17361d97e97dSmglocker 
17371d97e97dSmglocker 	wh = mtod(m, struct ieee80211_frame *);
17381d97e97dSmglocker 	ni = ieee80211_find_rxnode(ic, wh);
17391d97e97dSmglocker 
17401d97e97dSmglocker 	/* push the frame up to the 802.11 stack */
174152a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
174230d05aacSdamien 	rxi.rxi_flags = 0;
174330d05aacSdamien 	rxi.rxi_rssi = rxdesc->rssi;
174430d05aacSdamien 	ieee80211_input(ifp, m, ni, &rxi);
17451d97e97dSmglocker 
17461d97e97dSmglocker 	/* node is no longer needed */
17471d97e97dSmglocker 	ieee80211_release_node(ic, ni);
17481d97e97dSmglocker 
17491d97e97dSmglocker 	splx(s);
17501d97e97dSmglocker 
17511d97e97dSmglocker 	DPRINTF(3, "%s: RX done\n", sc->sc_dev.dv_xname);
17521d97e97dSmglocker }
17531d97e97dSmglocker 
17546b9aaf9aSmglocker void
upgt_setup_rates(struct upgt_softc * sc)17556b9aaf9aSmglocker upgt_setup_rates(struct upgt_softc *sc)
17566b9aaf9aSmglocker {
17576b9aaf9aSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
17586b9aaf9aSmglocker 
17596b9aaf9aSmglocker 	/*
17606b9aaf9aSmglocker 	 * 0x01 = OFMD6   0x10 = DS1
17616b9aaf9aSmglocker 	 * 0x04 = OFDM9   0x11 = DS2
17626b9aaf9aSmglocker 	 * 0x06 = OFDM12  0x12 = DS5
17636b9aaf9aSmglocker 	 * 0x07 = OFDM18  0x13 = DS11
17646b9aaf9aSmglocker 	 * 0x08 = OFDM24
17656b9aaf9aSmglocker 	 * 0x09 = OFDM36
17666b9aaf9aSmglocker 	 * 0x0a = OFDM48
17676b9aaf9aSmglocker 	 * 0x0b = OFDM54
17686b9aaf9aSmglocker 	 */
17696b9aaf9aSmglocker 	const uint8_t rateset_auto_11b[] =
17706b9aaf9aSmglocker 	    { 0x13, 0x13, 0x12, 0x11, 0x11, 0x10, 0x10, 0x10 };
17716b9aaf9aSmglocker 	const uint8_t rateset_auto_11g[] =
17726b9aaf9aSmglocker 	    { 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x04, 0x01 };
17736b9aaf9aSmglocker 	const uint8_t rateset_fix_11bg[] =
17746b9aaf9aSmglocker 	    { 0x10, 0x11, 0x12, 0x13, 0x01, 0x04, 0x06, 0x07,
17756b9aaf9aSmglocker 	      0x08, 0x09, 0x0a, 0x0b };
17766b9aaf9aSmglocker 
17776b9aaf9aSmglocker 	if (ic->ic_fixed_rate == -1) {
17786b9aaf9aSmglocker 		/*
17796b9aaf9aSmglocker 		 * Automatic rate control is done by the device.
17806b9aaf9aSmglocker 		 * We just pass the rateset from which the device
17816b9aaf9aSmglocker 		 * will pickup a rate.
17826b9aaf9aSmglocker 		 */
17836b9aaf9aSmglocker 		if (ic->ic_curmode == IEEE80211_MODE_11B)
17846b9aaf9aSmglocker 			bcopy(rateset_auto_11b, sc->sc_cur_rateset,
17856b9aaf9aSmglocker 			    sizeof(sc->sc_cur_rateset));
17866b9aaf9aSmglocker 		if (ic->ic_curmode == IEEE80211_MODE_11G ||
17876b9aaf9aSmglocker 		    ic->ic_curmode == IEEE80211_MODE_AUTO)
17886b9aaf9aSmglocker 			bcopy(rateset_auto_11g, sc->sc_cur_rateset,
17896b9aaf9aSmglocker 			    sizeof(sc->sc_cur_rateset));
17906b9aaf9aSmglocker 	} else {
17916b9aaf9aSmglocker 		/* set a fixed rate */
17926b9aaf9aSmglocker 		memset(sc->sc_cur_rateset, rateset_fix_11bg[ic->ic_fixed_rate],
17936b9aaf9aSmglocker 		    sizeof(sc->sc_cur_rateset));
17946b9aaf9aSmglocker 	}
17956b9aaf9aSmglocker }
17966b9aaf9aSmglocker 
179725e71d99Smglocker uint8_t
upgt_rx_rate(struct upgt_softc * sc,const int rate)179825e71d99Smglocker upgt_rx_rate(struct upgt_softc *sc, const int rate)
179925e71d99Smglocker {
180025e71d99Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
180125e71d99Smglocker 
180225e71d99Smglocker 	if (ic->ic_curmode == IEEE80211_MODE_11B) {
180325e71d99Smglocker 		if (rate < 0 || rate > 3)
180425e71d99Smglocker 			/* invalid rate */
180525e71d99Smglocker 			return (0);
180625e71d99Smglocker 
180725e71d99Smglocker 		switch (rate) {
180825e71d99Smglocker 		case 0:
180925e71d99Smglocker 			return (2);
181025e71d99Smglocker 		case 1:
181125e71d99Smglocker 			return (4);
181225e71d99Smglocker 		case 2:
181325e71d99Smglocker 			return (11);
181425e71d99Smglocker 		case 3:
181525e71d99Smglocker 			return (22);
181625e71d99Smglocker 		default:
181725e71d99Smglocker 			return (0);
181825e71d99Smglocker 		}
181925e71d99Smglocker 	}
182025e71d99Smglocker 
182125e71d99Smglocker 	if (ic->ic_curmode == IEEE80211_MODE_11G) {
182225e71d99Smglocker 		if (rate < 0 || rate > 11)
182325e71d99Smglocker 			/* invalid rate */
182425e71d99Smglocker 			return (0);
182525e71d99Smglocker 
182625e71d99Smglocker 		switch (rate) {
182725e71d99Smglocker 		case 0:
182825e71d99Smglocker 			return (2);
182925e71d99Smglocker 		case 1:
183025e71d99Smglocker 			return (4);
183125e71d99Smglocker 		case 2:
183225e71d99Smglocker 			return (11);
183325e71d99Smglocker 		case 3:
183425e71d99Smglocker 			return (22);
183525e71d99Smglocker 		case 4:
183625e71d99Smglocker 			return (12);
183725e71d99Smglocker 		case 5:
183825e71d99Smglocker 			return (18);
183925e71d99Smglocker 		case 6:
184025e71d99Smglocker 			return (24);
184125e71d99Smglocker 		case 7:
184225e71d99Smglocker 			return (36);
184325e71d99Smglocker 		case 8:
184425e71d99Smglocker 			return (48);
184525e71d99Smglocker 		case 9:
184625e71d99Smglocker 			return (72);
184725e71d99Smglocker 		case 10:
184825e71d99Smglocker 			return (96);
184925e71d99Smglocker 		case 11:
185025e71d99Smglocker 			return (108);
185125e71d99Smglocker 		default:
185225e71d99Smglocker 			return (0);
185325e71d99Smglocker 		}
185425e71d99Smglocker 	}
185525e71d99Smglocker 
185625e71d99Smglocker 	return (0);
185725e71d99Smglocker }
185825e71d99Smglocker 
18591d97e97dSmglocker int
upgt_set_macfilter(struct upgt_softc * sc,uint8_t state)18601d97e97dSmglocker upgt_set_macfilter(struct upgt_softc *sc, uint8_t state)
18611d97e97dSmglocker {
18621d97e97dSmglocker 	struct ieee80211com *ic = &sc->sc_ic;
18631d97e97dSmglocker 	struct ieee80211_node *ni = ic->ic_bss;
18641d97e97dSmglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
1865836f404eSmglocker 	struct upgt_lmac_mem *mem;
18661d97e97dSmglocker 	struct upgt_lmac_filter *filter;
18671d97e97dSmglocker 	int len;
18681d97e97dSmglocker 	uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
18691d97e97dSmglocker 
18701d97e97dSmglocker 	/*
1871836f404eSmglocker 	 * Transmit the URB containing the CMD data.
18721d97e97dSmglocker 	 */
18731d97e97dSmglocker 	bzero(data_cmd->buf, MCLBYTES);
18741d97e97dSmglocker 
1875836f404eSmglocker 	mem = (struct upgt_lmac_mem *)data_cmd->buf;
1876836f404eSmglocker 	mem->addr = htole32(sc->sc_memaddr_frame_start +
1877836f404eSmglocker 	    UPGT_MEMSIZE_FRAME_HEAD);
1878836f404eSmglocker 
1879836f404eSmglocker 	filter = (struct upgt_lmac_filter *)(mem + 1);
18801d97e97dSmglocker 
18811d97e97dSmglocker 	filter->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
18821d97e97dSmglocker 	filter->header1.type = UPGT_H1_TYPE_CTRL;
18831d97e97dSmglocker 	filter->header1.len = htole16(
18841d97e97dSmglocker 	    sizeof(struct upgt_lmac_filter) -
18851d97e97dSmglocker 	    sizeof(struct upgt_lmac_header));
18861d97e97dSmglocker 
18871d97e97dSmglocker 	filter->header2.reqid = htole32(sc->sc_memaddr_frame_start);
18881d97e97dSmglocker 	filter->header2.type = htole16(UPGT_H2_TYPE_MACFILTER);
18891d97e97dSmglocker 	filter->header2.flags = 0;
18901d97e97dSmglocker 
18911d97e97dSmglocker 	switch (state) {
18922d4d746fSmglocker 	case IEEE80211_S_INIT:
18932d4d746fSmglocker 		DPRINTF(1, "%s: set MAC filter to INIT\n",
18942d4d746fSmglocker 		    sc->sc_dev.dv_xname);
18952d4d746fSmglocker 
18962d4d746fSmglocker 		filter->type = htole16(UPGT_FILTER_TYPE_RESET);
18972d4d746fSmglocker 		break;
18981d97e97dSmglocker 	case IEEE80211_S_SCAN:
18991d97e97dSmglocker 		DPRINTF(1, "%s: set MAC filter to SCAN (bssid %s)\n",
19001d97e97dSmglocker 		    sc->sc_dev.dv_xname, ether_sprintf(broadcast));
19011d97e97dSmglocker 
19021d97e97dSmglocker 		filter->type = htole16(UPGT_FILTER_TYPE_NONE);
19031d97e97dSmglocker 		IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr);
19041d97e97dSmglocker 		IEEE80211_ADDR_COPY(filter->src, broadcast);
1905836f404eSmglocker 		filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1);
19061d97e97dSmglocker 		filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
1907836f404eSmglocker 		filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2);
1908f3a6740aSmglocker 		filter->rxhw = htole32(sc->sc_eeprom_hwrx);
1909836f404eSmglocker 		filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3);
19101d97e97dSmglocker 		break;
19111d97e97dSmglocker 	case IEEE80211_S_RUN:
19121d97e97dSmglocker 		DPRINTF(1, "%s: set MAC filter to RUN (bssid %s)\n",
19131d97e97dSmglocker 		    sc->sc_dev.dv_xname, ether_sprintf(ni->ni_bssid));
19141d97e97dSmglocker 
19151d97e97dSmglocker 		filter->type = htole16(UPGT_FILTER_TYPE_STA);
19161d97e97dSmglocker 		IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr);
19171d97e97dSmglocker 		IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid);
1918836f404eSmglocker 		filter->unknown1 = htole16(UPGT_FILTER_UNKNOWN1);
19191d97e97dSmglocker 		filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
1920836f404eSmglocker 		filter->unknown2 = htole16(UPGT_FILTER_UNKNOWN2);
1921f3a6740aSmglocker 		filter->rxhw = htole32(sc->sc_eeprom_hwrx);
1922836f404eSmglocker 		filter->unknown3 = htole16(UPGT_FILTER_UNKNOWN3);
19231d97e97dSmglocker 		break;
19241d97e97dSmglocker 	default:
19251d97e97dSmglocker 		printf("%s: MAC filter does not know that state!\n",
19261d97e97dSmglocker 		    sc->sc_dev.dv_xname);
19271d97e97dSmglocker 		break;
19281d97e97dSmglocker 	}
19291d97e97dSmglocker 
1930836f404eSmglocker 	len = sizeof(*mem) + sizeof(*filter);
1931836f404eSmglocker 
1932f3a6740aSmglocker 	mem->chksum = upgt_chksum_le((uint32_t *)filter,
1933f3a6740aSmglocker 	    len - sizeof(*mem));
1934836f404eSmglocker 
19351d97e97dSmglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
19361d97e97dSmglocker 		printf("%s: could not transmit macfilter CMD data URB!\n",
19371d97e97dSmglocker 		    sc->sc_dev.dv_xname);
19381d97e97dSmglocker 		return (EIO);
19391d97e97dSmglocker 	}
19401d97e97dSmglocker 
19411d97e97dSmglocker 	return (0);
19421d97e97dSmglocker }
19431d97e97dSmglocker 
19441d97e97dSmglocker int
upgt_set_channel(struct upgt_softc * sc,unsigned channel)19451d97e97dSmglocker upgt_set_channel(struct upgt_softc *sc, unsigned channel)
19461d97e97dSmglocker {
19471d97e97dSmglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
1948836f404eSmglocker 	struct upgt_lmac_mem *mem;
19491d97e97dSmglocker 	struct upgt_lmac_channel *chan;
19501d97e97dSmglocker 	int len;
19511d97e97dSmglocker 
19521d97e97dSmglocker 	DPRINTF(1, "%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, channel);
19531d97e97dSmglocker 
19541d97e97dSmglocker 	/*
1955836f404eSmglocker 	 * Transmit the URB containing the CMD data.
19561d97e97dSmglocker 	 */
19571d97e97dSmglocker 	bzero(data_cmd->buf, MCLBYTES);
19581d97e97dSmglocker 
1959836f404eSmglocker 	mem = (struct upgt_lmac_mem *)data_cmd->buf;
1960836f404eSmglocker 	mem->addr = htole32(sc->sc_memaddr_frame_start +
1961836f404eSmglocker 	    UPGT_MEMSIZE_FRAME_HEAD);
1962836f404eSmglocker 
1963836f404eSmglocker 	chan = (struct upgt_lmac_channel *)(mem + 1);
19641d97e97dSmglocker 
19651d97e97dSmglocker 	chan->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
19661d97e97dSmglocker 	chan->header1.type = UPGT_H1_TYPE_CTRL;
19671d97e97dSmglocker 	chan->header1.len = htole16(
19681d97e97dSmglocker 	    sizeof(struct upgt_lmac_channel) -
19691d97e97dSmglocker 	    sizeof(struct upgt_lmac_header));
19701d97e97dSmglocker 
19711d97e97dSmglocker 	chan->header2.reqid = htole32(sc->sc_memaddr_frame_start);
19721d97e97dSmglocker 	chan->header2.type = htole16(UPGT_H2_TYPE_CHANNEL);
19731d97e97dSmglocker 	chan->header2.flags = 0;
19741d97e97dSmglocker 
19751d97e97dSmglocker 	chan->unknown1 = htole16(UPGT_CHANNEL_UNKNOWN1);
19761d97e97dSmglocker 	chan->unknown2 = htole16(UPGT_CHANNEL_UNKNOWN2);
19771d97e97dSmglocker 	chan->freq6 = sc->sc_eeprom_freq6[channel];
19781d97e97dSmglocker 	chan->settings = sc->sc_eeprom_freq6_settings;
19791d97e97dSmglocker 	chan->unknown3 = UPGT_CHANNEL_UNKNOWN3;
19801d97e97dSmglocker 
19811d97e97dSmglocker 	bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_1,
19821d97e97dSmglocker 	    sizeof(chan->freq3_1));
19831d97e97dSmglocker 
19841d97e97dSmglocker 	bcopy(&sc->sc_eeprom_freq4[channel], chan->freq4,
19851d97e97dSmglocker 	    sizeof(sc->sc_eeprom_freq4[channel]));
19861d97e97dSmglocker 
19871d97e97dSmglocker 	bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_2,
19881d97e97dSmglocker 	    sizeof(chan->freq3_2));
19891d97e97dSmglocker 
1990836f404eSmglocker 	len = sizeof(*mem) + sizeof(*chan);
1991836f404eSmglocker 
1992f3a6740aSmglocker 	mem->chksum = upgt_chksum_le((uint32_t *)chan,
1993f3a6740aSmglocker 	    len - sizeof(*mem));
1994836f404eSmglocker 
19951d97e97dSmglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
19961d97e97dSmglocker 		printf("%s: could not transmit channel CMD data URB!\n",
19971d97e97dSmglocker 		    sc->sc_dev.dv_xname);
19981d97e97dSmglocker 		return (EIO);
19991d97e97dSmglocker 	}
20001d97e97dSmglocker 
20011d97e97dSmglocker 	return (0);
20021d97e97dSmglocker }
20031d97e97dSmglocker 
200402f087d8Smglocker void
upgt_set_led(struct upgt_softc * sc,int action)200502f087d8Smglocker upgt_set_led(struct upgt_softc *sc, int action)
200602f087d8Smglocker {
200702f087d8Smglocker 	struct ieee80211com *ic = &sc->sc_ic;
200802f087d8Smglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
200902f087d8Smglocker 	struct upgt_lmac_mem *mem;
201002f087d8Smglocker 	struct upgt_lmac_led *led;
201102f087d8Smglocker 	int len;
201202f087d8Smglocker 
201302f087d8Smglocker 	/*
201402f087d8Smglocker 	 * Transmit the URB containing the CMD data.
201502f087d8Smglocker 	 */
201602f087d8Smglocker 	bzero(data_cmd->buf, MCLBYTES);
201702f087d8Smglocker 
201802f087d8Smglocker 	mem = (struct upgt_lmac_mem *)data_cmd->buf;
201902f087d8Smglocker 	mem->addr = htole32(sc->sc_memaddr_frame_start +
202002f087d8Smglocker 	    UPGT_MEMSIZE_FRAME_HEAD);
202102f087d8Smglocker 
202202f087d8Smglocker 	led = (struct upgt_lmac_led *)(mem + 1);
202302f087d8Smglocker 
202402f087d8Smglocker 	led->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
202502f087d8Smglocker 	led->header1.type = UPGT_H1_TYPE_CTRL;
202602f087d8Smglocker 	led->header1.len = htole16(
202702f087d8Smglocker 	    sizeof(struct upgt_lmac_led) -
202802f087d8Smglocker 	    sizeof(struct upgt_lmac_header));
202902f087d8Smglocker 
203002f087d8Smglocker 	led->header2.reqid = htole32(sc->sc_memaddr_frame_start);
203102f087d8Smglocker 	led->header2.type = htole16(UPGT_H2_TYPE_LED);
203202f087d8Smglocker 	led->header2.flags = 0;
203302f087d8Smglocker 
203402f087d8Smglocker 	switch (action) {
203502f087d8Smglocker 	case UPGT_LED_OFF:
203602f087d8Smglocker 		led->mode = htole16(UPGT_LED_MODE_SET);
203702f087d8Smglocker 		led->action_fix = 0;
203802f087d8Smglocker 		led->action_tmp = htole16(UPGT_LED_ACTION_OFF);
203902f087d8Smglocker 		led->action_tmp_dur = 0;
204002f087d8Smglocker 		break;
204102f087d8Smglocker 	case UPGT_LED_ON:
204202f087d8Smglocker 		led->mode = htole16(UPGT_LED_MODE_SET);
204302f087d8Smglocker 		led->action_fix = 0;
204402f087d8Smglocker 		led->action_tmp = htole16(UPGT_LED_ACTION_ON);
204502f087d8Smglocker 		led->action_tmp_dur = 0;
204602f087d8Smglocker 		break;
204702f087d8Smglocker 	case UPGT_LED_BLINK:
204802f087d8Smglocker 		if (ic->ic_state != IEEE80211_S_RUN)
204902f087d8Smglocker 			return;
205002f087d8Smglocker 		if (sc->sc_led_blink)
205102f087d8Smglocker 			/* previous blink was not finished */
205202f087d8Smglocker 			return;
205302f087d8Smglocker 		led->mode = htole16(UPGT_LED_MODE_SET);
205402f087d8Smglocker 		led->action_fix = htole16(UPGT_LED_ACTION_OFF);
205502f087d8Smglocker 		led->action_tmp = htole16(UPGT_LED_ACTION_ON);
205602f087d8Smglocker 		led->action_tmp_dur = htole16(UPGT_LED_ACTION_TMP_DUR);
205702f087d8Smglocker 		/* lock blink */
205802f087d8Smglocker 		sc->sc_led_blink = 1;
2059b4661018Skn 		timeout_add_msec(&sc->led_to, UPGT_LED_ACTION_TMP_DUR);
206002f087d8Smglocker 		break;
206102f087d8Smglocker 	default:
206202f087d8Smglocker 		return;
206302f087d8Smglocker 	}
206402f087d8Smglocker 
206502f087d8Smglocker 	len = sizeof(*mem) + sizeof(*led);
206602f087d8Smglocker 
2067f3a6740aSmglocker 	mem->chksum = upgt_chksum_le((uint32_t *)led,
2068f3a6740aSmglocker 	    len - sizeof(*mem));
206902f087d8Smglocker 
207002f087d8Smglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
207102f087d8Smglocker 		printf("%s: could not transmit led CMD URB!\n",
207202f087d8Smglocker 		    sc->sc_dev.dv_xname);
207302f087d8Smglocker 	}
207402f087d8Smglocker }
207502f087d8Smglocker 
207602f087d8Smglocker void
upgt_set_led_blink(void * arg)207702f087d8Smglocker upgt_set_led_blink(void *arg)
207802f087d8Smglocker {
207902f087d8Smglocker 	struct upgt_softc *sc = arg;
208002f087d8Smglocker 
208102f087d8Smglocker 	/* blink finished, we are ready for a next one */
208202f087d8Smglocker 	sc->sc_led_blink = 0;
208302f087d8Smglocker 	timeout_del(&sc->led_to);
208402f087d8Smglocker }
208502f087d8Smglocker 
20861d97e97dSmglocker int
upgt_get_stats(struct upgt_softc * sc)2087ec72f164Smglocker upgt_get_stats(struct upgt_softc *sc)
2088ec72f164Smglocker {
2089ec72f164Smglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
2090ec72f164Smglocker 	struct upgt_lmac_mem *mem;
2091ec72f164Smglocker 	struct upgt_lmac_stats *stats;
2092ec72f164Smglocker 	int len;
2093ec72f164Smglocker 
2094ec72f164Smglocker 	/*
2095ec72f164Smglocker 	 * Transmit the URB containing the CMD data.
2096ec72f164Smglocker 	 */
2097ec72f164Smglocker 	bzero(data_cmd->buf, MCLBYTES);
2098ec72f164Smglocker 
2099ec72f164Smglocker 	mem = (struct upgt_lmac_mem *)data_cmd->buf;
2100ec72f164Smglocker 	mem->addr = htole32(sc->sc_memaddr_frame_start +
2101ec72f164Smglocker 	    UPGT_MEMSIZE_FRAME_HEAD);
2102ec72f164Smglocker 
2103ec72f164Smglocker 	stats = (struct upgt_lmac_stats *)(mem + 1);
2104ec72f164Smglocker 
2105ec72f164Smglocker 	stats->header1.flags = 0;
2106ec72f164Smglocker 	stats->header1.type = UPGT_H1_TYPE_CTRL;
2107ec72f164Smglocker 	stats->header1.len = htole16(
2108ec72f164Smglocker 	    sizeof(struct upgt_lmac_stats) -
2109ec72f164Smglocker 	    sizeof(struct upgt_lmac_header));
2110ec72f164Smglocker 
2111ec72f164Smglocker 	stats->header2.reqid = htole32(sc->sc_memaddr_frame_start);
2112ec72f164Smglocker 	stats->header2.type = htole16(UPGT_H2_TYPE_STATS);
2113ec72f164Smglocker 	stats->header2.flags = 0;
2114ec72f164Smglocker 
2115ec72f164Smglocker 	len = sizeof(*mem) + sizeof(*stats);
2116ec72f164Smglocker 
2117f3a6740aSmglocker 	mem->chksum = upgt_chksum_le((uint32_t *)stats,
2118f3a6740aSmglocker 	    len - sizeof(*mem));
2119ec72f164Smglocker 
2120ec72f164Smglocker 	if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
2121ec72f164Smglocker 		printf("%s: could not transmit statistics CMD data URB!\n",
2122ec72f164Smglocker 		    sc->sc_dev.dv_xname);
2123ec72f164Smglocker 		return (EIO);
2124ec72f164Smglocker 	}
2125ec72f164Smglocker 
2126ec72f164Smglocker 	return (0);
2127ec72f164Smglocker 
2128ec72f164Smglocker }
2129ec72f164Smglocker 
2130ec72f164Smglocker int
upgt_alloc_tx(struct upgt_softc * sc)21311d97e97dSmglocker upgt_alloc_tx(struct upgt_softc *sc)
21321d97e97dSmglocker {
21331d97e97dSmglocker 	int i;
21341d97e97dSmglocker 
21351d97e97dSmglocker 	sc->tx_queued = 0;
21361d97e97dSmglocker 
21371d97e97dSmglocker 	for (i = 0; i < UPGT_TX_COUNT; i++) {
21381d97e97dSmglocker 		struct upgt_data *data_tx = &sc->tx_data[i];
21391d97e97dSmglocker 
21401d97e97dSmglocker 		data_tx->sc = sc;
21411d97e97dSmglocker 
21421d97e97dSmglocker 		data_tx->xfer = usbd_alloc_xfer(sc->sc_udev);
21431d97e97dSmglocker 		if (data_tx->xfer == NULL) {
21441d97e97dSmglocker 			printf("%s: could not allocate TX xfer!\n",
21451d97e97dSmglocker 			    sc->sc_dev.dv_xname);
21461d97e97dSmglocker 			return (ENOMEM);
21471d97e97dSmglocker 		}
21481d97e97dSmglocker 
21491d97e97dSmglocker 		data_tx->buf = usbd_alloc_buffer(data_tx->xfer, MCLBYTES);
21501d97e97dSmglocker 		if (data_tx->buf == NULL) {
21511d97e97dSmglocker 			printf("%s: could not allocate TX buffer!\n",
21521d97e97dSmglocker 			    sc->sc_dev.dv_xname);
21531d97e97dSmglocker 			return (ENOMEM);
21541d97e97dSmglocker 		}
21551d97e97dSmglocker 
21561d97e97dSmglocker 		bzero(data_tx->buf, MCLBYTES);
21571d97e97dSmglocker 	}
21581d97e97dSmglocker 
21591d97e97dSmglocker 	return (0);
21601d97e97dSmglocker }
21611d97e97dSmglocker 
21621d97e97dSmglocker int
upgt_alloc_rx(struct upgt_softc * sc)21631d97e97dSmglocker upgt_alloc_rx(struct upgt_softc *sc)
21641d97e97dSmglocker {
21651d97e97dSmglocker 	struct upgt_data *data_rx = &sc->rx_data;
21661d97e97dSmglocker 
21671d97e97dSmglocker 	data_rx->sc = sc;
21681d97e97dSmglocker 
21691d97e97dSmglocker 	data_rx->xfer = usbd_alloc_xfer(sc->sc_udev);
21701d97e97dSmglocker 	if (data_rx->xfer == NULL) {
21711d97e97dSmglocker 		printf("%s: could not allocate RX xfer!\n",
21721d97e97dSmglocker 		    sc->sc_dev.dv_xname);
21731d97e97dSmglocker 		return (ENOMEM);
21741d97e97dSmglocker 	}
21751d97e97dSmglocker 
21761d97e97dSmglocker 	data_rx->buf = usbd_alloc_buffer(data_rx->xfer, MCLBYTES);
21771d97e97dSmglocker 	if (data_rx->buf == NULL) {
21781d97e97dSmglocker 		printf("%s: could not allocate RX buffer!\n",
21791d97e97dSmglocker 		    sc->sc_dev.dv_xname);
21801d97e97dSmglocker 		return (ENOMEM);
21811d97e97dSmglocker 	}
21821d97e97dSmglocker 
21831d97e97dSmglocker 	bzero(data_rx->buf, MCLBYTES);
21841d97e97dSmglocker 
21851d97e97dSmglocker 	return (0);
21861d97e97dSmglocker }
21871d97e97dSmglocker 
21881d97e97dSmglocker int
upgt_alloc_cmd(struct upgt_softc * sc)21891d97e97dSmglocker upgt_alloc_cmd(struct upgt_softc *sc)
21901d97e97dSmglocker {
21911d97e97dSmglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
21921d97e97dSmglocker 
21931d97e97dSmglocker 	data_cmd->sc = sc;
21941d97e97dSmglocker 
21951d97e97dSmglocker 	data_cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
21961d97e97dSmglocker 	if (data_cmd->xfer == NULL) {
21971d97e97dSmglocker 		printf("%s: could not allocate RX xfer!\n",
21981d97e97dSmglocker 		    sc->sc_dev.dv_xname);
21991d97e97dSmglocker 		return (ENOMEM);
22001d97e97dSmglocker 	}
22011d97e97dSmglocker 
22021d97e97dSmglocker 	data_cmd->buf = usbd_alloc_buffer(data_cmd->xfer, MCLBYTES);
22031d97e97dSmglocker 	if (data_cmd->buf == NULL) {
22041d97e97dSmglocker 		printf("%s: could not allocate RX buffer!\n",
22051d97e97dSmglocker 		    sc->sc_dev.dv_xname);
22061d97e97dSmglocker 		return (ENOMEM);
22071d97e97dSmglocker 	}
22081d97e97dSmglocker 
22091d97e97dSmglocker 	bzero(data_cmd->buf, MCLBYTES);
22101d97e97dSmglocker 
22111d97e97dSmglocker 	return (0);
22121d97e97dSmglocker }
22131d97e97dSmglocker 
22141d97e97dSmglocker void
upgt_free_tx(struct upgt_softc * sc)22151d97e97dSmglocker upgt_free_tx(struct upgt_softc *sc)
22161d97e97dSmglocker {
22171d97e97dSmglocker 	int i;
22181d97e97dSmglocker 
22191d97e97dSmglocker 	for (i = 0; i < UPGT_TX_COUNT; i++) {
22201d97e97dSmglocker 		struct upgt_data *data_tx = &sc->tx_data[i];
22211d97e97dSmglocker 
22221d97e97dSmglocker 		if (data_tx->xfer != NULL) {
22231d97e97dSmglocker 			usbd_free_xfer(data_tx->xfer);
22241d97e97dSmglocker 			data_tx->xfer = NULL;
22251d97e97dSmglocker 		}
22261d97e97dSmglocker 
22271d97e97dSmglocker 		data_tx->ni = NULL;
22281d97e97dSmglocker 	}
22291d97e97dSmglocker }
22301d97e97dSmglocker 
22311d97e97dSmglocker void
upgt_free_rx(struct upgt_softc * sc)22321d97e97dSmglocker upgt_free_rx(struct upgt_softc *sc)
22331d97e97dSmglocker {
22341d97e97dSmglocker 	struct upgt_data *data_rx = &sc->rx_data;
22351d97e97dSmglocker 
22361d97e97dSmglocker 	if (data_rx->xfer != NULL) {
22371d97e97dSmglocker 		usbd_free_xfer(data_rx->xfer);
22381d97e97dSmglocker 		data_rx->xfer = NULL;
22391d97e97dSmglocker 	}
22401d97e97dSmglocker 
22411d97e97dSmglocker 	data_rx->ni = NULL;
22421d97e97dSmglocker }
22431d97e97dSmglocker 
22441d97e97dSmglocker void
upgt_free_cmd(struct upgt_softc * sc)22451d97e97dSmglocker upgt_free_cmd(struct upgt_softc *sc)
22461d97e97dSmglocker {
22471d97e97dSmglocker 	struct upgt_data *data_cmd = &sc->cmd_data;
22481d97e97dSmglocker 
22491d97e97dSmglocker 	if (data_cmd->xfer != NULL) {
22501d97e97dSmglocker 		usbd_free_xfer(data_cmd->xfer);
22511d97e97dSmglocker 		data_cmd->xfer = NULL;
22521d97e97dSmglocker 	}
22531d97e97dSmglocker }
22541d97e97dSmglocker 
22551d97e97dSmglocker int
upgt_bulk_xmit(struct upgt_softc * sc,struct upgt_data * data,struct usbd_pipe * pipeh,uint32_t * size,int flags)22561d97e97dSmglocker upgt_bulk_xmit(struct upgt_softc *sc, struct upgt_data *data,
2257ab0b1be7Smglocker     struct usbd_pipe *pipeh, uint32_t *size, int flags)
22581d97e97dSmglocker {
22591d97e97dSmglocker         usbd_status status;
22601d97e97dSmglocker 
226147c02b80Smpi 	usbd_setup_xfer(data->xfer, pipeh, 0, data->buf, *size,
226247c02b80Smpi 	    USBD_NO_COPY | USBD_SYNCHRONOUS | flags, UPGT_USB_TIMEOUT, NULL);
226347c02b80Smpi 	status = usbd_transfer(data->xfer);
22641d97e97dSmglocker 	if (status != USBD_NORMAL_COMPLETION) {
22651d97e97dSmglocker 		printf("%s: %s: error %s!\n",
22661d97e97dSmglocker 		    sc->sc_dev.dv_xname, __func__, usbd_errstr(status));
22671d97e97dSmglocker 		return (EIO);
22681d97e97dSmglocker 	}
22691d97e97dSmglocker 
22701d97e97dSmglocker 	return (0);
22711d97e97dSmglocker }
22721d97e97dSmglocker 
22731d97e97dSmglocker void
upgt_hexdump(void * buf,int len)22741d97e97dSmglocker upgt_hexdump(void *buf, int len)
22751d97e97dSmglocker {
22761d97e97dSmglocker 	int i;
22771d97e97dSmglocker 
22781d97e97dSmglocker 	for (i = 0; i < len; i++) {
22791d97e97dSmglocker 		if (i % 16 == 0)
22801d97e97dSmglocker 			printf("%s%5i:", i ? "\n" : "", i);
22811d97e97dSmglocker 		if (i % 4 == 0)
22821d97e97dSmglocker 			printf(" ");
22831d97e97dSmglocker 		printf("%02x", (int)*((u_char *)buf + i));
22841d97e97dSmglocker 	}
22851d97e97dSmglocker 	printf("\n");
22861d97e97dSmglocker }
22871d97e97dSmglocker 
22881d97e97dSmglocker uint32_t
upgt_crc32_le(const void * buf,size_t size)2289f3a6740aSmglocker upgt_crc32_le(const void *buf, size_t size)
22901d97e97dSmglocker {
22911d97e97dSmglocker 	uint32_t crc;
22921d97e97dSmglocker 
22936c9b88abSmglocker 	crc = ether_crc32_le(buf, size);
22941d97e97dSmglocker 
22956c9b88abSmglocker 	/* apply final XOR value as common for CRC-32 */
2296f3a6740aSmglocker 	crc = htole32(crc ^ 0xffffffffU);
22971d97e97dSmglocker 
22986c9b88abSmglocker 	return (crc);
22991d97e97dSmglocker }
2300836f404eSmglocker 
2301836f404eSmglocker /*
2302836f404eSmglocker  * The firmware awaits a checksum for each frame we send to it.
2303836f404eSmglocker  * The algorithm used therefor is uncommon but somehow similar to CRC32.
2304836f404eSmglocker  */
2305836f404eSmglocker uint32_t
upgt_chksum_le(const uint32_t * buf,size_t size)2306f3a6740aSmglocker upgt_chksum_le(const uint32_t *buf, size_t size)
2307836f404eSmglocker {
2308836f404eSmglocker 	int i;
2309836f404eSmglocker 	uint32_t crc = 0;
2310836f404eSmglocker 
2311836f404eSmglocker 	for (i = 0; i < size; i += sizeof(uint32_t)) {
2312f3a6740aSmglocker 		crc = htole32(crc ^ *buf++);
2313f3a6740aSmglocker 		crc = htole32((crc >> 5) ^ (crc << 3));
2314836f404eSmglocker 	}
2315836f404eSmglocker 
2316836f404eSmglocker 	return (crc);
2317836f404eSmglocker }
2318