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