From fe77735960cd85f4ce0b914b40028b75230d458c Mon Sep 17 00:00:00 2001 From: Simon Piriou Date: Sun, 30 Apr 2017 20:29:48 +0200 Subject: [PATCH] bcmf: add netdev support for Broadcom FullMAC driver --- configs/photon/wlan/defconfig | 57 ++- drivers/wireless/ieee80211/bcmf_bdc.c | 82 +++- drivers/wireless/ieee80211/bcmf_bdc.h | 15 +- drivers/wireless/ieee80211/bcmf_cdc.c | 13 +- drivers/wireless/ieee80211/bcmf_driver.c | 76 +++- drivers/wireless/ieee80211/bcmf_driver.h | 34 +- drivers/wireless/ieee80211/bcmf_netdev.c | 473 +++++++++-------------- drivers/wireless/ieee80211/bcmf_netdev.h | 47 +++ drivers/wireless/ieee80211/bcmf_sdio.c | 116 +++++- drivers/wireless/ieee80211/bcmf_sdio.h | 25 +- drivers/wireless/ieee80211/bcmf_sdpcm.c | 295 +++++++------- drivers/wireless/ieee80211/bcmf_sdpcm.h | 11 +- drivers/wireless/ieee80211/bcmf_utils.c | 65 ++++ drivers/wireless/ieee80211/bcmf_utils.h | 10 + 14 files changed, 808 insertions(+), 511 deletions(-) create mode 100644 drivers/wireless/ieee80211/bcmf_netdev.h diff --git a/configs/photon/wlan/defconfig b/configs/photon/wlan/defconfig index 190a639960..7f886b89b1 100644 --- a/configs/photon/wlan/defconfig +++ b/configs/photon/wlan/defconfig @@ -16,7 +16,7 @@ CONFIG_HOST_LINUX=y # # Build Configuration # -# CONFIG_APPS_DIR="../apps" +CONFIG_APPS_DIR="../apps" CONFIG_BUILD_FLAT=y # CONFIG_BUILD_2PASS is not set @@ -65,7 +65,10 @@ CONFIG_DEBUG_INFO=y # CONFIG_DEBUG_GRAPHICS is not set # CONFIG_DEBUG_LIB is not set # CONFIG_DEBUG_MM is not set -# CONFIG_DEBUG_NET is not set +CONFIG_DEBUG_NET=y +CONFIG_DEBUG_NET_ERROR=y +CONFIG_DEBUG_NET_WARN=y +CONFIG_DEBUG_NET_INFO=y CONFIG_DEBUG_WIRELESS=y CONFIG_DEBUG_WIRELESS_ERROR=y CONFIG_DEBUG_WIRELESS_WARN=y @@ -986,9 +989,9 @@ CONFIG_NET=y # # Driver buffer configuration # -CONFIG_NET_ETH_MTU=590 +CONFIG_NET_ETH_MTU=800 CONFIG_NET_ETH_TCP_RECVWNDO=536 -CONFIG_NET_GUARDSIZE=2 +CONFIG_NET_GUARDSIZE=32 # # Data link support @@ -1018,12 +1021,14 @@ CONFIG_NET_IPv4=y # CONFIG_NSOCKET_DESCRIPTORS=8 CONFIG_NET_NACTIVESOCKETS=16 -# CONFIG_NET_SOCKOPTS is not set +CONFIG_NET_SOCKOPTS=y +# CONFIG_NET_SOLINGER is not set # # Raw Socket Support # -# CONFIG_NET_PKT is not set +CONFIG_NET_PKT=y +CONFIG_NET_PKT_CONNS=1 # # Unix Domain Socket Support @@ -1049,13 +1054,19 @@ CONFIG_NET_TCP_RECVDELAY=0 # # UDP Networking # -# CONFIG_NET_UDP is not set +CONFIG_NET_UDP=y # CONFIG_NET_UDP_NO_STACK is not set +# CONFIG_NET_UDP_CHECKSUMS is not set +CONFIG_NET_UDP_CONNS=8 +CONFIG_NET_BROADCAST=y +# CONFIG_NET_RXAVAIL is not set +CONFIG_NET_UDP_READAHEAD=y # # ICMP Networking Support # -# CONFIG_NET_ICMP is not set +CONFIG_NET_ICMP=y +# CONFIG_NET_ICMP_PING is not set # # IGMPv2 Client Support @@ -1070,6 +1081,7 @@ CONFIG_NET_ARPTAB_SIZE=16 CONFIG_NET_ARP_MAXAGE=120 # CONFIG_NET_ARP_IPIN is not set # CONFIG_NET_ARP_SEND is not set +# CONFIG_NET_ARP_DUMP is not set # # User-space networking stack API @@ -1106,6 +1118,7 @@ CONFIG_FS_READABLE=y CONFIG_FS_MQUEUE_MPATH="/var/mqueue" # CONFIG_FS_RAMMAP is not set # CONFIG_FS_FAT is not set +# CONFIG_NFS is not set # CONFIG_FS_NXFFS is not set # CONFIG_FS_ROMFS is not set # CONFIG_FS_TMPFS is not set @@ -1251,6 +1264,15 @@ CONFIG_LIBC_NETDB=y # NETDB Support # # CONFIG_NETDB_HOSTFILE is not set +CONFIG_NETDB_DNSCLIENT=y +CONFIG_NETDB_DNSCLIENT_ENTRIES=8 +CONFIG_NETDB_DNSCLIENT_NAMESIZE=32 +CONFIG_NETDB_DNSCLIENT_LIFESEC=3600 +CONFIG_NETDB_DNSCLIENT_MAXRESPONSE=96 +# CONFIG_NETDB_RESOLVCONF is not set +# CONFIG_NETDB_DNSSERVER_NOADDR is not set +CONFIG_NETDB_DNSSERVER_IPv4=y +CONFIG_NETDB_DNSSERVER_IPv4ADDR=0x0a000001 # CONFIG_LIBC_IOCTL_VARIADIC is not set CONFIG_LIB_SENDFILE_BUFSIZE=512 @@ -1306,6 +1328,7 @@ CONFIG_BUILTIN_PROXY_STACKSIZE=1024 # CONFIG_EXAMPLES_CONFIGDATA is not set # CONFIG_EXAMPLES_CXXTEST is not set # CONFIG_EXAMPLES_DHCPD is not set +# CONFIG_EXAMPLES_DISCOVER is not set # CONFIG_EXAMPLES_ELF is not set # CONFIG_EXAMPLES_FTPC is not set # CONFIG_EXAMPLES_FTPD is not set @@ -1320,6 +1343,7 @@ CONFIG_BUILTIN_PROXY_STACKSIZE=1024 # CONFIG_EXAMPLES_MM is not set # CONFIG_EXAMPLES_MODBUS is not set # CONFIG_EXAMPLES_MOUNT is not set +# CONFIG_EXAMPLES_NETPKT is not set # CONFIG_EXAMPLES_NETTEST is not set # CONFIG_EXAMPLES_NRF24L01TERM is not set CONFIG_EXAMPLES_NSH=y @@ -1351,6 +1375,8 @@ CONFIG_EXAMPLES_NSH=y # CONFIG_EXAMPLES_TELNETD is not set # CONFIG_EXAMPLES_TIFF is not set # CONFIG_EXAMPLES_TOUCHSCREEN is not set +# CONFIG_EXAMPLES_UDP is not set +# CONFIG_EXAMPLES_UDPBLASTER is not set # CONFIG_EXAMPLES_USBSERIAL is not set # CONFIG_EXAMPLES_WATCHDOG is not set # CONFIG_EXAMPLES_WEBSERVER is not set @@ -1391,12 +1417,20 @@ CONFIG_EXAMPLES_NSH=y # Network Utilities # # CONFIG_NETUTILS_CODECS is not set +CONFIG_NETUTILS_DHCPC=y +# CONFIG_NETUTILS_DHCPD is not set +# CONFIG_NETUTILS_DISCOVER is not set # CONFIG_NETUTILS_ESP8266 is not set # CONFIG_NETUTILS_FTPC is not set # CONFIG_NETUTILS_JSON is not set CONFIG_NETUTILS_NETLIB=y +# CONFIG_NETUTILS_NTPCLIENT is not set +CONFIG_NETUTILS_PING=y +CONFIG_NETUTILS_PING_SIGNO=13 +# CONFIG_NETUTILS_PPPD is not set # CONFIG_NETUTILS_SMTP is not set # CONFIG_NETUTILS_TELNETD is not set +# CONFIG_NETUTILS_TFTPC is not set # CONFIG_NETUTILS_WEBCLIENT is not set # CONFIG_NETUTILS_WEBSERVER is not set # CONFIG_NETUTILS_XMLRPC is not set @@ -1456,8 +1490,10 @@ CONFIG_NSH_DISABLE_LOSMART=y # CONFIG_NSH_DISABLE_MOUNT is not set # CONFIG_NSH_DISABLE_MV is not set # CONFIG_NSH_DISABLE_MW is not set +# CONFIG_NSH_DISABLE_NSLOOKUP is not set CONFIG_NSH_DISABLE_PRINTF=y # CONFIG_NSH_DISABLE_PS is not set +# CONFIG_NSH_DISABLE_PING is not set # CONFIG_NSH_DISABLE_PUT is not set # CONFIG_NSH_DISABLE_PWD is not set # CONFIG_NSH_DISABLE_RM is not set @@ -1510,13 +1546,14 @@ CONFIG_NSH_NETINIT=y # # IP Address Configuration # +CONFIG_NSH_DHCPC=y # # IPv4 Addresses # -CONFIG_NSH_IPADDR=0x0a000002 -CONFIG_NSH_DRIPADDR=0x0a000001 +CONFIG_NSH_DRIPADDR=0xc0a80001 CONFIG_NSH_NETMASK=0xffffff00 +# CONFIG_NSH_DNS is not set # CONFIG_NSH_NOMAC is not set CONFIG_NSH_MAX_ROUNDTRIP=20 # CONFIG_NSH_LOGIN is not set diff --git a/drivers/wireless/ieee80211/bcmf_bdc.c b/drivers/wireless/ieee80211/bcmf_bdc.c index 8f448e5bc0..2c92232ff3 100644 --- a/drivers/wireless/ieee80211/bcmf_bdc.c +++ b/drivers/wireless/ieee80211/bcmf_bdc.c @@ -101,31 +101,29 @@ static const uint8_t bcmf_broadcom_oui[] = {0x00, 0x10, 0x18}; * Private Functions ****************************************************************************/ -struct bcmf_frame_s* bcmf_bdc_allocate_frame(FAR struct bcmf_dev_s *priv, - uint32_t len, bool block) -{ - if (len <= 0) - { - return NULL; - } - - /* Allocate data frame */ - - return priv->bus->allocate_frame(priv, - sizeof(struct bcmf_bdc_header) + len, - false, block); -} - /**************************************************************************** * Public Functions ****************************************************************************/ -int bcmf_bdc_process_data_frame(FAR struct bcmf_dev_s *priv, - struct bcmf_frame_s *frame) +struct bcmf_frame_s* bcmf_bdc_allocate_frame(FAR struct bcmf_dev_s *priv, + uint32_t len, bool block) { - wlinfo("Data message\n"); - bcmf_hexdump(frame->base, frame->len, (unsigned long)frame->base); - return OK; + struct bcmf_frame_s *frame; + + /* Allocate data frame */ + + // TODO check for integer overflow + frame = priv->bus->allocate_frame(priv, + sizeof(struct bcmf_bdc_header) + len, block, false); + + if (!frame) + { + return NULL; + } + + frame->data += sizeof(struct bcmf_bdc_header); + + return frame; } int bcmf_bdc_process_event_frame(FAR struct bcmf_dev_s *priv, @@ -240,4 +238,48 @@ int bcmf_event_push_config(FAR struct bcmf_dev_s *priv) } return OK; +} + +int bcmf_bdc_transmit_frame(FAR struct bcmf_dev_s *priv, + struct bcmf_frame_s *frame) +{ + struct bcmf_bdc_header* header; + + /* Set frame data for lower layer */ + + frame->data -= sizeof(struct bcmf_bdc_header); + header = (struct bcmf_bdc_header*)frame->data; + + /* Setup data frame header */ + + header->flags = 0x20; /* Set bdc protocol version */ + header->priority = 0; // TODO handle priority + header->flags2 = CHIP_STA_INTERFACE; + header->data_offset = 0; + + /* Send frame */ + + return priv->bus->txframe(priv, frame, false); +} + +struct bcmf_frame_s* bcmf_bdc_rx_frame(FAR struct bcmf_dev_s *priv) +{ + unsigned int frame_len; + struct bcmf_frame_s *frame = priv->bus->rxframe(priv); + + /* Process bdc header */ + + frame_len = frame->len - (unsigned int)(frame->data - frame->base); + + if (frame_len < sizeof(struct bcmf_bdc_header)) + { + wlerr("Data frame too small\n"); + priv->bus->free_frame(priv, frame); + return NULL; + } + + /* Transmit frame to upper layer */ + + frame->data += sizeof(struct bcmf_bdc_header); + return frame; } \ No newline at end of file diff --git a/drivers/wireless/ieee80211/bcmf_bdc.h b/drivers/wireless/ieee80211/bcmf_bdc.h index 93e0b6c707..48003bdc5c 100644 --- a/drivers/wireless/ieee80211/bcmf_bdc.h +++ b/drivers/wireless/ieee80211/bcmf_bdc.h @@ -69,12 +69,23 @@ typedef void (*event_handler_t)(FAR struct bcmf_dev_s *priv, * Public Function Prototypes ****************************************************************************/ -int bcmf_bdc_process_data_frame(FAR struct bcmf_dev_s *priv, - struct bcmf_frame_s *frame); +/* Function called from lower layer */ int bcmf_bdc_process_event_frame(FAR struct bcmf_dev_s *priv, struct bcmf_frame_s *frame); +/* Function called from upper layer */ + +struct bcmf_frame_s* bcmf_bdc_allocate_frame(FAR struct bcmf_dev_s *priv, + uint32_t len, bool block); + +int bcmf_bdc_transmit_frame(FAR struct bcmf_dev_s *priv, + struct bcmf_frame_s *frame); + +struct bcmf_frame_s* bcmf_bdc_rx_frame(FAR struct bcmf_dev_s *priv); + +/* Event frames API */ + int bcmf_event_register(FAR struct bcmf_dev_s *priv, event_handler_t handler, unsigned int event_id); diff --git a/drivers/wireless/ieee80211/bcmf_cdc.c b/drivers/wireless/ieee80211/bcmf_cdc.c index bff79328e8..a825f6dbdd 100644 --- a/drivers/wireless/ieee80211/bcmf_cdc.c +++ b/drivers/wireless/ieee80211/bcmf_cdc.c @@ -128,13 +128,6 @@ struct bcmf_frame_s* bcmf_cdc_allocate_frame(FAR struct bcmf_dev_s *priv, data_len = 0; } - if (data_len + name_len + sizeof(struct bcmf_cdc_header) < data_len) - { - /* Integer overflow */ - - return NULL; - } - /* Allocate control frame */ frame = priv->bus->allocate_frame(priv, @@ -177,7 +170,7 @@ int bcmf_cdc_sendframe(FAR struct bcmf_dev_s *priv, uint32_t cmd, /* Send frame */ - return priv->bus->txframe(priv, frame); + return priv->bus->txframe(priv, frame, true); } int bcmf_cdc_control_request(FAR struct bcmf_dev_s *priv, @@ -230,7 +223,9 @@ int bcmf_cdc_control_request_unsafe(FAR struct bcmf_dev_s *priv, ret = bcmf_cdc_sendframe(priv, cmd, ifidx, set, frame); if (ret != OK) { - // TODO free allocated iovar buffer here + /* Free allocated iovar buffer */ + + priv->bus->free_frame(priv, frame); return ret; } diff --git a/drivers/wireless/ieee80211/bcmf_driver.c b/drivers/wireless/ieee80211/bcmf_driver.c index d2c522347a..8c623b75ad 100644 --- a/drivers/wireless/ieee80211/bcmf_driver.c +++ b/drivers/wireless/ieee80211/bcmf_driver.c @@ -49,13 +49,13 @@ #include #include +#include #include "bcmf_driver.h" #include "bcmf_cdc.h" #include "bcmf_ioctl.h" #include "bcmf_utils.h" - -#include +#include "bcmf_netdev.h" #include "bcmf_sdio.h" /**************************************************************************** @@ -90,9 +90,6 @@ static void bcmf_free_device(FAR struct bcmf_dev_s *priv); static int bcmf_driver_initialize(FAR struct bcmf_dev_s *priv); -// FIXME add bcmf_netdev.h file -int bcmf_netdev_register(FAR struct bcmf_dev_s *priv); - // FIXME only for debug purpose static void bcmf_wl_default_event_handler(FAR struct bcmf_dev_s *priv, struct bcmf_event_s *event, unsigned int len); @@ -103,9 +100,8 @@ static void bcmf_wl_radio_event_handler(FAR struct bcmf_dev_s *priv, static void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, struct bcmf_event_s *event, unsigned int len); -#if 0 -static int bcmf_run_escan(FAR struct bcmf_dev_s *priv); -#endif +static void bcmf_wl_auth_event_handler(FAR struct bcmf_dev_s *priv, + struct bcmf_event_s *event, unsigned int len); /**************************************************************************** * Private Data @@ -149,6 +145,18 @@ FAR struct bcmf_dev_s* bcmf_allocate_device(void) goto exit_free_priv; } + /* Init authentication signal semaphore */ + + if ((ret = sem_init(&priv->auth_signal, 0, 0)) != OK) + { + goto exit_free_priv; + } + + if ((ret = sem_setprotocol(&priv->auth_signal, SEM_PRIO_NONE)) != OK) + { + goto exit_free_priv; + } + /* Init scan timeout timer */ priv->scan_status = BCMF_SCAN_DISABLED; @@ -173,23 +181,27 @@ void bcmf_free_device(FAR struct bcmf_dev_s *priv) kmm_free(priv); } -int bcmf_wl_set_mac_address(FAR struct bcmf_dev_s *priv, uint8_t *addr) +int bcmf_wl_set_mac_address(FAR struct bcmf_dev_s *priv, struct ifreq *req) { int ret; - uint32_t out_len = 6; + uint32_t out_len = IFHWADDRLEN; ret = bcmf_cdc_iovar_request(priv, CHIP_STA_INTERFACE, true, - IOVAR_STR_CUR_ETHERADDR, addr, - &out_len); + IOVAR_STR_CUR_ETHERADDR, + (uint8_t*)req->ifr_hwaddr.sa_data, + &out_len); if (ret != OK) { return ret; } wlinfo("MAC address updated %02X:%02X:%02X:%02X:%02X:%02X\n", - addr[0], addr[1], addr[2], - addr[3], addr[4], addr[5]); - memcpy(priv->bc_dev.d_mac.ether.ether_addr_octet, addr, ETHER_ADDR_LEN); + req->ifr_hwaddr.sa_data[0], req->ifr_hwaddr.sa_data[1], + req->ifr_hwaddr.sa_data[2], req->ifr_hwaddr.sa_data[3], + req->ifr_hwaddr.sa_data[4], req->ifr_hwaddr.sa_data[5]); + + memcpy(priv->bc_dev.d_mac.ether.ether_addr_octet, + req->ifr_hwaddr.sa_data, ETHER_ADDR_LEN); return OK; } @@ -281,6 +293,10 @@ int bcmf_driver_initialize(FAR struct bcmf_dev_s *priv) bcmf_event_register(priv, bcmf_wl_scan_event_handler, WLC_E_ESCAN_RESULT); + /* Register SET_SSID event */ + + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_SET_SSID); + if (bcmf_event_push_config(priv)) { return -EIO; @@ -301,8 +317,25 @@ void bcmf_wl_default_event_handler(FAR struct bcmf_dev_s *priv, void bcmf_wl_radio_event_handler(FAR struct bcmf_dev_s *priv, struct bcmf_event_s *event, unsigned int len) { - wlinfo("Got radio event %d from <%s>\n", bcmf_getle32(&event->type), - event->src_name); + // wlinfo("Got radio event %d from <%s>\n", bcmf_getle32(&event->type), + // event->src_name); +} + +void bcmf_wl_auth_event_handler(FAR struct bcmf_dev_s *priv, + struct bcmf_event_s *event, unsigned int len) +{ + uint32_t type; + + type = bcmf_getle32(&event->type); + + wlinfo("Got auth event %d from <%s>\n", type, event->src_name); + + if (type == WLC_E_SET_SSID) + { + /* Auth complete */ + + sem_post(&priv->auth_signal); + } } void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, @@ -508,8 +541,6 @@ int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv) uint32_t out_len; uint32_t value; - wlinfo("Enter\n"); - /* Set active scan mode */ value = 0; @@ -517,14 +548,15 @@ int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv) if (bcmf_cdc_ioctl(priv, CHIP_STA_INTERFACE, true, WLC_SET_PASSIVE_SCAN, (uint8_t*)&value, &out_len)) { - return -EIO; + ret = -EIO; + goto exit_failed; } /* Lock control_mutex semaphore */ if ((ret = sem_wait(&priv->control_mutex)) != OK) { - return ret; + goto exit_failed; } /* Default request structure */ @@ -581,6 +613,8 @@ exit_free_params: exit_sem_post: sem_post(&priv->control_mutex); priv->scan_status = BCMF_SCAN_DISABLED; +exit_failed: + wlinfo("Failed\n"); return ret; } diff --git a/drivers/wireless/ieee80211/bcmf_driver.h b/drivers/wireless/ieee80211/bcmf_driver.h index edbd911845..c264ca5da9 100644 --- a/drivers/wireless/ieee80211/bcmf_driver.h +++ b/drivers/wireless/ieee80211/bcmf_driver.h @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -65,13 +66,13 @@ struct bcmf_dev_s bool bc_bifup; /* true:ifup false:ifdown */ WDOG_ID bc_txpoll; /* TX poll timer */ - WDOG_ID bc_txtimeout; /* TX timeout timer */ struct work_s bc_irqwork; /* For deferring interrupt work to the work queue */ struct work_s bc_pollwork; /* For deferring poll work to the work queue */ /* This holds the information visible to the NuttX network */ - struct net_driver_s bc_dev; /* Network interface structure */ + struct net_driver_s bc_dev; /* Network interface structure */ + struct bcmf_frame_s *cur_tx_frame; /* Frame used to interface network layer */ /* Event registration array */ @@ -91,35 +92,48 @@ struct bcmf_dev_s int scan_status; /* Current scan status */ WDOG_ID scan_timeout; /* Scan timeout timer */ struct wl_escan_params *scan_params; /* Current scan parameters */ + + sem_t auth_signal; /* Authentication notification signal */ }; /* Default bus interface structure */ -struct bcmf_bus_dev_s { +struct bcmf_bus_dev_s +{ void (*stop)(FAR struct bcmf_dev_s *priv); - int (*txframe)(FAR struct bcmf_dev_s *priv, struct bcmf_frame_s *frame); + int (*txframe)(FAR struct bcmf_dev_s *priv, struct bcmf_frame_s *frame, + bool control); + struct bcmf_frame_s* (*rxframe)(FAR struct bcmf_dev_s *priv); - /* Frame buffer allocation primitive + /* Frame buffer allocation primitives * len - requested payload length * control - true if control frame else false * block - true to block until free frame is available */ struct bcmf_frame_s* (*allocate_frame)(FAR struct bcmf_dev_s *priv, - unsigned int len, bool control, bool block); + unsigned int len, bool block, + bool control); + + void (*free_frame)(FAR struct bcmf_dev_s *priv, struct bcmf_frame_s* frame); }; /* bcmf frame definition */ -struct bcmf_frame_s { - uint8_t *base; /* Frame base buffer used by low level layer (SDIO) */ - uint8_t *data; /* Payload data (Control, data and event messages) */ - unsigned int len; /* Frame buffer size */ +struct bcmf_frame_s +{ + uint8_t *base; /* Frame base buffer used by low level layer (SDIO) */ + uint8_t *data; /* Payload data (Control, data and event messages) */ + uint16_t len; /* Frame buffer size */ }; +int bcmf_wl_set_mac_address(FAR struct bcmf_dev_s *priv, struct ifreq *req); + int bcmf_wl_enable(FAR struct bcmf_dev_s *priv, bool enable); int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv); int bcmf_wl_is_scan_done(FAR struct bcmf_dev_s *priv); +int bcmf_wl_associate(FAR struct bcmf_dev_s *priv); + #endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_DRIVER_H */ diff --git a/drivers/wireless/ieee80211/bcmf_netdev.c b/drivers/wireless/ieee80211/bcmf_netdev.c index 7d9a2db225..32dd54176c 100644 --- a/drivers/wireless/ieee80211/bcmf_netdev.c +++ b/drivers/wireless/ieee80211/bcmf_netdev.c @@ -64,6 +64,7 @@ #include "bcmf_driver.h" #include "bcmf_cdc.h" +#include "bcmf_bdc.h" #include "bcmf_ioctl.h" /**************************************************************************** @@ -117,43 +118,20 @@ * Private Data ****************************************************************************/ -/* These statically allocated structur would mean that only a single - * instance of the device could be supported. In order to support multiple - * devices instances, this data would have to be allocated dynamically. - */ - -/* A single packet buffer per device is used here. There might be multiple - * packet buffers in a more complex, pipelined design. - * - * NOTE that if CONFIG_IEEE80211_BROADCOM_NINTERFACES were greater than 1, you would - * need a minimum on one packetbuffer per instance. Much better to be - * allocated dynamically. - */ - -static uint8_t g_pktbuf[MAX_NET_DEV_MTU + CONFIG_NET_GUARDSIZE]; - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* Common TX logic */ -static int bcmf_transmit(FAR struct bcmf_dev_s *priv); -static int bcmf_txpoll(FAR struct net_driver_s *dev); - -/* Interrupt handling */ - +static int bcmf_transmit(FAR struct bcmf_dev_s *priv, + struct bcmf_frame_s *frame); static void bcmf_receive(FAR struct bcmf_dev_s *priv); -static void bcmf_txdone(FAR struct bcmf_dev_s *priv); - -static void bcmf_interrupt_work(FAR void *arg); -static int bcmf_interrupt(int irq, FAR void *context, FAR void *arg); +static int bcmf_txpoll(FAR struct net_driver_s *dev); +static void bcmf_rxpoll(FAR void *arg); /* Watchdog timer expirations */ -static void bcmf_txtimeout_work(FAR void *arg); -static void bcmf_txtimeout_expiry(int argc, wdparm_t arg, ...); - static void bcmf_poll_work(FAR void *arg); static void bcmf_poll_expiry(int argc, wdparm_t arg, ...); @@ -185,6 +163,27 @@ static int bcmf_ioctl(FAR struct net_driver_s *dev, int cmd, * Private Functions ****************************************************************************/ +int bcmf_netdev_alloc_tx_frame(FAR struct bcmf_dev_s *priv) +{ + if (priv->cur_tx_frame != NULL) + { + /* Frame available */ + + return OK; + } + + /* Allocate frame for TX */ + + priv->cur_tx_frame = bcmf_bdc_allocate_frame(priv, MAX_NET_DEV_MTU, true); + if (!priv->cur_tx_frame) + { + wlerr("Cannot allocate TX frame\n"); + return -ENOMEM; + } + + return OK; +} + /**************************************************************************** * Name: bcmf_transmit * @@ -204,100 +203,27 @@ static int bcmf_ioctl(FAR struct net_driver_s *dev, int cmd, * ****************************************************************************/ -static int bcmf_transmit(FAR struct bcmf_dev_s *priv) +static int bcmf_transmit(FAR struct bcmf_dev_s *priv, + struct bcmf_frame_s *frame) { - /* Verify that the hardware is ready to send another packet. If we get - * here, then we are committed to sending a packet; Higher level logic - * must have assured that there is no transmission in progress. - */ + int ret; - /* Increment statistics */ + frame->len = priv->bc_dev.d_len + + (unsigned int)(frame->data - frame->base); + + ret = bcmf_bdc_transmit_frame(priv, frame); + + if (ret) + { + wlerr("Failed to transmit frame\n"); + return -EIO; + } NETDEV_TXPACKETS(priv->bc_dev); - /* Send the packet: address=priv->bc_dev.d_buf, length=priv->bc_dev.d_len */ - - /* Enable Tx interrupts */ - - /* Setup the TX timeout watchdog (perhaps restarting the timer) */ - - (void)wd_start(priv->bc_txtimeout, BCMF_TXTIMEOUT, - bcmf_txtimeout_expiry, 1, (wdparm_t)priv); return OK; } -/**************************************************************************** - * Name: bcmf_txpoll - * - * Description: - * The transmitter is available, check if the network has any outgoing - * packets ready to send. This is a callback from devif_poll(). - * devif_poll() may be called: - * - * 1. When the preceding TX packet send is complete, - * 2. When the preceding TX packet send timesout and the interface is reset - * 3. During normal TX polling - * - * Parameters: - * dev - Reference to the NuttX driver state structure - * - * Returned Value: - * OK on success; a negated errno on failure - * - * Assumptions: - * May or may not be called from an interrupt handler. In either case, - * the network is locked. - * - ****************************************************************************/ - -static int bcmf_txpoll(FAR struct net_driver_s *dev) -{ - FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; - - /* If the polling resulted in data that should be sent out on the network, - * the field d_len is set to a value > 0. - */ - - if (priv->bc_dev.d_len > 0) - { - /* Look up the destination MAC address and add it to the Ethernet - * header. - */ - -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - if (IFF_IS_IPv4(priv->bc_dev.d_flags)) -#endif - { - arp_out(&priv->bc_dev); - } -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - else -#endif - { - neighbor_out(&priv->bc_dev); - } -#endif /* CONFIG_NET_IPv6 */ - - /* Send the packet */ - - bcmf_transmit(priv); - - /* Check if there is room in the device to hold another packet. If not, - * return a non-zero value to terminate the poll. - */ - } - - /* If zero is returned, the polling will continue until all connections have - * been examined. - */ - - return 0; -} - /**************************************************************************** * Name: bcmf_receive * @@ -317,8 +243,27 @@ static int bcmf_txpoll(FAR struct net_driver_s *dev) static void bcmf_receive(FAR struct bcmf_dev_s *priv) { + struct bcmf_frame_s *frame; + // wlinfo("Entry\n"); do { + /* Request frame buffer from bus interface */ + + frame = bcmf_bdc_rx_frame(priv); + + if (frame == NULL) + { + /* No more frame to process */ + break; + } + + priv->bc_dev.d_buf = frame->data; + priv->bc_dev.d_len = frame->len - (uint32_t)(frame->data - frame->base); + + wlinfo("Got frame ! %p %d\n", frame, priv->bc_dev.d_len); + // bcmf_hexdump(priv->bc_dev.d_buf, priv->bc_dev.d_len, + // (unsigned long)priv->bc_dev.d_buf); + /* Check for errors and update statistics */ /* Check if the packet is a valid size for the network buffer @@ -340,7 +285,7 @@ static void bcmf_receive(FAR struct bcmf_dev_s *priv) #ifdef CONFIG_NET_IPv4 if (BUF->type == HTONS(ETHTYPE_IP)) { - ninfo("IPv4 frame\n"); + // ninfo("IPv4 frame\n"); NETDEV_RXIPV4(&priv->bc_dev); /* Handle ARP on input then give the IPv4 packet to the network @@ -373,7 +318,13 @@ static void bcmf_receive(FAR struct bcmf_dev_s *priv) /* And send the packet */ - bcmf_transmit(priv); + bcmf_transmit(priv, frame); + } + else + { + /* Release RX frame buffer */ + + priv->bus->free_frame(priv, frame); } } else @@ -411,7 +362,13 @@ static void bcmf_receive(FAR struct bcmf_dev_s *priv) /* And send the packet */ - bcmf_transmit(priv); + bcmf_transmit(priv, frame); + } + else + { + /* Release RX frame buffer */ + + priv->bus->free_frame(priv, frame); } } else @@ -428,12 +385,19 @@ static void bcmf_receive(FAR struct bcmf_dev_s *priv) if (priv->bc_dev.d_len > 0) { - bcmf_transmit(priv); + bcmf_transmit(priv, frame); + } + else + { + /* Release RX frame buffer */ + + priv->bus->free_frame(priv, frame); } } else #endif { + wlinfo("RX dropped\n"); NETDEV_RXDROPPED(&priv->bc_dev); } } @@ -441,53 +405,89 @@ static void bcmf_receive(FAR struct bcmf_dev_s *priv) } /**************************************************************************** - * Name: bcmf_txdone + * Name: bcmf_txpoll * * Description: - * An interrupt was received indicating that the last TX packet(s) is done + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send timesout and the interface is reset + * 3. During normal TX polling * * Parameters: - * priv - Reference to the driver state structure + * dev - Reference to the NuttX driver state structure * * Returned Value: - * None + * OK on success; a negated errno on failure * * Assumptions: - * The network is locked. + * May or may not be called from an interrupt handler. In either case, + * the network is locked. * ****************************************************************************/ -static void bcmf_txdone(FAR struct bcmf_dev_s *priv) +static int bcmf_txpoll(FAR struct net_driver_s *dev) { - int delay; + FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; - /* Check for errors and update statistics */ - - NETDEV_TXDONE(priv->bc_dev); - - /* Check if there are pending transmissions */ - - /* If no further transmissions are pending, then cancel the TX timeout and - * disable further Tx interrupts. + wlinfo("Entry\n"); + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. */ - wd_cancel(priv->bc_txtimeout); + if (priv->bc_dev.d_len > 0) + { + /* Look up the destination MAC address and add it to the Ethernet + * header. + */ - /* And disable further TX interrupts. */ +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->bc_dev.d_flags)) +#endif + { + arp_out(&priv->bc_dev); + } +#endif /* CONFIG_NET_IPv4 */ - /* In any event, poll the network for new TX data */ +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + neighbor_out(&priv->bc_dev); + } +#endif /* CONFIG_NET_IPv6 */ - (void)devif_poll(&priv->bc_dev, bcmf_txpoll); + /* Send the packet */ + + bcmf_transmit(priv, priv->cur_tx_frame); + + /* Check if there is room in the device to hold another packet. If not, + * return a non-zero value to terminate the poll. + */ + // TODO + priv->cur_tx_frame = NULL; + return 1; + } + + /* If zero is returned, the polling will continue until all connections have + * been examined. + */ + + return 0; } /**************************************************************************** - * Name: bcmf_interrupt_work + * Name: bcmf_rxpoll * * Description: - * Perform interrupt related work from the worker thread + * Process RX frames * * Parameters: - * arg - The argument passed when work_queue() was called. + * arg - context of device to use * * Returned Value: * OK on success @@ -497,8 +497,9 @@ static void bcmf_txdone(FAR struct bcmf_dev_s *priv) * ****************************************************************************/ -static void bcmf_interrupt_work(FAR void *arg) +static void bcmf_rxpoll(FAR void *arg) { + // wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg; /* Lock the network and serialize driver operations if necessary. @@ -509,14 +510,6 @@ static void bcmf_interrupt_work(FAR void *arg) net_lock(); - /* Process pending Ethernet interrupts */ - - /* Get and clear interrupt status bits */ - - /* Handle interrupts according to status bit settings */ - - /* Check if we received an incoming packet, if so, call bcmf_receive() */ - bcmf_receive(priv); /* Check if a packet transmission just completed. If so, call bcmf_txdone. @@ -524,153 +517,42 @@ static void bcmf_interrupt_work(FAR void *arg) * transmissions. */ - bcmf_txdone(priv); - net_unlock(); - - /* Re-enable Ethernet interrupts */ -#warning Missing logic -} - -/**************************************************************************** - * Name: bcmf_interrupt - * - * Description: - * Hardware interrupt handler - * - * Parameters: - * irq - Number of the IRQ that generated the interrupt - * context - Interrupt register state save info (architecture-specific) - * - * Returned Value: - * OK on success - * - * Assumptions: - * - ****************************************************************************/ - -static int bcmf_interrupt(int irq, FAR void *context, FAR void *arg) -{ - FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg; - - DEBUGASSERT(priv != NULL); - - /* Disable further Ethernet interrupts. Because Ethernet interrupts are - * also disabled if the TX timeout event occurs, there can be no race - * condition here. - */ -#warning Missing logic - - /* TODO: Determine if a TX transfer just completed */ - - { - /* If a TX transfer just completed, then cancel the TX timeout so - * there will be no race condition between any subsequent timeout - * expiration and the deferred interrupt processing. - */ - - wd_cancel(priv->bc_txtimeout); - } - - /* Schedule to perform the interrupt processing on the worker thread. */ - - work_queue(BCMFWORK, &priv->bc_irqwork, bcmf_interrupt_work, priv, 0); - return OK; -} - -/**************************************************************************** - * Name: bcmf_txtimeout_work - * - * Description: - * Perform TX timeout related work from the worker thread - * - * Parameters: - * arg - The argument passed when work_queue() as called. - * - * Returned Value: - * OK on success - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -static void bcmf_txtimeout_work(FAR void *arg) -{ - FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg; - - /* Lock the network and serialize driver operations if necessary. - * NOTE: Serialization is only required in the case where the driver work - * is performed on an LP worker thread and where more than one LP worker - * thread has been configured. - */ - - net_lock(); - - /* Increment statistics and dump debug info */ - - NETDEV_TXTIMEOUTS(priv->bc_dev); - - /* Then reset the hardware */ - - /* Then poll the network for new XMIT data */ - - (void)devif_poll(&priv->bc_dev, bcmf_txpoll); + // bcmf_txdone(priv); net_unlock(); } /**************************************************************************** - * Name: bcmf_txtimeout_expiry + * Name: bcmf_netdev_notify_tx_done * * Description: - * Our TX watchdog timed out. Called from the timer interrupt handler. - * The last TX never completed. Reset the hardware and start again. - * - * Parameters: - * argc - The number of available arguments - * arg - The first argument - * - * Returned Value: - * None + * Notify callback called when TX frame is sent and freed. * * Assumptions: - * Global interrupts are disabled by the watchdog logic. * ****************************************************************************/ -static void bcmf_txtimeout_expiry(int argc, wdparm_t arg, ...) +void bcmf_netdev_notify_tx_done(FAR struct bcmf_dev_s *priv) { - FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg; + // wlinfo("Entry\n"); - /* Disable further Ethernet interrupts. This will prevent some race - * conditions with interrupt work. There is still a potential race - * condition with interrupt work that is already queued and in progress. - */ -#warning Missing logic - - /* Schedule to perform the TX timeout processing on the worker thread. */ - - work_queue(BCMFWORK, &priv->bc_irqwork, bcmf_txtimeout_work, priv, 0); + /* TODO TX buffer may be available, try to send again if needed */ } /**************************************************************************** - * Name: bcmf_poll_process + * Name: bcmf_netdev_notify_rx * * Description: - * Perform the periodic poll. This may be called either from watchdog - * timer logic or from the worker thread, depending upon the configuration. - * - * Parameters: - * priv - Reference to the driver state structure - * - * Returned Value: - * None + * Notify callback called when RX frame is available * * Assumptions: * ****************************************************************************/ -static inline void bcmf_poll_process(FAR struct bcmf_dev_s *priv) +void bcmf_netdev_notify_rx(FAR struct bcmf_dev_s *priv) { + /* Queue a job to process RX frames */ + + work_queue(BCMFWORK, &priv->bc_pollwork, bcmf_rxpoll, priv, 0); } /**************************************************************************** @@ -692,6 +574,7 @@ static inline void bcmf_poll_process(FAR struct bcmf_dev_s *priv) static void bcmf_poll_work(FAR void *arg) { + // wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg; /* Lock the network and serialize driver operations if necessary. @@ -708,17 +591,25 @@ static void bcmf_poll_work(FAR void *arg) * the TX poll if he are unable to accept another packet for transmission. */ + if (bcmf_netdev_alloc_tx_frame(priv)) + { + goto exit_unlock; + } + /* If so, update TCP timing states and poll the network for new XMIT data. * Hmmm.. might be bug here. Does this mean if there is a transmit in * progress, we will missing TCP time state updates? */ + priv->bc_dev.d_buf = priv->cur_tx_frame->data; + priv->bc_dev.d_len = 0; (void)devif_timer(&priv->bc_dev, bcmf_txpoll); /* Setup the watchdog poll timer again */ (void)wd_start(priv->bc_txpoll, BCMF_WDDELAY, bcmf_poll_expiry, 1, (wdparm_t)priv); +exit_unlock: net_unlock(); } @@ -742,6 +633,7 @@ static void bcmf_poll_work(FAR void *arg) static void bcmf_poll_expiry(int argc, wdparm_t arg, ...) { + // wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg; /* Schedule to perform the interrupt processing on the worker thread. */ @@ -768,6 +660,7 @@ static void bcmf_poll_expiry(int argc, wdparm_t arg, ...) static int bcmf_ifup(FAR struct net_driver_s *dev) { + wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; #ifdef CONFIG_NET_IPv4 @@ -828,20 +721,20 @@ static int bcmf_ifup(FAR struct net_driver_s *dev) static int bcmf_ifdown(FAR struct net_driver_s *dev) { + wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; irqstate_t flags; - bcmf_wl_enable(priv, false); + // bcmf_wl_enable(priv, false); /* Disable the hardware interrupt */ flags = enter_critical_section(); #warning Missing logic - /* Cancel the TX poll timer and TX timeout timers */ + /* Cancel the TX poll timer */ wd_cancel(priv->bc_txpoll); - wd_cancel(priv->bc_txtimeout); /* Put the EMAC in its reset, non-operational state. This should be * a known configuration that will guarantee the bcmf_ifup() always @@ -874,6 +767,7 @@ static int bcmf_ifdown(FAR struct net_driver_s *dev) static void bcmf_txavail_work(FAR void *arg) { + // wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg; /* Lock the network and serialize driver operations if necessary. @@ -890,11 +784,19 @@ static void bcmf_txavail_work(FAR void *arg) { /* Check if there is room in the hardware to hold another outgoing packet. */ + if (bcmf_netdev_alloc_tx_frame(priv)) + { + goto exit_unlock; + } + /* If so, then poll the network for new XMIT data */ + priv->bc_dev.d_buf = priv->cur_tx_frame->data; + priv->bc_dev.d_len = 0; (void)devif_poll(&priv->bc_dev, bcmf_txpoll); } +exit_unlock: net_unlock(); } @@ -957,6 +859,7 @@ static int bcmf_txavail(FAR struct net_driver_s *dev) #if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6) static int bcmf_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) { + wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; /* Add the MAC address to the hardware multicast routing table */ @@ -986,6 +889,7 @@ static int bcmf_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) #ifdef CONFIG_NET_IGMP static int bcmf_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) { + wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; /* Add the MAC address to the hardware multicast routing table */ @@ -1013,6 +917,7 @@ static int bcmf_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) #ifdef CONFIG_NET_ICMPv6 static void bcmf_ipv6multicast(FAR struct bcmf_dev_s *priv) { + wlinfo("Entry\n"); FAR struct net_driver_s *dev; uint16_t tmp16; uint8_t mac[6]; @@ -1105,6 +1010,11 @@ static int bcmf_ioctl(FAR struct net_driver_s *dev, int cmd, ret = bcmf_wl_is_scan_done(priv); break; + case SIOCSIFHWADDR: + /* Update device MAC address */ + ret = bcmf_wl_set_mac_address(priv, (struct ifreq*)arg); + break; + case SIOCSIWFREQ: /* Set channel/frequency (Hz) */ wlwarn("WARNING: SIOCSIWFREQ not implemented\n"); ret = -ENOSYS; @@ -1202,7 +1112,6 @@ int bcmf_netdev_register(FAR struct bcmf_dev_s *priv) /* Initialize network driver structure */ memset(&priv->bc_dev, 0, sizeof(priv->bc_dev)); - priv->bc_dev.d_buf = g_pktbuf; /* Single packet buffer */ priv->bc_dev.d_ifup = bcmf_ifup; /* I/F up (new IP address) callback */ priv->bc_dev.d_ifdown = bcmf_ifdown; /* I/F down callback */ priv->bc_dev.d_txavail = bcmf_txavail; /* New TX data callback */ @@ -1215,13 +1124,17 @@ int bcmf_netdev_register(FAR struct bcmf_dev_s *priv) #endif priv->bc_dev.d_private = (FAR void *)priv; /* Used to recover private state from dev */ - /* Create a watchdog for timing polling for and timing of transmisstions */ + /* Create a watchdog for timing polling */ priv->bc_txpoll = wd_create(); /* Create periodic poll timer */ - priv->bc_txtimeout = wd_create(); /* Create TX timeout timer */ DEBUGASSERT(priv->bc_txpoll != NULL && priv->bc_txtimeout != NULL); + /* Initialize network stack interface buffer */ + + priv->cur_tx_frame = NULL; + priv->bc_dev.d_buf = NULL; + /* Put the interface in the down state. This usually amounts to resetting * the device and/or calling bcmf_ifdown(). */ @@ -1235,9 +1148,9 @@ int bcmf_netdev_register(FAR struct bcmf_dev_s *priv) out_len = ETHER_ADDR_LEN; if (bcmf_cdc_iovar_request(priv, CHIP_STA_INTERFACE, false, - IOVAR_STR_CUR_ETHERADDR, - priv->bc_dev.d_mac.ether.ether_addr_octet, - &out_len) != OK) + IOVAR_STR_CUR_ETHERADDR, + priv->bc_dev.d_mac.ether.ether_addr_octet, + &out_len) != OK) { return -EIO; } diff --git a/drivers/wireless/ieee80211/bcmf_netdev.h b/drivers/wireless/ieee80211/bcmf_netdev.h new file mode 100644 index 0000000000..c062013a42 --- /dev/null +++ b/drivers/wireless/ieee80211/bcmf_netdev.h @@ -0,0 +1,47 @@ +/**************************************************************************** + * drivers/wireless/ieee80211/bcmf_netdev.h + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Simon Piriou + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __DRIVERS_WIRELESS_IEEE80211_BCMF_NETDEV_H +#define __DRIVERS_WIRELESS_IEEE80211_BCMF_NETDEV_H + +#include "bcmf_driver.h" + +int bcmf_netdev_register(FAR struct bcmf_dev_s *priv); + +void bcmf_netdev_notify_rx(FAR struct bcmf_dev_s *priv); + +void bcmf_netdev_notify_tx_done(FAR struct bcmf_dev_s *priv); + +#endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_NETDEV_H */ diff --git a/drivers/wireless/ieee80211/bcmf_sdio.c b/drivers/wireless/ieee80211/bcmf_sdio.c index 0756a1c685..e54e5cda80 100644 --- a/drivers/wireless/ieee80211/bcmf_sdio.c +++ b/drivers/wireless/ieee80211/bcmf_sdio.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,7 @@ #include "bcmf_sdio.h" #include "bcmf_core.h" #include "bcmf_sdpcm.h" +#include "bcmf_utils.h" #include "bcmf_sdio_core.h" #include "bcmf_sdio_regs.h" @@ -114,6 +116,13 @@ static int bcmf_sdio_find_block_size(unsigned int size); /* FIXME remove */ FAR struct bcmf_dev_s *g_sdio_priv; +/* Buffer pool for SDIO bus interface + This pool is shared between all driver devices */ + +static struct bcmf_sdio_frame g_pktframes[BCMF_PKT_POOL_SIZE]; + +// TODO free_queue should be static + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -564,16 +573,28 @@ int bcmf_bus_sdio_initialize(FAR struct bcmf_dev_s *priv, sbus->sleeping = true; sbus->bus.txframe = bcmf_sdpcm_queue_frame; - sbus->bus.allocate_frame = bcmf_sdpcm_allocate_frame; + sbus->bus.rxframe = bcmf_sdpcm_get_rx_frame; + sbus->bus.allocate_frame = bcmf_sdpcm_alloc_frame; + sbus->bus.free_frame = bcmf_sdpcm_free_frame; sbus->bus.stop = NULL; // TODO /* Init transmit frames queue */ - if ((ret = sem_init(&sbus->tx_queue_mutex, 0, 1)) != OK) + if ((ret = sem_init(&sbus->queue_mutex, 0, 1)) != OK) { goto exit_free_bus; } sq_init(&sbus->tx_queue); + sq_init(&sbus->rx_queue); + sq_init(&sbus->free_queue); + + /* Setup free buffer list */ + + // FIXME this should be static to driver + for (ret = 0; ret < BCMF_PKT_POOL_SIZE; ret++) + { + bcmf_dqueue_push(&sbus->free_queue, &g_pktframes[ret].list_entry); + } /* Init thread semaphore */ @@ -622,7 +643,6 @@ int bcmf_bus_sdio_initialize(FAR struct bcmf_dev_s *priv, goto exit_uninit_hw; } - up_mdelay(100); sbus->ready = true; @@ -642,7 +662,8 @@ int bcmf_bus_sdio_initialize(FAR struct bcmf_dev_s *priv, /* Start the waitdog timer */ - wd_start(sbus->waitdog, BCMF_WAITDOG_TIMEOUT_TICK, bcmf_sdio_waitdog_timeout, 0); + wd_start(sbus->waitdog, BCMF_WAITDOG_TIMEOUT_TICK, bcmf_sdio_waitdog_timeout, + (wdparm_t)priv); /* Spawn bcmf daemon thread */ @@ -659,8 +680,6 @@ int bcmf_bus_sdio_initialize(FAR struct bcmf_dev_s *priv, sbus->thread_id = ret; - - /* sdio bus is up and running */ return OK; @@ -685,7 +704,6 @@ int bcmf_chipinitialize(FAR struct bcmf_sdio_dev_s *sbus) { return ret; } - wlinfo("chip id is 0x%x\n", value); int chipid = value & 0xffff; switch (chipid) @@ -705,11 +723,12 @@ int bcmf_chipinitialize(FAR struct bcmf_sdio_dev_s *sbus) void bcmf_sdio_waitdog_timeout(int argc, wdparm_t arg1, ...) { - FAR struct bcmf_dev_s *priv = g_sdio_priv; + FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s*)arg1; FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; /* Notify bcmf thread */ + wlinfo("Notify bcmf thread\n"); sem_post(&sbus->thread_signal); } @@ -739,7 +758,7 @@ int bcmf_sdio_thread(int argc, char **argv) /* Restart the waitdog timer */ wd_start(sbus->waitdog, BCMF_WAITDOG_TIMEOUT_TICK, - bcmf_sdio_waitdog_timeout, 0); + bcmf_sdio_waitdog_timeout, (wdparm_t)priv); /* Wake up device */ @@ -789,6 +808,15 @@ int bcmf_sdio_thread(int argc, char **argv) ret = bcmf_sdpcm_sendframe(priv); } while (ret == OK); + /* Check if RX frames are available */ + + if (sbus->intstatus & I_HMB_FRAME_IND) + { + /* Try again */ + wlinfo("Try read again\n"); + continue; + } + /* If we're done for now, turn off clock request. */ // TODO add wakelock @@ -798,4 +826,74 @@ int bcmf_sdio_thread(int argc, char **argv) wlinfo("Exit\n"); return 0; +} + +struct bcmf_sdio_frame* bcmf_sdio_allocate_frame(FAR struct bcmf_dev_s *priv, + bool block, bool tx) +{ + FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; + struct bcmf_sdio_frame *sframe; + dq_entry_t *entry = NULL; + + while (1) + { + if (sem_wait(&sbus->queue_mutex)) + { + PANIC(); + } + + // if (!tx || sbus->tx_queue_count < BCMF_PKT_POOL_SIZE-1) + { + if ((entry = bcmf_dqueue_pop_tail(&sbus->free_queue)) != NULL) + { + if (tx) + { + sbus->tx_queue_count += 1; + } + + sem_post(&sbus->queue_mutex); + break; + } + } + + sem_post(&sbus->queue_mutex); + + if (block) + { + // TODO use signaling semaphore + wlinfo("alloc failed %d\n", tx); + up_mdelay(100); + continue; + } + wlinfo("No avail buffer\n"); + return NULL; + } + + sframe = container_of(entry, struct bcmf_sdio_frame, list_entry); + + sframe->header.len = HEADER_SIZE + MAX_NET_DEV_MTU; + sframe->header.base = sframe->data; + sframe->header.data = sframe->data; + sframe->tx = tx; + return sframe; +} + +void bcmf_sdio_free_frame(FAR struct bcmf_dev_s *priv, + struct bcmf_sdio_frame *sframe) +{ + // wlinfo("free %p\n", sframe); + FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; + + if (sem_wait(&sbus->queue_mutex)) + { + PANIC(); + } + + bcmf_dqueue_push(&sbus->free_queue, &sframe->list_entry); + + if (sframe->tx) + { + sbus->tx_queue_count -= 1; + } + sem_post(&sbus->queue_mutex); } \ No newline at end of file diff --git a/drivers/wireless/ieee80211/bcmf_sdio.h b/drivers/wireless/ieee80211/bcmf_sdio.h index 228fbb8d18..d21365a1c4 100644 --- a/drivers/wireless/ieee80211/bcmf_sdio.h +++ b/drivers/wireless/ieee80211/bcmf_sdio.h @@ -49,6 +49,10 @@ #include "bcmf_sdio_core.h" +#define HEADER_SIZE 0x12 /* Default sdpcm + bdc header size */ +// TODO move to Kconfig +#define BCMF_PKT_POOL_SIZE 4 /* Frame pool size */ + /**************************************************************************** * Public Types ****************************************************************************/ @@ -93,8 +97,21 @@ struct bcmf_sdio_dev_s uint8_t tx_seq; /* Transmit sequence number (next) */ uint8_t rx_seq; /* Receive sequence number (expected) */ - sem_t tx_queue_mutex; /* Lock for transmit queue */ + sem_t queue_mutex; /* Lock for TX/RX/free queues */ + dq_queue_t free_queue; /* Queue of available frames */ dq_queue_t tx_queue; /* Queue of frames to tramsmit */ + dq_queue_t rx_queue; /* Queue of frames used to receive */ + volatile int tx_queue_count; /* Count of items in TX queue */ +}; + +/* Structure used to manage SDIO frames */ + +struct bcmf_sdio_frame { + struct bcmf_frame_s header; + bool tx; + dq_entry_t list_entry; + uint8_t data[HEADER_SIZE + MAX_NET_DEV_MTU + + CONFIG_NET_GUARDSIZE]; }; /**************************************************************************** @@ -120,4 +137,10 @@ int bcmf_read_reg(FAR struct bcmf_sdio_dev_s *sbus, uint8_t function, int bcmf_write_reg(FAR struct bcmf_sdio_dev_s *sbus, uint8_t function, uint32_t address, uint8_t reg); +struct bcmf_sdio_frame* bcmf_sdio_allocate_frame(FAR struct bcmf_dev_s *priv, + bool block, bool tx); + +void bcmf_sdio_free_frame(FAR struct bcmf_dev_s *priv, + struct bcmf_sdio_frame *sframe); + #endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_SDIO_H */ \ No newline at end of file diff --git a/drivers/wireless/ieee80211/bcmf_sdpcm.c b/drivers/wireless/ieee80211/bcmf_sdpcm.c index 6047db9c86..065dafb61b 100644 --- a/drivers/wireless/ieee80211/bcmf_sdpcm.c +++ b/drivers/wireless/ieee80211/bcmf_sdpcm.c @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -56,6 +57,8 @@ #include "bcmf_bdc.h" #include "bcmf_utils.h" + #include "bcmf_netdev.h" + #include "bcmf_sdio_regs.h" /**************************************************************************** @@ -66,9 +69,6 @@ #define SDPCM_EVENT_CHANNEL 1 /* Asynchronous event frame id */ #define SDPCM_DATA_CHANNEL 2 /* Data frame id */ -#define container_of(ptr, type, member) \ - (type *)( (uint8_t *)(ptr) - offsetof(type,member) ) - /**************************************************************************** * Private Types ****************************************************************************/ @@ -85,11 +85,6 @@ struct __attribute__((packed)) bcmf_sdpcm_header { uint16_t padding; }; -struct bcmf_sdpcm_frame { - struct bcmf_frame_s frame_header; - dq_entry_t list_entry; -}; - /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -117,9 +112,10 @@ int bcmf_sdpcm_rxfail(FAR struct bcmf_sdio_dev_s *sbus, bool retry) /* TODO Wait until the packet has been flushed (device/FIFO stable) */ - /* Send NAK to retry to read frame */ if (retry) { + /* Send NAK to retry to read frame */ + bcmf_write_sbregb(sbus, CORE_BUS_REG(sbus->chip->core_base[SDIOD_CORE_ID], tosbmailbox), SMB_NAK); @@ -141,18 +137,7 @@ int bcmf_sdpcm_process_header(FAR struct bcmf_sdio_dev_s *sbus, /* Update tx credits */ - // wlinfo("update credit %x %x %x\n", header->credit, - // sbus->tx_seq, sbus->max_seq); - - if (header->credit - sbus->tx_seq > 0x40) - { - wlerr("seq %d: max tx seq number error\n", sbus->tx_seq); - sbus->max_seq = sbus->tx_seq + 2; - } - else - { - sbus->max_seq = header->credit; - } + sbus->max_seq = header->credit; return OK; } @@ -161,20 +146,25 @@ int bcmf_sdpcm_process_header(FAR struct bcmf_sdio_dev_s *sbus, * Public Functions ****************************************************************************/ -// FIXME remove -uint8_t tmp_buffer[1024]; int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv) { int ret; uint16_t len, checksum; struct bcmf_sdpcm_header *header; - struct bcmf_sdpcm_frame *sframe; + struct bcmf_sdio_frame *sframe; FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; - /* TODO request free frame buffer */ + /* Request free frame buffer */ - sframe = (struct bcmf_sdpcm_frame*)tmp_buffer; - header = (struct bcmf_sdpcm_header*)&sframe[1]; + sframe = bcmf_sdio_allocate_frame(priv, false, false); + + if (sframe == NULL) + { + wlinfo("fail alloc\n"); + return -EAGAIN; + } + + header = (struct bcmf_sdpcm_header*)sframe->data; /* Read header */ @@ -193,7 +183,8 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv) if (!(len | checksum)) { - return -ENODATA; + ret = -ENODATA; + goto exit_free_frame; } if (((~len & 0xffff) ^ checksum) || len < sizeof(struct bcmf_sdpcm_header)) @@ -203,25 +194,23 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv) goto exit_abort; } - // FIXME define for size - if (len > sizeof(tmp_buffer)) + if (len > sframe->header.len) { - wlerr("Frame is too large, cancel %d\n", len); + wlerr("Frame is too large, cancel %d %d\n", len, sframe->header.len); ret = -ENOMEM; goto exit_abort; } /* Read remaining frame data */ - // TODO allocate buffer ret = bcmf_transfer_bytes(sbus, false, 2, 0, (uint8_t*)header+4, len - 4); if (ret != OK) { ret = -EIO; - goto exit_free_abort; + goto exit_abort; } - // wlinfo("Receive frame\n"); + // wlinfo("Receive frame %p %d\n", sframe, len); // bcmf_hexdump((uint8_t*)header, header->size, (unsigned int)header); /* Process and validate header */ @@ -234,19 +223,18 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv) goto exit_free_frame; } - /* Setup new frame structure */ + /* Update frame structure */ - sframe->frame_header.len = header->size; - sframe->frame_header.data = (uint8_t*)header + header->data_offset; - sframe->frame_header.base = (uint8_t*)header; + sframe->header.len = header->size; + sframe->header.data += header->data_offset; /* Process received frame content */ switch (header->channel & 0x0f) { case SDPCM_CONTROL_CHANNEL: - ret = bcmf_cdc_process_control_frame(priv, &sframe->frame_header); - break; + ret = bcmf_cdc_process_control_frame(priv, &sframe->header); + goto exit_free_frame; case SDPCM_EVENT_CHANNEL: if (header->data_offset == header->size) @@ -257,34 +245,49 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv) } else { - ret = bcmf_bdc_process_event_frame(priv, &sframe->frame_header); + ret = bcmf_bdc_process_event_frame(priv, &sframe->header); } - break; + goto exit_free_frame; case SDPCM_DATA_CHANNEL: - ret = bcmf_bdc_process_data_frame(priv, &sframe->frame_header); + + /* Queue frame and notify network layer frame is available */ + + if (sem_wait(&sbus->queue_mutex)) + { + PANIC(); + } + bcmf_dqueue_push(&sbus->rx_queue, &sframe->list_entry); + sem_post(&sbus->queue_mutex); + + bcmf_netdev_notify_rx(priv); + + /* Upper layer have to free all received frames */ + + ret = OK; break; default: wlerr("Got unexpected message type %d\n", header->channel); - ret = OK; + ret = -EINVAL; + goto exit_free_frame; } -exit_free_frame: - // TODO free frame buffer return ret; -exit_free_abort: - // TODO free frame buffer exit_abort: bcmf_sdpcm_rxfail(sbus, false); +exit_free_frame: + bcmf_sdio_free_frame(priv, sframe); return ret; } int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv) { int ret; - struct bcmf_sdpcm_frame *sframe; + bool is_txframe; + dq_entry_t *entry; + struct bcmf_sdio_frame *sframe; struct bcmf_sdpcm_header *header; FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; @@ -303,25 +306,25 @@ int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv) } - if ((ret = sem_wait(&sbus->tx_queue_mutex)) != OK) + if (sem_wait(&sbus->queue_mutex)) { - return ret; + PANIC(); } - sframe = container_of(sbus->tx_queue.tail, - struct bcmf_sdpcm_frame, list_entry); - header = (struct bcmf_sdpcm_header*)sframe->frame_header.base; + entry = sbus->tx_queue.tail; + sframe = container_of(entry, struct bcmf_sdio_frame, list_entry); + header = (struct bcmf_sdpcm_header*)sframe->header.base; /* Set frame sequence id */ header->sequence = sbus->tx_seq++; - // wlinfo("Send frame\n"); - // bcmf_hexdump(sframe->frame_header.base, sframe->frame_header.len, - // (unsigned long)sframe->frame_header.base); + // wlinfo("Send frame %p\n", sframe); + // bcmf_hexdump(sframe->header.base, sframe->header.len, + // (unsigned long)sframe->header.base); - ret = bcmf_transfer_bytes(sbus, true, 2, 0, sframe->frame_header.base, - sframe->frame_header.len); + ret = bcmf_transfer_bytes(sbus, true, 2, 0, sframe->header.base, + sframe->header.len); if (ret != OK) { wlinfo("fail send frame %d\n", ret); @@ -332,128 +335,130 @@ int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv) /* Frame sent, remove it from queue */ - if (sbus->tx_queue.head == &sframe->list_entry) - { - /* List is empty */ + bcmf_dqueue_pop_tail(&sbus->tx_queue); + sem_post(&sbus->queue_mutex); + is_txframe = sframe->tx; - sbus->tx_queue.head = NULL; - sbus->tx_queue.tail = NULL; - } - else + /* Free frame buffer */ + + bcmf_sdio_free_frame(priv, sframe); + + if (is_txframe) { - sbus->tx_queue.tail = sframe->list_entry.blink; - sframe->list_entry.blink->flink = sbus->tx_queue.head; + /* Notify upper layer at least one TX buffer is available */ + + bcmf_netdev_notify_tx_done(priv); } - /* TODO free frame buffer */ - - goto exit_post_sem; + return OK; exit_abort: // bcmf_sdpcm_txfail(sbus, false); -exit_post_sem: - sem_post(&sbus->tx_queue_mutex); + sem_post(&sbus->queue_mutex); return ret; } -// FIXME remove -uint8_t tmp_buffer2[512]; -struct bcmf_frame_s* bcmf_sdpcm_allocate_frame(FAR struct bcmf_dev_s *priv, - unsigned int len, bool control, bool block) +int bcmf_sdpcm_queue_frame(FAR struct bcmf_dev_s *priv, + struct bcmf_frame_s *frame, bool control) { - unsigned int frame_len; - - /* Integer overflow check */ - - if (len > 512) - { - return NULL; - } - - frame_len = len + sizeof(struct bcmf_sdpcm_frame) - + sizeof(struct bcmf_sdpcm_header); - if (!control) - { - /* Data frames needs 2 bytes padding */ - - frame_len += 2; - } - - if (frame_len > 512) - { - return NULL; - } - - // FIXME allocate buffer and use max_size instead of 512 - // allocate buffer len + sizeof(struct bcmf_sdpcm_frame) - - struct bcmf_sdpcm_frame *sframe = (struct bcmf_sdpcm_frame*)tmp_buffer2; - struct bcmf_sdpcm_header *header = (struct bcmf_sdpcm_header*)&sframe[1]; + FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; + struct bcmf_sdio_frame *sframe = (struct bcmf_sdio_frame*)frame; + struct bcmf_sdpcm_header *header = (struct bcmf_sdpcm_header*)sframe->data; /* Prepare sw header */ memset(header, 0, sizeof(struct bcmf_sdpcm_header)); - header->size = frame_len - sizeof(struct bcmf_sdpcm_frame); + header->size = frame->len; header->checksum = ~header->size; + header->data_offset = (uint8_t)(frame->data - frame->base); if (control) { header->channel = SDPCM_CONTROL_CHANNEL; - header->data_offset = sizeof(struct bcmf_sdpcm_header); } else { header->channel = SDPCM_DATA_CHANNEL; - header->data_offset = sizeof(struct bcmf_sdpcm_header)+2; } - sframe->frame_header.len = header->size; - sframe->frame_header.base = (uint8_t*)header; - sframe->frame_header.data = (uint8_t*)header + header->data_offset; - - return &sframe->frame_header; -} - -int bcmf_sdpcm_queue_frame(FAR struct bcmf_dev_s *priv, - struct bcmf_frame_s *frame) -{ - int ret; - FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; - struct bcmf_sdpcm_frame *sframe = (struct bcmf_sdpcm_frame*)frame; - /* Add frame in tx queue */ - if ((ret = sem_wait(&sbus->tx_queue_mutex)) != OK) + if (sem_wait(&sbus->queue_mutex)) { - return ret; + PANIC(); } - if (sbus->tx_queue.head == NULL) - { - /* List is empty */ + bcmf_dqueue_push(&sbus->tx_queue, &sframe->list_entry); - sbus->tx_queue.head = &sframe->list_entry; - sbus->tx_queue.tail = &sframe->list_entry; - - sframe->list_entry.flink = &sframe->list_entry; - sframe->list_entry.blink = &sframe->list_entry; - } - else - { - /* Insert entry at list head */ - - sframe->list_entry.flink = sbus->tx_queue.head; - sframe->list_entry.blink = sbus->tx_queue.tail; - - sbus->tx_queue.head->blink = &sframe->list_entry; - sbus->tx_queue.head = &sframe->list_entry; - } - - sem_post(&sbus->tx_queue_mutex); + sem_post(&sbus->queue_mutex); /* Notify bcmf thread tx frame is ready */ sem_post(&sbus->thread_signal); return OK; +} + +struct bcmf_frame_s* bcmf_sdpcm_alloc_frame(FAR struct bcmf_dev_s *priv, + unsigned int len, bool block, + bool control) +{ + struct bcmf_sdio_frame *sframe; + unsigned int header_len = sizeof(struct bcmf_sdpcm_header); + + if (!control) + { + header_len += 2; /* Data frames need alignment padding */ + } + + if (len + header_len > MAX_NET_DEV_MTU + HEADER_SIZE || + len > len + header_len) + { + wlerr("Invalid size %d\n", len); + return NULL; + } + + /* Allocate a frame for RX in case of control frame */ + + sframe = bcmf_sdio_allocate_frame(priv, block, !control); + + if (sframe == NULL) + { + return NULL; + } + + sframe->header.len = header_len + len; + sframe->header.data += header_len; + return &sframe->header; +} + + +void bcmf_sdpcm_free_frame(FAR struct bcmf_dev_s *priv, + struct bcmf_frame_s *frame) +{ + return bcmf_sdio_free_frame(priv, (struct bcmf_sdio_frame*)frame); +} + +struct bcmf_frame_s* bcmf_sdpcm_get_rx_frame(FAR struct bcmf_dev_s *priv) +{ + dq_entry_t *entry; + struct bcmf_sdio_frame *sframe; + FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s*)priv->bus; + + if (sem_wait(&sbus->queue_mutex)) + { + PANIC(); + } + + entry = bcmf_dqueue_pop_tail(&sbus->rx_queue); + + sem_post(&sbus->queue_mutex); + + if (entry == NULL) + { + return NULL; + } + + sframe = container_of(entry, struct bcmf_sdio_frame, list_entry); + return &sframe->header; } \ No newline at end of file diff --git a/drivers/wireless/ieee80211/bcmf_sdpcm.h b/drivers/wireless/ieee80211/bcmf_sdpcm.h index eedcb60050..f1091fd519 100644 --- a/drivers/wireless/ieee80211/bcmf_sdpcm.h +++ b/drivers/wireless/ieee80211/bcmf_sdpcm.h @@ -51,10 +51,13 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv); int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv); int bcmf_sdpcm_queue_frame(FAR struct bcmf_dev_s *priv, - struct bcmf_frame_s *frame); + struct bcmf_frame_s *frame, bool control); -struct bcmf_frame_s *bcmf_sdpcm_allocate_frame(FAR struct bcmf_dev_s *priv, - unsigned int len, bool control, - bool block); +void bcmf_sdpcm_free_frame(FAR struct bcmf_dev_s *priv, struct bcmf_frame_s *frame); + +struct bcmf_frame_s* bcmf_sdpcm_alloc_frame(FAR struct bcmf_dev_s *priv, + unsigned int len, bool block, bool control); + +struct bcmf_frame_s* bcmf_sdpcm_get_rx_frame(FAR struct bcmf_dev_s *priv); #endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_SDPCM_H */ \ No newline at end of file diff --git a/drivers/wireless/ieee80211/bcmf_utils.c b/drivers/wireless/ieee80211/bcmf_utils.c index 7a9c780671..4fb126356e 100644 --- a/drivers/wireless/ieee80211/bcmf_utils.c +++ b/drivers/wireless/ieee80211/bcmf_utils.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "bcmf_utils.h" @@ -114,4 +115,68 @@ int bcmf_sem_wait(sem_t *sem, unsigned int timeout_ms) } return sem_timedwait(sem, &abstime); +} + +void bcmf_dqueue_push(dq_queue_t *queue, dq_entry_t *entry) +{ + if (queue->head == NULL) + { + /* List is empty */ + + queue->tail = entry; + + entry->flink = entry; + entry->blink = entry; + } + else + { + /* Insert entry at list head */ + + entry->flink = queue->head; + entry->blink = queue->tail; + + queue->head->blink = entry; + } + + queue->head = entry; +} + +dq_entry_t* bcmf_dqueue_pop_tail(dq_queue_t *queue) +{ + dq_entry_t *entry = queue->tail; + + if (queue->head == queue->tail) + { + /* List is empty */ + + queue->head = NULL; + queue->tail = NULL; + } + else + { + /* Pop from queue tail */ + + queue->tail = entry->blink; + entry->blink->flink = queue->head; + } + + return entry; +} + +void bcmf_squeue_push(sq_queue_t *queue, sq_entry_t *entry) +{ + entry->flink = queue->head; + queue->head = entry; +} + +sq_entry_t* bcmf_squeue_pop(sq_queue_t *queue) +{ + sq_entry_t *entry = queue->head; + + if (queue->head != NULL) + { + queue->head = entry->flink; + } + + return entry; } \ No newline at end of file diff --git a/drivers/wireless/ieee80211/bcmf_utils.h b/drivers/wireless/ieee80211/bcmf_utils.h index 704cb9ec06..1800cfc28b 100644 --- a/drivers/wireless/ieee80211/bcmf_utils.h +++ b/drivers/wireless/ieee80211/bcmf_utils.h @@ -42,6 +42,10 @@ #include #include +#include + +#define container_of(ptr, type, member) \ + (type *)( (uint8_t *)(ptr) - offsetof(type,member) ) /**************************************************************************** * Public Function Prototypes @@ -51,6 +55,12 @@ void bcmf_hexdump(uint8_t *data, unsigned int len, unsigned long offset); int bcmf_sem_wait(sem_t *sem, unsigned int timeout_ms); +sq_entry_t* bcmf_squeue_pop(sq_queue_t *queue); +void bcmf_squeue_push(sq_queue_t *queue, sq_entry_t *entry); + +dq_entry_t* bcmf_dqueue_pop_tail(dq_queue_t *queue); +void bcmf_dqueue_push(dq_queue_t *queue, dq_entry_t *entry); + static inline uint16_t bcmf_getle16(uint16_t *val) { uint8_t *valb = (uint8_t*)val;