ba5595126dd5656e25a2cb9fbd2e821a55d00873
[fw/altos] / src / ao_packet.c
1 /*
2  * Copyright © 2009 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 static __xdata struct ao_packet_recv rx_packet;
21 static __xdata struct ao_packet tx_packet;
22 static __xdata char tx_data[AO_PACKET_MAX];
23 static __xdata char rx_data[AO_PACKET_MAX];
24 static __pdata uint8_t rx_len, rx_used, tx_used;
25 static __pdata uint8_t rx_seq;
26
27 static __xdata struct ao_task   ao_packet_task;
28 static __xdata uint8_t ao_packet_enable;
29 static __xdata uint8_t ao_packet_master_sleeping;
30
31 void
32 ao_packet_send(void)
33 {
34         ao_led_on(AO_LED_RED);
35         ao_config_get();
36         ao_mutex_get(&ao_radio_mutex);
37         if (tx_used && tx_packet.len == 0) {
38                 memcpy(&tx_packet.d, tx_data, tx_used);
39                 tx_packet.len = tx_used;
40                 tx_packet.seq++;
41                 tx_used = 0;
42                 ao_wakeup(&tx_data);
43         }
44         ao_radio_idle();
45         ao_radio_done = 0;
46         RF_CHANNR = ao_config.radio_channel;
47         ao_dma_set_transfer(ao_radio_dma,
48                             &tx_packet,
49                             &RFDXADDR,
50                             sizeof (struct ao_packet),
51                             DMA_CFG0_WORDSIZE_8 |
52                             DMA_CFG0_TMODE_SINGLE |
53                             DMA_CFG0_TRIGGER_RADIO,
54                             DMA_CFG1_SRCINC_1 |
55                             DMA_CFG1_DESTINC_0 |
56                             DMA_CFG1_PRIORITY_HIGH);
57         ao_dma_start(ao_radio_dma);
58         RFST = RFST_STX;
59         __critical while (!ao_radio_done)
60                 ao_sleep(&ao_radio_done);
61         ao_mutex_put(&ao_radio_mutex);
62         ao_led_off(AO_LED_RED);
63 }
64
65 uint8_t
66 ao_packet_recv(void)
67 {
68         uint8_t dma_done;
69
70         ao_led_on(AO_LED_GREEN);
71         ao_config_get();
72         ao_mutex_get(&ao_radio_mutex);
73         ao_radio_idle();
74         RF_CHANNR = ao_config.radio_channel;
75         ao_dma_set_transfer(ao_radio_dma,
76                             &RFDXADDR,
77                             &rx_packet,
78                             sizeof (struct ao_packet_recv),
79                             DMA_CFG0_WORDSIZE_8 |
80                             DMA_CFG0_TMODE_SINGLE |
81                             DMA_CFG0_TRIGGER_RADIO,
82                             DMA_CFG1_SRCINC_0 |
83                             DMA_CFG1_DESTINC_1 |
84                             DMA_CFG1_PRIORITY_HIGH);
85         ao_dma_start(ao_radio_dma);
86         RFST = RFST_SRX;
87         __critical while (!ao_radio_dma_done)
88                            if (ao_sleep(&ao_radio_dma_done) != 0)
89                                    ao_radio_abort();
90         dma_done = ao_radio_dma_done;
91         ao_mutex_put(&ao_radio_mutex);
92         ao_led_off(AO_LED_GREEN);
93
94         if (dma_done & AO_DMA_DONE) {
95                 if (!(rx_packet.status & PKT_APPEND_STATUS_1_CRC_OK))
96                         return AO_DMA_ABORTED;
97                 if (rx_packet.packet.len == AO_PACKET_SYN) {
98                         rx_seq = rx_packet.packet.seq;
99                         tx_packet.seq = rx_packet.packet.ack;
100                         tx_packet.ack = rx_seq;
101                 } else if (rx_packet.packet.len) {
102                         if (rx_packet.packet.seq == (uint8_t) (rx_seq + (uint8_t) 1) && rx_used == rx_len) {
103 #if 0
104                                 printf ("rx len %3d seq %3d ack %3d\n",
105                                         rx_packet.packet.len,
106                                         rx_packet.packet.seq,
107                                         rx_packet.packet.ack);
108                                 flush();
109 #endif
110                                 memcpy(rx_data, rx_packet.packet.d, rx_packet.packet.len);
111                                 rx_used = 0;
112                                 rx_len = rx_packet.packet.len;
113                                 rx_seq = rx_packet.packet.seq;
114                                 tx_packet.ack = rx_seq;
115                                 ao_wakeup(&rx_data);
116                         }
117                 }
118                 if (rx_packet.packet.ack == tx_packet.seq) {
119                         tx_packet.len = 0;
120                         ao_wakeup(&tx_packet);
121                 }
122         }
123         return dma_done;
124 }
125
126 void
127 ao_packet_slave(void)
128 {
129         ao_radio_set_packet();
130         tx_packet.addr = ao_serial_number;
131         tx_packet.len = AO_PACKET_SYN;
132         while (ao_packet_enable) {
133                 ao_packet_recv();
134                 ao_packet_send();
135         }
136         ao_exit();
137 }
138
139 /* Thread for the master side of the packet link */
140
141 void
142 ao_packet_master(void)
143 {
144         uint8_t status;
145
146         ao_radio_set_packet();
147         tx_packet.addr = ao_serial_number;
148         tx_packet.len = AO_PACKET_SYN;
149         while (ao_packet_enable) {
150                 ao_packet_send();
151                 ao_alarm(AO_MS_TO_TICKS(100));
152                 status = ao_packet_recv();
153                 if (status & AO_DMA_DONE) {
154                         /* if we can transmit data, do so */
155                         if (tx_used && tx_packet.len == 0)
156                                 continue;
157                         ao_packet_master_sleeping = 1;
158                         ao_delay(AO_MS_TO_TICKS(1000));
159                         ao_packet_master_sleeping = 0;
160                 }
161         }
162         ao_exit();
163 }
164
165 void
166 ao_packet_flush(void)
167 {
168         /* If there is data to send, and this is the master,
169          * then poke the master to send all queued data
170          */
171         if (tx_used && ao_packet_master_sleeping)
172                 ao_wake_task(&ao_packet_task);
173 }
174
175 void
176 ao_packet_putchar(char c)
177 {
178         while (tx_used == AO_PACKET_MAX && ao_packet_enable) {
179                 ao_packet_flush();
180                 ao_sleep(&tx_data);
181         }
182
183         if (ao_packet_enable)
184                 tx_data[tx_used++] = c;
185 }
186
187 char
188 ao_packet_getchar(void) __critical
189 {
190         while (rx_used == rx_len && ao_packet_enable) {
191                 /* poke the master to get more data */
192                 if (ao_packet_master_sleeping)
193                         ao_wake_task(&ao_packet_task);
194                 ao_sleep(&rx_data);
195         }
196
197         if (!ao_packet_enable)
198                 return 0;
199
200         return rx_data[rx_used++];
201 }
202
203 static void
204 ao_packet_echo(void) __reentrant
205 {
206         uint8_t c;
207         while (ao_packet_enable) {
208                 c = ao_packet_getchar();
209                 if (ao_packet_enable) {
210                         putchar(c);
211                         if (c == (uint8_t) '\n' || c == (uint8_t) '\r')
212                                 flush();
213                 }
214         }
215         ao_exit();
216 }
217
218 static __xdata struct ao_task   ao_packet_echo_task;
219
220 static void
221 ao_packet_forward(void) __reentrant
222 {
223         char c;
224         ao_packet_enable = 1;
225         ao_cmd_white();
226
227         if (ao_cmd_lex_c == 'm')
228                 ao_add_task(&ao_packet_task, ao_packet_master, "master");
229         else
230                 ao_add_task(&ao_packet_task, ao_packet_slave, "slave");
231         ao_add_task(&ao_packet_echo_task, ao_packet_echo, "echo");
232         while ((c = getchar()) != '~')
233                 ao_packet_putchar(c);
234         ao_packet_enable = 0;
235         ao_radio_abort();
236         while (ao_packet_echo_task.wchan || ao_packet_task.wchan) {
237                 ao_wake_task(&ao_packet_echo_task);
238                 ao_wake_task(&ao_packet_task);
239         }
240 }
241
242 __code struct ao_cmds ao_packet_cmds[] = {
243         { 'p',  ao_packet_forward,      "p {m|s}                            Remote packet link. m=master, s=slave" },
244         { 0,    ao_packet_forward,      NULL },
245 };
246
247 void
248 ao_packet_init(void)
249 {
250         ao_cmd_register(&ao_packet_cmds[0]);
251 }