allow to dump GPS info
[protos/xbee-avr.git] / 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 "../fpv-common/i2c_commands.h"
33 #include "main.h"
34 #include "i2c_protocol.h"
35
36 #define I2C_STATE_MAX 2
37 #define I2C_PERIOD_MS 50
38
39 #define I2C_TIMEOUT 100 /* ms */
40 #define I2C_MAX_ERRORS 40
41
42 static volatile uint8_t i2c_poll_num = 0;
43 static volatile uint8_t i2c_state = 0;
44 static volatile uint16_t i2c_errors = 0;
45
46 #define OP_READY 0 /* no i2c op running */
47 #define OP_POLL  1 /* a user command is running */
48 #define OP_CMD   2 /* a polling (req / ans) is running */
49
50 static volatile uint8_t running_op = OP_READY;
51
52 #define I2C_MAX_LOG 3
53 static uint8_t error_log = 0;
54
55 static struct callout i2c_timer;
56
57 static int8_t i2c_req_imuboard_status(void);
58
59 /* latest received imuboard_status */
60 struct i2c_ans_imuboard_status imuboard_status;
61
62 /* used for commands */
63 uint8_t command_buf[I2C_SEND_BUFFER_SIZE];
64 volatile int8_t command_dest=-1;
65 volatile uint8_t command_size=0;
66
67 #define I2C_ERROR(args...) do {                                         \
68                 if (error_log < I2C_MAX_LOG) {                          \
69                         ERROR(E_USER_I2C_PROTO, args);                  \
70                         error_log ++;                                   \
71                         if (error_log == I2C_MAX_LOG) {                 \
72                                 ERROR(E_USER_I2C_PROTO,                 \
73                                       "i2c logs are now warnings");     \
74                         }                                               \
75                 }                                                       \
76                 else                                                    \
77                         WARNING(E_USER_I2C_PROTO, args);                \
78         } while(0)
79
80 void i2c_protocol_debug(void)
81 {
82         printf_P(PSTR("I2C protocol debug infos:\r\n"));
83         printf_P(PSTR("  i2c_state=%d\r\n"), i2c_state);
84         printf_P(PSTR("  i2c_errors=%d\r\n"), i2c_errors);
85         printf_P(PSTR("  running_op=%d\r\n"), running_op);
86         printf_P(PSTR("  command_size=%d\r\n"), command_size);
87         printf_P(PSTR("  command_dest=%d\r\n"), command_dest);
88         printf_P(PSTR("  i2c_status=%x\r\n"), i2c_status());
89 }
90
91 static void i2cproto_next_state(uint8_t inc)
92 {
93         i2c_state += inc;
94         if (i2c_state >= I2C_STATE_MAX) {
95                 i2c_state = 0;
96                 i2c_poll_num ++;
97         }
98 }
99
100 void i2cproto_wait_update(void)
101 {
102         uint8_t poll_num;
103         poll_num = i2c_poll_num;
104         (void)poll_num;
105         //WAIT_COND_OR_TIMEOUT((i2c_poll_num-poll_num) > 1, 150); /* XXX todo */
106 }
107
108 /* called periodically : the goal of this 'thread' is to send requests
109  * and read answers on i2c slaves in the correct order. */
110 static void i2c_poll_slaves(struct callout_mgr *cm, struct callout *tim, void *arg)
111 {
112         uint8_t flags;
113         int8_t err;
114
115         (void)cm;
116         (void)tim;
117         (void)arg;
118
119 #if 0
120         static uint8_t a = 0;
121
122         a++;
123         if (a & 0x4)
124                 LED2_TOGGLE();
125 #endif
126
127         /* already running */
128         IRQ_LOCK(flags);
129         if (running_op != OP_READY) {
130                 IRQ_UNLOCK(flags);
131                 goto reschedule;
132         }
133
134         /* if a command is ready to be sent, so send it */
135         if (command_size) {
136                 running_op = OP_CMD;
137                 err = i2c_send(command_dest, command_buf, command_size,
138                              I2C_CTRL_GENERIC);
139                 if (err < 0)
140                         goto error;
141                 IRQ_UNLOCK(flags);
142                 goto reschedule;
143         }
144
145         /* no command, so do the polling */
146         running_op = OP_POLL;
147
148         switch(i2c_state) {
149
150         /* poll status of imuboard */
151 #define I2C_REQ_IMUBOARD 0
152         case I2C_REQ_IMUBOARD:
153                 if ((err = i2c_req_imuboard_status()))
154                         goto error;
155                 break;
156
157 #define I2C_ANS_IMUBOARD 1
158         case I2C_ANS_IMUBOARD:
159                 if ((err = i2c_recv(I2C_IMUBOARD_ADDR,
160                                     sizeof(struct i2c_ans_imuboard_status),
161                                     I2C_CTRL_GENERIC)))
162                         goto error;
163                 break;
164
165         /* sync with I2C_STATE_MAX */
166
167         /* nothing, go to the first request */
168         default:
169                 i2c_state = 0;
170                 running_op = OP_READY;
171         }
172         IRQ_UNLOCK(flags);
173
174         goto reschedule;
175
176  error:
177         running_op = OP_READY;
178         IRQ_UNLOCK(flags);
179         i2c_errors++;
180         if (i2c_errors > I2C_MAX_ERRORS) {
181                 I2C_ERROR("I2C send is_cmd=%d proto_state=%d "
182                       "err=%d i2c_status=%x", !!command_size, i2c_state, err, i2c_status());
183                 i2c_reset();
184                 i2c_errors = 0;
185         }
186
187  reschedule:
188         /* reschedule */
189         callout_reschedule(cm, tim, I2C_PERIOD_MS);
190 }
191
192 /* called when the xmit is finished */
193 void i2c_sendevent(int8_t size)
194 {
195         if (size > 0) {
196                 if (running_op == OP_POLL) {
197                         i2cproto_next_state(1);
198                 }
199                 else
200                         command_size = 0;
201         }
202         else {
203                 i2c_errors++;
204                 NOTICE(E_USER_I2C_PROTO, "send error state=%d size=%d "
205                         "op=%d", i2c_state, size, running_op);
206                 if (i2c_errors > I2C_MAX_ERRORS) {
207                         I2C_ERROR("I2C error, slave not ready");
208                         i2c_reset();
209                         i2c_errors = 0;
210                 }
211
212                 if (running_op == OP_POLL) {
213                         /* skip associated answer */
214                         i2cproto_next_state(2);
215                 }
216         }
217         running_op = OP_READY;
218 }
219
220 /* called rx event */
221 void i2c_recvevent(uint8_t * buf, int8_t size)
222 {
223         if (running_op == OP_POLL)
224                 i2cproto_next_state(1);
225
226         /* recv is only trigged after a poll */
227         running_op = OP_READY;
228
229         if (size < 0) {
230                 goto error;
231         }
232
233         switch (buf[0]) {
234
235         case I2C_ANS_IMUBOARD_STATUS: {
236                 struct i2c_ans_imuboard_status *ans =
237                         (struct i2c_ans_imuboard_status *)buf;
238
239                 if (size != sizeof (*ans))
240                         goto error;
241
242                 /* copy status in a global struct */
243                 memcpy(&imuboard_status, ans, sizeof(imuboard_status));
244
245                 break;
246         }
247
248         default:
249                 break;
250         }
251
252         return;
253  error:
254         i2c_errors++;
255         NOTICE(E_USER_I2C_PROTO, "recv error state=%d op=%d",
256                i2c_state, running_op);
257         if (i2c_errors > I2C_MAX_ERRORS) {
258                 I2C_ERROR("I2C error, slave not ready");
259                 i2c_reset();
260                 i2c_errors = 0;
261         }
262 }
263
264 void i2c_recvbyteevent(uint8_t hwstatus, uint8_t i, uint8_t c)
265 {
266         (void)hwstatus;
267         (void)i;
268         (void)c;
269 }
270
271 /* ******** ******** ******** ******** */
272 /* commands */
273 /* ******** ******** ******** ******** */
274
275
276 static int8_t
277 i2c_send_command(uint8_t addr, uint8_t * buf, uint8_t size)
278 {
279         uint8_t flags;
280         uint16_t ms = get_time_ms();
281
282         while ((get_time_ms() - ms) < I2C_TIMEOUT) {
283                 IRQ_LOCK(flags);
284                 if (command_size == 0) {
285                         memcpy(command_buf, buf, size);
286                         command_size = size;
287                         command_dest = addr;
288                         IRQ_UNLOCK(flags);
289                         return 0;
290                 }
291                 IRQ_UNLOCK(flags);
292         }
293         /* this should not happen... except if we are called from an
294          * interrupt context, but it's forbidden */
295         I2C_ERROR("I2C command send failed");
296         return -EBUSY;
297 }
298
299 static int8_t i2c_req_imuboard_status(void)
300 {
301         struct i2c_req_imuboard_status buf;
302         int8_t err;
303
304         buf.hdr.cmd = I2C_REQ_IMUBOARD_STATUS;
305         err = i2c_send(I2C_IMUBOARD_ADDR, (uint8_t*)&buf,
306                         sizeof(buf), I2C_CTRL_GENERIC);
307
308         return err;
309 }
310
311 int8_t i2c_led_control(uint8_t addr, uint8_t led, uint8_t state)
312 {
313         struct i2c_cmd_led_control buf;
314         buf.hdr.cmd = I2C_CMD_GENERIC_LED_CONTROL;
315         buf.led_num = led;
316         buf.state = state;
317         return i2c_send_command(addr, (uint8_t*)&buf, sizeof(buf));
318 }
319
320 void i2c_protocol_init(void)
321 {
322         callout_init(&i2c_timer, i2c_poll_slaves, NULL, I2C_PRIO);
323         callout_schedule(&xbeeboard.intr_cm, &i2c_timer, I2C_PERIOD_MS);
324 }