2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <libsyscommon/libsystemd.h>
25 #include <libsyscommon/list.h>
26 #include <hal/device/hal-usb_gadget-interface.h>
28 #include "usb_gadget.h"
29 #include "hal-backend-common.h"
31 static struct usb_function *_available_funcs[];
33 /**************************************
34 * Those symbols must be initialized first in deviced
36 * The usb-gadget source code have been scattered throughout the deviced and device-common.
37 * Because of this, it might have been implemented that totally same data structure on the
38 * both side. To avoid this inefficiency, define/initialize the data structure on the
39 * deviced first, and device-common refer it through dlsym(). The deviced must have
40 * cflag -rdynamic or ldflag --export-dynamic so that the dlopen-ed device-common can refer
41 * those variables with dlsym().
43 * Accessing symbol in this way would be removed when the usb-gadget of the device-common
44 * is integrated into the deviced.
47 #include <hal/device/hal-usb_gadget-interface.h>
49 struct _usb_mode_mapping_table {
50 int mode_v; /* Integer defined by vconf */
51 unsigned int mode; /* Bitmap of usb function combination */
52 struct usb_gadget_attrs attrs;
54 static GList *usb_mode_mapping_table_custom;
56 struct service_config {
58 int remain_after_disable;
60 static GList *service_config_list;
61 /**************************************/
63 struct usb_function *find_usb_function_by_id(int id);
65 static void simple_cleanup_config(struct usb_configuration *config)
70 free(config->strs.config_str);
78 static void cleanup_gadget(struct usb_gadget *gadget)
85 free(gadget->strs.manufacturer);
86 free(gadget->strs.product);
87 free(gadget->strs.serial);
89 if (gadget->configs) {
90 for (i = 0; gadget->configs[i]; ++i)
91 simple_cleanup_config(gadget->configs[i]);
93 free(gadget->configs);
99 static int alloc_default_config(struct usb_configuration **_config)
101 struct usb_configuration *config;
103 config = calloc(1, sizeof(*config));
107 config->attrs.bmAttributs = DEFAULT_BMATTRIBUTES;
108 config->attrs.MaxPower = DEFAULT_MAX_POWER;
110 /* TODO. Here is where to set the string used in config of configfs */
117 static int alloc_default_gadget(char *serial, struct usb_gadget **_gadget)
119 struct usb_gadget *gadget;
120 struct usb_configuration **configs;
122 gadget = calloc(1, sizeof(*gadget));
126 gadget->attrs.idVendor = DEFAULT_VID;
127 gadget->attrs.idProduct = DEFAULT_PID;
128 gadget->attrs.bcdDevice = DEFAULT_BCD_DEVICE;
130 gadget->strs.lang_code = DEFAULT_LANG;
131 gadget->strs.manufacturer = strdup(DEFAULT_MANUFACTURER);
132 gadget->strs.product = strdup(DEFAULT_PRODUCT);
133 gadget->strs.serial = strdup(serial);
135 if (!gadget->strs.manufacturer || !gadget->strs.product || !gadget->strs.serial)
138 /* slp-gadget use max 2 confiuration and NULL termination */
139 configs = calloc(3, sizeof(*configs));
143 gadget->configs = configs;
149 free(gadget->strs.manufacturer);
150 free(gadget->strs.product);
151 free(gadget->strs.serial);
157 static int id_to_gadget(struct usb_gadget_id *gadget_id, char *serial, struct usb_gadget **_gadget)
162 struct usb_gadget *gadget;
163 int functions[2][sizeof(gadget_id->function_mask)*8]; /* zero terminates */
166 const struct _usb_mode_mapping_table *cm = NULL;
168 if (!gadget_id || !serial || !_gadget)
171 ret = alloc_default_gadget(serial, &gadget);
175 /* find custom mode */
176 SYS_G_LIST_FOREACH(usb_mode_mapping_table_custom, elem, cm) {
177 if (cm->mode == gadget_id->function_mask)
186 for (i = 0; i < USB_FUNCTION_IDX_MAX; ++i) {
187 if (cm->mode & (1 << i))
188 functions[0][j++] = (1 << i);
192 if (cm->attrs.idVendor)
193 gadget->attrs.idVendor = cm->attrs.idVendor;
194 if (cm->attrs.idProduct)
195 gadget->attrs.idProduct = cm->attrs.idProduct;
199 * Currently all gadgets use inly single configuration but
200 * slp-gadget is capable to handle two of them
202 * Order of interfaces in configuration is significant
203 * so in this switch we sort our functions in a correct order
205 switch (gadget_id->function_mask) {
207 case USB_FUNCTION_MTP | USB_FUNCTION_ACM:
209 functions[0][0] = USB_FUNCTION_MTP;
210 functions[0][1] = USB_FUNCTION_ACM;
212 gadget->attrs.idProduct = 0x6860;
215 case USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB:
217 functions[0][0] = USB_FUNCTION_MTP;
218 functions[0][1] = USB_FUNCTION_ACM;
219 functions[0][2] = USB_FUNCTION_SDB;
221 gadget->attrs.idProduct = 0x6860;
225 case USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_DIAG:
227 functions[0][0] = USB_FUNCTION_MTP;
228 functions[0][1] = USB_FUNCTION_ACM;
229 functions[0][2] = USB_FUNCTION_SDB;
230 functions[0][3] = USB_FUNCTION_DIAG;
232 gadget->attrs.idProduct = 0x6860;
236 case USB_FUNCTION_RNDIS:
238 functions[0][0] = USB_FUNCTION_RNDIS;
240 gadget->attrs.idProduct = 0x6863;
243 case USB_FUNCTION_RNDIS | USB_FUNCTION_DIAG:
245 functions[0][0] = USB_FUNCTION_RNDIS;
246 functions[0][1] = USB_FUNCTION_DIAG;
248 gadget->attrs.idProduct = 0x6864;
251 case USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_RNDIS:
253 functions[0][0] = USB_FUNCTION_RNDIS;
254 functions[0][1] = USB_FUNCTION_ACM;
255 functions[0][2] = USB_FUNCTION_SDB;
257 gadget->attrs.idProduct = 0x6864;
261 case USB_FUNCTION_DIAG | USB_FUNCTION_RMNET:
263 functions[0][0] = USB_FUNCTION_DIAG;
264 functions[0][1] = USB_FUNCTION_RMNET;
266 gadget->attrs.idProduct = 0x685d;
270 case USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_DM:
272 functions[0][0] = USB_FUNCTION_ACM;
273 functions[0][1] = USB_FUNCTION_SDB;
274 functions[0][2] = USB_FUNCTION_DM;
276 gadget->attrs.idProduct = 0x6860;
285 for (j = 0; j < n_configs; ++j) {
286 int n_funcs_in_config;
287 struct usb_configuration *config;
289 for (i = 0; functions[j][i]; ++i);
290 n_funcs_in_config = i;
292 ret = alloc_default_config(&config);
296 gadget->configs[j] = config;
297 config->funcs = calloc(n_funcs_in_config + 1, sizeof(void *));
301 for (i = 0; functions[j][i]; ++i) {
302 config->funcs[i] = find_usb_function_by_id(functions[j][i]);
303 if (!config->funcs[i])
314 cleanup_gadget(gadget);
319 void rndis_handler(int enable)
322 (void)systemd_start_unit_wait_started("rndis.service", NULL, -1);
324 (void)systemd_stop_unit_wait_stopped("rndis.service", NULL, -1);
327 #define DEFINE_USB_FUNCTION(_id, _name, _is_functionfs, _service, _handler) \
328 static struct usb_function _##_name##_function = { \
331 .instance = "default", \
332 .is_functionfs = _is_functionfs, \
333 .service = _service, \
334 .remain_after_disable = 0, \
335 .handler = _handler, \
338 DEFINE_USB_FUNCTION(USB_FUNCTION_MTP, mtp, 1, "mtp-responder", NULL);
339 DEFINE_USB_FUNCTION(USB_FUNCTION_ACM, acm, 0, "data-router", NULL);
340 DEFINE_USB_FUNCTION(USB_FUNCTION_SDB, sdb, 1, "sdbd", NULL);
341 DEFINE_USB_FUNCTION(USB_FUNCTION_RNDIS, rndis, 0, "sshd", rndis_handler);
342 DEFINE_USB_FUNCTION(USB_FUNCTION_DIAG, diag, 1, "diag", NULL);
343 DEFINE_USB_FUNCTION(USB_FUNCTION_CONN_GADGET, conn_gadget, 0, NULL, NULL);
344 DEFINE_USB_FUNCTION(USB_FUNCTION_DM, dm, 0, NULL, NULL);
345 DEFINE_USB_FUNCTION(USB_FUNCTION_RMNET, rmnet, 0, NULL, NULL);
347 #undef DEFINE_USB_FUNCTION
349 /* Caution: index order of arrary is important, because simple_translator_open() uses it. */
350 static struct usb_function *_available_funcs[] = {
351 [USB_FUNCTION_IDX_MTP] = &_mtp_function,
352 [USB_FUNCTION_IDX_ACM] = &_acm_function,
353 [USB_FUNCTION_IDX_SDB] = &_sdb_function,
354 [USB_FUNCTION_IDX_RNDIS] = &_rndis_function,
355 [USB_FUNCTION_IDX_DIAG] = &_diag_function,
356 [USB_FUNCTION_IDX_CONN_GADGET] = &_conn_gadget_function,
357 [USB_FUNCTION_IDX_DM] = &_dm_function,
358 [USB_FUNCTION_IDX_RMNET] = &_rmnet_function,
359 [USB_FUNCTION_IDX_MAX] = NULL /* An indicator to end the array */
362 struct usb_function *find_usb_function_by_id(int id)
366 for (i = 0; _available_funcs[i]; i++)
367 if (_available_funcs[i]->id == id)
368 return _available_funcs[i];
373 struct usb_function *find_usb_function_by_name(const char *name)
377 if(!name || !name[0])
380 for (i = 0; _available_funcs[i]; i++)
381 if (!strcmp(name, _available_funcs[i]->name))
382 return _available_funcs[i];
387 struct usb_function *find_usb_function_by_name_instance(const char *name, const char *instance)
391 if(!name || !name[0] || !instance || !instance[0])
394 for (i = 0; _available_funcs[i]; ++i)
395 if (!strcmp(name, _available_funcs[i]->name) && !strcmp(instance, _available_funcs[i]->instance))
396 return _available_funcs[i];
401 static void set_service_config(gpointer data, gpointer udata)
403 struct service_config *svc = (struct service_config *) data;
406 for (i = 0; _available_funcs[i]; ++i) {
407 if (!strcmp(svc->name, _available_funcs[i]->name)) {
408 _available_funcs[i]->remain_after_disable = svc->remain_after_disable;
409 _I("%s: remain_after_disable=%d", svc->name, svc->remain_after_disable);
415 int simple_translator_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
419 if (!usb_gadget_funcs)
422 dlerror(); // clear any existing dl error
424 usb_mode_mapping_table_custom = *(GList **) dlsym(RTLD_DEFAULT, "usb_mode_mapping_table_custom");
431 service_config_list = *(GList **) dlsym(RTLD_DEFAULT, "service_config_list");
439 * /hal/etc/deviced/usb_gadget.conf
443 * RemainAfterDisable=yes
445 g_list_foreach(service_config_list, set_service_config, NULL);
447 usb_gadget_funcs->id_to_gadget = id_to_gadget;
448 usb_gadget_funcs->cleanup_gadget = cleanup_gadget;
450 /* Use mtp-responder-dummy.socket when there is no mtp-responser.socket.
452 * The mtp-responder.socket is special in the configfs environment.
453 * If mtp-responder.socket is missing, gadget configuration will fail.
454 * As a result, all usb operations do not work properly.
455 * So in environments that mtp doesn't support, use dummy mtp.
457 if (access("/usr/lib/systemd/system/mtp-responder.socket", F_OK)) {
458 _available_funcs[USB_FUNCTION_IDX_MTP]->service = "mtp-responder-dummy";
465 int simple_translator_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs)