photon: add sdpcm tx basic support

This commit is contained in:
Simon Piriou 2017-04-16 11:28:08 +02:00
parent 11d3db5c35
commit d646bde1f8
9 changed files with 442 additions and 60 deletions

View File

@ -44,6 +44,7 @@ ifeq ($(CONFIG_IEEE80211_BROADCOM_FULLMAC_SDIO),y)
CSRCS += bcmf_sdio.c
CSRCS += bcmf_core.c
CSRCS += bcmf_sdpcm.c
CSRCS += bcmf_hexdump.c
CSRCS += mmc_sdio.c
endif

View File

@ -51,10 +51,6 @@
#define SOCSRAM_BASE_ADDRESS 0x18004000 /* SOCSRAM core register region */
#define BACKPLANE_ADDRESS_MASK 0x7FFF
#define CHIP_STA_INTERFACE 0
#define CHIP_AP_INTERFACE 1
#define CHIP_P2P_INTERFACE 2
/* Maximum value of bus data credit difference */
#define CHIP_MAX_BUS_DATA_CREDIT_DIFF 7

View File

@ -51,7 +51,7 @@ struct bcmf_dev_s
uint32_t (*get_core_base_address)(unsigned int core); /* Get chip specific
base address for evey cores */
sem_t sem; /* Semaphore for processing thread event */
sem_t thread_signal; /* Semaphore for processing thread event */
struct wdog_s *waitdog; /* Processing thread waitdog */
bool ready; /* Current device status */
bool sleeping; /* Current sleep status */
@ -61,6 +61,15 @@ struct bcmf_dev_s
uint8_t max_seq; /* Maximum transmit sequence allowed */
uint8_t tx_seq; /* Transmit sequence number (next) */
uint8_t rx_seq; /* Receive sequence number (expected) */
// FIXME use mutex instead of semaphore
sem_t control_mutex; /* Cannot handle multiple control requests */
sem_t control_timeout; /* Semaphore to wait for control frame rsp */
uint16_t control_reqid; /* Current control request id */
// FIXME use mutex instead of semaphore
sem_t tx_queue_mutex; /* Lock for transmit queue */
dq_queue_t tx_queue; /* Queue of frames to tramsmit */
};
#endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_DRIVER_H */

View File

@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <debug.h>
#define LINE_LEN 16
void bcmf_hexdump(uint8_t *data, unsigned int len, unsigned long offset)
{
unsigned int i;
unsigned int char_count = 0;
char char_line[20];
char hex_line[64];
for(i = 0; i < len; i++)
{
if (char_count >= LINE_LEN) {
/* Flush line */
_info("%08x: %s%s\n", offset+i-char_count, hex_line, char_line);
char_count = 0;
}
sprintf(hex_line+3*char_count, "%02x ", data[i]);
sprintf(char_line+char_count, "%c",
data[i] < 0x20 || data[i] >= 0x7f? '.': data[i]);
char_count ++;
}
if (char_count > 0) {
/* Flush last line */
memset(hex_line+3*char_count, ' ', 3*(LINE_LEN-char_count));
hex_line[3*LINE_LEN] = 0;
_info("%08x: %s%s\n", offset+i-char_count, hex_line, char_line);
}
}

View File

@ -45,6 +45,7 @@
#include <string.h>
#include <debug.h>
#include <errno.h>
#include <queue.h>
#include <nuttx/kmalloc.h>
#include <nuttx/sdio.h>
@ -59,6 +60,9 @@
#include "bcmf_core.h"
#include "bcmf_sdpcm.h"
// TODO remove
#include "bcmf_ioctl.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -114,7 +118,7 @@ int bcmf_oob_irq(int irq, FAR void *context, FAR void *arg)
/* Signal bmcf thread */
priv->irq_pending = true;
sem_post(&priv->sem);
sem_post(&priv->thread_signal);
}
return OK;
}
@ -362,8 +366,8 @@ int bcmf_bus_setup_interrupts(FAR struct bcmf_dev_s *priv)
/* Redirect, configure and enable io for out-of-band interrupt signal */
ret = sdio_io_rw_direct(priv->sdio_dev, true, 0, SDIO_CCCR_BRCM_SEPINT,
SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI, NULL);
ret = bcmf_write_reg(priv, 0, SDIO_CCCR_BRCM_SEPINT,
SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI);
if (ret != OK)
{
return ret;
@ -527,6 +531,29 @@ int bcmf_write_reg(FAR struct bcmf_dev_s *priv, uint8_t function,
return bcmf_transfer_bytes(priv, true, function, address, &reg, 1);
}
/****************************************************************************
* Name: bcmf_sem_wait
****************************************************************************/
int bcmf_sem_wait(sem_t *sem, unsigned int timeout_ms)
{
struct timespec abstime;
/* Get the current time */
(void)clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_nsec += 1000 * 1000* timeout_ms;
if (abstime.tv_nsec >= 1000 * 1000 * 1000)
{
abstime.tv_sec++;
abstime.tv_nsec -= 1000 * 1000 * 1000;
}
return sem_timedwait(sem, &abstime);
}
/****************************************************************************
* Name: bcmf_sdio_initialize
****************************************************************************/
@ -555,11 +582,11 @@ int bcmf_sdio_initialize(int minor, FAR struct sdio_dev_s *dev)
priv->ready = false;
priv->sleeping = true;
if ((ret = sem_init(&priv->sem, 0, 0)) != OK)
if ((ret = sem_init(&priv->thread_signal, 0, 0)) != OK)
{
goto exit_free_priv;
}
if ((ret = sem_setprotocol(&priv->sem, SEM_PRIO_NONE)) != OK)
if ((ret = sem_setprotocol(&priv->thread_signal, SEM_PRIO_NONE)) != OK)
{
goto exit_free_priv;
}
@ -571,6 +598,27 @@ int bcmf_sdio_initialize(int minor, FAR struct sdio_dev_s *dev)
goto exit_free_priv;
}
if ((ret = sem_init(&priv->control_mutex, 0, 1)) != OK)
{
goto exit_free_waitdog;
}
if ((ret = sem_init(&priv->control_timeout, 0, 0)) != OK)
{
goto exit_free_waitdog;
}
if ((ret = sem_setprotocol(&priv->control_timeout, SEM_PRIO_NONE)) != OK)
{
goto exit_free_waitdog;
}
/* Init transmit frames queue */
if ((ret = sem_init(&priv->tx_queue_mutex, 0, 1)) != OK)
{
goto exit_free_waitdog;
}
sq_init(&priv->tx_queue);
/* Initialize device hardware */
ret = bcmf_hwinitialize(priv);
@ -632,6 +680,15 @@ int bcmf_sdio_initialize(int minor, FAR struct sdio_dev_s *dev)
/* Device is up and running
TODO Create a wlan device name and register network driver here */
// TODO remove: basic iovar test
up_mdelay(2000);
char fw_version[64];
ret = bcmf_sdpcm_iovar_request(priv, CHIP_STA_INTERFACE, false,
IOVAR_STR_VERSION, fw_version,
sizeof(fw_version));
_info("fw version %d\n", ret);
return OK;
exit_uninit_hw:
@ -675,7 +732,8 @@ void bcmf_sdio_waitdog_timeout(int argc, wdparm_t arg1, ...)
FAR struct bcmf_dev_s *priv = g_sdio_priv;
/* Notify bcmf thread */
sem_post(&priv->sem);
sem_post(&priv->thread_signal);
}
int bcmf_sdio_thread(int argc, char **argv)
@ -693,7 +751,7 @@ int bcmf_sdio_thread(int argc, char **argv)
{
/* Wait for event (device interrupt, user request or waitdog timer) */
ret = sem_wait(&priv->sem);
ret = sem_wait(&priv->thread_signal);
if (ret != OK)
{
_err("Error while waiting for semaphore\n");
@ -715,8 +773,6 @@ int bcmf_sdio_thread(int argc, char **argv)
priv->irq_pending = false;
_info("process irq\n");
bcmf_read_sbregw(priv,
CORE_BUS_REG(priv->get_core_base_address(SDIOD_CORE_ID),
intstatus), &priv->intstatus);
@ -748,10 +804,16 @@ int bcmf_sdio_thread(int argc, char **argv)
}
}
// TODO send all queued frames
/* Send all queued frames */
do
{
ret = bcmf_sdpcm_sendframe(priv);
} while (ret == OK);
/* If we're done for now, turn off clock request. */
// TODO add wakelock
bcmf_sdio_bus_sleep(priv, true);
}
return 0;

View File

@ -24,4 +24,6 @@ int bcmf_read_reg(FAR struct bcmf_dev_s *priv, uint8_t function,
int bcmf_write_reg(FAR struct bcmf_dev_s *priv, uint8_t function,
uint32_t address, uint8_t reg);
int bcmf_sem_wait(sem_t *sem, unsigned int timeout_ms);
#endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_SDIO_H */

View File

@ -61,6 +61,10 @@
#define I_HMB_SW_MASK ( (uint32_t) 0x000000F0 )
#define I_HMB_FRAME_IND ( 1<<6 )
#define CHIP_STA_INTERFACE 0
#define CHIP_AP_INTERFACE 1
#define CHIP_P2P_INTERFACE 2
enum {
CHIPCOMMON_CORE_ID,
DOT11MAC_CORE_ID,

View File

@ -45,10 +45,18 @@
#include <nuttx/arch.h>
#include <stddef.h>
#include <string.h>
#include <queue.h>
#include <semaphore.h>
#include "bcmf_sdio.h"
#include "bcmf_core.h"
#include "bcmf_sdpcm.h"
#include "bcmf_ioctl.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* SDA_FRAMECTRL */
#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */
@ -57,25 +65,78 @@
#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */
/* tosbmailbox bits corresponding to intstatus bits */
#define SMB_NAK (1 << 0) /* Frame NAK */
#define SMB_NAK (1 << 0) /* Frame NAK */
#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* CDC flag definitions */
#define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */
#define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */
#define CDC_DCMD_IF_MASK 0xF000 /* I/F index */
#define CDC_DCMD_IF_SHIFT 12
#define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */
#define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */
#define CDC_DCMD_ID(flags) \
(((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT)
#define SDPCM_CONTROL_CHANNEL 0 /* Control */
#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */
#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */
#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */
#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */
#define SDPCM_CONTROL_TIMEOUT_MS 1000
// TODO remove
void bcmf_hexdump(uint8_t *data, unsigned int len, unsigned long offset);
/****************************************************************************
* Private Types
****************************************************************************/
struct bcmf_sdpcm_header {
uint16_t size;
uint16_t checksum;
uint8_t sequence;
uint8_t channel;
uint8_t next_length;
uint8_t data_offset;
uint8_t flow_control;
uint8_t credit;
uint16_t padding;
};
struct bcmf_sdpcm_cdc_header {
uint32_t cmd; /* dongle command value */
uint32_t len; /* lower 16: output buflen;
* upper 16: input buflen (excludes header) */
uint32_t flags; /* flag defns given below */
uint32_t status; /* status code returned from the device */
};
struct bcmf_sdpcm_frame {
dq_entry_t list_entry;
struct bcmf_sdpcm_header header;
uint8_t data[0];
};
struct bcmf_sdpcm_cdc_dcmd {
dq_entry_t list_entry;
struct bcmf_sdpcm_header header;
struct bcmf_sdpcm_cdc_header cdc_header;
uint8_t data[0];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int bcmf_sdpcm_rxfail(FAR struct bcmf_dev_s *priv, bool retry);
static int bcmf_sdpcm_process_header(FAR struct bcmf_dev_s *priv,
struct bcmf_sdpcm_header *header);
/****************************************************************************
* Private Data
****************************************************************************/
@ -105,19 +166,11 @@ int bcmf_sdpcm_rxfail(FAR struct bcmf_dev_s *priv, bool retry)
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int bcmf_sdpcm_process_header(FAR struct bcmf_dev_s *priv,
struct bcmf_sdpcm_header *header)
{
uint16_t len;
len = header->frametag[0];
if (header->data_offset < sizeof(struct bcmf_sdpcm_header) ||
header->data_offset > len)
header->data_offset > header->size)
{
_err("Invalid data offset\n");
bcmf_sdpcm_rxfail(priv, false);
@ -142,6 +195,10 @@ int bcmf_sdpcm_process_header(FAR struct bcmf_dev_s *priv,
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
// FIXME remove
uint8_t tmp_buffer[512];
int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv)
@ -160,8 +217,8 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv)
goto exit_abort;
}
len = header->frametag[0];
checksum = header->frametag[1];
len = header->size;
checksum = header->checksum;
/* All zero means no more to read */
@ -194,6 +251,9 @@ int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv)
goto exit_abort;
}
_info("Receive frame\n");
bcmf_hexdump((uint8_t*)header, len, (unsigned int)header);
/* Process and validate header */
ret = bcmf_sdpcm_process_header(priv, header);
@ -209,9 +269,242 @@ exit_abort:
return ret;
}
int bcmf_sdpcm_iovar_data_get(FAR struct bcmf_dev_s *priv, char *name,
void *data, unsigned int len)
int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv)
{
// TODO implement
return -EINVAL;
int ret;
struct bcmf_sdpcm_frame *frame;
if (priv->tx_queue.tail == NULL)
{
/* No more frames to send */
return -ENODATA;
}
if (priv->tx_seq == priv->max_seq)
{
// TODO handle this case
_err("No credit to send frame\n");
return -EAGAIN;
}
if ((ret = sem_wait(&priv->tx_queue_mutex)) != OK)
{
return ret;
}
frame = (struct bcmf_sdpcm_frame*)priv->tx_queue.tail;
/* Set frame sequence id */
frame->header.sequence = priv->tx_seq++;
_info("Send frame\n");
bcmf_hexdump((uint8_t*)&frame->header, frame->header.size,
(unsigned int)&frame->header);
ret = bcmf_transfer_bytes(priv, true, 2, 0, (uint8_t*)&frame->header,
frame->header.size);
if (ret != OK)
{
_info("fail send frame %d\n", ret);
ret = -EIO;
goto exit_abort;
// TODO handle retry count and remove frame from queue + abort TX
}
/* Frame sent, remove it from queue */
if (priv->tx_queue.head == &frame->list_entry)
{
/* List is empty */
priv->tx_queue.head = NULL;
priv->tx_queue.tail = NULL;
}
else
{
priv->tx_queue.tail = frame->list_entry.blink;
frame->list_entry.blink->flink = priv->tx_queue.head;
}
/* TODO free frame buffer */
goto exit_post_sem;
exit_abort:
// bcmf_sdpcm_txfail(priv, false);
exit_post_sem:
sem_post(&priv->tx_queue_mutex);
return ret;
}
// FIXME remove
uint8_t tmp_buffer2[512];
uint8_t* bcmf_sdpcm_allocate_iovar(FAR struct bcmf_dev_s *priv, char *name,
char *data, uint32_t *len)
{
uint32_t data_len;
uint16_t name_len = strlen(name) + 1;
if (!data)
{
data_len = 0;
}
else
{
data_len = *len;
}
// FIXME allocate buffer and use max_size instead of 512
if (data_len > 512-sizeof(struct bcmf_sdpcm_cdc_dcmd) ||
(data_len + name_len) > 512-sizeof(struct bcmf_sdpcm_cdc_dcmd))
{
*len = 0;
return NULL;
}
// TODO allocate buffer
/* Copy name string and data */
memcpy(tmp_buffer2+sizeof(struct bcmf_sdpcm_cdc_dcmd), name, name_len);
memcpy(tmp_buffer2+sizeof(struct bcmf_sdpcm_cdc_dcmd)+name_len,
data, data_len);
*len = sizeof(struct bcmf_sdpcm_cdc_dcmd)+name_len+data_len;
return tmp_buffer2;
}
int bcmf_sdpcm_queue_frame(FAR struct bcmf_dev_s *priv, uint8_t channel,
uint8_t *data, uint32_t len)
{
int ret;
struct bcmf_sdpcm_frame *frame = (struct bcmf_sdpcm_frame*)data;
uint16_t frame_size = len - sizeof(frame->list_entry);
/* Prepare sw header */
memset(&frame->header, 0, sizeof(struct bcmf_sdpcm_header));
frame->header.size = frame_size;
frame->header.checksum = ~frame_size;
frame->header.channel = channel;
frame->header.data_offset = sizeof(struct bcmf_sdpcm_header);
/* Add frame in tx queue */
if ((ret = sem_wait(&priv->tx_queue_mutex)) != OK)
{
return ret;
}
if (priv->tx_queue.head == NULL)
{
/* List is empty */
priv->tx_queue.head = &frame->list_entry;
priv->tx_queue.tail = &frame->list_entry;
frame->list_entry.flink = &frame->list_entry;
frame->list_entry.blink = &frame->list_entry;
}
else
{
/* Insert entry at list head */
frame->list_entry.flink = priv->tx_queue.head;
frame->list_entry.blink = priv->tx_queue.tail;
priv->tx_queue.head->blink = &frame->list_entry;
priv->tx_queue.head = &frame->list_entry;
}
sem_post(&priv->tx_queue_mutex);
/* Notify bcmf thread tx frame is ready */
sem_post(&priv->thread_signal);
return OK;
}
int bcmf_sdpcm_send_dcmd(FAR struct bcmf_dev_s *priv, uint32_t cmd,
int ifidx, bool set, uint8_t *data, uint32_t len)
{
struct bcmf_sdpcm_cdc_dcmd *msg = (struct bcmf_sdpcm_cdc_dcmd*)data;
/* Setup cdc_dcmd header */
msg->cdc_header.cmd = cmd;
msg->cdc_header.len = len-sizeof(struct bcmf_sdpcm_cdc_dcmd);
msg->cdc_header.status = 0;
msg->cdc_header.flags = ++priv->control_reqid << CDC_DCMD_ID_SHIFT;
msg->cdc_header.flags |= ifidx << CDC_DCMD_IF_SHIFT;
if (set)
{
msg->cdc_header.flags |= CDC_DCMD_SET;
}
/* Queue frame */
return bcmf_sdpcm_queue_frame(priv, SDPCM_CONTROL_CHANNEL, data, len);
}
int bcmf_sdpcm_iovar_request(FAR struct bcmf_dev_s *priv,
uint32_t ifidx, bool set, char *name,
char *data, uint32_t len)
{
int ret;
uint8_t *iovar_buf;
uint32_t iovar_buf_len = len;
/* Take device control mutex */
if ((ret = sem_wait(&priv->control_mutex)) !=OK)
{
return ret;
}
/* Prepare control frame */
iovar_buf = bcmf_sdpcm_allocate_iovar(priv, name, data, &iovar_buf_len);
if (!iovar_buf)
{
_err("Cannot allocate iovar buf\n");
ret = -ENOMEM;
goto exit_sem_post;
}
/* Send control frame */
ret = bcmf_sdpcm_send_dcmd(priv, set ? WLC_SET_VAR : WLC_GET_VAR,
ifidx, set, iovar_buf, iovar_buf_len);
if (ret != OK)
{
goto exit_free_iovar;
}
/* Wait for response */
ret = bcmf_sem_wait(&priv->control_timeout, SDPCM_CONTROL_TIMEOUT_MS);
if (ret != OK)
{
_err("Error while waiting for control response %d\n", ret);
goto exit_free_iovar;
}
if (!set)
{
/* Request sent, copy received data back */
memcpy(data, iovar_buf+sizeof(struct bcmf_sdpcm_cdc_dcmd), len);
}
exit_free_iovar:
// TODO free allocated buffer here
exit_sem_post:
sem_post(&priv->control_mutex);
return ret;
}

View File

@ -2,34 +2,13 @@
#define __DRIVERS_WIRELESS_IEEE80211_BCMF_SDPCM_H
#include "bcmf_driver.h"
#include <stdint.h>
struct bcmf_sdpcm_header {
uint16_t frametag[2];
uint8_t sequence;
uint8_t channel_flags;
uint8_t next_length;
uint8_t data_offset;
uint8_t flow_control;
uint8_t credit;
uint16_t padding;
};
struct bcmf_sdpcm_cdc_dcmd {
struct bcmf_sdpcm_header header;
uint32_t cmd; /* dongle command value */
uint32_t len; /* lower 16: output buflen;
* upper 16: input buflen (excludes header) */
uint32_t flags; /* flag defns given below */
uint32_t status; /* status code returned from the device */
};
int bcmf_sdpcm_process_header(FAR struct bcmf_dev_s *priv,
struct bcmf_sdpcm_header *header);
int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv);
int bcmf_sdpcm_iovar_data_get(FAR struct bcmf_dev_s *priv, char *name,
void *data, unsigned int len);
int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv);
int bcmf_sdpcm_iovar_request(FAR struct bcmf_dev_s *priv,
uint32_t ifidx, bool set, char *name,
char *data, uint32_t len);
#endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_SDPCM_H */