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