/* * Simple software defined radio PHYsical layer. * * Copyright (C) 2008 Michael Buesch * * 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. * * 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. */ #include "../mac2phy.h" #include "../common.h" #include "util.h" #include "debug.h" #include "transmitter.h" #include "receiver.h" #include "spi_slave.h" #include #include #include #include /* Physical interface to the lower MAC */ #define MAC_RXIRQ_PORT PORTB #define MAC_RXIRQ_DDR DDRB #define MAC_RXIRQ_BIT 0 /* Various constants */ #define SCRAMBLER_REG_INIT 0x1B #define RF_PREAMBLE_SIZE 2 static uint8_t tx_buf[RF_PREAMBLE_SIZE + RF_HEADER_SIZE + RF_MAX_PACK_SIZE]; static uint16_t tx_size; static uint16_t tx_pointer_byte; static int8_t tx_pointer_bit; static bool tx_requested; static bool transmitter_is_enabled; static uint8_t tx_waitcount; #define CARRIER_DETECT_NONE 0 #define CARRIER_DETECT_PREAMBLE 1 #define CARRIER_DETECT_DATA 2 struct rx_statemachine { /* Current RX carrier detection state. */ uint8_t carrier_detect_state; /* The statemachine for walking through the packet. * Note that the values have different meanings for the two * carrier detection states PREAMBLE and DATA. */ uint8_t statemachine; /* Wireless-Medium-is-calm counter. * Used to detect if other STAs are transmitting or not. */ uint8_t wm_is_calm_count; /* The bitcount for the currently receiving packet. */ int8_t bitcount; /* The bytecount for the currently receiving packet. */ uint16_t bytecount; /* The scrambler register state for the current packet. */ uint8_t scrambler_reg; /* The previous bit. Only used for preamble detection * and parsing. */ bool prev_bit; }; static struct rx_statemachine rx; static uint8_t rx_buf[RF_HEADER_SIZE + RF_MAX_PACK_SIZE]; static uint16_t rx_size; static bool rx_frame_valid; /* The frame in rx_buf is valid */ enum radio_switch_request { RADIO_REQUEST_NONE = 0, RADIO_REQUEST_TURN_ON, RADIO_REQUEST_TURN_OFF, }; struct radio_status { uint8_t tx_request; bool tx_on; uint8_t rx_request; bool rx_on; }; static struct radio_status radio; enum spi_state_id { SPI_STATE_CMDWAIT, SPI_STATE_GET_TXFRAME, SPI_STATE_WAIT_TXFRAME, SPI_STATE_PUSH_RXFRAME, SPI_STATE_WAIT_RXFRAME, }; struct spi_state { /* The SPI statemachine. SPI_STATE_*** */ uint8_t state; union { /* Buffer for receiving the current command. */ uint8_t cur_cmd; /* Buffer for result codes. */ uint8_t result_code; }; }; static struct spi_state spi_state; /* Reset the RX statemachine to the ground state. So it will * start trying to detect the preamble at the next timer IRQ. */ static inline void rx_statemachine_reset(void) { rx.carrier_detect_state = CARRIER_DETECT_NONE; rx.statemachine = 0; rx.prev_bit = 0; /* The rest doesn't need to be initialized here, as it's * initialized when switching carrier state from * PREAMBLE to DATA. */ } /* Mask the RX timer interrupt. */ static inline void rx_timer_irq_disable(void) { TIMSK &= ~(1 << OCIE1A); } /* Unmask the RX timer interrupt. */ static inline void rx_timer_irq_enable(void) { TIMSK |= (1 << OCIE1A); } /* Clear the "pending" flag for the RX timer interrupt. */ static inline void rx_timer_irq_clear(void) { TIFR |= (1 << OCF1A); } /* Reset the radio timer, so it will trigger in exactly one * timer period in the future. */ static inline void rx_timer_reset(void) { TCNT1 = 0; /* Clear possibly pending IRQ */ rx_timer_irq_clear(); } /* Start the RX timer. * Only call from mainloop with IRQs disabled. */ static void rx_start(void) { /* Timer 1: RX timer */ TCCR1B = (1 << WGM12) | (1 << CS10) | (1 << CS11); /* prescaler 64 */ OCR1A = 250; /* 1kHz timer at 16MHz crystal */ rx_timer_irq_enable(); } /* Stop the RX timer. * Only call from mainloop with IRQs disabled. */ static void rx_stop(void) { TCCR1B = 0; /* Stop timer */ rx_timer_irq_disable(); rx_timer_irq_clear(); rx_statemachine_reset(); } /* Start the TX timer. * Only call from mainloop with IRQs disabled. */ static void tx_start(void) { /* Timer 2: TX timer */ TCCR2 = (1 << WGM21) | (1 << CS22); /* prescaler 64 */ OCR2 = 250; /* 1kHz timer at 16MHz crystal */ TIMSK |= (1 << OCIE2); } /* Stop the TX timer. * Only call from mainloop with IRQs disabled. */ static void tx_stop(void) { TCCR2 = 0; /* Stop timer */ TIMSK &= ~(1 << OCIE2); /* Disable IRQ */ TIFR |= (1 << OCF2); /* Clear pending IRQ */ transmitter_disable(); transmitter_is_enabled = 0; tx_waitcount = 0; tx_requested = 0; } /* Returns whether the WM (Wireless Medium) is calm. * Calm means there are no other STAs transmitting. */ static inline bool wm_is_calm(void) { return (rx.wm_is_calm_count >= 5); } /* Scramble a packet. */ static inline uint8_t scramble(uint8_t reg, void *_output, const void *_input, uint16_t size) { const uint8_t *input = _input; uint8_t *output = _output; uint16_t i; uint8_t bitnr, bitval; uint8_t tmp, scrambled; for (i = 0; i < size; i++) { tmp = 0; for (bitnr = 0; bitnr < 8; bitnr++) { bitval = (input[i] >> bitnr) & 1; scrambled = (bitval ^ (reg >> 7) ^ (reg >> 4)) & 1; reg = (reg << 1) | scrambled; tmp |= (scrambled << bitnr); } output[i] = tmp; } return reg; } /* Descramble a packet. */ static inline uint8_t descramble(uint8_t reg, void *_output, const void *_input, uint16_t size) { const uint8_t *input = _input; uint8_t *output = _output; uint16_t i; uint8_t bitnr, bitval; uint8_t tmp, scrambled; for (i = 0; i < size; i++) { tmp = 0; for (bitnr = 0; bitnr < 8; bitnr++) { scrambled = (input[i] >> bitnr) & 1; bitval = (scrambled ^ (reg >> 7) ^ (reg >> 4)) & 1; reg = (reg << 1) | scrambled; tmp |= (bitval << bitnr); } output[i] = tmp; } return reg; } static void kick_tx(uint16_t tx_nr_bytes) { tx_size = tx_nr_bytes; tx_pointer_byte = 0; tx_pointer_bit = 7; /* Start transmission now. */ mb(); tx_requested = 1; mb(); } static void put_tx_data(const void *payload, uint16_t size) { uint8_t scrambler_reg = SCRAMBLER_REG_INIT; /* Setup the preamble */ tx_buf[0] = 0xAA; /* 0b10101010 */ tx_buf[1] = 0xAC; /* 0b10101100 */ /* Setup the header */ tx_buf[2] = hi8(size); tx_buf[3] = lo8(size); tx_buf[4] = tx_buf[2] ^ tx_buf[3] ^ 0xFF; /* Scramble the header. */ scrambler_reg = scramble(scrambler_reg, tx_buf + RF_PREAMBLE_SIZE, tx_buf + RF_PREAMBLE_SIZE, RF_HEADER_SIZE); /* Scramble (and copy) the data */ scrambler_reg = scramble(scrambler_reg, tx_buf + RF_PREAMBLE_SIZE + RF_HEADER_SIZE, payload, size); kick_tx(size + RF_PREAMBLE_SIZE + RF_HEADER_SIZE); } /* Trigger the RX IRQ on the MAC chip. * This will cause it to initiate SPI transfers to fetch the data. */ static inline void mac_rxirq_trigger(void) { /* The IRQ is raising-edge triggered. */ MAC_RXIRQ_PORT |= (1 << MAC_RXIRQ_BIT); __asm__ __volatile__("nop\n nop\n nop"); MAC_RXIRQ_PORT &= ~(1 << MAC_RXIRQ_BIT); } /* Initialize the interface to the lower MAC */ static void mac_interface_init(void) { /* Initialize the RX-IRQ pin. */ MAC_RXIRQ_PORT &= ~(1 << MAC_RXIRQ_BIT); MAC_RXIRQ_DDR |= (1 << MAC_RXIRQ_BIT); spi_state.state = SPI_STATE_CMDWAIT; spi_slave_init(); spi_slave_fetch(&spi_state.cur_cmd, sizeof(spi_state.cur_cmd)); } static uint8_t spi_handle_command(void) { uint8_t err = SPI_ERR_NONE; switch (spi_state.cur_cmd) { case SPI_CMD_NONE: break; case SPI_CMD_TXFRAME: if (tx_requested) { /* An old TX request is still pending. */ err = SPI_ERR_BUSY; } else spi_state.state = SPI_STATE_GET_TXFRAME; break; case SPI_CMD_RXFRAME: if (rx_frame_valid) { spi_state.state = SPI_STATE_PUSH_RXFRAME; } else { /* Whoops, no packet available. */ err = SPI_ERR_BUSY; } break; case SPI_CMD_TX_TURN_ON: radio.tx_request = RADIO_REQUEST_TURN_ON; break; case SPI_CMD_TX_TURN_OFF: radio.tx_request = RADIO_REQUEST_TURN_OFF; break; case SPI_CMD_RX_TURN_ON: radio.rx_request = RADIO_REQUEST_TURN_ON; break; case SPI_CMD_RX_TURN_OFF: radio.rx_request = RADIO_REQUEST_TURN_OFF; break; default: err = SPI_ERR_CMD; } return err; } void spi_slave_xfer_complete(uint16_t size) { switch (spi_state.state) { case SPI_STATE_CMDWAIT: if (unlikely(size != 1)) spi_state.result_code = SPI_ERR_CMD; else spi_state.result_code = spi_handle_command(); spi_slave_xmit(&spi_state.result_code, sizeof(spi_state.result_code)); break; case SPI_STATE_GET_TXFRAME: spi_slave_fetch(tx_buf + RF_PREAMBLE_SIZE + RF_HEADER_SIZE, sizeof(tx_buf) - RF_PREAMBLE_SIZE - RF_HEADER_SIZE); spi_state.state = SPI_STATE_WAIT_TXFRAME; break; case SPI_STATE_WAIT_TXFRAME: if (size && (size < sizeof(tx_buf) - RF_PREAMBLE_SIZE - RF_HEADER_SIZE)) { //FIXME enable IRQs for scrambling put_tx_data(tx_buf + RF_PREAMBLE_SIZE + RF_HEADER_SIZE, size); } spi_state.state = SPI_STATE_CMDWAIT; spi_slave_fetch(&spi_state.cur_cmd, sizeof(spi_state.cur_cmd)); break; case SPI_STATE_PUSH_RXFRAME: spi_slave_xmit(rx_buf, rx_size); spi_state.state = SPI_STATE_WAIT_RXFRAME; break; case SPI_STATE_WAIT_RXFRAME: /* Let the radio receive the next frame. */ rx_frame_valid = 0; mb(); spi_state.state = SPI_STATE_CMDWAIT; spi_slave_fetch(&spi_state.cur_cmd, sizeof(spi_state.cur_cmd)); break; } } static void tx_bitbanger(void) { uint8_t bit; if (!transmitter_is_enabled) { if (!wm_is_calm()) return; } if (tx_waitcount > 0) { tx_waitcount--; return; } if (!transmitter_is_enabled) { transmitter_is_enabled = 1; transmitter_enable(); } bit = !!(tx_buf[tx_pointer_byte] & (1 << tx_pointer_bit)); tx_pointer_bit--; if (tx_pointer_bit < 0) { tx_pointer_bit = 7; tx_pointer_byte++; if (tx_pointer_byte >= tx_size) { /* Transmission finished */ transmitter_disable(); transmitter_is_enabled = 0; /* Wait some time before starting the next TX. */ tx_waitcount = 50; tx_requested = 0; mb(); return; } } transmitter_data_bit_set(bit); } /* Calibrate the RX timer to trigger at exactly half-cycle of the * RX bitcycle. */ static inline bool rx_preamble_timer_calibrate(bool old_state) { bool cur_state; uint8_t timeout = 200; /* 2000 uSec */ /* Re-enable interrupts, so we can service the SPI interface * while waiting. However, mask the RX timer interrupt to * avoid recursions. */ rx_timer_irq_disable(); sei(); /* Wait for the input signal to change level. */ while (1) { cur_state = receiver_get_current_signal(); if (cur_state != old_state) break; udelay(10); if (unlikely(--timeout == 0)) goto out; } /* Delay exactly half a cycle. Take ADC delay into account. * This place needs changes, if the ADC prescaler is changed. */ udelay(485); /* Reset the timer, so it will trigger at exactly half-cycle. * This will ensure we have enough room upwards and downwards * to compensate crystal deviations for the duration of this packet. */ rx_timer_reset(); out: /* Finally restore interrupt state. */ cli(); rx_timer_irq_enable(); return cur_state; } static inline uint16_t get_rx_payload_size(void) { return (((uint16_t)(rx_buf[0])) << 8) | ((uint16_t)(rx_buf[1])); } static inline int8_t rx_validate_header(void) { uint8_t parity; parity = rx_buf[0] ^ rx_buf[1] ^ 0xFF; if (unlikely(parity != rx_buf[2])) return -1; /* Header parity error */ return 0; } static void descramble_received_payload(void) { /* Enable global interrupts while descrambling the payload, * so we can service SPI interrupts. */ rx_timer_irq_disable(); sei(); descramble(rx.scrambler_reg, rx_buf + RF_HEADER_SIZE, rx_buf + RF_HEADER_SIZE, rx_size - RF_HEADER_SIZE); cli(); rx_timer_irq_clear(); rx_timer_irq_enable(); } static void run_rx_statemachine(void) { bool cur_bit; int8_t err; if (rx_frame_valid) { /* Must first wait for the current data frame * to get pushed to the MAC. */ return; } /* Get the current RX bit. */ cur_bit = receiver_get_current_signal(); if (cur_bit) { rx.wm_is_calm_count = 0; } else { if (rx.wm_is_calm_count < 0xFF) rx.wm_is_calm_count++; } if (rx.carrier_detect_state == CARRIER_DETECT_DATA) { carrier_detected: /* We are receiving the data frame. */ switch (rx.statemachine) { case 0: /* Receiving header */ if (cur_bit) rx_buf[rx.bytecount] |= (1 << rx.bitcount); else rx_buf[rx.bytecount] &= ~(1 << rx.bitcount); rx.bitcount--; if (rx.bitcount < 0) { rx.bitcount = 7; rx.bytecount++; if (rx.bytecount == RF_HEADER_SIZE) { /* Descramble and validate header. * Save the scrambler state for descrambling * the payload later. */ rx.scrambler_reg = descramble(SCRAMBLER_REG_INIT, rx_buf, rx_buf, RF_HEADER_SIZE); err = rx_validate_header(); if (err) { /* Invalid header! Dump the rest of the frame */ rx.statemachine = 2; /* error */ break; } /* Valid header. Payload starts now. */ rx.statemachine = 1; rx_size = get_rx_payload_size() + RF_HEADER_SIZE; if (unlikely((rx_size <= RF_HEADER_SIZE) || (rx_size > RF_HEADER_SIZE + RF_MAX_PACK_SIZE))) { /* Whoops, frame too small or too big. */ rx.statemachine = 2; /* error */ } break; } } break; case 1: /* Receiving payload */ if (cur_bit) rx_buf[rx.bytecount] |= (1 << rx.bitcount); else rx_buf[rx.bytecount] &= ~(1 << rx.bitcount); rx.bitcount--; if (rx.bitcount < 0) { rx.bitcount = 7; rx.bytecount++; if (rx.bytecount == rx_size) { /* Data frame finished. */ rx_statemachine_reset(); /* Descramble the payload. */ descramble_received_payload(); /* Tell the world we have a new frame */ rx_frame_valid = 1; mac_rxirq_trigger(); break; } } break; case 2: /* Error */ /* Wait for carrier loss */ if (wm_is_calm()) { /* Ok, assume carrier lost. */ rx_statemachine_reset(); } break; } } else { /* Carrier detection in the preamble */ switch (rx.statemachine) { case 0: /* Inside of 0101010101...1100 calibration pattern */ if (rx.prev_bit == 0 && cur_bit == 0) { /* Both previous and current signal is low. * Carrier lost. */ rx.carrier_detect_state = CARRIER_DETECT_NONE; } else if (rx.prev_bit != cur_bit) { /* Last signal is different from the current one. * There might be a preamble going on. * Try to calibrate the timer to the signal, if * not already done so for the current preamble. */ if (rx.carrier_detect_state == CARRIER_DETECT_NONE) { bool new_bit; new_bit = rx_preamble_timer_calibrate(cur_bit); if (unlikely(new_bit == cur_bit)) break; /* We had a timeout */ cur_bit = new_bit; rx.carrier_detect_state = CARRIER_DETECT_PREAMBLE; } } else if (rx.prev_bit == 1 && cur_bit == 1) { /* Last signal and current signal are high. * We might be in the middle of 1100. * Switch the statemachine. */ rx.statemachine = 1; } break; case 1: /* We're inside of the 1100 pattern (The 11 part is already done). */ if (rx.prev_bit == 0 && cur_bit == 0) { /* Last signal was low and current signal is low. * The preamble has finished! * Switch the statemachine. */ rx.statemachine = 2; } else if (cur_bit == 1) { /* Huh, got high immediately after the 11 part. * There must be an error. * Reset the statemachine. */ rx.statemachine = 0; /* error */ } break; case 2: /* 00 pattern done */ /* Preamble finished. Real data starts now! * Setup the statemachine for the RX of DATA * and jump to the DATA statemachine code. */ rx.carrier_detect_state = CARRIER_DETECT_DATA; rx.statemachine = 0; rx.bytecount = 0; rx.bitcount = 7; goto carrier_detected; } rx.prev_bit = cur_bit; } } /* 1kHz PHY RX timer. * This is the main PHY timer for carrier detection and * bitbanging the RX data stream. * Note that the timer is not exactly monotonic, as it will adjust itself * to the received signals. * This timer re-enables interrupts, so other interrupts may run nested * inside of this one. (SPI-IRQ is not synchronized with this one!) */ ISR(TIMER1_COMPA_vect) { if (!transmitter_is_enabled) run_rx_statemachine(); } /* 1kHz PHY TX timer. * This is the main PHY timer for bitbanging the TX data stream. */ ISR(TIMER2_COMP_vect) { if (tx_requested) tx_bitbanger(); } static inline uint8_t crc8(uint8_t crc, uint8_t data) { /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */ static const prog_uint8_t t[] = { 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, }; return pgm_read_byte(&(t[crc ^ data])); } static inline uint8_t crc8_buffer(const void *_buf, uint16_t size) { const uint8_t *buf = _buf; uint16_t i; uint8_t crc = 0xFF; for (i = 0; i < size; i++) crc = crc8(crc, buf[i]); crc ^= 0xFF; return crc; } static void send_testframe(void) { static uint8_t pack[3 + 2 + 1]; uint16_t *p; mb(); if (tx_requested) return; p = (uint16_t *)(pack + 3); (*p)++; pack[0] = 0x00; /* DA */ pack[1] = 0x01; /* SA */ pack[2]++; /* SEQ */ pack[5] = crc8_buffer(pack, sizeof(pack) - 1); put_tx_data(pack, sizeof(pack)); } int main(void) { cli(); debug_initialize(); transmitter_initialize(); receiver_initialize(); tx_stop(); rx_stop(); mac_interface_init(); sei(); while (1) { //send_testframe(); mb(); /* Turn the radios on/off, if requested. * Optimization: First check the values without disabling IRQs. */ if (unlikely(radio.tx_request != RADIO_REQUEST_NONE)) { cli(); if (radio.tx_request == RADIO_REQUEST_TURN_ON && !radio.tx_on) { tx_start(); radio.tx_on = 1; } if (radio.tx_request == RADIO_REQUEST_TURN_OFF && radio.tx_on) { tx_stop(); radio.tx_on = 0; } radio.tx_request = RADIO_REQUEST_NONE; sei(); } if (unlikely(radio.rx_request != RADIO_REQUEST_NONE)) { cli(); if (radio.rx_request == RADIO_REQUEST_TURN_ON && !radio.rx_on) { rx_start(); radio.rx_on = 1; } if (radio.rx_request == RADIO_REQUEST_TURN_OFF && radio.rx_on) { rx_stop(); radio.rx_on = 0; } radio.rx_request = RADIO_REQUEST_NONE; sei(); } } }