usb-gadget: access external symbol using dlsym(), not extern keyword
[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/file.h>
24 #include <libsyscommon/libsystemd.h>
25 #include <hal/device/hal-usb_gadget-interface.h>
26
27 #include "usb_gadget.h"
28 #include "hal-backend-common.h"
29
30 #define MAX_GADGET_STR_LEN 256
31
32 #define LEGACY_ROOTPATH        "/sys/class/usb_mode/usb0"
33
34 /* Device descriptor values */
35 #define LEGACY_ID_VENDOR_PATH       LEGACY_ROOTPATH"/idVendor"
36 #define LEGACY_ID_PRODUCT_PATH      LEGACY_ROOTPATH"/idProduct"
37 #define LEGACY_BCD_DEVICE_PATH      LEGACY_ROOTPATH"/bcdDevice"
38 #define LEGACY_CLASS_PATH           LEGACY_ROOTPATH"/bDeviceClass"
39 #define LEGACY_SUBCLASS_PATH        LEGACY_ROOTPATH"/bDeviceSubClass"
40 #define LEGACY_PROTOCOL_PATH        LEGACY_ROOTPATH"/bDeviceProtocol"
41
42 /* Strings */
43 #define LEGACY_IMANUFACTURER_PATH   LEGACY_ROOTPATH"/iManufacturer"
44 #define LEGACY_IPRODUCT_PATH        LEGACY_ROOTPATH"/iProduct"
45
46 /* Functions in each config */
47 #define LEGACY_CONFIG_1_PATH        LEGACY_ROOTPATH"/funcs_fconf"
48 #define LEGACY_CONFIG_2_PATH        LEGACY_ROOTPATH"/funcs_sconf"
49
50 /* should be single char */
51 #define LEGACY_FUNC_SEP             ","
52
53 /* ON/OFF switch */
54 #define LEGACY_ENABLE_PATH          LEGACY_ROOTPATH"/enable"
55 #define LEGACY_ENABLE               "1"
56 #define LEGACY_DISABLE              "0"
57
58 static bool legacy_is_function_supported(struct usb_function *func)
59 {
60         char buf[PATH_MAX];
61
62         snprintf (buf, sizeof(buf), "%s/f_%s", LEGACY_ROOTPATH, func->name);
63         if (access(buf, F_OK))
64                 return false;
65
66         return true;
67 }
68
69 static bool legacy_is_gadget_supported(struct usb_gadget *gadget)
70 {
71         int i, j;
72         struct usb_configuration *config;
73
74         if (!gadget || !gadget->configs || !gadget->configs[0] || !gadget->configs[0]->funcs[0])
75                 return false;
76
77         if (!gadget->attrs.idVendor || !gadget->attrs.idProduct || !gadget->attrs.bcdDevice)
78                         return false;
79
80         /* only strings in US_en are allowed */
81         if (gadget->strs.lang_code != DEFAULT_LANG)
82                         return false;
83
84         if (!gadget->strs.manufacturer || !gadget->strs.product)
85                 return false;
86
87         for (j = 0; gadget->configs[j]; ++j) {
88                 config = gadget->configs[j];
89
90                 if (!config->funcs)
91                         return false;
92
93                 for (i = 0; config->funcs[i]; ++i)
94                         if (!legacy_is_function_supported(config->funcs[i]))
95                                 return false;
96         }
97
98         return true;
99 }
100
101 /* TODO. Maybe move this to sys ? */
102 static int legacy_set_int_hex(char *path, int val)
103 {
104         int r;
105         char buf[MAX_GADGET_STR_LEN];
106
107         if (!path)
108                 return -EINVAL;
109
110         snprintf(buf, sizeof(buf), "%x", val);
111         r = sys_set_str(path, buf);
112         if (r < 0)
113                 return r;
114
115         return 0;
116 }
117
118 static int legacy_set_gadget_attrs(struct usb_gadget_attrs *attrs)
119 {
120         int ret;
121
122         ret = sys_set_int(LEGACY_CLASS_PATH, attrs->bDeviceClass);
123         if (ret)
124                 return ret;
125
126         ret = sys_set_int(LEGACY_SUBCLASS_PATH, attrs->bDeviceSubClass);
127         if (ret)
128                 return ret;
129
130         ret = sys_set_int(LEGACY_PROTOCOL_PATH, attrs->bDeviceProtocol);
131         if (ret)
132                 return ret;
133
134         ret = legacy_set_int_hex(LEGACY_ID_VENDOR_PATH, attrs->idVendor);
135         if (ret)
136                 return ret;
137
138         ret = legacy_set_int_hex(LEGACY_ID_PRODUCT_PATH, attrs->idProduct);
139         if (ret)
140                 return ret;
141
142         ret = legacy_set_int_hex(LEGACY_BCD_DEVICE_PATH, attrs->bcdDevice);
143         if (ret)
144                 return ret;
145
146         return 0;
147 }
148
149 static int legacy_set_gadget_strs(struct usb_gadget_strings *strs)
150 {
151         int ret;
152
153         if (!strs->manufacturer || !strs->product)
154                 return -EINVAL;
155
156         ret = sys_set_str(LEGACY_IMANUFACTURER_PATH, strs->manufacturer);
157         if (ret)
158                 return ret;
159
160         ret = sys_set_str(LEGACY_IPRODUCT_PATH, strs->product);
161         if (ret)
162                 return ret;
163
164         /* The serial is written by the slp gadget kernel driver */
165
166         return 0;
167 }
168
169 static int legacy_set_gadget_config(char *cpath,
170                                     struct usb_configuration *config)
171 {
172         char buf[MAX_GADGET_STR_LEN];
173         int left = sizeof(buf);
174         char *pos = buf;
175         int ret;
176         int i;
177
178         if (!config) {
179                 buf[0] = '\n';
180                 buf[1] = '\0';
181                 goto empty_config;
182         }
183
184         for (i = 0; config->funcs[i]; ++i) {
185                 ret = snprintf(pos, left, "%s" LEGACY_FUNC_SEP,
186                                config->funcs[i]->name);
187                 if (ret >= left)
188                         return -EOVERFLOW;
189
190                 pos += ret;
191                 left -= ret;
192         }
193
194         /* eliminate last separator */
195         *(pos - 1) = '\0';
196
197 empty_config:
198         return sys_set_str(cpath, buf);
199 }
200
201 static int legacy_reconfigure_gadget(struct usb_gadget *gadget)
202 {
203         int ret;
204
205         if (!gadget)
206                 return -EINVAL;
207
208         /* Verify the gadget and check if function is supported */
209         if (!legacy_is_gadget_supported(gadget))
210                 return -ENOTSUP;
211
212         ret = legacy_set_gadget_attrs(&gadget->attrs);
213         if (ret)
214                 return ret;
215
216         ret = legacy_set_gadget_strs(&gadget->strs);
217         if (ret)
218                 return ret;
219
220         ret = legacy_set_gadget_config(LEGACY_CONFIG_1_PATH, gadget->configs[0]);
221         if (ret)
222                 return ret;
223
224         ret = legacy_set_gadget_config(LEGACY_CONFIG_2_PATH, gadget->configs[1]);
225         if (ret)
226                 return ret;
227
228         return 0;
229 }
230
231 static void legacy_start_stop_service_and_handler(bool start)
232 {
233         int i;
234         int ret;
235         int n_func = 0;
236         char *ptr;
237         char *begin;
238         char *fname;
239         char *sep = LEGACY_FUNC_SEP;
240         char buf[MAX_GADGET_STR_LEN];
241         struct usb_function *func;
242         struct usb_function *funcs[USB_FUNCTION_IDX_MAX];
243
244         /* SLP gadget uses two USB configuration.
245          * (/sys/class/usb_mode/usb0/funcs_fconf and /sys/class/usb_mode/usb0/funcs_sconf)
246          *
247          * One usb function can be included in two configurations simultaneously.
248          * In this situation, a handler associated with function can be called twice for a usb function.
249          * To prevent duplicate calls,
250          * Collect all functions and remove duplicates and process them.
251          */
252
253         /* First configuration */
254         ret = sys_get_str(LEGACY_CONFIG_1_PATH, buf, sizeof(buf));
255         if (ret)
256                 goto second_configuration;
257
258         /* Caution: buf ends with '\n' */
259         ptr = strchr(buf, '\n');
260         if (ptr)
261                 *ptr = 0;
262
263         begin = buf;
264         for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
265                 func = find_usb_function_by_name(fname);
266                 if (!func)
267                         continue;
268
269                 for (i = 0; i < n_func; i++)
270                         if (funcs[i] == func)
271                                 continue;
272
273                 if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
274                         break;
275
276                 funcs[n_func] = func;
277                 n_func++;
278         }
279
280         /* Second configuration */
281 second_configuration:
282         ret = sys_get_str(LEGACY_CONFIG_2_PATH, buf, sizeof(buf));
283         if (ret)
284                 return;
285
286         /* Caution: buf ends with '\n' */
287         ptr = strchr(buf, '\n');
288         if (ptr)
289                 *ptr = 0;
290
291         begin = buf;
292         for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
293                 func = find_usb_function_by_name(fname);
294                 if (!func)
295                         continue;
296
297                 for (i = 0; i < n_func; i++)
298                         if (funcs[i] == func)
299                                 continue;
300
301                 if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
302                         break;
303
304                 funcs[n_func] = func;
305                 n_func++;
306         }
307
308         for (i = 0; i < n_func; i++) {
309                 if (start) {
310                         if (funcs[i]->handler)
311                                 funcs[i]->handler(1);
312
313                         if (funcs[i]->service)
314                                 (void)systemd_start_unit_wait_started(funcs[i]->service, ".service", -1);
315                 } else {
316                         if (funcs[i]->service && !funcs[i]->remain_after_disable)
317                                 (void)systemd_stop_unit_wait_stopped(funcs[i]->service, ".service", -1);
318
319                         if (funcs[i]->handler)
320                                 funcs[i]->handler(0);
321                 }
322         }
323 }
324
325 static int legacy_enable(void)
326 {
327         int ret;
328
329         ret = sys_set_str(LEGACY_ENABLE_PATH, LEGACY_ENABLE);
330         if (ret < 0)
331                 return ret;
332
333         legacy_start_stop_service_and_handler(true);
334
335         return 0;
336 }
337
338 static int legacy_disable(void)
339 {
340         legacy_start_stop_service_and_handler(false);
341
342         return sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
343 }
344
345 EXPORT
346 int hw_legacy_gadget_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
347 {
348         if (!usb_gadget_funcs)
349                 return -EINVAL;
350
351         /* check if slp usb gadget exists */
352         if (access("/sys/class/usb_mode/usb0/enable", F_OK))
353                 return -ENOENT;
354
355         usb_gadget_funcs->enable = legacy_enable;
356         usb_gadget_funcs->disable = legacy_disable;
357         usb_gadget_funcs->reconfigure_gadget = legacy_reconfigure_gadget;
358
359         return 0;
360 }
361
362 EXPORT
363 int hw_legacy_gadget_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
364 {
365         return 0;
366 }