altos-avr: Add SPI-slave companion support
[fw/altos] / src-avr / ao_spi_slave.c
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19
20 __xdata struct ao_fifo  ao_spi_slave_rx_fifo;
21 __xdata struct ao_fifo  ao_spi_slave_tx_fifo;
22
23 static volatile uint8_t ao_spi_slave_tx_started;
24
25 static void
26 ao_spi_slave_tx_start(void)
27 {
28         if (!ao_spi_slave_tx_started && !ao_fifo_empty(ao_spi_slave_tx_fifo)) {
29                 ao_spi_slave_tx_started = 1;
30                 ao_fifo_remove(ao_spi_slave_tx_fifo, SPDR);
31         }
32 }
33
34 ISR(SPI_STC_vect)
35 {
36         uint8_t spsr;
37
38         spsr = SPSR;
39         if (SPIF & (1 << SPIF)) {
40                 uint8_t byte = SPDR;
41                 if (!ao_fifo_full(ao_spi_slave_rx_fifo))
42                         ao_fifo_insert(ao_spi_slave_rx_fifo, byte);
43                 ao_spi_slave_tx_started = 0;
44                 ao_spi_slave_tx_start();
45                 ao_wakeup(&ao_spi_slave_rx_fifo);
46                 ao_wakeup(&ao_spi_slave_tx_fifo);
47         }
48 }
49
50 static void
51 ao_spi_slave_put(uint8_t b) __critical
52 {
53         cli();
54         while (ao_fifo_full(ao_spi_slave_tx_fifo))
55                 ao_sleep(&ao_spi_slave_tx_fifo);
56         ao_fifo_insert(ao_spi_slave_tx_fifo, b);
57         ao_spi_slave_tx_start();
58         sei();
59 }
60
61 static uint8_t
62 ao_spi_slave_get(void) __critical
63 {
64         uint8_t b;
65
66         cli();
67         while (ao_fifo_empty(ao_spi_slave_rx_fifo))
68                 ao_sleep(&ao_spi_slave_rx_fifo);
69         ao_fifo_remove(ao_spi_slave_rx_fifo, b);
70         sei();
71         return b;
72 }
73
74 void
75 ao_spi_slave_read(uint8_t *data, int len)
76 {
77         while (len--) {
78                 ao_spi_slave_put(0);
79                 *data++ = ao_spi_slave_get();
80         }
81 }
82
83 void
84 ao_spi_slave_write(uint8_t *data, int len)
85 {
86         while (len--) {
87                 ao_spi_slave_put(*data++);
88                 (void) ao_spi_slave_get();
89         }
90 }
91
92 void
93 ao_spi_slave_init(void)
94 {
95         SPCR = (1 << SPIE) |            /* Enable SPI interrupts */
96                 (1 << SPE) |            /* Enable SPI */
97                 (0 << DORD) |           /* MSB first */
98                 (0 << MSTR) |           /* Slave mode */
99                 (0 << CPOL) |           /* Clock low when idle */
100                 (0 << CPHA);            /* Sample at leading clock edge */
101 }