From c31d07fb35a5b4d283facf649bed3f0f9802d1fc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 27 Aug 2012 13:49:07 -0700 Subject: [PATCH] altos: Add SPI linked radio API Forward the necessary radio functions over the SPI link Signed-off-by: Keith Packard --- src/drivers/ao_radio_master.c | 266 ++++++++++++++++++++++++++++++++++ src/drivers/ao_radio_slave.c | 127 ++++++++++++++++ src/drivers/ao_radio_spi.h | 58 ++++++++ 3 files changed, 451 insertions(+) create mode 100644 src/drivers/ao_radio_master.c create mode 100644 src/drivers/ao_radio_slave.c create mode 100644 src/drivers/ao_radio_spi.h diff --git a/src/drivers/ao_radio_master.c b/src/drivers/ao_radio_master.c new file mode 100644 index 00000000..6edea66d --- /dev/null +++ b/src/drivers/ao_radio_master.c @@ -0,0 +1,266 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include + +static __xdata struct ao_radio_spi_reply ao_radio_spi_reply; +static __xdata struct ao_radio_spi_request ao_radio_spi_request; +static __xdata uint8_t ao_radio_done; +static __xdata uint8_t ao_radio_mutex; + +__xdata int8_t ao_radio_cmac_rssi; + +static void +ao_radio_isr(void) +{ + ao_exti_disable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN); + ao_radio_done = 1; + ao_wakeup(&ao_radio_done); +} + +static void +ao_radio_master_start(void) +{ + ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS, + AO_RADIO_SPI_BUS, + AO_SPI_SPEED_1MHz); +} + +static void +ao_radio_master_stop(void) +{ + ao_spi_put_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS, + AO_RADIO_SPI_BUS); +} + + +static uint8_t +ao_radio_master_send(void) +{ + ao_radio_done = 0; + ao_exti_enable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN); + ao_radio_master_start(); + ao_spi_send(&ao_radio_spi_request, + ao_radio_spi_request.len, + AO_RADIO_SPI_BUS); + ao_radio_master_stop(); + cli(); + while (!ao_radio_done) + if (ao_sleep(&ao_radio_done)) + break; + sei(); + return ao_radio_done; +} + +static void +ao_radio_master_recv(uint16_t len) +{ + ao_radio_master_start(); + ao_spi_recv(&ao_radio_spi_reply, + len, + AO_RADIO_SPI_BUS); + ao_radio_master_stop(); +} + +static void +ao_radio_get(uint8_t req, uint8_t len) +{ + ao_config_get(); + ao_mutex_get(&ao_radio_mutex); + ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN + len; + ao_radio_spi_request.request = req; + ao_radio_spi_request.setting = ao_config.radio_setting; +} + +static void +ao_radio_put(void) +{ + ao_mutex_put(&ao_radio_mutex); +} + +static void +ao_radio_get_data(__xdata void *d, uint8_t size) +{ + ao_radio_master_start(); + ao_spi_recv(&ao_radio_spi_reply, + AO_RADIO_SPI_REPLY_HEADER_LEN + size, + AO_RADIO_SPI_BUS); + ao_radio_master_stop(); + ao_xmemcpy(d, ao_radio_spi_reply.payload, size); +} + +void +ao_radio_recv_abort(void) +{ + ao_radio_get(AO_RADIO_SPI_RECV_ABORT, 0); + ao_radio_master_send(); + ao_radio_put(); +} + +void +ao_radio_send(const void *d, uint8_t size) +{ + ao_radio_get(AO_RADIO_SPI_SEND, size); + ao_xmemcpy(&ao_radio_spi_request.payload, d, size); + ao_radio_master_send(); + ao_radio_put(); +} + + +uint8_t +ao_radio_recv(__xdata void *d, uint8_t size) +{ + int8_t ret; + uint8_t recv; + + /* Recv the data + */ + + ao_radio_get(AO_RADIO_SPI_RECV, 0); + ao_radio_spi_request.recv_len = size; + recv = ao_radio_master_send(); + if (!recv) { + ao_radio_put(); + ao_radio_recv_abort(); + return 0; + } + ao_radio_get_data(d, size); + recv = ao_radio_spi_reply.status; + ao_radio_put(); + return recv; +} + +int8_t +ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant +{ + if (len > AO_CMAC_MAX_LEN) + return AO_RADIO_CMAC_LEN_ERROR; + + /* Set the key. + */ + ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN); + ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN); + ao_radio_master_send(); + ao_radio_put(); + + /* Send the data + */ + + ao_radio_get(AO_RADIO_SPI_CMAC_SEND, len); + ao_xmemcpy(&ao_radio_spi_request.payload, packet, len); + ao_radio_master_send(); + ao_radio_put(); + return AO_RADIO_CMAC_OK; +} + +int8_t +ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant +{ + int8_t ret; + uint8_t recv; + + if (len > AO_CMAC_MAX_LEN) + return AO_RADIO_CMAC_LEN_ERROR; + + /* Set the key. + */ + ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN); + ao_radio_spi_request.timeout = timeout; + ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN); + ao_radio_master_send(); + ao_radio_put(); + + /* Recv the data + */ + + ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0); + ao_radio_spi_request.recv_len = len; + recv = ao_radio_master_send(); + if (!recv) { + ao_radio_put(); + ao_radio_recv_abort(); + return AO_RADIO_CMAC_TIMEOUT; + } + ao_radio_get_data(packet, len); + recv = ao_radio_spi_reply.status; + ao_radio_put(); + return recv; +} + +static uint8_t ao_radio_test_on; + +void +ao_radio_test(uint8_t on) +{ + if (on) { + if (!ao_radio_test_on) { + ao_radio_get(AO_RADIO_SPI_TEST_ON, 0); + ao_radio_test_on = 1; + ao_radio_master_send(); + } + } else { + if (ao_radio_test_on) { + ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN; + ao_radio_spi_request.request = AO_RADIO_SPI_TEST_OFF; + ao_radio_master_send(); + ao_radio_test_on = 0; + ao_radio_put(); + } + } +} + +static void +ao_radio_test_cmd(void) +{ + uint8_t mode = 2; + ao_cmd_white(); + if (ao_cmd_lex_c != '\n') { + ao_cmd_decimal(); + mode = (uint8_t) ao_cmd_lex_u32; + } + mode++; + if ((mode & 2)) + ao_radio_test(1); + if (mode == 3) { + printf ("Hit a character to stop..."); flush(); + getchar(); + putchar('\n'); + } + if ((mode & 1)) + ao_radio_test(0); +} + +__code struct ao_cmds ao_radio_cmds[] = { + { ao_radio_test_cmd, "C <1 start, 0 stop, none both>\0Radio carrier test" }, + { 0, NULL }, +}; + +void +ao_radio_init(void) +{ + ao_spi_init_cs(AO_RADIO_CS_PORT, (1 << AO_RADIO_CS_PIN)); + + ao_enable_port(AO_RADIO_INT_PORT); + ao_exti_setup(AO_RADIO_INT_PORT, + AO_RADIO_INT_PIN, + AO_EXTI_MODE_FALLING, + ao_radio_isr); + ao_cmd_register(&ao_radio_cmds[0]); +} diff --git a/src/drivers/ao_radio_slave.c b/src/drivers/ao_radio_slave.c new file mode 100644 index 00000000..9dff511b --- /dev/null +++ b/src/drivers/ao_radio_slave.c @@ -0,0 +1,127 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include + +static __xdata struct ao_radio_spi_reply ao_radio_spi_reply; + +static __xdata struct ao_radio_spi_request ao_radio_spi_request; + +static __xdata uint8_t ao_radio_spi_recv_request; +static __xdata uint8_t ao_radio_spi_recv_len; +static __xdata uint16_t ao_radio_spi_recv_timeout; + +static void +ao_radio_slave_signal(void) +{ + ao_gpio_set(AO_RADIO_SLAVE_INT_PORT, AO_RADIO_SLAVE_INT_BIT, AO_RADIO_SLAVE_INT_PIN, 0); + ao_arch_nop(); + ao_arch_nop(); + ao_arch_nop(); + ao_gpio_set(AO_RADIO_SLAVE_INT_PORT, AO_RADIO_SLAVE_INT_BIT, AO_RADIO_SLAVE_INT_PIN, 0); +} + +static void +ao_radio_slave_spi(void) +{ + for (;;) { + ao_spi_get_slave(AO_RADIO_SLAVE_BUS); + ao_spi_recv(&ao_radio_spi_request, (2 << 13) | sizeof (ao_radio_spi_request), AO_RADIO_SLAVE_BUS); + ao_spi_put_slave(AO_RADIO_SLAVE_BUS); + ao_led_for(AO_LED_RED, AO_MS_TO_TICKS(1000)); + switch (ao_radio_spi_request.request) { + case AO_RADIO_SPI_RECV: + case AO_RADIO_SPI_CMAC_RECV: + ao_config.radio_setting = ao_radio_spi_request.setting; + ao_radio_spi_recv_request = ao_radio_spi_request.request; + ao_radio_spi_recv_len = ao_radio_spi_request.recv_len; + ao_radio_spi_recv_timeout = ao_radio_spi_request.timeout; + ao_wakeup(&ao_radio_spi_recv_len); + break; + case AO_RADIO_SPI_RECV_FETCH: + ao_spi_get_slave(AO_RADIO_SLAVE_BUS); + ao_spi_send(&ao_radio_spi_reply, + ao_radio_spi_request.recv_len + AO_RADIO_SPI_REPLY_HEADER_LEN, + AO_RADIO_SLAVE_BUS); + ao_spi_put_slave(AO_RADIO_SLAVE_BUS); + break; + case AO_RADIO_SPI_RECV_ABORT: + ao_radio_recv_abort(); + break; + case AO_RADIO_SPI_SEND: + ao_config.radio_setting = ao_radio_spi_request.setting; + ao_radio_send(&ao_radio_spi_request.payload, ao_radio_spi_request.len - AO_RADIO_SPI_REQUEST_HEADER_LEN); + ao_radio_slave_signal(); + break; + + case AO_RADIO_SPI_CMAC_SEND: + ao_config.radio_setting = ao_radio_spi_request.setting; + ao_radio_cmac_send(&ao_radio_spi_request.payload, ao_radio_spi_request.len - AO_RADIO_SPI_REQUEST_HEADER_LEN); + ao_radio_slave_signal(); + break; + + case AO_RADIO_SPI_CMAC_KEY: + ao_xmemcpy(&ao_config.aes_key, ao_radio_spi_request.payload, AO_AES_LEN); + ao_radio_slave_signal(); + break; + + case AO_RADIO_SPI_TEST_ON: + ao_radio_test(1); + ao_radio_slave_signal(); + break; + + case AO_RADIO_SPI_TEST_OFF: + ao_radio_test(0); + ao_radio_slave_signal(); + break; + } + } +} + +static void +ao_radio_slave_recv(void) +{ + uint8_t len; + for (;;) { + while (!ao_radio_spi_recv_len) + ao_sleep(&ao_radio_spi_recv_len); + len = ao_radio_spi_recv_len; + ao_radio_spi_recv_len = 0; + if (ao_radio_spi_recv_request == AO_RADIO_SPI_RECV) { + ao_radio_spi_reply.status = ao_radio_recv(&ao_radio_spi_reply.payload, len); + ao_radio_spi_reply.rssi = 0; + } else { + ao_radio_spi_reply.status = ao_radio_cmac_recv(&ao_radio_spi_reply.payload, len, + ao_radio_spi_recv_timeout); + ao_radio_spi_reply.rssi = ao_radio_cmac_rssi; + } + ao_radio_slave_signal(); + } +} + +static __xdata struct ao_task ao_radio_slave_spi_task; +static __xdata struct ao_task ao_radio_slave_recv_task; + +void +ao_radio_slave_init(void) +{ + ao_add_task(&ao_radio_slave_spi_task, ao_radio_slave_spi, "radio_spi"); + ao_add_task(&ao_radio_slave_recv_task, ao_radio_slave_recv, "radio_recv"); + ao_enable_output(AO_RADIO_SLAVE_INT_PORT, AO_RADIO_SLAVE_INT_BIT, AO_RADIO_SLAVE_INT_PIN, 1); +} diff --git a/src/drivers/ao_radio_spi.h b/src/drivers/ao_radio_spi.h new file mode 100644 index 00000000..2957f70d --- /dev/null +++ b/src/drivers/ao_radio_spi.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_RADIO_SPI_H_ +#define _AO_RADIO_SPI_H_ + +#define AO_RADIO_SPI_RECV 0 +#define AO_RADIO_SPI_RECV_ABORT 1 +#define AO_RADIO_SPI_RECV_FETCH 2 +#define AO_RADIO_SPI_SEND 3 + +#define AO_RADIO_SPI_CMAC_KEY 4 +#define AO_RADIO_SPI_CMAC_RECV 5 +#define AO_RADIO_SPI_CMAC_SEND 6 + +#define AO_RADIO_SPI_TEST_ON 7 +#define AO_RADIO_SPI_TEST_OFF 8 + +#define AO_RADIO_SPI_MAX_PAYLOAD 128 + +struct ao_radio_spi_request { + uint8_t len; /* required to be first by cc1111 DMA engine */ + uint8_t request; + uint8_t recv_len; + uint8_t pad; + uint32_t setting; + uint16_t timeout; + uint8_t payload[AO_RADIO_SPI_MAX_PAYLOAD]; +}; + +#define AO_RADIO_SPI_REQUEST_HEADER_LEN (sizeof (struct ao_radio_spi_request) - AO_RADIO_SPI_MAX_PAYLOAD) + +struct ao_radio_spi_reply { + uint8_t status; + int8_t rssi; + uint8_t payload[AO_RADIO_SPI_MAX_PAYLOAD]; +}; + +#define AO_RADIO_SPI_REPLY_HEADER_LEN (sizeof (struct ao_radio_spi_reply) - AO_RADIO_SPI_MAX_PAYLOAD) + +void +ao_radio_slave_init(void); + +#endif /* _AO_RADIO_SPI_H_ */ -- 2.30.2