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