*/
+#include <string.h>
#include <vconf.h>
#include <bundle.h>
#include <eventsystem.h>
#include <hal/device/hal-usb_gadget-interface.h>
+#include <libsyscommon/ini-parser.h>
#include "core/log.h"
#include "apps/apps.h"
#include "usb.h"
#include "usb-debug.h"
+#define PATH_USB_GADGET_CONF "/hal/etc/deviced/usb_gadget.conf"
+
static int noti_id = -1;
static unsigned int usb_current_mode = USB_FUNCTION_NONE;
static const struct _usb_mode_mapping_table {
int mode_v; /* Integer defined by vconf */
unsigned int mode; /* Bitmap of usb function combination */
+ struct usb_gadget_attrs attrs;
} usb_mode_mapping_table[] = {
/* Hack for performance. In most cases this is the value. */
{SET_USB_DEFAULT, USB_FUNCTION_MTP | USB_FUNCTION_ACM},
{SET_USB_RNDIS_SDB_ACM, USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_RNDIS},
};
+static const int mapping_table_len = sizeof(usb_mode_mapping_table) / sizeof(usb_mode_mapping_table[0]);
+
+/*
+ * Private, custom defined mode mapping.
+ * You can define custom mode_v with mode from /hal/etc/deviced/usb_gadget.conf
+ * A custom mapping precedes the above default mapping if the custom mode_v overlaps
+ * default mode_v.
+ *
+ * Do not declare it with static storage class as it will be exported as dynamic symbol
+ * so that dlopen-ed device-common can refer it.
+ */
+GList *usb_mode_mapping_table_custom;
+
+/*
+ * Do not declare it with static storage class as it will be exported as dynamic symbol
+ * so that dlopen-ed device-common can refer it.
+ */
+struct service_config {
+ char name[128];
+ int remain_after_disable; /* do not stop the service on disabling usb-gadget function */
+};
+GList *service_config_list;
+
unsigned int get_mode_bitmap_from_vconf(int mode_v)
{
int i;
- int array_len = sizeof(usb_mode_mapping_table)/sizeof(usb_mode_mapping_table[0]);
+ GList *elem;
+ struct _usb_mode_mapping_table *table;
+
+ /* lookup from custom mapping table first */
+ SYS_G_LIST_FOREACH(usb_mode_mapping_table_custom, elem, table) {
+ if (table->mode_v == mode_v)
+ return table->mode;
+ }
- for (i = 0; i < array_len; i++) {
+ /* and then lookup the default mapping table */
+ for (i = 0; i < mapping_table_len; i++) {
if (usb_mode_mapping_table[i].mode_v == mode_v)
return usb_mode_mapping_table[i].mode;
}
static int get_mode_vconf_from_bitmap(unsigned int mode)
{
int i;
- int array_len = sizeof(usb_mode_mapping_table)/sizeof(usb_mode_mapping_table[0]);
+ GList *elem;
+ struct _usb_mode_mapping_table *table;
+
+ /* lookup from custom mapping table first */
+ SYS_G_LIST_FOREACH(usb_mode_mapping_table_custom, elem, table) {
+ if (table->mode == mode)
+ return table->mode_v;
+ }
/* Caution: The order to search the usb_mode_mapping_table must start with a low index. */
- for (i = 0; i < array_len; i++) {
+ for (i = 0; i < mapping_table_len; i++) {
if (usb_mode_mapping_table[i].mode == mode)
return usb_mode_mapping_table[i].mode_v;
}
bundle_free(b);
}
+static void parse_property_systemd_unit(gpointer data, gpointer udata)
+{
+ struct section_property *prop = (struct section_property *) data;
+ struct service_config *svc = (struct service_config *) udata;
+
+ if (MATCH(prop->key, "Service")) {
+ strncpy(svc->name, prop->value, sizeof(svc->name));
+ } else if (MATCH(prop->key, "RemainAfterDisable")) {
+ svc->remain_after_disable = MATCH(prop->value, "yes");
+ }
+}
+
+static unsigned int parse_usb_function(char *fstr)
+{
+ // Parse Function=, e.g., "Function=diag,acm"
+ unsigned int ret = USB_FUNCTION_NONE;
+ char *p, *saveptr;
+
+ if (!fstr)
+ return ret;
+
+ p = strtok_r(fstr, ",", &saveptr);
+ if (!p)
+ return ret;
+
+ do {
+ if (strncasecmp(p, "mtp", sizeof("mtp")) == 0) {
+ ret |= USB_FUNCTION_MTP;
+ } else if (strncasecmp(p, "acm", sizeof("acm")) == 0) {
+ ret |= USB_FUNCTION_ACM;
+ } else if(strncasecmp(p, "sdb", sizeof("sdb")) == 0) {
+ ret |= USB_FUNCTION_SDB;
+ } else if (strncasecmp(p, "rndis", sizeof("rndis")) == 0) {
+ ret |= USB_FUNCTION_RNDIS;
+ } else if (strncasecmp(p, "diag", sizeof("diag")) == 0) {
+ ret |= USB_FUNCTION_DIAG;
+ } else if (strncasecmp(p, "conngadget", sizeof("conngadget")) == 0) {
+ ret |= USB_FUNCTION_CONN_GADGET;
+ } else if (strncasecmp(p, "dm", sizeof("dm")) == 0) {
+ ret |= USB_FUNCTION_DM;
+ } else if (strncasecmp(p, "rmnet", sizeof("rmnet")) == 0) {
+ ret |= USB_FUNCTION_RMNET;
+ }
+ } while ((p = strtok_r(NULL, ",", &saveptr)));
+
+ return ret;
+}
+
+static void parse_property_attribute(gpointer data, gpointer udata)
+{
+ struct section_property *prop = (struct section_property *) data;
+ struct _usb_mode_mapping_table *table = (struct _usb_mode_mapping_table *) udata;
+ unsigned int value;
+
+ if (MATCH(prop->key, "Mode")) {
+ sscanf(prop->value, "%d", &table->mode_v);
+ } else if (MATCH(prop->key, "Function")) {
+ table->mode = parse_usb_function(prop->value);
+ } else if (MATCH(prop->key, "bDeviceClass")) {
+ sscanf(prop->value, "%x", &value);
+ table->attrs.bDeviceClass = (uint8_t) value;
+ } else if (MATCH(prop->key, "bDeviceSubClass")) {
+ sscanf(prop->value, "%x", &value);
+ table->attrs.bDeviceSubClass = (uint8_t) value;
+ } else if (MATCH(prop->key, "bDeviceProtocol")) {
+ sscanf(prop->value, "%x", &value);
+ table->attrs.bDeviceProtocol = (uint8_t) value;
+ } else if (MATCH(prop->key, "idVendor")) {
+ sscanf(prop->value, "%x", &value);
+ table->attrs.idVendor = (uint16_t) value;
+ } else if (MATCH(prop->key, "idProduct")) {
+ sscanf(prop->value, "%x", &value);
+ table->attrs.idProduct = (uint16_t) value;
+ } else if (MATCH(prop->key, "bcdDevice")) {
+ sscanf(prop->value, "%x", &value);
+ table->attrs.bcdDevice = (uint16_t) value;
+ }
+}
+
+static int load_usb_gadget_config(const struct parse_result *result, void *data)
+{
+ if (MATCH(result->section, "SystemdUnit")) {
+ struct service_config svc = { 0, };
+ void *entry = NULL;
+
+ g_list_foreach(result->props, parse_property_systemd_unit, &svc);
+
+ entry = malloc(sizeof(struct service_config));
+ if (!entry) {
+ _E("Failed to alloc service config");
+ return 0;
+ }
+
+ _I("usb-gadget service config: name=%s, remain_after_disable=%d",
+ svc.name, svc.remain_after_disable);
+ service_config_list = g_list_prepend(service_config_list, memcpy(entry, &svc, sizeof(svc)));
+
+ } else if (MATCH(result->section, "Attribute")) {
+ struct _usb_mode_mapping_table table = { 0, };
+ void *entry = NULL;
+
+ g_list_foreach(result->props, parse_property_attribute, &table);
+
+ // if it hasn't defined mode, use default or pre-defined one
+ if (table.mode == USB_FUNCTION_NONE)
+ table.mode = get_mode_bitmap_from_vconf(table.mode_v);
+
+ if (table.mode == USB_FUNCTION_INVALID)
+ return 0;
+
+ if (table.mode_v >= SET_USB_NONE && table.mode_v <= SET_USB_RNDIS_SDB_ACM)
+ _W("The custom mode=%d replaces the predefined usb-gadget configuration", table.mode_v);
+
+ entry = malloc(sizeof(struct _usb_mode_mapping_table));
+ if (!entry) {
+ _E("Failed to alloc mapping table");
+ return 0;
+ }
+
+ _I("Custom usb-gadget: mode=%d, function=%#x, idVendor=%#x, idProduct=%#x",
+ table.mode_v, table.mode, table.attrs.idVendor, table.attrs.idProduct);
+
+ usb_mode_mapping_table_custom = g_list_prepend(usb_mode_mapping_table_custom, memcpy(entry, &table, sizeof(table)));
+ }
+
+ return 0;
+}
+
+void usb_state_load_custom_mode(void)
+{
+ libsys_config_parse_by_section(PATH_USB_GADGET_CONF, load_usb_gadget_config, NULL);
+}
+
void usb_state_retrieve_selected_mode(void)
{
int mode_v;