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