/* * 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 volatile __xdata uint8_t ao_radio_done = 1; static __xdata uint8_t ao_radio_mutex; __xdata int8_t ao_radio_cmac_rssi; #if 0 #define PRINTD(...) do { printf ("\r%s: ", __func__); printf(__VA_ARGS__); flush(); } while(0) #else #define PRINTD(...) #endif static void ao_radio_isr(void) { ao_exti_disable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN); ao_radio_done = 1; ao_wakeup((void *) &ao_radio_done); } static void ao_radio_master_delay(void) { // uint16_t i; // for (i = 0; i < 1000; i++) // ao_arch_nop(); ao_delay(1); } static void ao_radio_master_start(void) { ao_radio_master_delay(); ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS, AO_RADIO_SPI_BUS, AO_SPI_SPEED_200kHz); } 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); // ao_delay(1); } static uint8_t ao_radio_master_send(void) { if (!ao_radio_done) printf ("radio not done in ao_radio_master_send\n"); PRINTD("send %d\n", ao_radio_spi_request.len); 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((void *) &ao_radio_done)) { printf ("ao_radio_master awoken\n"); break; } sei(); PRINTD ("sent, radio done %d\n", ao_radio_done); if (!ao_radio_done) printf ("radio didn't finish after ao_radio_master_send\n"); return ao_radio_done; } 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) { uint8_t ret; PRINTD ("send fetch req\n"); ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN; ao_radio_spi_request.request = AO_RADIO_SPI_RECV_FETCH; ao_radio_spi_request.recv_len = size; ret = ao_radio_master_send(); PRINTD ("fetch req sent %d\n", ret); PRINTD ("fetch\n"); 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); PRINTD ("fetched %d\n", 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; PRINTD ("cmac_send: send %d\n", len); /* Set the key. */ PRINTD ("set key\n"); 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(); PRINTD ("key set\n"); /* Send the data */ PRINTD ("sending packet\n"); 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(); PRINTD ("packet sent\n"); return AO_RADIO_CMAC_OK; } int8_t ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant { int8_t ret; int8_t recv; if (len > AO_CMAC_MAX_LEN) return AO_RADIO_CMAC_LEN_ERROR; /* Set the key. */ PRINTD ("setting key\n"); 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); recv = ao_radio_master_send(); ao_radio_put(); PRINTD ("key set: %d\n", recv); /* Recv the data */ PRINTD ("queuing recv\n"); ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0); ao_radio_spi_request.recv_len = len; recv = ao_radio_master_send(); PRINTD ("recv queued: %d\n", recv); if (!recv) { ao_radio_put(); ao_radio_recv_abort(); return AO_RADIO_CMAC_TIMEOUT; } PRINTD ("fetching data\n"); ao_radio_get_data(packet, len); recv = ao_radio_spi_reply.status; ao_radio_cmac_rssi = ao_radio_spi_reply.rssi; ao_radio_put(); PRINTD ("data fetched: %d %d\n", recv, ao_radio_cmac_rssi); 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]); }