f08ac6195ebb7887fa308c73bbd67798aa16837e
[platform/hal/backend/device-common.git] / src / usb_gadget / usb_client_common.c
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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 <stdio.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <stdbool.h>
21 #include <linux/limits.h>
22
23 #include <libsyscommon/dbus-systemd.h>
24 #include <hal/device/hal-usb_gadget-interface.h>
25
26 #include "usb_gadget.h"
27
28 #define MAX_GADGET_STR_LEN 256
29
30 #define LEGACY_ROOTPATH        "/sys/class/usb_mode/usb0"
31
32 /* Device descriptor values */
33 #define LEGACY_ID_VENDOR_PATH       LEGACY_ROOTPATH"/idVendor"
34 #define LEGACY_ID_PRODUCT_PATH      LEGACY_ROOTPATH"/idProduct"
35 #define LEGACY_BCD_DEVICE_PATH      LEGACY_ROOTPATH"/bcdDevice"
36 #define LEGACY_CLASS_PATH           LEGACY_ROOTPATH"/bDeviceClass"
37 #define LEGACY_SUBCLASS_PATH        LEGACY_ROOTPATH"/bDeviceSubClass"
38 #define LEGACY_PROTOCOL_PATH        LEGACY_ROOTPATH"/bDeviceProtocol"
39
40 /* Strings */
41 #define LEGACY_IMANUFACTURER_PATH   LEGACY_ROOTPATH"/iManufacturer"
42 #define LEGACY_IPRODUCT_PATH        LEGACY_ROOTPATH"/iProduct"
43
44 /* Functions in each config */
45 #define LEGACY_CONFIG_1_PATH        LEGACY_ROOTPATH"/funcs_fconf"
46 #define LEGACY_CONFIG_2_PATH        LEGACY_ROOTPATH"/funcs_sconf"
47
48 /* should be single char */
49 #define LEGACY_FUNC_SEP             ","
50
51 /* ON/OFF switch */
52 #define LEGACY_ENABLE_PATH          LEGACY_ROOTPATH"/enable"
53 #define LEGACY_ENABLE               "1"
54 #define LEGACY_DISABLE              "0"
55
56 #ifndef EXPORT
57 #define EXPORT  __attribute__ ((visibility("default")))
58 #endif
59
60 // TODO move to common code
61 #if 1
62 #include <unistd.h>
63 #include <stdlib.h>
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <fcntl.h>
67
68 #define SHARED_H_BUF_MAX 255
69
70 static inline int sys_read_buf(char *file, char *buf, int len)
71 {
72         int fd, r;
73
74         if (!file || !buf || len < 0)
75                 return -EINVAL;
76
77         fd = open(file, O_RDONLY);
78         if (fd == -1)
79                 return -ENOENT;
80
81         r = read(fd, buf, len);
82         close(fd);
83         if ((r >= 0) && (r < len))
84                 buf[r] = '\0';
85         else
86                 return -EIO;
87
88         return 0;
89 }
90
91 static inline int sys_write_buf(char *file, char *buf)
92 {
93         int fd, r;
94
95         if (!file || !buf)
96                 return -EINVAL;
97
98         fd = open(file, O_WRONLY);
99         if (fd == -1)
100                 return -EPERM;
101
102         r = write(fd, buf, strlen(buf));
103         close(fd);
104         if (r < 0)
105                 return -EIO;
106
107         return 0;
108 }
109
110 static inline int sys_get_str(char *fname, char *str, int len)
111 {
112         int r;
113
114         if (!fname || !str || len < 0)
115                 return -EINVAL;
116
117         r = sys_read_buf(fname, str, len);
118         if (r < 0)
119                 return r;
120
121         return 0;
122 }
123
124 static inline int sys_set_str(char *fname, char *val)
125 {
126         int r;
127
128         if (!fname || !val)
129                 return -EINVAL;
130
131         r = sys_write_buf(fname, val);
132         if (r < 0)
133                 return r;
134
135         return 0;
136 }
137
138 static inline int sys_get_int(char *fname, int *val)
139 {
140         char buf[SHARED_H_BUF_MAX];
141         int r;
142
143         if (!fname || !val)
144                 return -EINVAL;
145
146         r = sys_read_buf(fname, buf, sizeof(buf));
147         if (r < 0)
148                 return r;
149
150         *val = atoi(buf);
151         return 0;
152 }
153
154 static inline int sys_set_int(char *fname, int val)
155 {
156         char buf[SHARED_H_BUF_MAX];
157         int r;
158
159         if (!fname)
160                 return -EINVAL;
161
162         snprintf(buf, sizeof(buf), "%d", val);
163         r = sys_write_buf(fname, buf);
164         if (r < 0)
165                 return r;
166
167         return 0;
168 }
169 #endif
170
171 static bool legacy_is_function_supported(struct usb_function *func)
172 {
173         char buf[PATH_MAX];
174
175         snprintf (buf, sizeof(buf), "%s/f_%s", LEGACY_ROOTPATH, func->name);
176         if (access(buf, F_OK))
177                 return false;
178
179         return true;
180 }
181
182 static bool legacy_is_gadget_supported(struct usb_gadget *gadget)
183 {
184         int i, j;
185         struct usb_configuration *config;
186
187         if (!gadget || !gadget->configs || !gadget->configs[0] || !gadget->configs[0]->funcs[0])
188                 return false;
189
190         if (!gadget->attrs.idVendor || !gadget->attrs.idProduct || !gadget->attrs.bcdDevice)
191                         return false;
192
193         /* only strings in US_en are allowed */
194         if (gadget->strs.lang_code != DEFAULT_LANG)
195                         return false;
196
197         if (!gadget->strs.manufacturer || !gadget->strs.product)
198                 return false;
199
200         for (j = 0; gadget->configs[j]; ++j) {
201                 config = gadget->configs[j];
202
203                 if (!config->funcs)
204                         return false;
205
206                 for (i = 0; config->funcs[i]; ++i)
207                         if (!legacy_is_function_supported(config->funcs[i]))
208                                 return false;
209         }
210
211         return true;
212 }
213
214 /* TODO. Maybe move this to sys ? */
215 static int legacy_set_int_hex(char *path, int val)
216 {
217         int r;
218         char buf[MAX_GADGET_STR_LEN];
219
220         if (!path)
221                 return -EINVAL;
222
223         snprintf(buf, sizeof(buf), "%x", val);
224         r = sys_set_str(path, buf);
225         if (r < 0)
226                 return r;
227
228         return 0;
229 }
230
231 static int legacy_set_gadget_attrs(struct usb_gadget_attrs *attrs)
232 {
233         int ret;
234
235         ret = sys_set_int(LEGACY_CLASS_PATH, attrs->bDeviceClass);
236         if (ret)
237                 return ret;
238
239         ret = sys_set_int(LEGACY_SUBCLASS_PATH, attrs->bDeviceSubClass);
240         if (ret)
241                 return ret;
242
243         ret = sys_set_int(LEGACY_PROTOCOL_PATH, attrs->bDeviceProtocol);
244         if (ret)
245                 return ret;
246
247         ret = legacy_set_int_hex(LEGACY_ID_VENDOR_PATH, attrs->idVendor);
248         if (ret)
249                 return ret;
250
251         ret = legacy_set_int_hex(LEGACY_ID_PRODUCT_PATH, attrs->idProduct);
252         if (ret)
253                 return ret;
254
255         ret = legacy_set_int_hex(LEGACY_BCD_DEVICE_PATH, attrs->bcdDevice);
256         if (ret)
257                 return ret;
258
259         return 0;
260 }
261
262 static int legacy_set_gadget_strs(struct usb_gadget_strings *strs)
263 {
264         int ret;
265
266         if (!strs->manufacturer || !strs->product)
267                 return -EINVAL;
268
269         ret = sys_set_str(LEGACY_IMANUFACTURER_PATH, strs->manufacturer);
270         if (ret)
271                 return ret;
272
273         ret = sys_set_str(LEGACY_IPRODUCT_PATH, strs->product);
274         if (ret)
275                 return ret;
276
277         /* The serial is written by the slp gadget kernel driver */
278
279         return 0;
280 }
281
282 static int legacy_set_gadget_config(char *cpath,
283                                     struct usb_configuration *config)
284 {
285         char buf[MAX_GADGET_STR_LEN];
286         int left = sizeof(buf);
287         char *pos = buf;
288         int ret;
289         int i;
290
291         if (!config) {
292                 buf[0] = '\n';
293                 buf[1] = '\0';
294                 goto empty_config;
295         }
296
297         for (i = 0; config->funcs[i]; ++i) {
298                 ret = snprintf(pos, left, "%s" LEGACY_FUNC_SEP,
299                                config->funcs[i]->name);
300                 if (ret >= left)
301                         return -EOVERFLOW;
302
303                 pos += ret;
304                 left -= ret;
305         }
306
307         /* eliminate last separator */
308         *(pos - 1) = '\0';
309
310 empty_config:
311         return sys_set_str(cpath, buf);
312 }
313
314 static int legacy_reconfigure_gadget(struct usb_gadget *gadget)
315 {
316         int ret;
317
318         if (!gadget)
319                 return -EINVAL;
320
321         /* Verify the gadget and check if function is supported */
322         if (!legacy_is_gadget_supported(gadget))
323                 return -ENOTSUP;
324
325         ret = legacy_set_gadget_attrs(&gadget->attrs);
326         if (ret)
327                 return ret;
328
329         ret = legacy_set_gadget_strs(&gadget->strs);
330         if (ret)
331                 return ret;
332
333         ret = legacy_set_gadget_config(LEGACY_CONFIG_1_PATH, gadget->configs[0]);
334         if (ret)
335                 return ret;
336
337         ret = legacy_set_gadget_config(LEGACY_CONFIG_2_PATH, gadget->configs[1]);
338         if (ret)
339                 return ret;
340
341         return 0;
342 }
343
344 static void legacy_start_stop_service_and_handler(bool start)
345 {
346         int i;
347         int ret;
348         int n_func = 0;
349         char *ptr;
350         char *begin;
351         char *fname;
352         char *sep = LEGACY_FUNC_SEP;
353         char buf[MAX_GADGET_STR_LEN];
354         struct usb_function *func;
355         struct usb_function *funcs[USB_FUNCTION_IDX_MAX];
356
357         /* SLP gadget uses two USB configuration.
358          * (/sys/class/usb_mode/usb0/funcs_fconf and /sys/class/usb_mode/usb0/funcs_sconf)
359          *
360          * One usb function can be included in two configurations simultaneously.
361          * In this situation, a handler associated with function can be called twice for a usb function.
362          * To prevent duplicate calls,
363          * Collect all functions and remove duplicates and process them.
364          */
365
366         /* First configuration */
367         ret = sys_get_str(LEGACY_CONFIG_1_PATH, buf, sizeof(buf));
368         if (ret)
369                 goto second_configuration;
370
371         /* Caution: buf ends with '\n' */
372         ptr = strchr(buf, '\n');
373         if (ptr)
374                 *ptr = 0;
375
376         begin = buf;
377         for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
378                 func = find_usb_function_by_name(fname);
379                 if (!func)
380                         continue;
381
382                 for (i = 0; i < n_func; i++)
383                         if (funcs[i] == func)
384                                 continue;
385
386                 if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
387                         break;
388
389                 funcs[n_func] = func;
390                 n_func++;
391         }
392
393         /* Second configuration */
394 second_configuration:
395         ret = sys_get_str(LEGACY_CONFIG_2_PATH, buf, sizeof(buf));
396         if (ret)
397                 return;
398
399         /* Caution: buf ends with '\n' */
400         ptr = strchr(buf, '\n');
401         if (ptr)
402                 *ptr = 0;
403
404         begin = buf;
405         for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
406                 func = find_usb_function_by_name(fname);
407                 if (!func)
408                         continue;
409
410                 for (i = 0; i < n_func; i++)
411                         if (funcs[i] == func)
412                                 continue;
413
414                 if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
415                         break;
416
417                 funcs[n_func] = func;
418                 n_func++;
419         }
420
421         for (i = 0; i < n_func; i++) {
422                 if (start) {
423                         if (funcs[i]->handler)
424                                 funcs[i]->handler(1);
425
426                         if (funcs[i]->service)
427                                 (void)systemd_start_unit_wait_started(funcs[i]->service, ".service", -1);
428                 } else {
429                         if (funcs[i]->service)
430                                 (void)systemd_stop_unit_wait_stopped(funcs[i]->service, ".service", -1);
431
432                         if (funcs[i]->handler)
433                                 funcs[i]->handler(0);
434                 }
435         }
436 }
437
438 static int legacy_enable(void)
439 {
440         int ret;
441
442         ret = sys_set_str(LEGACY_ENABLE_PATH, LEGACY_ENABLE);
443         if (ret < 0)
444                 return ret;
445
446         legacy_start_stop_service_and_handler(true);
447
448         return 0;
449 }
450
451 static int legacy_disable(void)
452 {
453         legacy_start_stop_service_and_handler(false);
454
455         return sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
456 }
457
458 EXPORT
459 int hw_legacy_gadget_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
460 {
461         if (!usb_gadget_funcs)
462                 return -EINVAL;
463
464         /* check if slp usb gadget exists */
465         if (access("/sys/class/usb_mode/usb0/enable", F_OK))
466                 return -ENOENT;
467
468         usb_gadget_funcs->enable = legacy_enable;
469         usb_gadget_funcs->disable = legacy_disable;
470         usb_gadget_funcs->reconfigure_gadget = legacy_reconfigure_gadget;
471
472         return 0;
473 }
474
475 EXPORT
476 int hw_legacy_gadget_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
477 {
478         return 0;
479 }