Index: head/sys/dev/rtwn/pci/rtwn_pci_rx.c =================================================================== --- head/sys/dev/rtwn/pci/rtwn_pci_rx.c (revision 308383) +++ head/sys/dev/rtwn/pci/rtwn_pci_rx.c (revision 308384) @@ -1,293 +1,295 @@ /* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2015 Stefan Sperling * Copyright (c) 2016 Andriy Voskoboinyk * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void rtwn_pci_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); *(bus_addr_t *)arg = segs[0].ds_addr; } void rtwn_pci_setup_rx_desc(struct rtwn_pci_softc *pc, struct r92ce_rx_stat *desc, bus_addr_t addr, size_t len, int idx) { memset(desc, 0, sizeof(*desc)); desc->rxdw0 = htole32(SM(R92C_RXDW0_PKTLEN, len) | ((idx == RTWN_PCI_RX_LIST_COUNT - 1) ? R92C_RXDW0_EOR : 0)); desc->rxbufaddr = htole32(addr); bus_space_barrier(pc->pc_st, pc->pc_sh, 0, pc->pc_mapsize, BUS_SPACE_BARRIER_WRITE); desc->rxdw0 |= htole32(R92C_RXDW0_OWN); } static void rtwn_pci_rx_frame(struct rtwn_softc *sc, struct r92ce_rx_stat *rx_desc, int desc_idx) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *ring = &pc->rx_ring; struct rtwn_rx_data *rx_data = &ring->rx_data[desc_idx]; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; uint32_t rxdw0; struct mbuf *m, *m1; int8_t rssi = 0, nf; int infosz, pktlen, shift, error; /* Dump Rx descriptor. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV_DESC, "%s: dw: 0 %08X, 1 %08X, 2 %08X, 3 %08X, 4 %08X, tsfl %08X, " "addr: %08X (64: %08X)\n", __func__, le32toh(rx_desc->rxdw0), le32toh(rx_desc->rxdw1), le32toh(rx_desc->rxdw2), le32toh(rx_desc->rxdw3), le32toh(rx_desc->rxdw4), le32toh(rx_desc->tsf_low), le32toh(rx_desc->rxbufaddr), le32toh(rx_desc->rxbufaddr64)); rxdw0 = le32toh(rx_desc->rxdw0); if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: RX flags error (%s)\n", __func__, rxdw0 & R92C_RXDW0_CRCERR ? "CRC" : "ICV"); goto fail; } pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack) || pktlen > MCLBYTES)) { RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: frame is too short/long: %d\n", __func__, pktlen); goto fail; } infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; shift = MS(rxdw0, R92C_RXDW0_SHIFT); m1 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m1 == NULL)) { device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n", __func__); goto fail; } bus_dmamap_sync(ring->data_dmat, rx_data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, rx_data->map); error = bus_dmamap_load(ring->data_dmat, rx_data->map, mtod(m1, void *), MCLBYTES, rtwn_pci_dma_map_addr, &rx_data->paddr, 0); if (error != 0) { m_freem(m1); error = bus_dmamap_load(ring->data_dmat, rx_data->map, mtod(rx_data->m, void *), MCLBYTES, rtwn_pci_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT); if (error != 0) panic("%s: could not load old RX mbuf", device_get_name(sc->sc_dev)); /* Physical address may have changed. */ rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, MCLBYTES, desc_idx); goto fail; } /* Finalize mbuf. */ m = rx_data->m; rx_data->m = m1; m->m_pkthdr.len = m->m_len = pktlen + infosz + shift; nf = RTWN_NOISE_FLOOR; ni = rtwn_rx_common(sc, m, rx_desc, &rssi); RTWN_DPRINTF(sc, RTWN_DEBUG_RECV, "%s: Rx frame len %d, infosz %d, shift %d, rssi %d\n", __func__, pktlen, infosz, shift, rssi); /* Update RX descriptor. */ rtwn_pci_setup_rx_desc(pc, rx_desc, rx_data->paddr, MCLBYTES, desc_idx); /* Send the frame to the 802.11 layer. */ RTWN_UNLOCK(sc); if (ni != NULL) { (void)ieee80211_input(ni, m, rssi - nf, nf); /* Node is no longer needed. */ ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, rssi - nf, nf); RTWN_LOCK(sc); return; fail: counter_u64_add(ic->ic_ierrors, 1); } static void rtwn_pci_tx_done(struct rtwn_softc *sc, int qid) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_tx_ring *ring = &pc->tx_ring[qid]; struct rtwn_tx_desc_common *desc; struct rtwn_tx_data *data; RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: qid %d, last %d, cur %d\n", __func__, qid, ring->last, ring->cur); bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); while(ring->last != ring->cur) { data = &ring->tx_data[ring->last]; desc = (struct rtwn_tx_desc_common *) ((uint8_t *)ring->desc + sc->txdesc_len * ring->last); KASSERT(data->m != NULL, ("no mbuf")); if (desc->flags0 & RTWN_FLAGS0_OWN) break; /* Unmap and free mbuf. */ bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); if (data->ni != NULL) { /* not a beacon frame */ ieee80211_tx_complete(data->ni, data->m, 0); data->ni = NULL; ring->queued--; } else m_freem(data->m); data->m = NULL; ring->last = (ring->last + 1) % RTWN_PCI_TX_LIST_COUNT; #ifndef D4054 if (ring->queued > 0) sc->sc_tx_timer = 5; else sc->sc_tx_timer = 0; #endif } if (ring->queued < (RTWN_PCI_TX_LIST_COUNT - 1)) sc->qfullmsk &= ~(1 << qid); rtwn_start(sc); } static void rtwn_pci_rx_done(struct rtwn_softc *sc) { struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); struct rtwn_rx_ring *ring = &pc->rx_ring; bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD); for (;;) { struct r92ce_rx_stat *rx_desc = &ring->desc[ring->cur]; if (le32toh(rx_desc->rxdw0) & R92C_RXDW0_OWN) break; rtwn_pci_rx_frame(sc, rx_desc, ring->cur); if (!(sc->sc_flags & RTWN_RUNNING)) return; ring->cur = (ring->cur + 1) % RTWN_PCI_RX_LIST_COUNT; } } void rtwn_pci_intr(void *arg) { struct rtwn_softc *sc = arg; struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc); int i, status, tx_rings; RTWN_LOCK(sc); status = rtwn_classify_intr(sc, &tx_rings, 0); RTWN_DPRINTF(sc, RTWN_DEBUG_INTR, "%s: status %08X, tx_rings %08X\n", __func__, status, tx_rings); - if (status == 0 && tx_rings == 0) { - RTWN_UNLOCK(sc); - return; - } + if (status == 0 && tx_rings == 0) + goto unlock; - if (status & RTWN_PCI_INTR_RX) + if (status & RTWN_PCI_INTR_RX) { rtwn_pci_rx_done(sc); + if (!(sc->sc_flags & RTWN_RUNNING)) + goto unlock; + } if (tx_rings != 0) for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) if (tx_rings & (1 << i)) rtwn_pci_tx_done(sc, i); if (sc->sc_flags & RTWN_RUNNING) rtwn_pci_enable_intr(pc); +unlock: RTWN_UNLOCK(sc); } Index: head/sys/dev/rtwn/usb/rtwn_usb_attach.c =================================================================== --- head/sys/dev/rtwn/usb/rtwn_usb_attach.c (revision 308383) +++ head/sys/dev/rtwn/usb/rtwn_usb_attach.c (revision 308384) @@ -1,433 +1,435 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015-2016 Andriy Voskoboinyk * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #include #include #include #include #include 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_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; error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT, RTWN_RXBUFSZ); 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_stop(uc->uc_xfer[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; } 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);