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