2011-02-14 04:46:36 +02:00
|
|
|
/*
|
2011-02-14 17:48:42 +02:00
|
|
|
* fw/ep0.c - EP0 extension protocol
|
2011-02-14 04:46:36 +02:00
|
|
|
*
|
2013-03-30 00:14:11 +02:00
|
|
|
* Written 2008-2011, 2013 by Werner Almesberger
|
|
|
|
* Copyright 2008-2011, 2013 Werner Almesberger
|
2011-02-14 04:46:36 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
#include <stdbool.h>
|
2011-02-14 04:46:36 +02:00
|
|
|
#include <stdint.h>
|
2011-05-30 03:05:28 +03:00
|
|
|
#include <string.h>
|
2011-02-14 04:46:36 +02:00
|
|
|
|
|
|
|
#include <avr/io.h>
|
|
|
|
|
|
|
|
#ifndef NULL
|
|
|
|
#define NULL 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "usb.h"
|
2011-06-11 05:26:36 +03:00
|
|
|
#include "dfu.h"
|
2011-02-14 04:46:36 +02:00
|
|
|
|
|
|
|
#include "at86rf230.h"
|
|
|
|
#include "atusb/ep0.h"
|
2011-02-14 16:26:17 +02:00
|
|
|
#include "version.h"
|
2011-02-14 04:46:36 +02:00
|
|
|
#include "board.h"
|
2011-06-11 05:26:36 +03:00
|
|
|
#include "sernum.h"
|
2011-02-14 04:46:36 +02:00
|
|
|
#include "spi.h"
|
2011-07-12 17:23:21 +03:00
|
|
|
#include "mac.h"
|
2011-02-14 04:46:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
#define HW_TYPE HW_TYPE_110131
|
|
|
|
|
|
|
|
#define debug(...)
|
|
|
|
#define error(...)
|
|
|
|
|
|
|
|
|
|
|
|
static const uint8_t id[] = { EP0ATUSB_MAJOR, EP0ATUSB_MINOR, HW_TYPE };
|
|
|
|
static uint8_t buf[MAX_PSDU+3]; /* command, PHDR, and LQI */
|
|
|
|
static uint8_t size;
|
|
|
|
|
|
|
|
|
|
|
|
static void do_buf_write(void *user)
|
|
|
|
{
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
spi_begin();
|
|
|
|
for (i = 0; i != size; i++)
|
|
|
|
spi_send(buf[i]);
|
|
|
|
spi_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define BUILD_OFFSET 7 /* '#' plus "65535" plus ' ' */
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
static bool my_setup(const struct setup_request *setup)
|
2011-02-14 04:46:36 +02:00
|
|
|
{
|
2011-06-19 21:37:40 +03:00
|
|
|
uint16_t req = setup->bmRequestType | setup->bRequest << 8;
|
2011-02-14 04:46:36 +02:00
|
|
|
unsigned tmp;
|
|
|
|
uint8_t i;
|
2011-05-30 03:05:28 +03:00
|
|
|
uint64_t tmp64;
|
2011-02-14 04:46:36 +02:00
|
|
|
|
2011-06-19 21:37:40 +03:00
|
|
|
switch (req) {
|
2011-02-14 04:46:36 +02:00
|
|
|
case ATUSB_FROM_DEV(ATUSB_ID):
|
|
|
|
debug("ATUSB_ID\n");
|
|
|
|
if (setup->wLength > 3)
|
|
|
|
return 0;
|
|
|
|
usb_send(&eps[0], id, setup->wLength, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
case ATUSB_FROM_DEV(ATUSB_BUILD):
|
|
|
|
debug("ATUSB_BUILD\n");
|
|
|
|
tmp = build_number;
|
|
|
|
for (i = BUILD_OFFSET-2; tmp; i--) {
|
|
|
|
buf[i] = (tmp % 10)+'0';
|
|
|
|
tmp /= 10;
|
|
|
|
}
|
|
|
|
buf[i] = '#';
|
|
|
|
buf[BUILD_OFFSET-1] = ' ';
|
|
|
|
for (size = 0; build_date[size]; size++)
|
|
|
|
buf[BUILD_OFFSET+size] = build_date[size];
|
|
|
|
size += BUILD_OFFSET-i;
|
|
|
|
if (size > setup->wLength)
|
|
|
|
return 0;
|
|
|
|
usb_send(&eps[0], buf+i, size, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ATUSB_TO_DEV(ATUSB_RESET):
|
|
|
|
debug("ATUSB_RESET\n");
|
2011-05-10 05:52:00 +03:00
|
|
|
reset_cpu();
|
2011-02-14 04:46:36 +02:00
|
|
|
while (1);
|
|
|
|
|
|
|
|
case ATUSB_TO_DEV(ATUSB_RF_RESET):
|
|
|
|
debug("ATUSB_RF_RESET\n");
|
|
|
|
reset_rf();
|
2013-02-13 14:30:21 +02:00
|
|
|
mac_reset();
|
2011-02-14 04:46:36 +02:00
|
|
|
//ep_send_zlp(EP_CTRL);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ATUSB_FROM_DEV(ATUSB_POLL_INT):
|
|
|
|
debug("ATUSB_POLL_INT\n");
|
|
|
|
if (setup->wLength < 1)
|
|
|
|
return 0;
|
|
|
|
*buf = read_irq();
|
|
|
|
usb_send(&eps[0], buf, 1, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
|
2011-05-30 03:05:28 +03:00
|
|
|
case ATUSB_FROM_DEV(ATUSB_TIMER):
|
|
|
|
debug("ATUSB_TIMER\n");
|
|
|
|
size = setup->wLength;
|
|
|
|
if (size > sizeof(tmp64))
|
|
|
|
size = sizeof(tmp64);
|
|
|
|
tmp64 = timer_read();
|
|
|
|
memcpy(buf, &tmp64, sizeof(tmp64));
|
|
|
|
usb_send(&eps[0], buf, size, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
|
2011-06-04 15:40:34 +03:00
|
|
|
case ATUSB_FROM_DEV(ATUSB_GPIO):
|
|
|
|
debug("ATUSB_GPIO\n");
|
|
|
|
if (setup->wLength < 3)
|
|
|
|
return 0;
|
|
|
|
if (!gpio(setup->wIndex, setup->wValue, setup->wValue >> 8,
|
|
|
|
setup->wIndex >> 8, buf))
|
|
|
|
return 0;
|
|
|
|
usb_send(&eps[0], buf, 3, NULL, NULL);
|
|
|
|
return 1;
|
2011-06-21 05:11:09 +03:00
|
|
|
case ATUSB_TO_DEV(ATUSB_GPIO_CLEANUP):
|
|
|
|
gpio_cleanup();
|
|
|
|
return 1;
|
2011-06-04 15:40:34 +03:00
|
|
|
|
2011-06-06 04:34:49 +03:00
|
|
|
case ATUSB_TO_DEV(ATUSB_SLP_TR):
|
|
|
|
debug("ATUSB_SLP_TR\n");
|
|
|
|
slp_tr();
|
|
|
|
return 1;
|
|
|
|
|
2011-02-14 04:46:36 +02:00
|
|
|
case ATUSB_TO_DEV(ATUSB_REG_WRITE):
|
|
|
|
debug("ATUSB_REG_WRITE\n");
|
|
|
|
spi_begin();
|
|
|
|
spi_send(AT86RF230_REG_WRITE | setup->wIndex);
|
|
|
|
spi_send(setup->wValue);
|
|
|
|
spi_end();
|
|
|
|
//ep_send_zlp(EP_CTRL);
|
|
|
|
return 1;
|
|
|
|
case ATUSB_FROM_DEV(ATUSB_REG_READ):
|
|
|
|
debug("ATUSB_REG_READ\n");
|
|
|
|
spi_begin();
|
|
|
|
spi_send(AT86RF230_REG_READ | setup->wIndex);
|
|
|
|
*buf = spi_recv();
|
|
|
|
spi_end();
|
|
|
|
usb_send(&eps[0], buf, 1, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ATUSB_TO_DEV(ATUSB_BUF_WRITE):
|
|
|
|
debug("ATUSB_BUF_WRITE\n");
|
|
|
|
if (setup->wLength < 1)
|
|
|
|
return 0;
|
|
|
|
if (setup->wLength > MAX_PSDU)
|
|
|
|
return 0;
|
|
|
|
buf[0] = AT86RF230_BUF_WRITE;
|
|
|
|
buf[1] = setup->wLength;
|
|
|
|
size = setup->wLength+2;
|
|
|
|
usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
|
|
|
|
return 1;
|
|
|
|
case ATUSB_FROM_DEV(ATUSB_BUF_READ):
|
|
|
|
debug("ATUSB_BUF_READ\n");
|
|
|
|
if (setup->wLength < 2) /* PHR+LQI */
|
|
|
|
return 0;
|
|
|
|
if (setup->wLength > MAX_PSDU+2) /* PHR+PSDU+LQI */
|
|
|
|
return 0;
|
|
|
|
spi_begin();
|
|
|
|
spi_send(AT86RF230_BUF_READ);
|
|
|
|
size = spi_recv();
|
|
|
|
if (size >= setup->wLength)
|
|
|
|
size = setup->wLength-1;
|
|
|
|
for (i = 0; i != size+1; i++)
|
|
|
|
buf[i] = spi_recv();
|
|
|
|
spi_end();
|
|
|
|
usb_send(&eps[0], buf, size+1, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case ATUSB_TO_DEV(ATUSB_SRAM_WRITE):
|
|
|
|
debug("ATUSB_SRAM_WRITE\n");
|
|
|
|
if (setup->wIndex > SRAM_SIZE)
|
|
|
|
return 0;
|
|
|
|
if (setup->wIndex+setup->wLength > SRAM_SIZE)
|
|
|
|
return 0;
|
|
|
|
buf[0] = AT86RF230_SRAM_WRITE;
|
|
|
|
buf[1] = setup->wIndex;
|
|
|
|
size = setup->wLength+2;
|
|
|
|
usb_recv(&eps[0], buf+2, setup->wLength, do_buf_write, NULL);
|
|
|
|
return 1;
|
2011-06-03 20:25:19 +03:00
|
|
|
case ATUSB_FROM_DEV(ATUSB_SRAM_READ):
|
2011-02-14 04:46:36 +02:00
|
|
|
debug("ATUSB_SRAM_READ\n");
|
|
|
|
if (setup->wIndex > SRAM_SIZE)
|
|
|
|
return 0;
|
|
|
|
if (setup->wIndex+setup->wLength > SRAM_SIZE)
|
|
|
|
return 0;
|
|
|
|
spi_begin();
|
|
|
|
spi_send(AT86RF230_SRAM_READ);
|
|
|
|
spi_send(setup->wIndex);
|
2011-06-03 20:25:19 +03:00
|
|
|
for (i = 0; i != setup->wLength; i++)
|
2011-02-14 04:46:36 +02:00
|
|
|
buf[i] = spi_recv();
|
|
|
|
spi_end();
|
2011-06-03 20:25:19 +03:00
|
|
|
usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
|
2011-02-14 04:46:36 +02:00
|
|
|
return 1;
|
|
|
|
|
2011-06-19 21:37:40 +03:00
|
|
|
case ATUSB_TO_DEV(ATUSB_SPI_WRITE):
|
|
|
|
size = setup->wLength+2;
|
|
|
|
if (size > sizeof(buf))
|
|
|
|
return 0;
|
|
|
|
buf[0] = setup->wValue;
|
|
|
|
buf[1] = setup->wIndex;
|
|
|
|
if (setup->wLength)
|
|
|
|
usb_recv(&eps[0], buf+2, setup->wLength,
|
|
|
|
do_buf_write, NULL);
|
|
|
|
else
|
|
|
|
do_buf_write(NULL);
|
|
|
|
return 1;
|
atusb/fw/: added improved support for interrupt synchronization
At an interrupt barrier, the host must be able to ensure that no
interrupt generated before reaching the barrier is still pending and
will be delivered after crossing the barrier.
For this, we introduce the following concept:
- interrupts have a serial number. This number is sent to the host
on EP 1 (currently bulk) to signal the interrupt, instead of the
zero byte we used previously.
- the new request ATUSB_SPI_WRITE2_SYNC returns the interrupt
serial number from after the register write (the register write
itself is the interrupt barrier).
- the host can now check if the serial indicated from bulk and the
serial from ATUSB_SPI_WRITE2_SYNC are the same. If yes, interrupts
are synchronized. If not, it has to wait for the interrupt to be
signaled on EP 1.
We should also consider the case that the interrupt serial has gotten
ahead of ATUSB_SPI_WRITE2_SYNC. But that seems to happen rarely. In
any case, it's something for the host driver to worry about, not for
the firmware.
- board.h (irq_serial), board_app.c (irq_serial, INT0_vect): count
the interrupt serial number and return it when signaling the
interrupt
- include/atusb/ep0.h (ATUSB_SPI_WRITE2_SYNC), ep0.c (my_setup):
new request ATUSB_SPI_WRITE2_SYNC that does a register write, then
returns the interrupt serial
2011-07-07 21:51:07 +03:00
|
|
|
case ATUSB_FROM_DEV(ATUSB_SPI_WRITE2_SYNC):
|
|
|
|
spi_begin();
|
|
|
|
spi_send(setup->wValue);
|
|
|
|
spi_send(setup->wIndex);
|
|
|
|
spi_end();
|
|
|
|
buf[0] = irq_serial;
|
|
|
|
if (setup->wLength)
|
|
|
|
usb_send(&eps[0], buf, 1, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
|
2011-06-19 21:37:40 +03:00
|
|
|
case ATUSB_FROM_DEV(ATUSB_SPI_READ1):
|
|
|
|
case ATUSB_FROM_DEV(ATUSB_SPI_READ2):
|
|
|
|
spi_begin();
|
|
|
|
spi_send(setup->wValue);
|
2011-06-20 19:43:17 +03:00
|
|
|
if (req == ATUSB_FROM_DEV(ATUSB_SPI_READ2))
|
2011-06-19 21:37:40 +03:00
|
|
|
spi_send(setup->wIndex);
|
|
|
|
for (i = 0; i != setup->wLength; i++)
|
|
|
|
buf[i] = spi_recv();
|
|
|
|
spi_end();
|
|
|
|
usb_send(&eps[0], buf, setup->wLength, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
|
2011-07-12 17:23:21 +03:00
|
|
|
case ATUSB_TO_DEV(ATUSB_RX_MODE):
|
|
|
|
return mac_rx(setup->wValue);
|
|
|
|
case ATUSB_TO_DEV(ATUSB_TX):
|
2013-04-02 04:04:05 +03:00
|
|
|
return mac_tx(setup->wValue, setup->wIndex, setup->wLength);
|
2011-07-12 17:23:21 +03:00
|
|
|
|
2011-02-14 04:46:36 +02:00
|
|
|
default:
|
|
|
|
error("Unrecognized SETUP: 0x%02x 0x%02x ...\n",
|
|
|
|
setup->bmRequestType, setup->bRequest);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
static bool my_dfu_setup(const struct setup_request *setup)
|
2011-06-11 05:26:36 +03:00
|
|
|
{
|
|
|
|
switch (setup->bmRequestType | setup->bRequest << 8) {
|
|
|
|
case DFU_TO_DEV(DFU_DETACH):
|
|
|
|
/* @@@ should use wTimeout */
|
|
|
|
dfu.state = appDETACH;
|
|
|
|
return 1;
|
|
|
|
default:
|
|
|
|
return dfu_setup_common(setup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void my_set_interface(int nth)
|
|
|
|
{
|
|
|
|
if (nth) {
|
|
|
|
user_setup = my_dfu_setup;
|
|
|
|
user_get_descriptor = dfu_my_descr;
|
|
|
|
dfu.state = appIDLE;
|
|
|
|
} else {
|
|
|
|
user_setup = my_setup;
|
|
|
|
user_get_descriptor = sernum_get_descr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void my_reset(void)
|
|
|
|
{
|
|
|
|
if (dfu.state == appDETACH)
|
|
|
|
reset_cpu();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-14 04:46:36 +02:00
|
|
|
void ep0_init(void)
|
|
|
|
{
|
2011-06-11 03:50:42 +03:00
|
|
|
user_setup = my_setup;
|
2011-06-11 05:26:36 +03:00
|
|
|
user_set_interface = my_set_interface;
|
|
|
|
my_set_interface(0);
|
|
|
|
user_reset = my_reset;
|
2011-02-14 04:46:36 +02:00
|
|
|
}
|