ini
[aversive.git] / modules / comm / mf2_client / mf2_client.c
1 /*  
2  *  Copyright Droids Corporation, Microb Technology, Eirbot (2005)
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: mf2_client.c,v 1.1.2.5 2007-05-23 17:18:11 zer0 Exp $
19  *
20  */
21
22 /* Olivier MATZ, Droids-corp 2006 */
23
24 #include <stdio.h>
25
26 #include <aversive.h>
27 #include <mf2_client.h>
28 #include <mf2_client_config.h>
29
30 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
31 #include <scheduler.h>
32 #else
33 #include <aversive/wait.h>
34 #endif
35
36 #define WATCHDOG_TIMEOUT 10000
37
38 #define MF2_CLIENT_STATE_IDLE 0
39 #define MF2_CLIENT_STATE_RECV 1
40 #define MF2_CLIENT_STATE_XMIT 2
41 #define MF2_CLIENT_STATE_ACK  3
42
43 #define data_Z() cbi(DDR(MF2_CLIENT_DATA_PORT), MF2_CLIENT_DATA_BIT)
44 #define data_0() sbi(DDR(MF2_CLIENT_DATA_PORT), MF2_CLIENT_DATA_BIT)
45 #define clk_Z()  cbi(DDR(MF2_CLIENT_CLK_PORT), MF2_CLIENT_CLK_BIT)
46 #define clk_0()  sbi(DDR(MF2_CLIENT_CLK_PORT), MF2_CLIENT_CLK_BIT)
47
48 /* XXX s08 -> int8_t */
49
50 static volatile u08 state=MF2_CLIENT_STATE_IDLE;
51 static volatile u08 current_bitnum;
52 static volatile u16 rx_buf;
53 static volatile u16 tx_buf;
54 static volatile u08 tx_c;
55 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
56 static volatile s08 sched_event = -1;
57 #endif
58
59 typedef void (event)(char);
60
61 static event * tx_event = NULL;
62 static event * rx_event = NULL;
63
64 static s16 check_rx_buf(u16 buf);
65
66 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
67 static void watchdog_timeout(void * dummy);
68 #endif
69
70 static void disable_intr(void);
71 static void set_falling_edge(void);
72
73 #define START_BIT  0x0001
74 #define PARITY_BIT 0x0200
75 #define STOP_BIT   0x0400
76
77 SIGNAL(MF2_CLIENT_INTERRUPT)
78 {
79         s16 c;
80
81
82         switch(state) {
83         case MF2_CLIENT_STATE_IDLE:
84                 rx_buf=0;
85                 state=MF2_CLIENT_STATE_RECV;
86                 current_bitnum=1;
87 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
88                 sched_event = scheduler_add_single_event(watchdog_timeout,
89                        NULL, WATCHDOG_TIMEOUT/SCHEDULER_UNIT);
90 #endif
91                 break;
92                 
93         case MF2_CLIENT_STATE_RECV:
94                 if(PIN(MF2_CLIENT_DATA_PORT) & (1<<MF2_CLIENT_DATA_BIT))
95                         rx_buf |= (1 << current_bitnum);
96                 
97                 if (current_bitnum==10) {
98                         disable_intr();
99                         clk_0();
100                         if (rx_event) {
101                                 c=check_rx_buf(rx_buf);
102                                 if( c >= 0 ) {
103                                         rx_event((char)(c));
104                                 }
105                         }
106                         current_bitnum=0;       
107 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
108                         if (sched_event != -1) {
109                                 scheduler_del_event(sched_event);
110                                 sched_event = -1;
111                         }
112 #endif
113                         data_Z();
114                         clk_Z();
115                         set_falling_edge();
116                         state=MF2_CLIENT_STATE_IDLE;
117                 }
118                 else {
119                         current_bitnum++;
120                 }
121                 break;
122                 
123         case MF2_CLIENT_STATE_XMIT:
124                 if (current_bitnum < 10) {
125                         if (tx_buf & (1<<current_bitnum))
126                                 data_Z();
127                         else
128                                 data_0();
129                         current_bitnum ++;
130                 }
131                 else {
132                         data_Z();
133                         current_bitnum=0;
134                         state=MF2_CLIENT_STATE_ACK;
135                 }
136                 break;
137                 
138         case MF2_CLIENT_STATE_ACK:
139                 /*              if(PIN(MF2_CLIENT_DATA_PORT) & (1<<MF2_CLIENT_DATA_BIT)) */
140                 /*                      XXX error; */
141                 /*              else */
142                 if (tx_event) {
143                         tx_event((char)(tx_c));
144                 }
145                 state=MF2_CLIENT_STATE_IDLE;
146 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
147                 if (sched_event != -1) {
148                         scheduler_del_event(sched_event);
149                         sched_event = -1;
150                 }
151 #endif
152                 break;
153                 
154         default:
155                 break;
156         }
157 }
158
159 static void set_falling_edge(void)
160 {
161         /* disable intr */
162         cbi(MF2_CLIENT_IMASK, MF2_CLIENT_IMASK_BIT);
163
164         /* intr on falling edge */
165         cbi(MF2_CLIENT_INT_REG, MF2_CLIENT_INT_BIT0);
166         sbi(MF2_CLIENT_INT_REG, MF2_CLIENT_INT_BIT1);
167
168         /* reset flag */
169         sbi(MF2_CLIENT_IFLAG, MF2_CLIENT_IMASK_BIT);
170
171         /* enable intr */
172         sbi(MF2_CLIENT_IMASK, MF2_CLIENT_IMASK_BIT);
173
174 }
175
176 static void disable_intr(void)
177 {
178         /* disable intr */
179         cbi(MF2_CLIENT_IMASK, MF2_CLIENT_IMASK_BIT);
180 }
181
182
183 void mf2_client_init(void)
184 {
185         u08 flags;
186
187         IRQ_LOCK(flags);
188         /*      ports are inputs, and values are 0 */
189         data_Z();
190         clk_Z();
191         cbi(MF2_CLIENT_DATA_PORT, MF2_CLIENT_DATA_BIT);
192         cbi(MF2_CLIENT_CLK_PORT, MF2_CLIENT_CLK_BIT);
193         
194         set_falling_edge();
195
196         state = MF2_CLIENT_STATE_IDLE;
197         current_bitnum = 0;
198         IRQ_UNLOCK(flags);
199 }
200
201 static s16 check_rx_buf(u16 buf)
202 {
203         u16 mask, cpt=0;
204
205         /* start bit should be 0 */
206         if(buf & START_BIT)
207                 return -1;
208
209         /* stop bit should be 1 */
210         if(! (buf & STOP_BIT))
211                 return -2;
212
213         for (mask = START_BIT ; mask != STOP_BIT; mask<<=1 ) {
214                 if (buf & mask)
215                         cpt++;
216         }
217         
218         /* bad parity */
219         if (!(cpt % 2))
220                 return -3;
221         
222         return (buf>>1) & 0xFF;
223 }
224
225
226 s08 mf2_client_ready(void)
227 {
228         return state==MF2_CLIENT_STATE_IDLE;
229 }
230
231 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
232 static void watchdog_timeout(void * dummy)
233 {
234         printf("TIMEOUT\n");
235         current_bitnum=0;       
236         state=MF2_CLIENT_STATE_IDLE;
237         data_Z();
238         clk_Z();
239         set_falling_edge();
240 }
241 #endif
242
243 static void start_sending(void * dummy)
244 {
245         /* set clk to Z and data to 0 */
246         clk_Z();
247         data_0();
248         set_falling_edge();
249 #ifdef CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
250         sched_event = scheduler_add_single_event(watchdog_timeout, 
251                                                            NULL, WATCHDOG_TIMEOUT/SCHEDULER_UNIT);
252 #endif
253 }
254
255 s08 mf2_client_send(char c)
256 {
257         u16 mask, cpt=0;
258         u08 flags;
259         
260         IRQ_LOCK(flags);
261
262         /* we don't preempt the remote device, even if the
263            protocol allow it */
264         if (!mf2_client_ready()) {
265                 IRQ_UNLOCK(flags);
266                 return -1;
267         }
268
269         state=MF2_CLIENT_STATE_XMIT;
270         current_bitnum = 1;
271         
272         disable_intr();
273
274         /* set clk to 0 */
275         clk_0();
276
277         IRQ_UNLOCK(flags);
278
279         tx_buf = c;
280         tx_c = c;
281         tx_buf <<= 1;
282         tx_buf |= STOP_BIT;
283
284         for (mask = START_BIT ; mask != STOP_BIT; mask<<=1 ) {
285                 if (tx_buf & mask)
286                         cpt++;
287         }
288
289         if (!(cpt % 2))
290                 tx_buf |= PARITY_BIT;
291
292 #if CONFIG_MODULE_MF2_CLIENT_USE_SCHEDULER
293         scheduler_add_single_event(start_sending, NULL, 1000L/SCHEDULER_UNIT);
294 #else
295         wait_ms(1);
296         start_sending(NULL);
297 #endif
298
299         return 0;
300 }
301
302 /* This function is used to register another function which will be */
303 /* executed at each byte transmission. */
304 void mf2_client_register_tx_event(void (*f)(char))
305 {
306         u08 flags;
307         IRQ_LOCK(flags);
308         tx_event = f;
309         IRQ_UNLOCK(flags);
310 }
311
312 /* This function is used to register another function which will be */
313 /* executed at each byte reception. */
314 void mf2_client_register_rx_event(void (*f)(char))
315 {
316         u08 flags;
317         IRQ_LOCK(flags);
318         rx_event = f;
319         IRQ_UNLOCK(flags);
320 }
321