minor cleanup
[gnuk/gnuk.git] / src / main.c
1 /*
2  * main.c - main routine of Gnuk
3  *
4  * Copyright (C) 2010, 2011 Free Software Initiative of Japan
5  * Author: NIIBE Yutaka <gniibe@fsij.org>
6  *
7  * This file is a part of Gnuk, a GnuPG USB Token implementation.
8  *
9  * Gnuk is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Gnuk is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "config.h"
25
26 #include "usb_lib.h"
27
28 #include "ch.h"
29 #include "gnuk.h"
30 #include "usb_lld.h"
31 #include "usb_istr.h"
32 #include "usb_desc.h"
33 #include "hw_config.h"
34 #include "usb_pwr.h"
35
36 #ifdef DEBUG
37 struct stdout {
38   Mutex m;
39   CondVar start_cnd;
40   CondVar finish_cnd;
41   const char *str;
42   int size;
43 };
44
45 static struct stdout stdout;
46
47 static void
48 stdout_init (void)
49 {
50   chMtxInit (&stdout.m);
51   chCondInit (&stdout.start_cnd);
52   chCondInit (&stdout.finish_cnd);
53   stdout.size = 0;
54   stdout.str = NULL;
55 }
56
57 void
58 _write (const char *s, int size)
59 {
60   if (size == 0)
61     return;
62
63   chMtxLock (&stdout.m);
64   while (stdout.str)
65     chCondWait (&stdout.finish_cnd);
66   stdout.str = s;
67   stdout.size = size;
68   chCondSignal (&stdout.start_cnd);
69   chCondWait (&stdout.finish_cnd);
70   chMtxUnlock ();
71 }
72
73 Thread *stdout_thread;
74 uint32_t count_in;
75 uint8_t buffer_in[VIRTUAL_COM_PORT_DATA_SIZE];
76
77 static WORKING_AREA(waSTDOUTthread, 128);
78
79 static msg_t
80 STDOUTthread (void *arg)
81 {
82   (void)arg;
83   stdout_thread = chThdSelf ();
84
85  again:
86
87   while (1)
88     {
89       if (bDeviceState == CONFIGURED)
90         break;
91
92       chThdSleepMilliseconds (100);
93     }
94
95   while (1)
96     {
97       const char *p;
98       int len;
99
100       if (bDeviceState != CONFIGURED)
101         break;
102
103       chMtxLock (&stdout.m);
104       if (stdout.str == NULL)
105         chCondWait (&stdout.start_cnd);
106
107       p = stdout.str;
108       len = stdout.size;
109       while (len > 0)
110         {
111           int i;
112
113           if (len < VIRTUAL_COM_PORT_DATA_SIZE)
114             {
115               for (i = 0; i < len; i++)
116                 buffer_in[i] = p[i];
117               count_in = len;
118               len = 0;
119             }
120           else
121             {
122               for (i = 0; i < VIRTUAL_COM_PORT_DATA_SIZE; i++)
123                 buffer_in[i] = p[i];
124               len -= VIRTUAL_COM_PORT_DATA_SIZE;
125               count_in = VIRTUAL_COM_PORT_DATA_SIZE;
126               p += count_in;
127             }
128
129           chEvtClear (EV_TX_READY);
130
131           USB_SIL_Write (EP3_IN, buffer_in, count_in);
132           SetEPTxValid (ENDP3);
133
134           chEvtWaitOne (EV_TX_READY);
135         }
136
137       stdout.str = NULL;
138       stdout.size = 0;
139       chCondBroadcast (&stdout.finish_cnd);
140       chMtxUnlock ();
141     }
142
143   goto again;
144   return 0;
145 }
146 #else
147 void
148 _write (const char *s, int size)
149 {
150   (void)s;
151   (void)size;
152 }
153 #endif
154
155 static WORKING_AREA(waUSBthread, 128);
156 extern msg_t USBthread (void *arg);
157
158 /*
159  * main thread does 1-bit LED display output
160  */
161 #define LED_TIMEOUT_INTERVAL    MS2ST(100)
162 #define LED_TIMEOUT_ZERO        MS2ST(50)
163 #define LED_TIMEOUT_ONE         MS2ST(200)
164 #define LED_TIMEOUT_STOP        MS2ST(500)
165
166
167 #define ID_OFFSET 12
168 static void
169 device_initialize_once (void)
170 {
171   const uint8_t *p = &gnukStringSerial[ID_OFFSET];
172
173   if (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff && p[3] == 0xff)
174     {
175       /*
176        * This is the first time invocation.
177        * Setup serial number by unique device ID.
178        */
179       const uint8_t *u = unique_device_id ();
180       int i;
181
182       for (i = 0; i < 4; i++)
183         {
184           uint8_t b = u[i];
185           uint8_t nibble; 
186
187           nibble = (b >> 4);
188           nibble += (nibble >= 10 ? ('A' - 10) : '0');
189           flash_put_data_internal (&p[i*4], nibble);
190           nibble = (b & 0x0f);
191           nibble += (nibble >= 10 ? ('A' - 10) : '0');
192           flash_put_data_internal (&p[i*4+2], nibble);
193         }
194     }
195 }
196
197 static volatile uint8_t fatal_code;
198
199 Thread *main_thread;
200
201 #define GNUK_INIT           0
202 #define GNUK_RUNNING        1
203 #define GNUK_INPUT_WAIT     2
204 #define GNUK_FATAL        255
205 /*
206  * 0 for initializing
207  * 1 for normal mode
208  * 2 for input waiting
209  * 255 for fatal
210  */
211 static uint8_t main_mode;
212
213 static void display_interaction (void)
214 {
215   eventmask_t m;
216
217   while (1)
218     {
219       m = chEvtWaitOne (ALL_EVENTS);
220       set_led (1);
221       switch (m)
222         {
223         case LED_ONESHOT_SHORT:
224           chThdSleep (MS2ST (100));
225           break;
226         case LED_ONESHOT_LONG:
227           chThdSleep (MS2ST (400));
228           break;
229         case LED_TWOSHOT:
230           chThdSleep (MS2ST (50));
231           set_led (0);
232           chThdSleep (MS2ST (50));
233           set_led (1);
234           chThdSleep (MS2ST (50));
235           break;
236         case LED_STATUS_MODE:
237           chThdSleep (MS2ST (400));
238           set_led (0);
239           return;
240         case LED_FATAL_MODE:
241           main_mode = GNUK_FATAL;
242           set_led (0);
243           return;
244         default:
245           break;
246         }
247       set_led (0);
248     }
249 }
250
251 static void display_fatal_code (void)
252 {
253   set_led (1);
254   chThdSleep (LED_TIMEOUT_ZERO);
255   set_led (0);
256   chThdSleep (LED_TIMEOUT_INTERVAL);
257   set_led (1);
258   chThdSleep (LED_TIMEOUT_ZERO);
259   set_led (0);
260   chThdSleep (LED_TIMEOUT_INTERVAL);
261   set_led (1);
262   chThdSleep (LED_TIMEOUT_ZERO);
263   set_led (0);
264   chThdSleep (LED_TIMEOUT_STOP);
265   set_led (1);
266   if (fatal_code & 1)
267     chThdSleep (LED_TIMEOUT_ONE);
268   else
269     chThdSleep (LED_TIMEOUT_ZERO);
270   set_led (0);
271   chThdSleep (LED_TIMEOUT_INTERVAL);
272   set_led (1);
273   if (fatal_code & 2)
274     chThdSleep (LED_TIMEOUT_ONE);
275   else
276     chThdSleep (LED_TIMEOUT_ZERO);
277   set_led (0);
278   chThdSleep (LED_TIMEOUT_INTERVAL);
279   set_led (1);
280   chThdSleep (LED_TIMEOUT_STOP);
281   set_led (0);
282   chThdSleep (LED_TIMEOUT_INTERVAL);
283 }
284
285 static void display_status_code (void)
286 {
287   if (icc_state == ICC_STATE_START)
288     {
289       set_led (1);
290       chThdSleep (LED_TIMEOUT_ONE);
291       set_led (0);
292       chThdSleep (LED_TIMEOUT_STOP * 3);
293     }
294   else
295     /* GPGthread  running */
296     {
297       set_led (1);
298       if ((auth_status & AC_ADMIN_AUTHORIZED) != 0)
299         chThdSleep (LED_TIMEOUT_ONE);
300       else
301         chThdSleep (LED_TIMEOUT_ZERO);
302       set_led (0);
303       chThdSleep (LED_TIMEOUT_INTERVAL);
304       set_led (1);
305       if ((auth_status & AC_OTHER_AUTHORIZED) != 0)
306         chThdSleep (LED_TIMEOUT_ONE);
307       else
308         chThdSleep (LED_TIMEOUT_ZERO);
309       set_led (0);
310       chThdSleep (LED_TIMEOUT_INTERVAL);
311       set_led (1);
312       if ((auth_status & AC_PSO_CDS_AUTHORIZED) != 0)
313         chThdSleep (LED_TIMEOUT_ONE);
314       else
315         chThdSleep (LED_TIMEOUT_ZERO);
316
317       if (icc_state == ICC_STATE_WAIT)
318         {
319           set_led (0);
320           chThdSleep (LED_TIMEOUT_STOP * 2);
321         }
322       else if (icc_state == ICC_STATE_RECEIVE)
323         {
324           set_led (0);
325           chThdSleep (LED_TIMEOUT_INTERVAL);
326           set_led (1);
327           chThdSleep (LED_TIMEOUT_ONE);
328           set_led (0);
329           chThdSleep (LED_TIMEOUT_STOP);
330         }
331       else
332         {
333           set_led (0);
334           chThdSleep (LED_TIMEOUT_INTERVAL);
335           set_led (1);
336           chThdSleep (LED_TIMEOUT_STOP);
337           set_led (0);
338           chThdSleep (LED_TIMEOUT_INTERVAL);
339         }
340     }
341 }
342
343 void
344 led_blink (int spec)
345 {
346   if (spec == 0)
347     chEvtSignal (main_thread, LED_ONESHOT_SHORT);
348   else if (spec == 1)
349     chEvtSignal (main_thread, LED_ONESHOT_LONG);
350   else
351     chEvtSignal (main_thread, LED_TWOSHOT);
352 }
353
354 /*
355  * Entry point.
356  *
357  * NOTE: the main function is already a thread in the system on entry.
358  *       See the hwinit1_common function.
359  */
360 int
361 main (int argc, char **argv)
362 {
363   int count = 0;
364
365   (void)argc;
366   (void)argv;
367
368   main_thread = chThdSelf ();
369
370   flash_unlock ();
371   device_initialize_once ();
372   usb_lld_init ();
373   USB_Init ();
374   random_init ();
375
376 #ifdef DEBUG
377   stdout_init ();
378
379   /*
380    * Creates 'stdout' thread.
381    */
382   chThdCreateStatic (waSTDOUTthread, sizeof(waSTDOUTthread),
383                      NORMALPRIO, STDOUTthread, NULL);
384 #endif
385
386   chThdCreateStatic (waUSBthread, sizeof(waUSBthread),
387                      NORMALPRIO, USBthread, NULL);
388
389   while (1)
390     {
391       eventmask_t m;
392
393       count++;
394       m = chEvtWaitOneTimeout (ALL_EVENTS, LED_TIMEOUT_INTERVAL);
395       switch (m)
396         {
397         case LED_STATUS_MODE:
398           main_mode = GNUK_RUNNING;
399           break;
400         case LED_FATAL_MODE:
401           main_mode = GNUK_FATAL;
402           break;
403         case LED_INPUT_MODE:
404           main_mode = GNUK_INPUT_WAIT;
405           set_led (1);
406           chThdSleep (MS2ST (400));
407           set_led (0);
408           break;
409         default:
410           break;
411         }
412
413       switch (main_mode)
414         {
415         case GNUK_FATAL:
416           display_fatal_code ();
417           break;
418         case GNUK_INIT:
419           set_led (1);
420           chThdSleep (LED_TIMEOUT_ZERO);
421           set_led (0);
422           chThdSleep (LED_TIMEOUT_STOP * 3);
423           break;
424         case GNUK_INPUT_WAIT:
425           display_interaction ();
426           break;
427         case GNUK_RUNNING:
428         default:
429           display_status_code ();
430           break;
431         }         
432
433 #ifdef DEBUG_MORE
434       if (bDeviceState == CONFIGURED && (count % 10) == 0)
435         {
436           DEBUG_SHORT (count / 10);
437           _write ("\r\nThis is ChibiOS 2.0.8 on STM32.\r\n"
438                   "Testing USB driver.\n\n"
439                   "Hello world\r\n\r\n", 35+21+15);
440         }
441 #endif
442     }
443
444   return 0;
445 }
446
447 void
448 fatal (uint8_t code)
449 {
450   fatal_code = code;
451   chEvtSignal (main_thread, LED_FATAL_MODE);
452   _write ("fatal\r\n", 7);
453   for (;;);
454 }