Add common code from usb_client HAL
[platform/core/system/libdevice-node.git] / hw / usb_client_common.c
1 /*
2  * libdevice-node
3  *
4  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <hw/usb_client.h>
20 #include <hw/systemd.h>
21 #include <hw/shared.h>
22
23 #include <limits.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #define zalloc(amount) calloc(1, amount)
29
30 #define MAX_GADGET_STR_LEN 256
31 #define MAX_FUNCS 32
32
33 #define LEGACY_ROOTPATH        "/sys/class/usb_mode/usb0"
34
35 /* Device descriptor values */
36 #define LEGACY_ID_VENDOR_PATH       LEGACY_ROOTPATH"/idVendor"
37 #define LEGACY_ID_PRODUCT_PATH      LEGACY_ROOTPATH"/idProduct"
38 #define LEGACY_BCD_DEVICE_PATH      LEGACY_ROOTPATH"/bcdDevice"
39 #define LEGACY_CLASS_PATH           LEGACY_ROOTPATH"/bDeviceClass"
40 #define LEGACY_SUBCLASS_PATH        LEGACY_ROOTPATH"/bDeviceSubClass"
41 #define LEGACY_PROTOCOL_PATH        LEGACY_ROOTPATH"/bDeviceProtocol"
42
43 /* Strings */
44 #define LEGACY_IMANUFACTURER_PATH   LEGACY_ROOTPATH"/iManufacturer"
45 #define LEGACY_IPRODUCT_PATH        LEGACY_ROOTPATH"/iProduct"
46 #define LEGACY_ISERIAL_PATH         LEGACY_ROOTPATH"/iSerial"
47
48 /* Functions in each config */
49 #define LEGACY_CONFIG_1_PATH        LEGACY_ROOTPATH"/funcs_fconf"
50 #define LEGACY_CONFIG_2_PATH        LEGACY_ROOTPATH"/funcs_sconf"
51 /* should be single char */
52 #define LEGACY_FUNC_SEP             ","
53
54 /* ON/OFF switch */
55 #define LEGACY_ENABLE_PATH          LEGACY_ROOTPATH"/enable"
56 #define LEGACY_ENABLE               "1"
57 #define LEGACY_DISABLE              "0"
58
59 #define LEGACY_BMATTRIBUTES ((1 << 7) | (1 << 6))
60 #define LEGACY_MAX_POWER 500
61
62 /* +5 to be always big enough */
63 #define INT_BUF_SIZE (sizeof(int)*8 + 5)
64
65 #ifndef EXPORT
66 #define EXPORT  __attribute__ ((visibility("default")))
67 #endif
68
69 static int get_int_from_file(char *path, int *_val, int base)
70 {
71         char buf[INT_BUF_SIZE];
72         char *endptr;
73         long int val;
74         int ret;
75
76         ret = sys_get_str(path, buf, sizeof(buf));
77         if (ret)
78                 return ret;
79
80         val = strtol(buf, &endptr, base);
81         if (val == LONG_MIN || val == LONG_MAX ||
82             buf[0] == '\0' || (*endptr != '\0' && *endptr != '\n')
83             || val >= INT_MAX)
84                 return -EINVAL;
85
86         *_val = (int)val;
87         return 0;
88 }
89
90 static int legacy_read_gadget_attrs_strs(struct usb_gadget *gadget)
91 {
92         int val;
93         int ret;
94         /* We assume that values received from kernel will be valid */
95 #define GET_VALUE_FROM_SYSFS(path, field, type, base)   \
96         do {                                            \
97                 ret = get_int_from_file(path, &val, base);      \
98                 if (ret)                                \
99                         return ret;                     \
100                                                         \
101                 gadget->attrs.field = (type)val;        \
102         } while (0)
103
104         GET_VALUE_FROM_SYSFS(LEGACY_CLASS_PATH, bDeviceClass, uint8_t, 0);
105         GET_VALUE_FROM_SYSFS(LEGACY_SUBCLASS_PATH, bDeviceSubClass, uint8_t, 0);
106         GET_VALUE_FROM_SYSFS(LEGACY_PROTOCOL_PATH, bDeviceProtocol, uint8_t, 0);
107         GET_VALUE_FROM_SYSFS(LEGACY_ID_VENDOR_PATH, idVendor, uint16_t, 16);
108         GET_VALUE_FROM_SYSFS(LEGACY_ID_PRODUCT_PATH, idProduct, uint16_t, 16);
109         GET_VALUE_FROM_SYSFS(LEGACY_BCD_DEVICE_PATH, bcdDevice, uint16_t, 0);
110 #undef GET_VALUE_FROM_SYSFS
111
112 #define GET_STRING_FROM_SYSFS(path, field)                      \
113         do {                                                    \
114                 char buf[MAX_GADGET_STR_LEN];                   \
115                                                                 \
116                 ret = sys_get_str(path, buf, sizeof(buf));      \
117                 if (ret)                                        \
118                         goto err_##field;                       \
119                                                                 \
120                 gadget->strs[0].field = strdup(buf);            \
121                 if (!gadget->strs[0].field) {                   \
122                         ret = -ENOMEM;                          \
123                         goto err_##field;                       \
124                 }                                               \
125         } while (0)
126
127         GET_STRING_FROM_SYSFS(LEGACY_IMANUFACTURER_PATH, manufacturer);
128         GET_STRING_FROM_SYSFS(LEGACY_IPRODUCT_PATH, product);
129         GET_STRING_FROM_SYSFS(LEGACY_ISERIAL_PATH, serial);
130 #undef GET_STRING_FROM_SYSFS
131
132         return 0;
133
134 err_serial:
135         free(gadget->strs[0].product);
136 err_product:
137         free(gadget->strs[0].manufacturer);
138 err_manufacturer:
139         return ret;
140 }
141
142 static int legacy_find_func(const char *name)
143 {
144         int i;
145
146         for (i = 0; i < ARRAY_SIZE(_available_funcs); ++i)
147                 if (!strcmp(name, _available_funcs[i]->name))
148                         return i;
149
150         return -1;
151 }
152
153 static struct usb_function *legacy_find_func_in_gadget(
154         struct usb_gadget *gadget, const char *name)
155 {
156         int i;
157
158         for (i = 0; gadget->funcs[i]; ++i)
159                 if (!strcmp(name, gadget->funcs[i]->name))
160                         return gadget->funcs[i];
161         return NULL;
162 }
163
164 static int legacy_alloc_config(int n_funcs, struct usb_configuration **_config)
165 {
166         struct usb_configuration *config;
167
168         config = zalloc(sizeof(*config));
169         if (!config)
170                 goto out;
171
172         config->strs = calloc(1, sizeof(*config->strs));
173         if (!config->strs)
174                 goto free_config;
175
176         config->funcs = calloc(n_funcs + 1, sizeof(*config->funcs));
177         if (!config->funcs)
178                 goto free_strs;
179
180         /*
181          * We cannot read correct values
182          * so assume that they are always default
183          */
184         config->attrs.bmAttributs = LEGACY_BMATTRIBUTES;
185         config->attrs.MaxPower = LEGACY_MAX_POWER;
186
187         *_config = config;
188
189         return 0;
190 free_strs:
191         free(config->strs);
192 free_config:
193         free(config);
194 out:
195         return -ENOMEM;
196 }
197
198 static int legacy_alloc_new_func(struct usb_gadget *gadget, const char *fname,
199                                  struct usb_function **_func)
200 {
201         struct usb_function *func;
202         int ret;
203
204         ret = legacy_find_func(fname);
205         if (ret < 0)
206                 return -ENOTSUP;
207
208         ret = _available_funcs[ret]->clone(_available_funcs[ret], &func);
209         if (ret)
210                 return ret;
211
212         *_func = func;
213         return 0;
214 }
215
216 static int legacy_read_config(struct usb_gadget *gadget,
217                               char *cpath,
218                               struct usb_configuration **_config)
219 {
220         struct usb_configuration *config;
221         char buf[MAX_GADGET_STR_LEN];
222         char *begin = buf;
223         char *fname;
224         char *sep = LEGACY_FUNC_SEP;
225         int i, f_cnt;
226         int f_idx;
227         int g_f_idx;
228         int ret;
229
230         ret = sys_get_str(cpath, buf, sizeof(buf));
231         if (ret)
232                 return ret;
233
234         /* Empty */
235         if (buf[0] == '\0' || buf[0] == '\n')
236                 return 0;
237
238         /* count number of functions in this config */
239         f_cnt = 1;
240         for (i = 0; buf[i] != '\0'; ++i) {
241                 if (buf[i] == sep[0])
242                         ++f_cnt;
243                 if (buf[i] == '\n')  /* buf ends with it */
244                         buf[i] = 0;
245         }
246
247         ret = legacy_alloc_config(f_cnt, &config);
248         if (ret)
249                 return ret;
250
251         for (g_f_idx = 0; gadget->funcs[g_f_idx]; ++g_f_idx);
252
253         f_idx = 0;
254         for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
255                 struct usb_function *func;
256
257                 func = legacy_find_func_in_gadget(gadget, fname);
258                 if (!func) {
259                         /* new function not added yet to gadget */
260                         ret = legacy_alloc_new_func(gadget, fname, &func);
261                         if (ret)
262                                 goto free_config;
263
264                         gadget->funcs[g_f_idx++] = func;
265                 }
266
267                 config->funcs[f_idx++] = func;
268         }
269
270         *_config = config;
271         return 0;
272 free_config:
273         free(config->strs);
274         free(config->funcs);
275         free(config);
276         return ret;
277 }
278
279 static int legacy_get_current_gadget(struct usb_client *usb,
280                                      struct usb_gadget **_gadget)
281 {
282         struct usb_gadget *gadget;
283         struct usb_gadget_strings *strs;
284         struct usb_configuration **configs;
285         struct usb_function **funcs;
286         int i;
287         int ret = -ENOMEM;
288
289         gadget = zalloc(sizeof(*gadget));
290         if (!gadget)
291                 goto out;
292
293         strs = calloc(2, sizeof(*strs));
294         if (!strs)
295                 goto free_gadget;
296
297         strs[0].lang_code = 0x409;
298
299         gadget->strs = strs;
300
301         ret = legacy_read_gadget_attrs_strs(gadget);
302         if (ret)
303                 goto free_strs;
304
305         /* There will be no more functions than bits in int */
306         funcs = calloc(MAX_FUNCS, sizeof(*funcs));
307         if (!funcs)
308                 goto free_strs_with_content;
309
310         gadget->funcs = funcs;
311
312         /* slp-gadget use max 2 confiuration and NULL termination */
313         configs = calloc(3, sizeof(*configs));
314         if (!configs)
315                 goto free_funcs;
316
317         gadget->configs = configs;
318
319         ret = legacy_read_config(gadget, LEGACY_CONFIG_1_PATH, configs + 0);
320         if (ret)
321                 goto free_configs;
322
323         ret = legacy_read_config(gadget, LEGACY_CONFIG_2_PATH, configs + 1);
324         if (ret)
325                 goto free_config_1;
326
327         *_gadget = gadget;
328         return 0;
329
330 free_config_1:
331         free(configs[0]->funcs);
332         free(configs[0]->strs);
333         free(configs[0]);
334 free_configs:
335         free(configs);
336         for (i = 0; gadget->funcs[i]; ++i)
337                 gadget->funcs[i]->free_func(gadget->funcs[i]);
338 free_funcs:
339         free(funcs);
340 free_strs_with_content:
341         free(gadget->strs[0].manufacturer);
342         free(gadget->strs[0].product);
343         free(gadget->strs[0].serial);
344 free_strs:
345         free(gadget->strs);
346 free_gadget:
347         free(gadget);
348 out:
349         return ret;
350 }
351
352 static bool legacy_is_function_supported(struct usb_client *usb,
353                                          struct usb_function *func)
354 {
355         int ret;
356
357         /*
358          * TODO
359          * Instead of only checking whether we know this function
360          * we should also parse sysfs to check if it is build into
361          * slp-gadget.
362          */
363         ret = legacy_find_func(func->name);
364
365         return ret >= 0;
366 }
367
368 static bool legacy_is_gadget_supported(struct usb_client *usb,
369                                        struct usb_gadget *gadget)
370 {
371         int i, j;
372
373         if (!gadget || !gadget->configs || !gadget->funcs)
374                 return false;
375
376         /*
377          * TODO
378          * Here is a good place to ensure that serial is immutable
379          */
380         if (gadget->strs) {
381                 /* only strings in US_en are allowed */
382                 if (gadget->strs[0].lang_code != 0x409 ||
383                     gadget->strs[1].lang_code)
384                         return false;
385         }
386
387         for (j = 0; gadget->configs[j]; ++j) {
388                 struct usb_configuration *config = gadget->configs[j];
389
390                 if (config->strs && config->strs[0].lang_code)
391                         return false;
392
393                 if (!config->funcs)
394                         return false;
395
396                 for (i = 0; config->funcs[i]; ++i)
397                         if (!legacy_is_function_supported(usb, config->funcs[i]))
398                                 return false;
399         }
400
401         if (j == 0 || j > 2)
402                 return false;
403
404         return true;
405 }
406
407 /* TODO. Maybe move this to sys ? */
408 static int legacy_set_int_hex(char *path, int val)
409 {
410         char buf[MAX_GADGET_STR_LEN];
411         int r;
412
413         if (!path)
414                 return -EINVAL;
415
416         snprintf(buf, sizeof(buf), "%x", val);
417         r = sys_set_str(path, buf);
418         if (r < 0)
419                 return r;
420
421         return 0;
422 }
423
424 static int legacy_set_gadget_attrs(struct usb_gadget_attrs *attrs)
425 {
426         int ret;
427
428         ret = sys_set_int(LEGACY_CLASS_PATH, attrs->bDeviceClass);
429         if (ret)
430                 return ret;
431
432         ret = sys_set_int(LEGACY_SUBCLASS_PATH, attrs->bDeviceSubClass);
433         if (ret)
434                 return ret;
435
436         ret = sys_set_int(LEGACY_PROTOCOL_PATH, attrs->bDeviceProtocol);
437         if (ret)
438                 return ret;
439
440         ret = legacy_set_int_hex(LEGACY_ID_VENDOR_PATH, attrs->idVendor);
441         if (ret)
442                 return ret;
443
444         ret = legacy_set_int_hex(LEGACY_ID_PRODUCT_PATH, attrs->idProduct);
445         if (ret)
446                 return ret;
447
448         ret = legacy_set_int_hex(LEGACY_BCD_DEVICE_PATH, attrs->bcdDevice);
449
450         return ret;
451 }
452
453 static int legacy_set_gadget_strs(struct usb_gadget_strings *strs)
454 {
455         int ret = 0;
456
457         /*
458          * TODO
459          * Here is a good place to ensure that serial is immutable
460          */
461
462         if (strs->manufacturer) {
463                 ret = sys_set_str(LEGACY_IMANUFACTURER_PATH,
464                                   strs->manufacturer);
465                 if (ret)
466                         return ret;
467         }
468
469         if (strs->product) {
470                 ret = sys_set_str(LEGACY_IPRODUCT_PATH,
471                                   strs->product);
472                 if (ret)
473                         return ret;
474         }
475
476         return ret;
477 }
478
479 static int legacy_set_gadget_config(char *cpath,
480                                     struct usb_configuration *config)
481 {
482         char buf[MAX_GADGET_STR_LEN];
483         int left = sizeof(buf);
484         char *pos = buf;
485         int ret;
486         int i;
487
488         if (!config) {
489                 buf[0] = '\n';
490                 buf[1] = '\0';
491                 goto empty_config;
492         }
493
494         for (i = 0; config->funcs[i]; ++i) {
495                 ret = snprintf(pos, left, "%s" LEGACY_FUNC_SEP,
496                                config->funcs[i]->name);
497                 if (ret >= left)
498                         return -EOVERFLOW;
499
500                 pos += ret;
501                 left -= ret;
502         }
503
504         /* eliminate last separator */
505         *(pos - 1) = '\0';
506
507 empty_config:
508         return sys_set_str(cpath, buf);
509 }
510
511 static int legacy_reconfigure_gadget(struct usb_client *usb,
512                                      struct usb_gadget *gadget)
513 {
514         int ret;
515
516         if (!usb || !gadget || !legacy_is_gadget_supported(usb, gadget))
517                 return -EINVAL;
518
519         ret = legacy_set_gadget_attrs(&gadget->attrs);
520         if (ret)
521                 return ret;
522
523         if (gadget->strs) {
524                 ret = legacy_set_gadget_strs(gadget->strs + 0);
525                 if (ret)
526                         return ret;
527         }
528
529         ret = legacy_set_gadget_config(LEGACY_CONFIG_1_PATH, gadget->configs[0]);
530         if (ret)
531                 return ret;
532
533         ret = legacy_set_gadget_config(LEGACY_CONFIG_2_PATH, gadget->configs[1]);
534
535         return ret;
536 }
537
538 static int legacy_enable(struct usb_client *usb)
539 {
540         int ret;
541         int i;
542         struct usb_gadget *gadget;
543         struct usb_function_with_service *fws;
544
545         ret = sys_set_str(LEGACY_ENABLE_PATH,
546                            LEGACY_ENABLE);
547         if (ret < 0)
548                 return ret;
549
550         ret = legacy_get_current_gadget(usb, &gadget);
551         if (ret < 0)
552                 goto disable_gadget;
553
554         for (i = 0; gadget->funcs[i]; ++i) {
555                 if (gadget->funcs[i]->function_group !=
556                     USB_FUNCTION_GROUP_WITH_SERVICE)
557                         continue;
558
559                 fws = container_of(gadget->funcs[i],
560                                    struct usb_function_with_service, func);
561                 ret = systemd_start_service(fws->service);
562                 if (ret < 0)
563                         goto stop_services;
564         }
565
566         return 0;
567 stop_services:
568         while (--i >= 0) {
569                 if (gadget->funcs[i]->function_group !=
570                     USB_FUNCTION_GROUP_WITH_SERVICE)
571                         continue;
572
573                 fws = container_of(gadget->funcs[i],
574                                    struct usb_function_with_service, func);
575                 systemd_stop_service(fws->service);
576         }
577
578 disable_gadget:
579         sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
580         return ret;
581 }
582
583 static int legacy_disable(struct usb_client *usb)
584 {
585         int ret;
586         int i;
587         struct usb_gadget *gadget;
588         struct usb_function_with_service *fws;
589
590         ret = legacy_get_current_gadget(usb, &gadget);
591         if (ret < 0)
592                 return ret;
593
594         for (i = 0; gadget->funcs[i]; ++i) {
595                 if (gadget->funcs[i]->function_group != USB_FUNCTION_GROUP_WITH_SERVICE)
596                         continue;
597
598                 fws = container_of(gadget->funcs[i], struct usb_function_with_service, func);
599                 ret = systemd_stop_service(fws->service);
600                 if (ret < 0)
601                         return ret;
602         }
603
604         return sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
605 }
606
607 static void legacy_free_config(struct usb_configuration *config)
608 {
609         int i;
610
611         if (!config)
612                 return;
613
614         if (config->strs) {
615                 for (i = 0; config->strs[i].lang_code; ++i)
616                         free(config->strs[i].config_str);
617
618                 free(config->strs);
619         }
620
621         /*
622          * Each function will be free later,
623          * for now we cleanup only pointers.
624          */
625         if (config->funcs)
626                 free(config->funcs);
627
628         free(config);
629 }
630
631 static void legacy_free_gadget(struct usb_gadget *gadget)
632 {
633         int i;
634
635         if (!gadget)
636                 return;
637
638         if (gadget->strs) {
639                 for (i = 0; gadget->strs[i].lang_code; ++i) {
640                         free(gadget->strs[i].manufacturer);
641                         free(gadget->strs[i].product);
642                         free(gadget->strs[i].serial);
643                 }
644                 free(gadget->strs);
645         }
646
647         if (gadget->configs) {
648                 for (i = 0; gadget->configs[i]; ++i)
649                         legacy_free_config(gadget->configs[i]);
650
651                 free(gadget->configs);
652         }
653
654         if (gadget->funcs) {
655                 for (i = 0; gadget->funcs[i]; ++i)
656                         gadget->funcs[i]->free_func(gadget->funcs[i]);
657
658                 free(gadget->funcs);
659         }
660 }
661
662 EXPORT
663 int hw_legacy_gadget_open(struct hw_info *info,
664                 const char *id, struct hw_common **common)
665 {
666         struct usb_client *legacy;
667
668         if (!info || !common)
669                 return -EINVAL;
670
671         /* check if slp usb gadget exists */
672         if (access("/sys/class/usb_mode/usb0/enable", F_OK))
673                 return -ENOENT;
674
675         legacy = zalloc(sizeof(*legacy));
676         if (!legacy)
677                 return -ENOMEM;
678
679         legacy->common.info = info;
680         legacy->get_current_gadget = legacy_get_current_gadget;
681         legacy->reconfigure_gadget = legacy_reconfigure_gadget;
682         legacy->is_gadget_supported = legacy_is_gadget_supported;
683         legacy->is_function_supported = legacy_is_function_supported;
684         legacy->enable = legacy_enable;
685         legacy->disable = legacy_disable;
686         legacy->free_gadget = legacy_free_gadget;
687
688         *common = &legacy->common;
689         return 0;
690 }
691
692 EXPORT
693 int hw_legacy_gadget_close(struct hw_common *common)
694 {
695         struct usb_client *legacy;
696
697         if (!common)
698                 return -EINVAL;
699
700         legacy = container_of(common, struct usb_client,
701                                          common);
702
703         free(legacy);
704         return 0;
705 }