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