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