outline text with black
[fpv.git] / mainboard / i2c_protocol.c
1 /*
2  *  Copyright Droids Corporation (2009)
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; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Revision : $Id: i2c_protocol.c,v 1.8 2009-11-08 17:24:33 zer0 Exp $
19  *
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <aversive/pgmspace.h>
26 #include <aversive/wait.h>
27 #include <aversive/error.h>
28
29 #include <callout.h>
30 #include <i2c.h>
31
32 #include "../common/i2c_commands.h"
33 #include "i2c_protocol.h"
34 #include "beep.h"
35 #include "main.h"
36
37 #define I2C_STATE_MAX 2
38 #define I2C_PERIOD_MS 50
39
40 #define I2C_TIMEOUT 100 /* ms */
41 #define I2C_MAX_ERRORS 40
42
43 #define IMUBOARD_BEEP_PERIOD_MS 2000
44
45 static volatile uint8_t i2c_poll_num = 0;
46 static volatile uint8_t i2c_state = 0;
47 static volatile uint8_t i2c_rx_count = 0;
48 static volatile uint8_t i2c_tx_count = 0;
49 static volatile uint16_t i2c_errors = 0;
50
51 #define OP_READY 0 /* no i2c op running */
52 #define OP_POLL  1 /* a user command is running */
53 #define OP_CMD   2 /* a polling (req / ans) is running */
54
55 static volatile uint8_t running_op = OP_READY;
56
57 #define I2C_MAX_LOG 3
58 static uint8_t error_log = 0;
59
60 static struct callout i2c_timer;
61 static struct callout imuboard_beep_timer;
62
63 static int8_t i2c_req_imuboard_status(void);
64
65 /* latest received imuboard_status */
66 struct i2c_ans_imuboard_status imuboard_status;
67
68 /* used for commands */
69 uint8_t command_buf[I2C_SEND_BUFFER_SIZE];
70 volatile int8_t command_dest=-1;
71 volatile uint8_t command_size=0;
72
73 static uint8_t i2c_enabled = 0;
74
75 #define I2C_ERROR(args...) do {                                         \
76                 if (error_log < I2C_MAX_LOG) {                          \
77                         ERROR(E_USER_I2C_PROTO, args);                  \
78                         error_log ++;                                   \
79                         if (error_log == I2C_MAX_LOG) {                 \
80                                 ERROR(E_USER_I2C_PROTO,                 \
81                                       "i2c logs are now warnings");     \
82                         }                                               \
83                 }                                                       \
84                 else                                                    \
85                         WARNING(E_USER_I2C_PROTO, args);                \
86         } while(0)
87
88 void i2c_protocol_debug(void)
89 {
90         printf_P(PSTR("I2C protocol debug infos:\r\n"));
91         printf_P(PSTR("  i2c_send=%d\r\n"), i2c_tx_count);
92         printf_P(PSTR("  i2c_recv=%d\r\n"), i2c_rx_count);
93         printf_P(PSTR("  i2c_state=%d\r\n"), i2c_state);
94         printf_P(PSTR("  i2c_errors=%d\r\n"), i2c_errors);
95         printf_P(PSTR("  running_op=%d\r\n"), running_op);
96         printf_P(PSTR("  command_size=%d\r\n"), command_size);
97         printf_P(PSTR("  command_dest=%d\r\n"), command_dest);
98         printf_P(PSTR("  i2c_status=%x\r\n"), i2c_status());
99 }
100
101 static void i2cproto_next_state(uint8_t inc)
102 {
103         i2c_state += inc;
104         if (i2c_state >= I2C_STATE_MAX) {
105                 i2c_state = 0;
106                 i2c_poll_num ++;
107         }
108 }
109
110 void i2cproto_wait_update(void)
111 {
112         uint8_t poll_num;
113         poll_num = i2c_poll_num;
114         (void)poll_num;
115         //WAIT_COND_OR_TIMEOUT((i2c_poll_num-poll_num) > 1, 150); /* XXX todo */
116 }
117
118 static void imuboard_beep_cb(struct callout_mgr *cm, struct callout *tim, void *arg)
119 {
120         (void)cm;
121         (void)tim;
122         (void)arg;
123
124         if (i2c_enabled == 0)
125                 goto reschedule;
126
127         if ((imuboard_status.flags & I2C_IMUBOARD_STATUS_SDCARD_OK) == 0) {
128                 beep(0, 1, 1);
129                 goto reschedule;
130         }
131
132         if ((imuboard_status.flags & I2C_IMUBOARD_STATUS_BOOT_OK) == 0) {
133                 beep(0, 1, 1);
134                 beep(0, 1, 1);
135                 goto reschedule;
136         }
137
138         if ((imuboard_status.flags & I2C_IMUBOARD_STATUS_GPS_OK) == 0) {
139                 beep(0, 1, 1);
140                 beep(0, 1, 1);
141                 beep(0, 1, 1);
142                 goto reschedule;
143         }
144
145  reschedule:
146         callout_reschedule(cm, tim, IMUBOARD_BEEP_PERIOD_MS);
147 }
148
149
150 /* called periodically : the goal of this 'thread' is to send requests
151  * and read answers on i2c slaves in the correct order. */
152 static void i2c_poll_slaves(struct callout_mgr *cm, struct callout *tim, void *arg)
153 {
154         uint8_t flags;
155         int8_t err;
156
157         (void)cm;
158         (void)tim;
159         (void)arg;
160
161 #if 0
162         static uint8_t a = 0;
163
164         a++;
165         if (a & 0x4)
166                 LED2_TOGGLE();
167 #endif
168
169         if (i2c_enabled == 0)
170                 goto reschedule;
171
172         /* already running */
173         IRQ_LOCK(flags);
174         if (running_op != OP_READY) {
175                 IRQ_UNLOCK(flags);
176                 goto reschedule;
177         }
178
179         /* if a command is ready to be sent, so send it */
180         if (command_size) {
181                 running_op = OP_CMD;
182                 err = i2c_send(command_dest, command_buf, command_size,
183                              I2C_CTRL_GENERIC);
184                 if (err < 0)
185                         goto error;
186                 IRQ_UNLOCK(flags);
187                 goto reschedule;
188         }
189
190         /* no command, so do the polling */
191         running_op = OP_POLL;
192
193         switch(i2c_state) {
194
195         /* poll status of imuboard */
196 #define I2C_REQ_IMUBOARD 0
197         case I2C_REQ_IMUBOARD:
198                 if ((err = i2c_req_imuboard_status()))
199                         goto error;
200                 break;
201
202 #define I2C_ANS_IMUBOARD 1
203         case I2C_ANS_IMUBOARD:
204                 if ((err = i2c_recv(I2C_IMUBOARD_ADDR,
205                                     sizeof(struct i2c_ans_imuboard_status),
206                                     I2C_CTRL_GENERIC)))
207                         goto error;
208                 break;
209
210         /* sync with I2C_STATE_MAX */
211
212         /* nothing, go to the first request */
213         default:
214                 i2c_state = 0;
215                 running_op = OP_READY;
216         }
217         IRQ_UNLOCK(flags);
218
219         goto reschedule;
220
221  error:
222         running_op = OP_READY;
223         IRQ_UNLOCK(flags);
224         i2c_errors++;
225         if (i2c_errors > I2C_MAX_ERRORS) {
226                 I2C_ERROR("I2C send is_cmd=%d proto_state=%d "
227                       "err=%d i2c_status=%x", !!command_size, i2c_state, err, i2c_status());
228                 i2c_reset();
229                 i2c_errors = 0;
230         }
231
232  reschedule:
233         /* reschedule */
234         callout_reschedule(cm, tim, I2C_PERIOD_MS);
235 }
236
237 /* called when the xmit is finished */
238 void i2c_sendevent(int8_t size)
239 {
240         if (size > 0) {
241                 if (running_op == OP_POLL) {
242                         i2cproto_next_state(1);
243                 }
244                 else
245                         command_size = 0;
246                 i2c_tx_count++;
247         }
248         else {
249                 i2c_errors++;
250                 NOTICE(E_USER_I2C_PROTO, "send error state=%d size=%d "
251                         "op=%d", i2c_state, size, running_op);
252                 if (i2c_errors > I2C_MAX_ERRORS) {
253                         I2C_ERROR("I2C error, slave not ready");
254                         i2c_reset();
255                         i2c_errors = 0;
256                 }
257
258                 if (running_op == OP_POLL) {
259                         /* skip associated answer */
260                         i2cproto_next_state(2);
261                 }
262         }
263         running_op = OP_READY;
264 }
265
266 /* called rx event */
267 void i2c_recvevent(uint8_t * buf, int8_t size)
268 {
269         if (running_op == OP_POLL)
270                 i2cproto_next_state(1);
271
272         /* recv is only trigged after a poll */
273         running_op = OP_READY;
274
275         if (size < 0) {
276                 goto error;
277         }
278
279         i2c_rx_count++;
280
281         switch (buf[0]) {
282
283         case I2C_ANS_IMUBOARD_STATUS: {
284                 struct i2c_ans_imuboard_status *ans =
285                         (struct i2c_ans_imuboard_status *)buf;
286
287                 if (size != sizeof (*ans))
288                         goto error;
289
290                 /* copy status in a global struct */
291                 memcpy(&imuboard_status, ans, sizeof(imuboard_status));
292
293                 break;
294         }
295
296         default:
297                 break;
298         }
299
300         return;
301  error:
302         i2c_errors++;
303         NOTICE(E_USER_I2C_PROTO, "recv error state=%d op=%d",
304                i2c_state, running_op);
305         if (i2c_errors > I2C_MAX_ERRORS) {
306                 I2C_ERROR("I2C error, slave not ready");
307                 i2c_reset();
308                 i2c_errors = 0;
309         }
310 }
311
312 void i2c_recvbyteevent(uint8_t hwstatus, uint8_t i, uint8_t c)
313 {
314         (void)hwstatus;
315         (void)i;
316         (void)c;
317 }
318
319 /* ******** ******** ******** ******** */
320 /* commands */
321 /* ******** ******** ******** ******** */
322
323
324 static int8_t
325 i2c_send_command(uint8_t addr, uint8_t * buf, uint8_t size)
326 {
327         uint8_t flags;
328         uint16_t ms = get_time_ms();
329
330         while ((get_time_ms() - ms) < I2C_TIMEOUT) {
331                 IRQ_LOCK(flags);
332                 if (command_size == 0) {
333                         memcpy(command_buf, buf, size);
334                         command_size = size;
335                         command_dest = addr;
336                         IRQ_UNLOCK(flags);
337                         return 0;
338                 }
339                 IRQ_UNLOCK(flags);
340         }
341         /* this should not happen... except if we are called from an
342          * interrupt context, but it's forbidden */
343         I2C_ERROR("I2C command send failed");
344         return -EBUSY;
345 }
346
347 static int8_t i2c_req_imuboard_status(void)
348 {
349         struct i2c_req_imuboard_status buf;
350         int8_t err;
351
352         buf.hdr.cmd = I2C_REQ_IMUBOARD_STATUS;
353         err = i2c_send(I2C_IMUBOARD_ADDR, (uint8_t*)&buf,
354                         sizeof(buf), I2C_CTRL_GENERIC);
355
356         return err;
357 }
358
359 int8_t i2c_led_control(uint8_t addr, uint8_t led, uint8_t state)
360 {
361         struct i2c_cmd_led_control buf;
362         buf.hdr.cmd = I2C_CMD_GENERIC_LED_CONTROL;
363         buf.led_num = led;
364         buf.state = state;
365         return i2c_send_command(addr, (uint8_t*)&buf, sizeof(buf));
366 }
367
368 void i2c_protocol_enable(uint8_t enable)
369 {
370         i2c_enabled = enable;
371 }
372
373 void i2c_protocol_init(void)
374 {
375         callout_init(&i2c_timer, i2c_poll_slaves, NULL, I2C_PRIO);
376         callout_schedule(&xbeeboard.intr_cm, &i2c_timer, I2C_PERIOD_MS);
377         callout_init(&imuboard_beep_timer, imuboard_beep_cb, NULL, I2C_PRIO); // prio ok ? XXX
378         callout_schedule(&xbeeboard.intr_cm, &imuboard_beep_timer,
379                 IMUBOARD_BEEP_PERIOD_MS);
380 }