From: patrick Date: Sun, 24 Dec 2017 19:50:56 +0000 (+0000) Subject: Add a PCI attachment driver for bwfm(4). It's not finished, but it's X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=e5ec1e726bc6e8e32507f9f08379b1b6411e3989;p=openbsd Add a PCI attachment driver for bwfm(4). It's not finished, but it's already past the point where development can occur out of the tree. With this I can successfully scan for access points and tell the chip to attach to an SSID. RX path should work as well, but since I forgot to bring the antenna with me to my parents, the reception is a bit horrible in the metal enclosure. There are a few reasons this driver is rather big. First we set up the ARM Cores, uploading the firmware and kicking it off. Then we need to read all needed information from the registers. Once that is done we have to set up countless buffers. There are 2 TX rings and 3 RX rings, plus N TX rings for the actual data that is yet to be implemented. Merry Christmas! ok kettenis@ --- diff --git a/share/man/man4/bwfm.4 b/share/man/man4/bwfm.4 index dfb863bae7e..e016c1a7d87 100644 --- a/share/man/man4/bwfm.4 +++ b/share/man/man4/bwfm.4 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bwfm.4,v 1.3 2017/10/30 15:49:10 patrick Exp $ +.\" $OpenBSD: bwfm.4,v 1.4 2017/12/24 19:50:56 patrick Exp $ .\" .\" Copyright (c) 2017 Patrick Wildt .\" @@ -14,13 +14,14 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: October 30 2017 $ +.Dd $Mdocdate: December 24 2017 $ .Dt BWFM 4 .Os .Sh NAME .Nm bwfm .Nd Broadcom and Cypress wireless network device .Sh SYNOPSIS +.Cd "bwfm* at pci?" .Cd "bwfm* at usb?" .Sh DESCRIPTION The diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index 6cc15c1d8ec..f76f70697c2 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.449 2017/12/03 19:44:04 kettenis Exp $ +# $OpenBSD: GENERIC,v 1.450 2017/12/24 19:50:56 patrick Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -408,6 +408,7 @@ pcscp* at pci? # AMD 53c974 PCscsi-PCI SCSI #trm* at pci? # Tekram DC-3x5U SCSI Controllers vmwpvs* at pci? # VMware ParaVirtual SCSI nvme* at pci? # NVMe controllers +#bwfm* at pci? # Broadcom FullMAC scsibus* at scsi? sd* at scsibus? # SCSI disk drives diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index a1a21f91cfe..9d0c00a728a 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.332 2017/11/28 23:54:04 dlg Exp $ +# $OpenBSD: files.pci,v 1.333 2017/12/24 19:50:56 patrick Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -816,5 +816,8 @@ file dev/pci/virtio_pci.c virtio_pci attach dwiic at pci with dwiic_pci file dev/pci/dwiic_pci.c dwiic_pci +attach bwfm at pci with bwfm_pci: firmload +file dev/pci/if_bwfm_pci.c bwfm_pci + include "dev/pci/files.agp" include "dev/pci/drm/files.drm" diff --git a/sys/dev/pci/if_bwfm_pci.c b/sys/dev/pci/if_bwfm_pci.c new file mode 100644 index 00000000000..84fdcb36ce0 --- /dev/null +++ b/sys/dev/pci/if_bwfm_pci.c @@ -0,0 +1,1434 @@ +/* $OpenBSD: if_bwfm_pci.c,v 1.1 2017/12/24 19:50:56 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2017 Patrick Wildt + * + * Permission to use, copy, modify, and/or 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 "bpfilter.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if NBPFILTER > 0 +#include +#endif +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#define BWFM_DMA_D2H_SCRATCH_BUF_LEN 8 +#define BWFM_DMA_D2H_RINGUPD_BUF_LEN 1024 +#define BWFM_DMA_H2D_IOCTL_BUF_LEN ETHER_MAX_LEN + +#define BWFM_NUM_TX_MSGRINGS 2 +#define BWFM_NUM_RX_MSGRINGS 3 + +#define BWFM_NUM_TX_PKTIDS 2048 +#define BWFM_NUM_RX_PKTIDS 1024 + +#define BWFM_NUM_TX_DESCS 1 +#define BWFM_NUM_RX_DESCS 1 + +#ifdef BWFM_DEBUG +#define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0) +static int bwfm_debug = 2; +#else +#define DPRINTF(x) do { ; } while (0) +#define DPRINTFN(n, x) do { ; } while (0) +#endif + +#define DEVNAME(sc) ((sc)->sc_sc.sc_dev.dv_xname) + +struct bwfm_pci_msgring { + uint32_t w_idx_addr; + uint32_t r_idx_addr; + uint32_t w_ptr; + uint32_t r_ptr; + int nitem; + int itemsz; + struct bwfm_pci_dmamem *ring; +}; + +struct bwfm_pci_buf { + bus_dmamap_t bb_map; + struct mbuf *bb_m; +}; + +struct bwfm_pci_pkts { + struct bwfm_pci_buf *pkts; + uint32_t npkt; + int last; +}; + +struct bwfm_pci_softc { + struct bwfm_softc sc_sc; + pci_chipset_tag_t sc_pc; + pcitag_t sc_tag; + pcireg_t sc_id; + void *sc_ih; + + bus_space_tag_t sc_reg_iot; + bus_space_handle_t sc_reg_ioh; + bus_size_t sc_reg_ios; + + bus_space_tag_t sc_tcm_iot; + bus_space_handle_t sc_tcm_ioh; + bus_size_t sc_tcm_ios; + + bus_dma_tag_t sc_dmat; + + uint32_t sc_shared_address; + uint32_t sc_shared_flags; + uint8_t sc_shared_version; + + uint8_t sc_dma_idx_sz; + struct bwfm_pci_dmamem *sc_dma_idx_buf; + size_t sc_dma_idx_bufsz; + + uint16_t sc_max_rxbufpost; + uint32_t sc_rx_dataoffset; + uint32_t sc_htod_mb_data_addr; + uint32_t sc_dtoh_mb_data_addr; + uint32_t sc_ring_info_addr; + + uint32_t sc_console_base_addr; + uint32_t sc_console_buf_addr; + uint32_t sc_console_buf_size; + + uint16_t sc_max_flowrings; + uint16_t sc_max_submissionrings; + uint16_t sc_max_completionrings; + + struct bwfm_pci_msgring sc_ctrl_submit; + struct bwfm_pci_msgring sc_rxpost_submit; + struct bwfm_pci_msgring sc_ctrl_complete; + struct bwfm_pci_msgring sc_tx_complete; + struct bwfm_pci_msgring sc_rx_complete; + struct bwfm_pci_msgring *sc_flowrings; + + struct bwfm_pci_dmamem *sc_scratch_buf; + struct bwfm_pci_dmamem *sc_ringupd_buf; + + struct bwfm_pci_dmamem *sc_ioctl_buf; + int sc_ioctl_reqid; + uint32_t sc_ioctl_resp_pktid; + uint32_t sc_ioctl_resp_ret_len; + uint32_t sc_ioctl_resp_status; + int sc_ioctl_poll; + + struct if_rxring sc_ioctl_ring; + struct if_rxring sc_event_ring; + struct if_rxring sc_rxbuf_ring; + + struct bwfm_pci_pkts sc_tx_pkts; + struct bwfm_pci_pkts sc_rx_pkts; +}; + +struct bwfm_pci_dmamem { + bus_dmamap_t bdm_map; + bus_dma_segment_t bdm_seg; + size_t bdm_size; + caddr_t bdm_kva; +}; + +#define BWFM_PCI_DMA_MAP(_bdm) ((_bdm)->bdm_map) +#define BWFM_PCI_DMA_LEN(_bdm) ((_bdm)->bdm_size) +#define BWFM_PCI_DMA_DVA(_bdm) ((_bdm)->bdm_map->dm_segs[0].ds_addr) +#define BWFM_PCI_DMA_KVA(_bdm) ((void *)(_bdm)->bdm_kva) + +int bwfm_pci_match(struct device *, void *, void *); +void bwfm_pci_attachhook(struct device *); +void bwfm_pci_attach(struct device *, struct device *, void *); +int bwfm_pci_detach(struct device *, int); + +int bwfm_pci_intr(void *); +void bwfm_pci_intr_enable(struct bwfm_pci_softc *); +void bwfm_pci_intr_disable(struct bwfm_pci_softc *); +int bwfm_pci_load_microcode(struct bwfm_pci_softc *, const u_char *, + size_t); +void bwfm_pci_select_core(struct bwfm_pci_softc *, int ); + +struct bwfm_pci_dmamem * + bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *, bus_size_t, + bus_size_t); +void bwfm_pci_dmamem_free(struct bwfm_pci_softc *, struct bwfm_pci_dmamem *); +int bwfm_pci_pktid_new(struct bwfm_pci_softc *, + struct bwfm_pci_pkts *, struct mbuf *, + uint32_t *, paddr_t *); +struct mbuf * bwfm_pci_pktid_free(struct bwfm_pci_softc *, + struct bwfm_pci_pkts *, uint32_t); +void bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *, + struct if_rxring *, uint32_t); +void bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *); +void bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *); +int bwfm_pci_setup_ring(struct bwfm_pci_softc *, struct bwfm_pci_msgring *, + int, size_t, uint32_t, uint32_t, int, uint32_t, uint32_t *); + +void bwfm_pci_ring_bell(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void * bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void * bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int, int *); +void * bwfm_pci_ring_read_avail(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int *); +void bwfm_pci_ring_read_commit(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int); +void bwfm_pci_ring_write_commit(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int); + +void bwfm_pci_ring_rx(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_msg_rx(struct bwfm_pci_softc *, void *); + +uint32_t bwfm_pci_buscore_read(struct bwfm_softc *, uint32_t); +void bwfm_pci_buscore_write(struct bwfm_softc *, uint32_t, + uint32_t); +int bwfm_pci_buscore_prepare(struct bwfm_softc *); +int bwfm_pci_buscore_reset(struct bwfm_softc *); +void bwfm_pci_buscore_activate(struct bwfm_softc *, uint32_t); + +int bwfm_pci_txdata(struct bwfm_softc *, struct mbuf *); + +int bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *, int, + int, char *, size_t *); +int bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *, int, + int, char *, size_t); + +struct bwfm_buscore_ops bwfm_pci_buscore_ops = { + .bc_read = bwfm_pci_buscore_read, + .bc_write = bwfm_pci_buscore_write, + .bc_prepare = bwfm_pci_buscore_prepare, + .bc_reset = bwfm_pci_buscore_reset, + .bc_setup = NULL, + .bc_activate = bwfm_pci_buscore_activate, +}; + +struct bwfm_bus_ops bwfm_pci_bus_ops = { + .bs_init = NULL, + .bs_stop = NULL, + .bs_txdata = bwfm_pci_txdata, + .bs_txctl = NULL, + .bs_rxctl = NULL, +}; + +struct bwfm_proto_ops bwfm_pci_msgbuf_ops = { + .proto_query_dcmd = bwfm_pci_msgbuf_query_dcmd, + .proto_set_dcmd = bwfm_pci_msgbuf_set_dcmd, +}; + +struct cfattach bwfm_pci_ca = { + sizeof(struct bwfm_pci_softc), + bwfm_pci_match, + bwfm_pci_attach, + bwfm_pci_detach, +}; + +static const struct pci_matchid bwfm_pci_devices[] = { + { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM43602 }, +}; + +int +bwfm_pci_match(struct device *parent, void *match, void *aux) +{ + return (pci_matchbyid(aux, bwfm_pci_devices, + nitems(bwfm_pci_devices))); +} + +void +bwfm_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct bwfm_pci_softc *sc = (struct bwfm_pci_softc *)self; + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + const char *intrstr; + pci_intr_handle_t ih; + + if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x00, + PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_reg_iot, &sc->sc_reg_ioh, + NULL, &sc->sc_reg_ios, 0)) { + printf(": can't map bar0\n"); + return; + } + + if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x08, + PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_tcm_iot, &sc->sc_tcm_ioh, + NULL, &sc->sc_tcm_ios, 0)) { + printf(": can't map bar1\n"); + goto bar0; + } + + sc->sc_pc = pa->pa_pc; + sc->sc_tag = pa->pa_tag; + sc->sc_id = pa->pa_id; + sc->sc_dmat = pa->pa_dmat; + + /* Map and establish the interrupt. */ + if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { + printf(": couldn't map interrupt\n"); + goto bar1; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + + sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_NET | IPL_MPSAFE, + bwfm_pci_intr, sc, DEVNAME(sc)); + if (sc->sc_ih == NULL) { + printf(": couldn't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + goto bar1; + } + printf(": %s\n", intrstr); + + config_mountroot(self, bwfm_pci_attachhook); + return; + +bar1: + bus_space_unmap(sc->sc_tcm_iot, sc->sc_tcm_ioh, sc->sc_tcm_ios); +bar0: + bus_space_unmap(sc->sc_reg_iot, sc->sc_reg_ioh, sc->sc_reg_ios); +} + +void +bwfm_pci_attachhook(struct device *self) +{ + struct bwfm_pci_softc *sc = (struct bwfm_pci_softc *)self; + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_pci_ringinfo ringinfo; + const char *name = NULL; + u_char *ucode; size_t size; + uint32_t d2h_w_idx_ptr, d2h_r_idx_ptr; + uint32_t h2d_w_idx_ptr, h2d_r_idx_ptr; + uint32_t idx_offset, reg; + int i; + + sc->sc_sc.sc_buscore_ops = &bwfm_pci_buscore_ops; + if (bwfm_chip_attach(&sc->sc_sc) != 0) { + printf("%s: cannot attach chip\n", DEVNAME(sc)); + return; + } + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGADDR, 0x4e0); + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA, reg); + + switch (bwfm->sc_chip.ch_chip) + { + case BRCM_CC_43602_CHIP_ID: + name = "brcmfmac43602-pcie.bin"; + break; + default: + break; + } + + if (name == NULL) { + printf("%s: unknown firmware\n", DEVNAME(sc)); + return; + } + + if (loadfirmware(name, &ucode, &size) != 0) { + printf("%s: failed loadfirmware of file %s\n", + DEVNAME(sc), name); + return; + } + + /* Retrieve RAM size from firmware. */ + if (size >= BWFM_RAMSIZE + 8) { + uint32_t *ramsize = (uint32_t *)&ucode[BWFM_RAMSIZE]; + if (letoh32(ramsize[0]) == BWFM_RAMSIZE_MAGIC) + bwfm->sc_chip.ch_ramsize = letoh32(ramsize[1]); + } + + if (bwfm_pci_load_microcode(sc, ucode, size) != 0) { + printf("%s: could not load microcode\n", + DEVNAME(sc)); + free(ucode, M_DEVBUF, size); + return; + } + free(ucode, M_DEVBUF, size); + + sc->sc_shared_flags = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_INFO); + sc->sc_shared_version = sc->sc_shared_flags; + if (sc->sc_shared_version > BWFM_SHARED_INFO_MAX_VERSION || + sc->sc_shared_version < BWFM_SHARED_INFO_MIN_VERSION) { + printf("%s: PCIe version %d unsupported\n", + DEVNAME(sc), sc->sc_shared_version); + return; + } + + if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_INDEX) { + if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_2B_IDX) + sc->sc_dma_idx_sz = sizeof(uint16_t); + else + sc->sc_dma_idx_sz = sizeof(uint32_t); + } + + /* Maximum RX data buffers in the ring. */ + sc->sc_max_rxbufpost = bus_space_read_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_MAX_RXBUFPOST); + if (sc->sc_max_rxbufpost == 0) + sc->sc_max_rxbufpost = BWFM_SHARED_MAX_RXBUFPOST_DEFAULT; + + /* Alternative offset of data in a packet */ + sc->sc_rx_dataoffset = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_RX_DATAOFFSET); + + /* For Power Management */ + sc->sc_htod_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_HTOD_MB_DATA_ADDR); + sc->sc_dtoh_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DTOH_MB_DATA_ADDR); + + /* Ring information */ + sc->sc_ring_info_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_RING_INFO_ADDR); + + /* Firmware's "dmesg" */ + sc->sc_console_base_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_CONSOLE_ADDR); + sc->sc_console_buf_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_BUFADDR); + sc->sc_console_buf_size = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_BUFSIZE); + + /* Read ring information. */ + bus_space_read_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo)); + + if (sc->sc_shared_version >= 6) { + sc->sc_max_submissionrings = le16toh(ringinfo.max_submissionrings); + sc->sc_max_flowrings = le16toh(ringinfo.max_flowrings); + sc->sc_max_completionrings = le16toh(ringinfo.max_completionrings); + } else { + sc->sc_max_submissionrings = le16toh(ringinfo.max_flowrings); + sc->sc_max_flowrings = sc->sc_max_submissionrings - + BWFM_NUM_TX_MSGRINGS; + sc->sc_max_completionrings = BWFM_NUM_RX_MSGRINGS; + } + + if (sc->sc_dma_idx_sz == 0) { + d2h_w_idx_ptr = letoh32(ringinfo.d2h_w_idx_ptr); + d2h_r_idx_ptr = letoh32(ringinfo.d2h_r_idx_ptr); + h2d_w_idx_ptr = letoh32(ringinfo.h2d_w_idx_ptr); + h2d_r_idx_ptr = letoh32(ringinfo.h2d_r_idx_ptr); + idx_offset = sizeof(uint32_t); + } else { + uint64_t address; + + /* Each TX/RX Ring has a Read and Write Ptr */ + sc->sc_dma_idx_bufsz = (sc->sc_max_submissionrings + + sc->sc_max_completionrings) * sc->sc_dma_idx_sz * 2; + sc->sc_dma_idx_buf = bwfm_pci_dmamem_alloc(sc, + sc->sc_dma_idx_bufsz, 8); + if (sc->sc_dma_idx_buf == NULL) { + /* XXX: Fallback to TCM? */ + printf("%s: cannot allocate idx buf\n", + DEVNAME(sc)); + return; + } + + idx_offset = sc->sc_dma_idx_sz; + h2d_w_idx_ptr = 0; + address = BWFM_PCI_DMA_DVA(sc->sc_dma_idx_buf); + ringinfo.h2d_w_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.h2d_w_idx_hostaddr_high = + htole32(address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + + sc->sc_max_submissionrings * idx_offset; + address += sc->sc_max_submissionrings * idx_offset; + ringinfo.h2d_r_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.h2d_r_idx_hostaddr_high = + htole32(address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + + sc->sc_max_submissionrings * idx_offset; + address += sc->sc_max_submissionrings * idx_offset; + ringinfo.d2h_w_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.d2h_w_idx_hostaddr_high = + htole32(address >> 32); + + d2h_r_idx_ptr = d2h_w_idx_ptr + + sc->sc_max_completionrings * idx_offset; + address += sc->sc_max_completionrings * idx_offset; + ringinfo.d2h_r_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.d2h_r_idx_hostaddr_high = + htole32(address >> 32); + + bus_space_write_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo)); + } + + uint32_t ring_mem_ptr = letoh32(ringinfo.ringmem); + /* TX ctrl ring: Send ctrl buffers, send IOCTLs */ + if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_submit, 64, 40, + h2d_w_idx_ptr, h2d_r_idx_ptr, 0, idx_offset, + &ring_mem_ptr)) + goto cleanup; + /* TX rxpost ring: Send clean data mbufs for RX */ + if (bwfm_pci_setup_ring(sc, &sc->sc_rxpost_submit, 512, 32, + h2d_w_idx_ptr, h2d_r_idx_ptr, 1, idx_offset, + &ring_mem_ptr)) + goto cleanup; + /* RX completion rings: recv our filled buffers back */ + if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_complete, 64, 24, + d2h_w_idx_ptr, d2h_r_idx_ptr, 0, idx_offset, + &ring_mem_ptr)) + goto cleanup; + if (bwfm_pci_setup_ring(sc, &sc->sc_tx_complete, 1024, 16, + d2h_w_idx_ptr, d2h_r_idx_ptr, 1, idx_offset, + &ring_mem_ptr)) + goto cleanup; + if (bwfm_pci_setup_ring(sc, &sc->sc_rx_complete, 512, 32, + d2h_w_idx_ptr, d2h_r_idx_ptr, 2, idx_offset, + &ring_mem_ptr)) + goto cleanup; + +#if 0 + /* Dynamic TX rings for actual data */ + sc->sc_flowrings = malloc(sc->sc_max_flowrings * + sizeof(struct bwfm_pci_msgring), M_DEVBUF, M_WAITOK | M_ZERO); +#endif + + /* Scratch and ring update buffers for firmware */ + if ((sc->sc_scratch_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_D2H_SCRATCH_BUF_LEN, 8)) == NULL) + goto cleanup; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_LOW, + BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_HIGH, + BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) >> 32); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_LEN, + BWFM_DMA_D2H_SCRATCH_BUF_LEN); + + if ((sc->sc_ringupd_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_D2H_RINGUPD_BUF_LEN, 8)) == NULL) + goto cleanup; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_LOW, + BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_HIGH, + BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) >> 32); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_LEN, + BWFM_DMA_D2H_RINGUPD_BUF_LEN); + + if ((sc->sc_ioctl_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_H2D_IOCTL_BUF_LEN, 8)) == NULL) + goto cleanup; + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + bwfm_pci_intr_enable(sc); + + /* Maps RX mbufs to a packet id and back. */ + sc->sc_rx_pkts.npkt = BWFM_NUM_RX_PKTIDS; + sc->sc_rx_pkts.pkts = malloc(BWFM_NUM_RX_PKTIDS * + sizeof(struct bwfm_pci_buf), M_DEVBUF, M_WAITOK | M_ZERO); + for (i = 0; i < BWFM_NUM_RX_PKTIDS; i++) + bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE, + BWFM_NUM_RX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK, + &sc->sc_rx_pkts.pkts[i].bb_map); + sc->sc_tx_pkts.npkt = BWFM_NUM_TX_PKTIDS; + + /* Maps TX mbufs to a packet id and back. */ + sc->sc_tx_pkts.pkts = malloc(BWFM_NUM_TX_PKTIDS + * sizeof(struct bwfm_pci_buf), M_DEVBUF, M_WAITOK | M_ZERO); + for (i = 0; i < BWFM_NUM_TX_PKTIDS; i++) + bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE, + BWFM_NUM_TX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK, + &sc->sc_tx_pkts.pkts[i].bb_map); + + if_rxr_init(&sc->sc_ioctl_ring, 8, 8); + if_rxr_init(&sc->sc_event_ring, 8, 8); + if_rxr_init(&sc->sc_rxbuf_ring, 2, sc->sc_max_rxbufpost); + bwfm_pci_fill_rx_rings(sc); + + sc->sc_ioctl_poll = 1; + sc->sc_sc.sc_bus_ops = &bwfm_pci_bus_ops; + sc->sc_sc.sc_proto_ops = &bwfm_pci_msgbuf_ops; + bwfm_attach(&sc->sc_sc); + sc->sc_ioctl_poll = 0; + return; + +cleanup: + if (sc->sc_ioctl_buf) + bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf); + if (sc->sc_ringupd_buf) + bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf); + if (sc->sc_scratch_buf) + bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf); + if (sc->sc_rx_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring); + if (sc->sc_tx_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring); + if (sc->sc_ctrl_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring); + if (sc->sc_rxpost_submit.ring) + bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring); + if (sc->sc_ctrl_submit.ring) + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring); + if (sc->sc_dma_idx_buf) + bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf); +} + +int +bwfm_pci_load_microcode(struct bwfm_pci_softc *sc, const u_char *ucode, size_t size) +{ + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_core *core; + uint32_t shared; + int i; + + if (bwfm->sc_chip.ch_chip == BRCM_CC_43602_CHIP_ID) { + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_ARM_CR4); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKIDX, 5); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKPDA, 0); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKIDX, 7); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKPDA, 0); + } + + for (i = 0; i < size; i++) + bus_space_write_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + i, ucode[i]); + + /* Firmware replaces this with a pointer once up. */ + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4, 0); + + /* TODO: restore NVRAM */ + + /* Load reset vector from firmware and kickstart core. */ + core = bwfm_chip_get_core(bwfm, BWFM_AGENT_INTERNAL_MEM); + bwfm->sc_chip.ch_core_reset(bwfm, core, 0, 0, 0); + bwfm_chip_set_active(bwfm, *(uint32_t *)ucode); + + for (i = 0; i < 40; i++) { + delay(50 * 1000); + shared = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4); + if (shared) + break; + } + if (!shared) { + printf("%s: firmware did not come up\n", DEVNAME(sc)); + return 1; + } + + sc->sc_shared_address = shared; + return 0; +} + +int +bwfm_pci_detach(struct device *self, int flags) +{ + struct bwfm_pci_softc *sc = (struct bwfm_pci_softc *)self; + + bwfm_detach(&sc->sc_sc, flags); + + /* FIXME: free RX buffers */ + /* FIXME: free TX buffers */ + /* FIXME: free more memory */ + + bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf); + bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf); + bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf); + bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring); + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring); + bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf); + return 0; +} + +/* DMA code */ +struct bwfm_pci_dmamem * +bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *sc, bus_size_t size, bus_size_t align) +{ + struct bwfm_pci_dmamem *bdm; + int nsegs; + + bdm = malloc(sizeof(*bdm), M_DEVBUF, M_WAITOK | M_ZERO); + bdm->bdm_size = size; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &bdm->bdm_map) != 0) + goto bdmfree; + + if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &bdm->bdm_seg, 1, + &nsegs, BUS_DMA_WAITOK) != 0) + goto destroy; + + if (bus_dmamem_map(sc->sc_dmat, &bdm->bdm_seg, nsegs, size, + &bdm->bdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0) + goto free; + + if (bus_dmamap_load(sc->sc_dmat, bdm->bdm_map, bdm->bdm_kva, size, + NULL, BUS_DMA_WAITOK) != 0) + goto unmap; + + bzero(bdm->bdm_kva, size); + + return (bdm); + +unmap: + bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, size); +free: + bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map); +bdmfree: + free(bdm, M_DEVBUF, 0); + + return (NULL); +} + +void +bwfm_pci_dmamem_free(struct bwfm_pci_softc *sc, struct bwfm_pci_dmamem *bdm) +{ + bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, bdm->bdm_size); + bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map); + free(bdm, M_DEVBUF, 0); +} + +/* + * We need a simple mapping from a packet ID to mbufs, because when + * a transfer completed, we only know the ID so we have to look up + * the memory for the ID. This simply looks for an empty slot. + */ +int +bwfm_pci_pktid_new(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts, + struct mbuf *m, uint32_t *pktid, paddr_t *paddr) +{ + int i, idx; + + idx = pkts->last + 1; + for (i = 0; i < pkts->npkt; i++) { + if (idx == pkts->npkt) + idx = 0; + if (pkts->pkts[idx].bb_m == NULL) { + if (bus_dmamap_load_mbuf(sc->sc_dmat, + pkts->pkts[idx].bb_map, m, BUS_DMA_NOWAIT) != 0) { + printf("%s: could not load mbuf DMA map", + DEVNAME(sc)); + return 1; + } + pkts->last = idx; + pkts->pkts[idx].bb_m = m; + *pktid = idx; + *paddr = pkts->pkts[idx].bb_map->dm_segs[0].ds_addr; + return 0; + } + idx++; + } + return 1; +} + +struct mbuf * +bwfm_pci_pktid_free(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts, + uint32_t pktid) +{ + struct mbuf *m; + + if (pktid >= pkts->npkt || pkts->pkts[pktid].bb_m == NULL) + return NULL; + bus_dmamap_unload(sc->sc_dmat, pkts->pkts[pktid].bb_map); + m = pkts->pkts[pktid].bb_m; + pkts->pkts[pktid].bb_m = NULL; + return m; +} + +void +bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *sc) +{ + bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_ioctl_ring, + MSGBUF_TYPE_IOCTLRESP_BUF_POST); + bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_event_ring, + MSGBUF_TYPE_EVENT_BUF_POST); + bwfm_pci_fill_rx_buf_ring(sc); +} + +void +bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *sc, struct if_rxring *rxring, + uint32_t msgtype) +{ + struct msgbuf_rx_ioctl_resp_or_event *req; + struct mbuf *m; + uint32_t pktid; + paddr_t paddr; + int s, slots; + + s = splnet(); + for (slots = if_rxr_get(rxring, 8); slots > 0; slots--) { + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) + break; + m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE); + if (m == NULL) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1); + break; + } + m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE; + if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1); + m_freem(m); + break; + } + memset(req, 0, sizeof(*req)); + req->msg.msgtype = msgtype; + req->msg.request_id = htole32(pktid); + req->host_buf_len = htole16(MSGBUF_MAX_PKT_SIZE); + req->host_buf_addr.high_addr = htole32(paddr >> 32); + req->host_buf_addr.low_addr = htole32(paddr & 0xffffffff); + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); + } + if_rxr_put(rxring, slots); + splx(s); +} + +void +bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *sc) +{ + struct msgbuf_rx_bufpost *req; + struct mbuf *m; + uint32_t pktid; + paddr_t paddr; + int s, slots; + + s = splnet(); + for (slots = if_rxr_get(&sc->sc_rxbuf_ring, sc->sc_max_rxbufpost); + slots > 0; slots--) { + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_rxpost_submit); + if (req == NULL) + break; + m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE); + if (m == NULL) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 1); + break; + } + m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE; + if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 1); + m_freem(m); + break; + } + memset(req, 0, sizeof(*req)); + req->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; + req->msg.request_id = htole32(pktid); + req->data_buf_len = htole16(MSGBUF_MAX_PKT_SIZE); + req->data_buf_addr.high_addr = htole32(paddr >> 32); + req->data_buf_addr.low_addr = htole32(paddr & 0xffffffff); + bwfm_pci_ring_write_commit(sc, &sc->sc_rxpost_submit); + } + if_rxr_put(&sc->sc_rxbuf_ring, slots); + splx(s); +} + +int +bwfm_pci_setup_ring(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring, + int nitem, size_t itemsz, uint32_t w_idx, uint32_t r_idx, + int idx, uint32_t idx_off, uint32_t *ring_mem) +{ + ring->w_idx_addr = w_idx + idx * idx_off; + ring->r_idx_addr = r_idx + idx * idx_off; + ring->nitem = nitem; + ring->itemsz = itemsz; + bwfm_pci_ring_write_rptr(sc, ring); + bwfm_pci_ring_write_wptr(sc, ring); + + ring->ring = bwfm_pci_dmamem_alloc(sc, nitem * itemsz, 8); + if (ring->ring == NULL) + return ENOMEM; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MEM_BASE_ADDR_LOW, + BWFM_PCI_DMA_DVA(ring->ring) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MEM_BASE_ADDR_HIGH, + BWFM_PCI_DMA_DVA(ring->ring) >> 32); + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MAX_ITEM, nitem); + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_LEN_ITEMS, itemsz); + *ring_mem = *ring_mem + BWFM_RING_MEM_SZ; + return 0; +} + +/* Ring helpers */ +void +bwfm_pci_ring_bell(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_H2D_MAILBOX, 1); +} + +void +bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + ring->r_ptr = bus_space_read_2(sc->sc_tcm_iot, + sc->sc_tcm_ioh, ring->r_idx_addr); + } else { + ring->r_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->r_idx_addr); + } +} + +void +bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + ring->w_ptr = bus_space_read_2(sc->sc_tcm_iot, + sc->sc_tcm_ioh, ring->w_idx_addr); + } else { + ring->w_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->w_idx_addr); + } +} + +void +bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + ring->r_idx_addr, ring->r_ptr); + } else { + *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->r_idx_addr) = ring->r_ptr; + } +} + +void +bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + ring->w_idx_addr, ring->w_ptr); + } else { + *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->w_idx_addr) = ring->w_ptr; + } +} + +/* + * Retrieve a free descriptor to put new stuff in, but don't commit + * to it yet so we can rollback later if any error occurs. + */ +void * +bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + int available; + char *ret; + + bwfm_pci_ring_update_rptr(sc, ring); + + if (ring->r_ptr > ring->w_ptr) + available = ring->r_ptr - ring->w_ptr; + else + available = ring->r_ptr + (ring->nitem - ring->w_ptr); + + if (available < 1) + return NULL; + + ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz); + ring->w_ptr += 1; + if (ring->w_ptr == ring->nitem) + ring->w_ptr = 0; + return ret; +} + +void * +bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int count, int *avail) +{ + int available; + char *ret; + + bwfm_pci_ring_update_rptr(sc, ring); + + if (ring->r_ptr > ring->w_ptr) + available = ring->r_ptr - ring->w_ptr; + else + available = ring->r_ptr + (ring->nitem - ring->w_ptr); + + if (available < 1) + return NULL; + + ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz); + *avail = min(count, available - 1); + if (*avail + ring->w_ptr > ring->nitem) + *avail = ring->nitem - ring->w_ptr; + ring->w_ptr += *avail; + if (ring->w_ptr == ring->nitem) + ring->w_ptr = 0; + return ret; +} + +/* + * Read number of descriptors available (submitted by the firmware) + * and retrieve pointer to first descriptor. + */ +void * +bwfm_pci_ring_read_avail(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int *avail) +{ + bwfm_pci_ring_update_wptr(sc, ring); + + if (ring->w_ptr >= ring->r_ptr) + *avail = ring->w_ptr - ring->r_ptr; + else + *avail = ring->nitem - ring->r_ptr; + + if (*avail == 0) + return NULL; + + return BWFM_PCI_DMA_KVA(ring->ring) + (ring->r_ptr * ring->itemsz); +} + +/* + * Let firmware know we read N descriptors. + */ +void +bwfm_pci_ring_read_commit(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int nitem) +{ + ring->r_ptr += nitem; + if (ring->r_ptr == ring->nitem) + ring->r_ptr = 0; + bwfm_pci_ring_write_rptr(sc, ring); +} + +/* + * Let firmware know that we submitted some descriptors. + */ +void +bwfm_pci_ring_write_commit(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + bwfm_pci_ring_write_wptr(sc, ring); + bwfm_pci_ring_bell(sc, ring); +} + +/* + * Rollback N descriptors in case we don't actually want + * to commit to it. + */ +void +bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int nitem) +{ + if (ring->w_ptr == 0) + ring->w_ptr = ring->nitem - nitem; + else + ring->w_ptr -= nitem; +} + +/* + * Foreach written descriptor on the ring, pass the descriptor to + * a message handler and let the firmware know we handled it. + */ +void +bwfm_pci_ring_rx(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring) +{ + void *buf; + int avail, processed; + +again: + buf = bwfm_pci_ring_read_avail(sc, ring, &avail); + if (buf == NULL) + return; + + processed = 0; + while (avail) { + bwfm_pci_msg_rx(sc, buf + sc->sc_rx_dataoffset); + buf += ring->itemsz; + processed++; + if (processed == 48) { + bwfm_pci_ring_read_commit(sc, ring, processed); + processed = 0; + } + avail--; + } + if (processed) + bwfm_pci_ring_read_commit(sc, ring, processed); + if (ring->r_ptr == 0) + goto again; +} + +void +bwfm_pci_msg_rx(struct bwfm_pci_softc *sc, void *buf) +{ + struct msgbuf_ioctl_resp_hdr *resp; + struct msgbuf_rx_event *event; + struct msgbuf_common_hdr *msg; + struct mbuf *m; + + msg = (struct msgbuf_common_hdr *)buf; + switch (msg->msgtype) + { + case MSGBUF_TYPE_IOCTLPTR_REQ_ACK: + break; + case MSGBUF_TYPE_IOCTL_CMPLT: + resp = (struct msgbuf_ioctl_resp_hdr *)buf; + sc->sc_ioctl_resp_pktid = letoh32(resp->msg.request_id); + sc->sc_ioctl_resp_ret_len = letoh16(resp->resp_len); + sc->sc_ioctl_resp_status = letoh16(resp->compl_hdr.status); + if_rxr_put(&sc->sc_ioctl_ring, 1); + bwfm_pci_fill_rx_rings(sc); + wakeup(&sc->sc_ioctl_buf); + break; + case MSGBUF_TYPE_WL_EVENT: + event = (struct msgbuf_rx_event *)buf; + m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, + letoh32(event->msg.request_id)); + if (m == NULL) + break; + m_adj(m, sc->sc_rx_dataoffset); + bwfm_rx(&sc->sc_sc, mtod(m, char *), letoh16(event->event_data_len)); + m_freem(m); + if_rxr_put(&sc->sc_event_ring, 1); + bwfm_pci_fill_rx_rings(sc); + break; + default: + printf("%s: msgtype 0x%08x\n", __func__, msg->msgtype); + break; + } +} + +/* Bus core helpers */ +void +bwfm_pci_select_core(struct bwfm_pci_softc *sc, int id) +{ + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_core *core; + + core = bwfm_chip_get_core(bwfm, id); + if (core == NULL) { + printf("%s: could not find core to select", DEVNAME(sc)); + return; + } + + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW, core->co_base); + if (pci_conf_read(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW) != core->co_base) + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW, core->co_base); +} + +uint32_t +bwfm_pci_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + uint32_t page, offset; + + page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1); + offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page); + return bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset); +} + +void +bwfm_pci_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + uint32_t page, offset; + + page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1); + offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset, val); +} + +int +bwfm_pci_buscore_prepare(struct bwfm_softc *bwfm) +{ + return 0; +} + +int +bwfm_pci_buscore_reset(struct bwfm_softc *bwfm) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct bwfm_core *core; + uint32_t reg; + int i; + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + reg = pci_conf_read(sc->sc_pc, sc->sc_tag, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_CFGREG_LINK_STATUS_CTRL, + reg & ~BWFM_PCI_CFGREG_LINK_STATUS_CTRL_ASPM_ENAB); + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_CHIPCOMMON); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_CHIP_REG_WATCHDOG, 4); + delay(100 * 1000); + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL, reg); + + core = bwfm_chip_get_core(bwfm, BWFM_AGENT_CORE_PCIE2); + if (core->co_rev <= 13) { + uint16_t cfg_offset[] = { + BWFM_PCI_CFGREG_STATUS_CMD, + BWFM_PCI_CFGREG_PM_CSR, + BWFM_PCI_CFGREG_MSI_CAP, + BWFM_PCI_CFGREG_MSI_ADDR_L, + BWFM_PCI_CFGREG_MSI_ADDR_H, + BWFM_PCI_CFGREG_MSI_DATA, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL2, + BWFM_PCI_CFGREG_RBAR_CTRL, + BWFM_PCI_CFGREG_PML1_SUB_CTRL1, + BWFM_PCI_CFGREG_REG_BAR2_CONFIG, + BWFM_PCI_CFGREG_REG_BAR3_CONFIG, + }; + + for (i = 0; i < nitems(cfg_offset); i++) { + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGADDR, cfg_offset[i]); + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA); + DPRINTFN(3, ("%s: config offset 0x%04x, value 0x%04x\n", + DEVNAME(sc), cfg_offset[i], reg)); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA, reg); + } + } + + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT); + if (reg != 0xffffffff) + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT, reg); + + return 0; +} + +void +bwfm_pci_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, 0, rstvec); +} + +int +bwfm_pci_txdata(struct bwfm_softc *bwfm, struct mbuf *m) +{ +#ifdef BWFM_DEBUG + struct bwfm_pci_softc *sc = (void *)bwfm; + DPRINTF(("%s: %s\n", DEVNAME(sc), __func__)); +#endif + return ENOBUFS; +} + +int +bwfm_pci_intr(void *v) +{ + struct bwfm_pci_softc *sc = (void *)v; + uint32_t status; + + if ((status = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT)) == 0) + return 0; + + bwfm_pci_intr_disable(sc); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT, status); + + if (status & (BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1)) + printf("%s: handle MB data\n", __func__); + + if (status & BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB) { + bwfm_pci_ring_rx(sc, &sc->sc_rx_complete); + bwfm_pci_ring_rx(sc, &sc->sc_tx_complete); + bwfm_pci_ring_rx(sc, &sc->sc_ctrl_complete); + } + +#if 0 + { + uint32_t newidx = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_WRITEIDX); + uint32_t readidx = 0; + + while (newidx != readidx) { + uint8_t ch = bus_space_read_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_buf_addr + readidx); + readidx++; + if (readidx == sc->sc_console_buf_size) + readidx = 0; + if (ch == '\r') + continue; + printf("%c", ch); + } + } +#endif + + bwfm_pci_intr_enable(sc); + return 1; +} + +void +bwfm_pci_intr_enable(struct bwfm_pci_softc *sc) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXMASK, + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB); +} + +void +bwfm_pci_intr_disable(struct bwfm_pci_softc *sc) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXMASK, 0); +} + +/* Msgbuf protocol implementation */ +int +bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *bwfm, int ifidx, + int cmd, char *buf, size_t *len) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct msgbuf_ioctl_req_hdr *req; + struct mbuf *m; + size_t buflen; + int s; + + s = splnet(); + sc->sc_ioctl_resp_pktid = -1; + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) { + printf("%s: cannot reserve for write\n", DEVNAME(sc)); + splx(s); + return 1; + } + req->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; + req->msg.ifidx = 0; + req->msg.flags = 0; + req->msg.request_id = htole32(MSGBUF_IOCTL_REQ_PKTID); + req->cmd = htole32(cmd); + req->output_buf_len = htole16(*len); + req->trans_id = htole16(sc->sc_ioctl_reqid++); + + buflen = min(*len, BWFM_DMA_H2D_IOCTL_BUF_LEN); + req->input_buf_len = htole16(buflen); + req->req_buf_addr.high_addr = + htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) >> 32); + req->req_buf_addr.low_addr = + htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) & 0xffffffff); + if (buf) + memcpy(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), buf, buflen); + else + memset(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), 0, buflen); + + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); + splx(s); + + if (sc->sc_ioctl_poll) { + int i; + for (i = 0; i < 100; i++) { + if (sc->sc_ioctl_resp_pktid != -1) + break; + delay(10 * 1000); + } + if (i == 100) { + printf("%s: timeout waiting for ioctl response\n", + DEVNAME(sc)); + return 1; + } + } else if (tsleep(&sc->sc_ioctl_buf, PCATCH, "bwfm", hz)) { + printf("%s: timeout waiting for ioctl response\n", + DEVNAME(sc)); + return 1; + } + + m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, sc->sc_ioctl_resp_pktid); + if (m == NULL) + return 1; + + *len = min(buflen, sc->sc_ioctl_resp_ret_len); + if (buf) + memcpy(buf, mtod(m, char *), *len); + m_freem(m); + splx(s); + + return 0; +} + +int +bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *bwfm, int ifidx, + int cmd, char *buf, size_t len) +{ + return bwfm_pci_msgbuf_query_dcmd(bwfm, ifidx, cmd, buf, &len); +} diff --git a/sys/dev/pci/if_bwfm_pci.h b/sys/dev/pci/if_bwfm_pci.h new file mode 100644 index 00000000000..afb29bf9f4d --- /dev/null +++ b/sys/dev/pci/if_bwfm_pci.h @@ -0,0 +1,277 @@ +/* $OpenBSD: if_bwfm_pci.h,v 1.1 2017/12/24 19:50:56 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2017 Patrick Wildt + * + * Permission to use, copy, modify, and/or 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. + */ + +/* Registers */ +#define BWFM_PCI_BAR0_WINDOW 0x80 +#define BWFM_PCI_BAR0_REG_SIZE 0x1000 + +#define BWFM_PCI_ARMCR4REG_BANKIDX 0x40 +#define BWFM_PCI_ARMCR4REG_BANKPDA 0x4C + +#define BWFM_PCI_PCIE2REG_INTMASK 0x24 +#define BWFM_PCI_PCIE2REG_MAILBOXINT 0x48 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK 0x4C +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 0x0100 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1 0x0200 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB0 0x10000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB1 0x20000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB0 0x40000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB1 0x80000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB0 0x100000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB1 0x200000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB0 0x400000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB1 0x800000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB \ + (BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB1 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB1 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB1 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB1) + +#define BWFM_PCI_PCIE2REG_CONFIGADDR 0x120 +#define BWFM_PCI_PCIE2REG_CONFIGDATA 0x124 +#define BWFM_PCI_PCIE2REG_H2D_MAILBOX 0x140 + +#define BWFM_PCI_CFGREG_STATUS_CMD 0x004 +#define BWFM_PCI_CFGREG_PM_CSR 0x04C +#define BWFM_PCI_CFGREG_MSI_CAP 0x058 +#define BWFM_PCI_CFGREG_MSI_ADDR_L 0x05C +#define BWFM_PCI_CFGREG_MSI_ADDR_H 0x060 +#define BWFM_PCI_CFGREG_MSI_DATA 0x064 +#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL 0x0BC +#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL_ASPM_ENAB 0x3 +#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL2 0x0DC +#define BWFM_PCI_CFGREG_RBAR_CTRL 0x228 +#define BWFM_PCI_CFGREG_PML1_SUB_CTRL1 0x248 +#define BWFM_PCI_CFGREG_REG_BAR2_CONFIG 0x4E0 +#define BWFM_PCI_CFGREG_REG_BAR3_CONFIG 0x4F4 + +#define BWFM_RAMSIZE 0x6c +#define BWFM_RAMSIZE_MAGIC 0x534d4152 /* SMAR */ + +#define BWFM_SHARED_INFO 0x000 +#define BWFM_SHARED_INFO_MIN_VERSION 5 +#define BWFM_SHARED_INFO_MAX_VERSION 6 +#define BWFM_SHARED_INFO_VERSION_MASK 0x00FF +#define BWFM_SHARED_INFO_DMA_INDEX 0x10000 +#define BWFM_SHARED_INFO_DMA_2B_IDX 0x100000 +#define BWFM_SHARED_CONSOLE_ADDR 0x14 +#define BWFM_SHARED_MAX_RXBUFPOST 0x22 +#define BWFM_SHARED_MAX_RXBUFPOST_DEFAULT 255 +#define BWFM_SHARED_RX_DATAOFFSET 0x24 +#define BWFM_SHARED_HTOD_MB_DATA_ADDR 0x28 +#define BWFM_SHARED_DTOH_MB_DATA_ADDR 0x2c +#define BWFM_SHARED_RING_INFO_ADDR 0x30 +#define BWFM_SHARED_DMA_SCRATCH_LEN 0x34 +#define BWFM_SHARED_DMA_SCRATCH_ADDR_LOW 0x38 +#define BWFM_SHARED_DMA_SCRATCH_ADDR_HIGH 0x3c +#define BWFM_SHARED_DMA_RINGUPD_LEN 0x40 +#define BWFM_SHARED_DMA_RINGUPD_ADDR_LOW 0x44 +#define BWFM_SHARED_DMA_RINGUPD_ADDR_HIGH 0x48 + +#define BWFM_RING_MAX_ITEM 0x04 +#define BWFM_RING_LEN_ITEMS 0x06 +#define BWFM_RING_MEM_BASE_ADDR_LOW 0x08 +#define BWFM_RING_MEM_BASE_ADDR_HIGH 0x0c +#define BWFM_RING_MEM_SZ 16 + +#define BWFM_CONSOLE_BUFADDR 0x08 +#define BWFM_CONSOLE_BUFSIZE 0x0c +#define BWFM_CONSOLE_WRITEIDX 0x10 + +struct bwfm_pci_ringinfo { + uint32_t ringmem; + uint32_t h2d_w_idx_ptr; + uint32_t h2d_r_idx_ptr; + uint32_t d2h_w_idx_ptr; + uint32_t d2h_r_idx_ptr; + uint32_t h2d_w_idx_hostaddr_low; + uint32_t h2d_w_idx_hostaddr_high; + uint32_t h2d_r_idx_hostaddr_low; + uint32_t h2d_r_idx_hostaddr_high; + uint32_t d2h_w_idx_hostaddr_low; + uint32_t d2h_w_idx_hostaddr_high; + uint32_t d2h_r_idx_hostaddr_low; + uint32_t d2h_r_idx_hostaddr_high; + uint16_t max_flowrings; + uint16_t max_submissionrings; + uint16_t max_completionrings; +}; + +/* Msgbuf defines */ +#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 /* msecs */ +#define MSGBUF_IOCTL_REQ_PKTID 0xFFFE +#define MSGBUF_MAX_PKT_SIZE 2048 + +#define MSGBUF_TYPE_GEN_STATUS 0x1 +#define MSGBUF_TYPE_RING_STATUS 0x2 +#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3 +#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4 +#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5 +#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6 +#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7 +#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8 +#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9 +#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA +#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB +#define MSGBUF_TYPE_IOCTL_CMPLT 0xC +#define MSGBUF_TYPE_EVENT_BUF_POST 0xD +#define MSGBUF_TYPE_WL_EVENT 0xE +#define MSGBUF_TYPE_TX_POST 0xF +#define MSGBUF_TYPE_TX_STATUS 0x10 +#define MSGBUF_TYPE_RXBUF_POST 0x11 +#define MSGBUF_TYPE_RX_CMPLT 0x12 +#define MSGBUF_TYPE_LPBK_DMAXFER 0x13 +#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 + +struct msgbuf_common_hdr { + uint8_t msgtype; + uint8_t ifidx; + uint8_t flags; + uint8_t rsvd0; + uint32_t request_id; +}; + +struct msgbuf_buf_addr { + uint32_t low_addr; + uint32_t high_addr; +}; + +struct msgbuf_ioctl_req_hdr { + struct msgbuf_common_hdr msg; + uint32_t cmd; + uint16_t trans_id; + uint16_t input_buf_len; + uint16_t output_buf_len; + uint16_t rsvd0[3]; + struct msgbuf_buf_addr req_buf_addr; + uint32_t rsvd1[2]; +}; + +struct msgbuf_tx_msghdr { + struct msgbuf_common_hdr msg; + uint8_t txhdr[ETHER_ADDR_LEN]; + uint8_t flags; + uint8_t seg_cnt; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; + uint16_t metadata_buf_len; + uint16_t data_len; + uint32_t rsvd0; +}; + +struct msgbuf_rx_bufpost { + struct msgbuf_common_hdr msg; + uint16_t metadata_buf_len; + uint16_t data_buf_len; + uint32_t rsvd0; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; +}; + +struct msgbuf_rx_ioctl_resp_or_event { + struct msgbuf_common_hdr msg; + uint16_t host_buf_len; + uint16_t rsvd0[3]; + struct msgbuf_buf_addr host_buf_addr; + uint32_t rsvd1[4]; +}; + +struct msgbuf_completion_hdr { + uint16_t status; + uint16_t flow_ring_id; +}; + +struct msgbuf_rx_event { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t event_data_len; + uint16_t seqnum; + uint16_t rsvd0[4]; +}; + +struct msgbuf_ioctl_resp_hdr { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t resp_len; + uint16_t trans_id; + uint32_t cmd; + uint32_t rsvd0; +}; + +struct msgbuf_tx_status { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t metadata_len; + uint16_t tx_status; +}; + +struct msgbuf_rx_complete { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t metadata_len; + uint16_t data_len; + uint16_t data_offset; + uint16_t flags; + uint32_t rx_status_0; + uint32_t rx_status_1; + uint32_t rsvd0; +}; + +struct msgbuf_tx_flowring_create_req { + struct msgbuf_common_hdr msg; + uint8_t da[ETHER_ADDR_LEN]; + uint8_t sa[ETHER_ADDR_LEN]; + uint8_t tid; + uint8_t if_flags; + uint16_t flow_ring_id; + uint8_t tc; + uint8_t priority; + uint16_t int_vector; + uint16_t max_items; + uint16_t len_item; + struct msgbuf_buf_addr flow_ring_addr; +}; + +struct msgbuf_tx_flowring_delete_req { + struct msgbuf_common_hdr msg; + uint16_t flow_ring_id; + uint16_t reason; + uint32_t rsvd0[7]; +}; + +struct msgbuf_flowring_create_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint32_t rsvd0[3]; +}; + +struct msgbuf_flowring_delete_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint32_t rsvd0[3]; +}; + +struct msgbuf_flowring_flush_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint32_t rsvd0[3]; +};