diff options
Diffstat (limited to 'freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c')
-rw-r--r-- | freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c b/freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c new file mode 100644 index 00000000..f378ca3c --- /dev/null +++ b/freebsd/sys/dev/rtwn/usb/rtwn_usb_attach.c @@ -0,0 +1,444 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ + +/*- + * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org> + * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/sysctl.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mutex.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/linker.h> +#include <sys/kdb.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/ethernet.h> +#include <net/if_media.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <rtems/bsd/local/usbdevs.h> + +#include <dev/rtwn/if_rtwnvar.h> +#include <dev/rtwn/if_rtwn_nop.h> + +#include <dev/rtwn/usb/rtwn_usb_var.h> + +#include <dev/rtwn/usb/rtwn_usb_attach.h> +#include <dev/rtwn/usb/rtwn_usb_ep.h> +#include <dev/rtwn/usb/rtwn_usb_reg.h> +#include <dev/rtwn/usb/rtwn_usb_tx.h> + +#include <dev/rtwn/rtl8192c/r92c_reg.h> + +static device_probe_t rtwn_usb_match; +static device_attach_t rtwn_usb_attach; +static device_detach_t rtwn_usb_detach; +static device_suspend_t rtwn_usb_suspend; +static device_resume_t rtwn_usb_resume; + +static int rtwn_usb_alloc_list(struct rtwn_softc *, + struct rtwn_data[], int, int); +static int rtwn_usb_alloc_rx_list(struct rtwn_softc *); +static int rtwn_usb_alloc_tx_list(struct rtwn_softc *); +static void rtwn_usb_free_list(struct rtwn_softc *, + struct rtwn_data data[], int); +static void rtwn_usb_free_rx_list(struct rtwn_softc *); +static void rtwn_usb_free_tx_list(struct rtwn_softc *); +static void rtwn_usb_reset_lists(struct rtwn_softc *, + struct ieee80211vap *); +static void rtwn_usb_reset_tx_list(struct rtwn_usb_softc *, + rtwn_datahead *, struct ieee80211vap *); +static void rtwn_usb_start_xfers(struct rtwn_softc *); +static void rtwn_usb_abort_xfers(struct rtwn_softc *); +static int rtwn_usb_fw_write_block(struct rtwn_softc *, + const uint8_t *, uint16_t, int); +static void rtwn_usb_drop_incorrect_tx(struct rtwn_softc *); +static void rtwn_usb_attach_methods(struct rtwn_softc *); + +#define RTWN_CONFIG_INDEX 0 + + +static int +rtwn_usb_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != RTWN_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != RTWN_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(rtwn_devs, sizeof(rtwn_devs), uaa)); +} + +static int +rtwn_usb_alloc_list(struct rtwn_softc *sc, struct rtwn_data data[], + int ndata, int maxsz) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct rtwn_data *dp = &data[i]; + dp->m = NULL; + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); + if (dp->buf == NULL) { + device_printf(sc->sc_dev, + "could not allocate buffer\n"); + error = ENOMEM; + goto fail; + } + dp->ni = NULL; + } + + return (0); +fail: + rtwn_usb_free_list(sc, data, ndata); + return (error); +} + +static int +rtwn_usb_alloc_rx_list(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + int error, i; + + /* XXX recheck */ + error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT, + sc->rx_dma_size + 1024); + if (error != 0) + return (error); + + STAILQ_INIT(&uc->uc_rx_active); + STAILQ_INIT(&uc->uc_rx_inactive); + + for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&uc->uc_rx_inactive, &uc->uc_rx[i], next); + + return (0); +} + +static int +rtwn_usb_alloc_tx_list(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + int error, i; + + error = rtwn_usb_alloc_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT, + RTWN_TXBUFSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&uc->uc_tx_active); + STAILQ_INIT(&uc->uc_tx_inactive); + STAILQ_INIT(&uc->uc_tx_pending); + + for (i = 0; i < RTWN_USB_TX_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&uc->uc_tx_inactive, &uc->uc_tx[i], next); + + return (0); +} + +static void +rtwn_usb_free_list(struct rtwn_softc *sc, struct rtwn_data data[], int ndata) +{ + int i; + + for (i = 0; i < ndata; i++) { + struct rtwn_data *dp = &data[i]; + + if (dp->buf != NULL) { + free(dp->buf, M_USBDEV); + dp->buf = NULL; + } + if (dp->ni != NULL) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + } + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + } + } +} + +static void +rtwn_usb_free_rx_list(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + + rtwn_usb_free_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT); + + STAILQ_INIT(&uc->uc_rx_active); + STAILQ_INIT(&uc->uc_rx_inactive); +} + +static void +rtwn_usb_free_tx_list(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + + rtwn_usb_free_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT); + + STAILQ_INIT(&uc->uc_tx_active); + STAILQ_INIT(&uc->uc_tx_inactive); + STAILQ_INIT(&uc->uc_tx_pending); +} + +static void +rtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + + RTWN_ASSERT_LOCKED(sc); + + rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap); + rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap); + if (vap == NULL) + sc->qfullmsk = 0; +} + +static void +rtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc, + rtwn_datahead *head, struct ieee80211vap *vap) +{ + struct rtwn_vap *uvp = RTWN_VAP(vap); + struct rtwn_data *dp, *tmp; + int id; + + id = (uvp != NULL ? uvp->id : RTWN_VAP_ID_INVALID); + + STAILQ_FOREACH_SAFE(dp, head, next, tmp) { + if (vap == NULL || (dp->ni == NULL && + (dp->id == id || id == RTWN_VAP_ID_INVALID)) || + (dp->ni != NULL && dp->ni->ni_vap == vap)) { + if (dp->ni != NULL) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + } + + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + } + + STAILQ_REMOVE(head, dp, rtwn_data, next); + STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, dp, next); + } + } +} + +static void +rtwn_usb_start_xfers(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + + usbd_transfer_start(uc->uc_xfer[RTWN_BULK_RX]); +} + +static void +rtwn_usb_abort_xfers(struct rtwn_softc *sc) +{ + struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc); + int i; + + RTWN_ASSERT_LOCKED(sc); + + /* abort any pending transfers */ + RTWN_UNLOCK(sc); + for (i = 0; i < RTWN_N_TRANSFER; i++) + usbd_transfer_drain(uc->uc_xfer[i]); + RTWN_LOCK(sc); +} + +static int +rtwn_usb_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf, + uint16_t reg, int mlen) +{ + int error; + + /* XXX fix this deconst */ + error = rtwn_usb_write_region_1(sc, reg, __DECONST(uint8_t *, buf), + mlen); + + return (error); +} + +static void +rtwn_usb_drop_incorrect_tx(struct rtwn_softc *sc) +{ + + rtwn_setbits_1_shift(sc, R92C_TXDMA_OFFSET_CHK, 0, + R92C_TXDMA_OFFSET_DROP_DATA_EN, 1); +} + +static void +rtwn_usb_attach_methods(struct rtwn_softc *sc) +{ + sc->sc_write_1 = rtwn_usb_write_1; + sc->sc_write_2 = rtwn_usb_write_2; + sc->sc_write_4 = rtwn_usb_write_4; + sc->sc_read_1 = rtwn_usb_read_1; + sc->sc_read_2 = rtwn_usb_read_2; + sc->sc_read_4 = rtwn_usb_read_4; + sc->sc_delay = rtwn_usb_delay; + sc->sc_tx_start = rtwn_usb_tx_start; + sc->sc_start_xfers = rtwn_usb_start_xfers; + sc->sc_reset_lists = rtwn_usb_reset_lists; + sc->sc_abort_xfers = rtwn_usb_abort_xfers; + sc->sc_fw_write_block = rtwn_usb_fw_write_block; + sc->sc_get_qmap = rtwn_usb_get_qmap; + sc->sc_set_desc_addr = rtwn_nop_softc; + sc->sc_drop_incorrect_tx = rtwn_usb_drop_incorrect_tx; + sc->sc_beacon_update_begin = rtwn_nop_softc_vap; + sc->sc_beacon_update_end = rtwn_nop_softc_vap; + sc->sc_beacon_unload = rtwn_nop_softc_int; + + sc->bcn_check_interval = 100; +} + +static int +rtwn_usb_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct rtwn_usb_softc *uc = device_get_softc(self); + struct rtwn_softc *sc = &uc->uc_sc; + struct ieee80211com *ic = &sc->sc_ic; + int error; + + device_set_usb_desc(self); + uc->uc_udev = uaa->device; + sc->sc_dev = self; + ic->ic_name = device_get_nameunit(self); + + /* Need to be initialized early. */ + rtwn_sysctlattach(sc); + mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF); + + rtwn_usb_attach_methods(sc); + rtwn_usb_attach_private(uc, USB_GET_DRIVER_INFO(uaa)); + + error = rtwn_usb_setup_endpoints(uc); + if (error != 0) + goto detach; + + /* Allocate Tx/Rx buffers. */ + error = rtwn_usb_alloc_rx_list(sc); + if (error != 0) + goto detach; + + error = rtwn_usb_alloc_tx_list(sc); + if (error != 0) + goto detach; + + /* Generic attach. */ + error = rtwn_attach(sc); + if (error != 0) + goto detach; + + return (0); + +detach: + rtwn_usb_detach(self); /* failure */ + return (ENXIO); +} + +static int +rtwn_usb_detach(device_t self) +{ + struct rtwn_usb_softc *uc = device_get_softc(self); + struct rtwn_softc *sc = &uc->uc_sc; + + /* Generic detach. */ + rtwn_detach(sc); + + /* Free Tx/Rx buffers. */ + rtwn_usb_free_tx_list(sc); + rtwn_usb_free_rx_list(sc); + + /* Detach all USB transfers. */ + usbd_transfer_unsetup(uc->uc_xfer, RTWN_N_TRANSFER); + + rtwn_detach_private(sc); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static int +rtwn_usb_suspend(device_t self) +{ + struct rtwn_usb_softc *uc = device_get_softc(self); + + rtwn_suspend(&uc->uc_sc); + + return (0); +} + +static int +rtwn_usb_resume(device_t self) +{ + struct rtwn_usb_softc *uc = device_get_softc(self); + + rtwn_resume(&uc->uc_sc); + + return (0); +} + +static device_method_t rtwn_usb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rtwn_usb_match), + DEVMETHOD(device_attach, rtwn_usb_attach), + DEVMETHOD(device_detach, rtwn_usb_detach), + DEVMETHOD(device_suspend, rtwn_usb_suspend), + DEVMETHOD(device_resume, rtwn_usb_resume), + + DEVMETHOD_END +}; + +static driver_t rtwn_usb_driver = { + "rtwn", + rtwn_usb_methods, + sizeof(struct rtwn_usb_softc) +}; + +static devclass_t rtwn_usb_devclass; + +DRIVER_MODULE(rtwn_usb, uhub, rtwn_usb_driver, rtwn_usb_devclass, NULL, NULL); +MODULE_VERSION(rtwn_usb, 1); +MODULE_DEPEND(rtwn_usb, usb, 1, 1, 1); +MODULE_DEPEND(rtwn_usb, wlan, 1, 1, 1); +MODULE_DEPEND(rtwn_usb, rtwn, 2, 2, 2); +USB_PNP_HOST_INFO(rtwn_devs); |