1
0
mirror of git://projects.qi-hardware.com/ben-wpan.git synced 2024-11-23 04:57:10 +02:00
ben-wpan/atusb/fw/mac.c
Stefan Schmidt 869f3c43bd atusb: fw: use extended operation mode also for transmitting
With this switch we have RX as well as TX extended operation mode for the
transceiver enabled. Tested and verified for atusb as well as rzusb.

The biggest change coming with this is that the hardware no handles automatic
retransmit of frames if an ACK was requested but not received. The needed
changes to the atusb kernel driver are also done already and will be submitted
once we release version 0.3 with this change included.
2016-04-22 22:25:20 +02:00

275 lines
4.9 KiB
C

/*
* fw/mac.c - HardMAC functions
*
* Written 2011, 2013 by Werner Almesberger
* Copyright 2011, 2013 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include "usb.h"
#include "at86rf230.h"
#include "spi.h"
#include "board.h"
#include "mac.h"
#define RX_BUFS 3
bool (*mac_irq)(void) = NULL;
static uint8_t rx_buf[RX_BUFS][MAX_PSDU+2]; /* PHDR+payload+LQ */
static uint8_t tx_buf[MAX_PSDU];
static uint8_t tx_size = 0;
static bool txing = 0;
static bool queued_tx_ack = 0;
static uint8_t next_seq, this_seq, queued_seq;
/* ----- Receive buffer management ----------------------------------------- */
static uint8_t rx_in = 0, rx_out = 0;
static inline void next_buf(uint8_t *index)
{
*index = (*index+1) % RX_BUFS;
}
/* ----- Register access --------------------------------------------------- */
static uint8_t reg_read(uint8_t reg)
{
uint8_t value;
spi_begin();
spi_send(AT86RF230_REG_READ | reg);
value = spi_recv();
spi_end();
return value;
}
static void reg_write(uint8_t reg, uint8_t value)
{
spi_begin();
spi_send(AT86RF230_REG_WRITE | reg);
spi_send(value);
spi_end();
}
/* ----- Interrupt handling ------------------------------------------------ */
static void rx_done(void *user);
static void tx_ack_done(void *user);
static void usb_next(void)
{
const uint8_t *buf;
if (rx_in != rx_out) {
buf = rx_buf[rx_out];
led(1);
usb_send(&eps[1], buf, buf[0]+2, rx_done, NULL);
}
if (queued_tx_ack) {
usb_send(&eps[1], &queued_seq, 1, tx_ack_done, NULL);
queued_tx_ack = 0;
}
}
static void tx_ack_done(void *user)
{
usb_next();
}
static void change_state(uint8_t new)
{
while ((reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK) ==
TRX_STATUS_TRANSITION);
reg_write(REG_TRX_STATE, new);
}
static void rx_done(void *user)
{
led(0);
next_buf(&rx_out);
usb_next();
#ifdef AT86RF230
/* slap at86rf230 - reduce fragmentation issue */
change_state(TRX_STATUS_RX_AACK_ON);
#endif
}
static void receive_frame(void)
{
uint8_t size;
uint8_t *buf;
spi_begin();
spi_io(AT86RF230_BUF_READ);
size = spi_recv();
if (!size || (size & 0x80)) {
spi_end();
return;
}
buf = rx_buf[rx_in];
spi_recv_block(buf+1, size+1);
spi_end();
buf[0] = size;
next_buf(&rx_in);
if (eps[1].state == EP_IDLE)
usb_next();
}
static bool handle_irq(void)
{
uint8_t irq;
irq = reg_read(REG_IRQ_STATUS);
if (!(irq & IRQ_TRX_END))
return 1;
if (txing) {
if (eps[1].state == EP_IDLE) {
usb_send(&eps[1], &this_seq, 1, tx_ack_done, NULL);
} else {
queued_tx_ack = 1;
queued_seq = this_seq;
}
txing = 0;
return 1;
}
/* likely */
if (eps[1].state == EP_IDLE || rx_in != rx_out)
receive_frame();
return 1;
}
/* ----- TX/RX ------------------------------------------------------------- */
bool mac_rx(int on)
{
if (on) {
mac_irq = handle_irq;
reg_read(REG_IRQ_STATUS);
change_state(TRX_CMD_RX_AACK_ON);
} else {
mac_irq = NULL;
change_state(TRX_CMD_FORCE_TRX_OFF);
txing = 0;
}
return 1;
}
static void do_tx(void *user)
{
uint16_t timeout = 0xffff;
uint8_t status;
uint8_t i;
/*
* If we time out here, the host driver will time out waiting for the
* TRX_END acknowledgement.
*/
do {
if (!--timeout)
return;
status = reg_read(REG_TRX_STATUS) & TRX_STATUS_MASK;
}
while (status != TRX_STATUS_RX_ON && status != TRX_STATUS_RX_AACK_ON);
#ifdef AT86RF231
/*
* We use TRX_CMD_FORCE_PLL_ON instead of TRX_CMD_PLL_ON because a new
* reception may have begun while we were still working on the previous
* one.
*/
reg_write(REG_TRX_STATE, TRX_CMD_FORCE_PLL_ON);
#endif
#ifdef AT86RF230
/*
* at86rf230 doesn't support force change, nevetherless this works
* somehow
*/
reg_write(REG_TRX_STATE, TRX_CMD_PLL_ON);
#endif
handle_irq();
spi_begin();
spi_send(AT86RF230_BUF_WRITE);
spi_send(tx_size+2); /* CRC */
for (i = 0; i != tx_size; i++)
spi_send(tx_buf[i]);
spi_end();
change_state(TRX_STATUS_TX_ARET_ON);
slp_tr();
txing = 1;
this_seq = next_seq;
/*
* Wait until we reach BUSY_TX_ARET, so that we command the transition to
* RX_AACK_ON which will be executed upon TX completion.
*/
change_state(TRX_CMD_PLL_ON);
change_state(TRX_CMD_RX_AACK_ON);
}
bool mac_tx(uint16_t flags, uint8_t seq, uint16_t len)
{
if (len > MAX_PSDU)
return 0;
tx_size = len;
next_seq = seq;
usb_recv(&eps[0], tx_buf, len, do_tx, NULL);
return 1;
}
void mac_reset(void)
{
mac_irq = NULL;
txing = 0;
queued_tx_ack = 0;
rx_in = rx_out = 0;
next_seq = this_seq = queued_seq = 0;
/* enable CRC and PHY_RSSI (with RX_CRC_VALID) in SPI status return */
reg_write(REG_TRX_CTRL_1,
TX_AUTO_CRC_ON | SPI_CMD_MODE_PHY_RSSI << SPI_CMD_MODE_SHIFT);
}