7bddfc22c4a86ce8567c673625dd779550eb2323
[gnuk/gnuk.git] / src / usb_prop.c
1 /*
2  * usb_prop.c - interface code between Gnuk and USB
3  *
4  * Copyright (C) 2010, 2011, 2012 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 /* Packet size of USB Bulk transfer for full speed */
25 #define GNUK_MAX_PACKET_SIZE 64
26
27 #include "config.h"
28 #include "ch.h"
29 #include "usb_lld.h"
30 #include "usb_conf.h"
31 #include "gnuk.h"
32
33 #ifdef ENABLE_VIRTUAL_COM_PORT
34 #include "usb-cdc.h"
35
36 struct line_coding
37 {
38   uint32_t bitrate;
39   uint8_t format;
40   uint8_t paritytype;
41   uint8_t datatype;
42 };
43
44 static struct line_coding line_coding = {
45   115200, /* baud rate: 115200    */
46   0x00,   /* stop bits: 1         */
47   0x00,   /* parity:    none      */
48   0x08    /* bits:      8         */
49 };
50
51 static int
52 vcom_port_data_setup (uint8_t req, uint8_t req_no)
53 {
54   if (USB_SETUP_GET (req))
55     {
56       if (req_no == USB_CDC_REQ_GET_LINE_CODING)
57         {
58           usb_lld_set_data_to_send (&line_coding, sizeof(line_coding));
59           return USB_SUCCESS;
60         }
61     }
62   else  /* USB_SETUP_SET (req) */
63     {
64       if (req_no == USB_CDC_REQ_SET_LINE_CODING)
65         {
66           usb_lld_set_data_to_recv (&line_coding, sizeof(line_coding));
67           return USB_SUCCESS;
68         }
69       else if (req_no == USB_CDC_REQ_SET_CONTROL_LINE_STATE)
70         /* Do nothing and success  */
71         return USB_SUCCESS;
72     }
73
74   return USB_UNSUPPORT;
75 }
76
77 #define VCOM_NUM_INTERFACES 2
78 #else
79 #define VCOM_NUM_INTERFACES 0
80 #endif
81
82 #ifdef PINPAD_DND_SUPPORT
83 #include "usb-msc.h"
84 #define MSC_NUM_INTERFACES 1
85 #else
86 #define MSC_NUM_INTERFACES 0
87 #endif
88
89 #define NUM_INTERFACES (1+VCOM_NUM_INTERFACES+MSC_NUM_INTERFACES)
90 #define MSC_INTERFACE_NO (1+VCOM_NUM_INTERFACES)
91
92 uint32_t bDeviceState = UNCONNECTED; /* USB device status */
93
94 static void
95 gnuk_device_init (void)
96 {
97   usb_lld_set_configuration (0);
98   USB_Cable_Config (1);
99   bDeviceState = UNCONNECTED;
100 }
101
102 static void
103 gnuk_setup_endpoints_for_interface (uint16_t interface, int stop)
104 {
105   if (interface == 0)
106     {
107       if (!stop)
108         usb_lld_setup_endpoint (ENDP1, EP_BULK, 0, ENDP1_RXADDR, ENDP1_TXADDR,
109                                 GNUK_MAX_PACKET_SIZE);
110       else
111         {
112           usb_lld_stall_rx (ENDP1);
113           usb_lld_stall_tx (ENDP1);
114         }
115     }
116 #ifdef ENABLE_VIRTUAL_COM_PORT
117   else if (interface == 1)
118     {
119       if (!stop)
120         usb_lld_setup_endpoint (ENDP4, EP_INTERRUPT, 0, 0, ENDP4_TXADDR, 0);
121       else
122         usb_lld_stall_tx (ENDP4);
123     }
124   else if (interface == 2)
125     {
126       if (!stop)
127         {
128           usb_lld_setup_endpoint (ENDP3, EP_BULK, 0, 0, ENDP3_TXADDR, 0);
129           usb_lld_setup_endpoint (ENDP5, EP_BULK, 0, ENDP5_RXADDR, 0,
130                                   VIRTUAL_COM_PORT_DATA_SIZE);
131         }
132       else
133         {
134           usb_lld_stall_tx (ENDP3);
135           usb_lld_stall_rx (ENDP5);
136         }
137     }
138 #endif
139 #ifdef PINPAD_DND_SUPPORT
140   else if (interface == MSC_INTERFACE_NO)
141     {
142       if (!stop)
143         {
144           usb_lld_setup_endpoint (ENDP6, EP_BULK, 0,
145                                   ENDP6_RXADDR, ENDP6_TXADDR, 64);
146           usb_lld_stall_rx (ENDP6);
147         }
148       else
149         {
150           usb_lld_stall_tx (ENDP6);
151           usb_lld_stall_rx (ENDP6);
152         }
153     }
154 #endif
155 }
156
157 static void
158 gnuk_device_reset (void)
159 {
160   int i;
161
162   /* Set DEVICE as not configured */
163   usb_lld_set_configuration (0);
164
165   /* Current Feature initialization */
166   usb_lld_set_feature (Config_Descriptor.Descriptor[7]);
167
168   usb_lld_reset ();
169
170   /* Initialize Endpoint 0 */
171   usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR,
172                           GNUK_MAX_PACKET_SIZE);
173
174   for (i = 0; i < NUM_INTERFACES; i++)
175     gnuk_setup_endpoints_for_interface (i, 0);
176
177   bDeviceState = ATTACHED;
178 }
179
180 #define USB_CCID_REQ_ABORT                      0x01
181 #define USB_CCID_REQ_GET_CLOCK_FREQUENCIES      0x02
182 #define USB_CCID_REQ_GET_DATA_RATES             0x03
183
184 static const uint8_t freq_table[] = { 0xf3, 0x0d, 0, 0, }; /* dwDefaultClock */
185
186 static const uint8_t data_rate_table[] = { 0x80, 0x25, 0, 0, }; /* dwDataRate */
187
188 #if defined(PINPAD_DND_SUPPORT)
189 static const uint8_t lun_table[] = { 0, 0, 0, 0, };
190 #endif
191
192 static const uint8_t *mem_info[] = { &_regnual_start,  &__heap_end__, };
193
194 #define USB_FSIJ_GNUK_MEMINFO  0
195 #define USB_FSIJ_GNUK_DOWNLOAD 1
196 #define USB_FSIJ_GNUK_EXEC     2
197
198 static int download_check_crc32 (const uint8_t *p)
199 {
200   return USB_SUCCESS;
201 }
202
203 static int
204 gnuk_setup (uint8_t req, uint8_t req_no,
205             uint16_t value, uint16_t index, uint16_t len)
206 {
207   uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
208
209   if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT))
210     {
211       if (USB_SETUP_GET (req))
212         {
213           if (req_no == USB_FSIJ_GNUK_MEMINFO)
214             {
215               usb_lld_set_data_to_send (mem_info, sizeof (mem_info));
216               return USB_SUCCESS;
217             }
218         }
219       else /* SETUP_SET */
220         {
221           uint8_t *addr = (uint8_t *)(0x20000000 + value * 0x100 + index);
222
223           if (req_no == USB_FSIJ_GNUK_DOWNLOAD)
224             {
225               if (icc_state_p == NULL || *icc_state_p != ICC_STATE_EXITED)
226                 return USB_UNSUPPORT;
227
228               if (addr < &_regnual_start || addr + len > &__heap_end__)
229                 return USB_UNSUPPORT;
230
231               if (index == 0)
232                 memset (addr, 0, 256);
233               usb_lld_set_data_to_recv (addr, len);
234               return USB_SUCCESS;
235             }
236           else if (req_no == USB_FSIJ_GNUK_EXEC && len == 0)
237             {
238               if (icc_state_p == NULL || *icc_state_p != ICC_STATE_EXITED)
239                 return USB_UNSUPPORT;
240
241               /* There is a trailer at addr: size, crc32 */
242               return download_check_crc32 (addr);
243             }
244         }
245     }
246   else if (type_rcp == (CLASS_REQUEST | INTERFACE_RECIPIENT))
247     if (index == 0)
248       {
249         if (USB_SETUP_GET (req))
250           {
251             if (req_no == USB_CCID_REQ_GET_CLOCK_FREQUENCIES)
252               {
253                 usb_lld_set_data_to_send (freq_table, sizeof (freq_table));
254                 return USB_SUCCESS;
255               }
256             else if (req_no == USB_CCID_REQ_GET_DATA_RATES)
257               {
258                 usb_lld_set_data_to_send (data_rate_table,
259                                           sizeof (data_rate_table));
260                 return USB_SUCCESS;
261               }
262           }
263         else
264           {
265             if (req_no == USB_CCID_REQ_ABORT)
266               /* wValue: bSeq, bSlot */
267               /* Abortion is not supported in Gnuk */
268               return USB_UNSUPPORT;
269           }
270       }
271 #ifdef ENABLE_VIRTUAL_COM_PORT
272     else if (index == 1)
273       return vcom_port_data_setup (req, req_no);
274 #endif
275 #ifdef PINPAD_DND_SUPPORT
276     else if (index == MSC_INTERFACE_NO)
277       {
278         if (USB_SETUP_GET (req))
279           {
280             if (req_no == MSC_GET_MAX_LUN_COMMAND)
281               {
282                 usb_lld_set_data_to_send (lun_table, sizeof (lun_table));
283                 return USB_SUCCESS;
284               }
285           }
286         else
287           if (req_no == MSC_MASS_STORAGE_RESET_COMMAND)
288             /* Should call resetting MSC thread, something like msc_reset() */
289             return USB_SUCCESS;
290       }
291 #endif
292
293   return USB_UNSUPPORT;
294 }
295
296 static void gnuk_ctrl_write_finish (uint8_t req, uint8_t req_no,
297                                     uint16_t value, uint16_t index,
298                                     uint16_t len)
299 {
300   uint8_t type_rcp = req & (REQUEST_TYPE|RECIPIENT);
301
302   if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)
303       && USB_SETUP_SET (req) && req_no == USB_FSIJ_GNUK_EXEC && len == 0)
304     {
305       if (icc_state_p == NULL || *icc_state_p != ICC_STATE_EXITED)
306         return;
307
308       (void)value; (void)index;
309       usb_lld_prepare_shutdown (); /* No further USB communication */
310       *icc_state_p = ICC_STATE_EXEC_REQUESTED;
311     }
312 }
313
314
315 static int
316 gnuk_get_descriptor (uint8_t desc_type, uint16_t index, uint16_t value)
317 {
318   (void)index;
319   if (desc_type == DEVICE_DESCRIPTOR)
320     {
321       usb_lld_set_data_to_send (Device_Descriptor.Descriptor,
322                                 Device_Descriptor.Descriptor_Size);
323       return USB_SUCCESS;
324     }
325   else if (desc_type == CONFIG_DESCRIPTOR)
326     {
327       usb_lld_set_data_to_send (Config_Descriptor.Descriptor,
328                                 Config_Descriptor.Descriptor_Size);
329       return USB_SUCCESS;
330     }
331   else if (desc_type == STRING_DESCRIPTOR)
332     {
333       uint8_t desc_index = value & 0xff;
334
335       if (desc_index < NUM_STRING_DESC)
336         {
337           usb_lld_set_data_to_send (String_Descriptors[desc_index].Descriptor,
338                             String_Descriptors[desc_index].Descriptor_Size);
339           return USB_SUCCESS;
340         }
341     }
342
343   return USB_UNSUPPORT;
344 }
345
346 static int gnuk_usb_event (uint8_t event_type, uint16_t value)
347 {
348   int i;
349
350   switch (event_type)
351     {
352     case USB_EVENT_ADDRESS:
353       bDeviceState = ADDRESSED;
354       return USB_SUCCESS;
355     case USB_EVENT_CONFIG:
356       if (usb_lld_current_configuration () == 0)
357         {
358           if (value != 1)
359             return USB_UNSUPPORT;
360
361           usb_lld_set_configuration (value);
362           for (i = 0; i < NUM_INTERFACES; i++)
363             gnuk_setup_endpoints_for_interface (i, 0);
364           bDeviceState = CONFIGURED;
365           chEvtSignalI (main_thread, LED_STATUS_MODE);
366         }
367       else
368         {
369           if (value != 0)
370             return USB_UNSUPPORT;
371
372           usb_lld_set_configuration (0);
373           for (i = 0; i < NUM_INTERFACES; i++)
374             gnuk_setup_endpoints_for_interface (i, 1);
375           bDeviceState = ADDRESSED;
376         }
377       return USB_SUCCESS;
378     default:
379       break;
380     }
381
382   return USB_UNSUPPORT;
383 }
384
385 static int gnuk_interface (uint8_t cmd, uint16_t interface, uint16_t alt)
386 {
387   static uint8_t zero = 0;
388
389   if (interface >= NUM_INTERFACES)
390     return USB_UNSUPPORT;
391
392   switch (cmd)
393     {
394     case USB_SET_INTERFACE:
395       if (alt != 0)
396         return USB_UNSUPPORT;
397       else
398         {
399           gnuk_setup_endpoints_for_interface (interface, 0);
400           return USB_SUCCESS;
401         }
402
403     case USB_GET_INTERFACE:
404       usb_lld_set_data_to_send (&zero, 1);
405       return USB_SUCCESS;
406
407     default:
408     case USB_QUERY_INTERFACE:
409       return USB_SUCCESS;
410     }
411 }
412
413 /*
414  * Interface to USB core
415  */
416
417 const struct usb_device_method Device_Method = {
418   gnuk_device_init,
419   gnuk_device_reset,
420   gnuk_ctrl_write_finish,
421   gnuk_setup,
422   gnuk_get_descriptor,
423   gnuk_usb_event,
424   gnuk_interface,
425 };