interface: usb: Add support for USB interface
[platform/core/system/initrd-flash.git] / src / usb.c
1 /*
2  * flash-manager - Tizen kernel-level image flashing solution
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <sys/types.h>
24 #include <sys/mount.h>
25 #include <sys/stat.h>
26 #include <endian.h>
27 #include <pthread.h>
28 #include <usbg/usbg.h>
29 #include <asm/byteorder.h>
30
31 #include <linux/usb/functionfs.h>
32 #include <linux/usb/cdc.h>
33
34 #include "interface.h"
35
36 #define TFMFFS_PATH             "/dev/tfmffs"
37 #define TFMFFS_GADGET           "g1"
38 #define TFMFFS_DEV              "tfm"
39 #define TFMFFS_BIND_LINK        "tfm.ffs"
40 #define TFMFFS_VID 0x04e8
41 #define TFMFFS_PID 0x685d
42
43 enum {
44         TFMFFS_EP0,
45         TFMFFS_EP_INT_IN,
46         TFMFFS_EP_BULK_IN,
47         TFMFFS_EP_BULK_OUT,
48         TFMFFS_EP_MAX,
49 };
50
51 struct usb_context {
52         usbg_state *usbg;
53         pthread_t ep0_thread;
54         int eps[TFMFFS_EP_MAX];
55
56         int enabled;
57
58         pthread_mutex_t mutex;
59         pthread_cond_t ready;
60 };
61
62 struct usbg_gadget_attrs g_attrs = {
63         .idVendor = TFMFFS_VID,
64         .idProduct = TFMFFS_PID,
65 };
66
67 struct usbg_gadget_strs g_strs = {
68         .serial = "Unavailable", /* Serial number */
69         .manufacturer = "Tizen", /* Manufacturer */
70         .product = "iot-refboard" /* Product string */
71 };
72
73 struct usbg_config_strs c_strs = {
74         .configuration = "Conf 1"
75 };
76
77 /******************** Descriptors and Strings *******************************/
78
79 static const struct {
80         struct usb_functionfs_descs_head header;
81         struct {
82                 struct usb_interface_descriptor acm;
83                 struct usb_endpoint_descriptor_no_audio int_in;
84                 struct usb_interface_descriptor intf;
85                 struct usb_endpoint_descriptor_no_audio bulk_in;
86                 struct usb_endpoint_descriptor_no_audio bulk_out;
87         } __attribute__ ((__packed__)) fs_descs, hs_descs;
88 } __attribute__ ((__packed__)) descriptors = {
89         .header = {
90                 .magic = __cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC),
91                 .length = __cpu_to_le32(sizeof(descriptors)),
92                 .fs_count = __cpu_to_le32(5),
93                 .hs_count = __cpu_to_le32(5),
94         },
95         .fs_descs = {
96                 .acm = {
97                         .bLength = sizeof(descriptors.fs_descs.intf),
98                         .bDescriptorType = USB_DT_INTERFACE,
99                         .bInterfaceNumber = 0,
100                         .bNumEndpoints = 1,
101                         .bInterfaceClass = USB_CLASS_COMM,
102                         .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
103                         .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
104                         .iInterface = 1,
105                 },
106                 .int_in = {
107                         .bLength = sizeof(descriptors.fs_descs.bulk_in),
108                         .bDescriptorType = USB_DT_ENDPOINT,
109                         .bEndpointAddress = 3 | USB_DIR_IN,
110                         .bmAttributes = USB_ENDPOINT_XFER_INT,
111                 },
112                 .intf = {
113                         .bLength = sizeof(descriptors.fs_descs.intf),
114                         .bDescriptorType = USB_DT_INTERFACE,
115                         .bInterfaceNumber = 1,
116                         .bNumEndpoints = 2,
117                         .bInterfaceClass = USB_CLASS_CDC_DATA,
118                         .iInterface = 1,
119                 },
120                 .bulk_in = {
121                         .bLength = sizeof(descriptors.fs_descs.bulk_in),
122                         .bDescriptorType = USB_DT_ENDPOINT,
123                         .bEndpointAddress = 1 | USB_DIR_IN,
124                         .bmAttributes = USB_ENDPOINT_XFER_BULK,
125                 },
126                 .bulk_out = {
127                         .bLength = sizeof(descriptors.fs_descs.bulk_out),
128                         .bDescriptorType = USB_DT_ENDPOINT,
129                         .bEndpointAddress = 2 | USB_DIR_OUT,
130                         .bmAttributes = USB_ENDPOINT_XFER_BULK,
131                 },
132         },
133         .hs_descs = {
134                 .acm = {
135                         .bLength = sizeof(descriptors.fs_descs.intf),
136                         .bDescriptorType = USB_DT_INTERFACE,
137                         .bInterfaceNumber = 0,
138                         .bNumEndpoints = 1,
139                         .bInterfaceClass = USB_CLASS_COMM,
140                         .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
141                         .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
142                         .iInterface = 1,
143                 },
144                 .int_in = {
145                         .bLength = sizeof(descriptors.fs_descs.bulk_in),
146                         .bDescriptorType = USB_DT_ENDPOINT,
147                         .bEndpointAddress = 3 | USB_DIR_IN,
148                         .bmAttributes = USB_ENDPOINT_XFER_INT,
149                         .wMaxPacketSize = __cpu_to_le16(16),
150                         .bInterval = 10,
151                 },
152                 .intf = {
153                         .bLength = sizeof(descriptors.hs_descs.intf),
154                         .bDescriptorType = USB_DT_INTERFACE,
155                         .bInterfaceNumber = 1,
156                         .bNumEndpoints = 2,
157                         .bInterfaceClass = USB_CLASS_CDC_DATA,
158                         .iInterface = 1,
159                 },
160                 .bulk_in = {
161                         .bLength = sizeof(descriptors.hs_descs.bulk_in),
162                         .bDescriptorType = USB_DT_ENDPOINT,
163                         .bEndpointAddress = 1 | USB_DIR_IN,
164                         .bmAttributes = USB_ENDPOINT_XFER_BULK,
165                         .wMaxPacketSize = __cpu_to_le16(512),
166                 },
167                 .bulk_out = {
168                         .bLength = sizeof(descriptors.hs_descs.bulk_out),
169                         .bDescriptorType = USB_DT_ENDPOINT,
170                         .bEndpointAddress = 2 | USB_DIR_OUT,
171                         .bmAttributes = USB_ENDPOINT_XFER_BULK,
172                         .wMaxPacketSize = __cpu_to_le16(512),
173                 },
174         },
175 };
176
177 #define STR_INTERFACE "TIZEN USB DRIVER"
178
179 static const struct {
180         struct usb_functionfs_strings_head header;
181         struct {
182                 __le16 code;
183                 const char str1[sizeof(STR_INTERFACE)];
184         } __attribute__ ((__packed__)) lang0;
185 } __attribute__ ((__packed__)) strings = {
186         .header = {
187                 .magic = __cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
188                 .length = __cpu_to_le32(sizeof(strings)),
189                 .str_count = __cpu_to_le32(1),
190                 .lang_count = __cpu_to_le32(1),
191         },
192         .lang0 = {
193                 __cpu_to_le16(0x0409), /* en-us */
194                 STR_INTERFACE,
195         },
196 };
197
198 static ssize_t usb_rx_data(int fd, void *buf, ssize_t len)
199 {
200         return read(fd, buf, len);
201 }
202
203 static ssize_t usb_tx_data(int fd, void *buf, ssize_t len)
204 {
205         return write(fd, buf, len);
206 }
207
208 static void *ep0_handler(void *data)
209 {
210         struct usb_context *ctx = data;
211         struct usb_functionfs_event evt;
212         int ret, ep0 = ctx->eps[TFMFFS_EP0];
213
214         pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
215
216         for (;;) {
217                 ret = read(ep0, &evt, sizeof(evt));
218                 if (ret <= 0)
219                         goto err;
220
221                 switch (evt.type) {
222                 case FUNCTIONFS_SETUP:
223                 {
224                         struct usb_ctrlrequest *ctrl = &evt.u.setup;
225
226                         if (ctrl->bRequestType & USB_DIR_IN) {
227                                 if (write(ep0, NULL, 0) < 0)
228                                         goto err;
229                         } else { /* ctrl->bRequestType & USB_DIR_OUT */
230                                 switch (ctrl->bRequest) {
231                                 /* handle CDC ACM setup request */
232                                 case USB_CDC_REQ_SET_LINE_CODING:
233                                 {
234                                         int value = __le16_to_cpu(ctrl->wLength);
235                                         char *buf;
236
237                                         buf = malloc(value);
238                                         if (!buf)
239                                                 goto err;
240
241                                         if (read(ep0, buf, value) < 0) {
242                                                 free(buf);
243                                                 goto err;
244                                         }
245                                         free(buf);
246                                         break;
247                                 }
248                                 case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
249                                 {
250                                         int buf;
251
252                                         if (read(ep0, &buf, sizeof(buf)) < 0)
253                                                 goto err;
254                                         break;
255                                 }
256                                 default:
257                                         if (read(ep0, NULL, 0) < 0)
258                                                 goto err;
259                                 }
260                         }
261                         break;
262                 }
263                 case FUNCTIONFS_ENABLE:
264                         pthread_mutex_lock(&ctx->mutex);
265                         ctx->enabled = 1;
266                         pthread_mutex_unlock(&ctx->mutex);
267                         pthread_cond_signal(&ctx->ready);
268                         break;
269                 case FUNCTIONFS_DISABLE:
270                 case FUNCTIONFS_BIND:
271                 case FUNCTIONFS_UNBIND:
272                 default:
273                         /* do nothing */
274                         break;
275                 }
276         }
277 err:
278         fprintf(stderr, "Error occurs while handling ep0 event");
279         return NULL;
280 }
281
282 static int usb_setup_ffs_daemon(struct usb_context *ctx)
283 {
284         char *ep_path;
285         int *ep = ctx->eps;
286         int ep_path_len = strlen(TFMFFS_PATH) + 4 /* "/ep#" */ + 1 /* '\0' */;
287         int i, ret;
288
289         ep_path = malloc(ep_path_len);
290         if (!ep_path)
291                 return -ENOMEM;
292
293         snprintf(ep_path, ep_path_len, TFMFFS_PATH"/ep%d", TFMFFS_EP0);
294
295         ep[TFMFFS_EP0] = open(ep_path, O_RDWR);
296         if (ep[TFMFFS_EP0] < 0) {
297                 fprintf(stderr, "failed to open ffs ep0\n");
298                 ret = -ENOENT;
299                 goto err_free;
300         }
301
302         if (write(ep[TFMFFS_EP0], &descriptors, sizeof(descriptors)) < 0) {
303                 fprintf(stderr, "failed to write descriptors\n");
304                 ret = -EIO;
305                 goto err_close;
306         }
307         if (write(ep[TFMFFS_EP0], &strings, sizeof(strings)) < 0) {
308                 fprintf(stderr, "failed to write strings\n");
309                 ret = -EIO;
310                 goto err_close;
311         }
312
313         for (i = 1; i < TFMFFS_EP_MAX; i++) {
314                 snprintf(ep_path, ep_path_len, TFMFFS_PATH"/ep%d", i);
315
316                 ep[i] = open(ep_path, O_RDWR);
317                 if (ep[i] < 0) {
318                         fprintf(stderr, "failed to open ffs ep%d\n", i);
319                         /* close opened endpoints */
320                         for (i -= 1; i >= 1; i--)
321                                 close(ep[i]);
322                         ret = -EIO;
323                         goto err_close;
324                 }
325         }
326
327         pthread_mutex_init(&ctx->mutex, NULL);
328         pthread_cond_init(&ctx->ready, NULL);
329
330         pthread_create(&ctx->ep0_thread, NULL, ep0_handler, ctx);
331         pthread_detach(ctx->ep0_thread);
332
333         free(ep_path);
334
335         return 0;
336
337 err_close:
338         close(ep[TFMFFS_EP0]);
339
340 err_free:
341         free(ep_path);
342
343         return ret;
344 }
345
346 static void usb_cleanup_ffs_daemon(struct usb_context *ctx)
347 {
348         int *ep = ctx->eps;
349
350         pthread_cancel(ctx->ep0_thread);
351         close(ep[TFMFFS_EP0]);
352 }
353
354 static int mount_ffs(void)
355 {
356         int ret;
357
358         ret = mkdir(TFMFFS_PATH, 0770);
359         if (ret < 0) {
360                 fprintf(stderr, "Failed to create ffs directory\n");
361                 return ret;
362         }
363
364         ret = mount("tfm", TFMFFS_PATH, "functionfs", 0, NULL);
365         if (ret < 0) {
366                 fprintf(stderr, "Failed to mount ffs function\n");
367                 rmdir(TFMFFS_PATH);
368         }
369
370         return ret;
371 }
372
373 static void umount_ffs(void)
374 {
375         umount(TFMFFS_PATH);
376         rmdir(TFMFFS_PATH);
377 }
378
379 static int usb_connect(struct tfm_interface *intf)
380 {
381         struct usb_context *ctx;
382         usbg_state *s;
383         usbg_gadget *g;
384         usbg_config *c;
385         usbg_function *f;
386         int ret;
387
388         ctx = (struct usb_context *)malloc(sizeof(*ctx));
389         if (!ctx)
390                 return -ENOMEM;
391
392         ret = usbg_init("/sys/kernel/config", &s);
393         if (ret < 0) {
394                 fprintf(stderr, "Failed to init USB gadget\n");
395                 goto err_freectx;
396         }
397
398         ret = usbg_create_gadget(s, TFMFFS_GADGET, &g_attrs, &g_strs, &g);
399         if (ret < 0) {
400                 fprintf(stderr, "Failed to create USB gadget\n");
401                 goto err_cleanup;
402         }
403
404         ret = usbg_create_function(g, USBG_F_FFS, TFMFFS_DEV, NULL, &f);
405         if (ret < 0) {
406                 fprintf(stderr, "Failed to create USB function\n");
407                 goto err_cleanup;
408         }
409
410         ret = usbg_create_config(g, 1, NULL, NULL, &c_strs, &c);
411         if (ret < 0) {
412                 fprintf(stderr, "Failed to create USB config\n");
413                 goto err_cleanup;
414         }
415
416         ret = usbg_add_config_function(c, TFMFFS_BIND_LINK, f);
417         if (ret < 0) {
418                 fprintf(stderr, "Failed to bind USB function\n");
419                 goto err_cleanup;
420         }
421
422         if (mount_ffs() < 0) {
423                 fprintf(stderr, "Failed to mount functionfs\n");
424                 goto err_cleanup;
425         }
426
427         if (usb_setup_ffs_daemon(ctx) < 0) {
428                 fprintf(stderr, "Failed to setup ffs daemon\n");
429                 goto err_umount;
430         }
431
432         ret = usbg_enable_gadget(g, DEFAULT_UDC);
433         if (ret < 0) {
434                 fprintf(stderr, "Failed to enable USB gadget\n");
435                 goto err_ffs;
436         }
437
438         pthread_mutex_lock(&ctx->mutex);
439         while (!ctx->enabled)
440                 pthread_cond_wait(&ctx->ready, &ctx->mutex);
441         pthread_mutex_unlock(&ctx->mutex);
442
443         ctx->usbg = s;
444
445         intf->txd = ctx->eps[TFMFFS_EP_BULK_IN];
446         intf->rxd = ctx->eps[TFMFFS_EP_BULK_OUT];
447         intf->priv = (void *)ctx;
448
449         return 0;
450
451 err_ffs:
452         usb_cleanup_ffs_daemon(ctx);
453
454 err_umount:
455         umount_ffs();
456
457 err_cleanup:
458         usbg_cleanup(s);
459
460 err_freectx:
461         free(ctx);
462
463         return -EINVAL;
464 }
465
466 static int usb_disconnect(struct tfm_interface *intf)
467 {
468         struct usb_context *ctx = intf->priv;
469         int *ep = ctx->eps;
470         int i;
471
472         for (i = 1; i < TFMFFS_EP_MAX; i++)
473                 close(ep[i]);
474
475         usb_cleanup_ffs_daemon(ctx);
476         umount_ffs();
477         if (ctx->usbg)
478                 usbg_cleanup(ctx->usbg);
479
480         return 0;
481 }
482
483 static struct tfm_interface_driver usb = {
484         .name = "usb",
485         .ops = {
486                 .connect = usb_connect,
487                 .disconnect = usb_disconnect,
488                 .rx_data = usb_rx_data,
489                 .tx_data = usb_tx_data,
490         },
491 };
492 INTERFACE_REGISTER(usb)