summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorINSUN PYO <insun.pyo@samsung.com>2021-01-22 19:03:19 +0900
committerINSUN PYO <insun.pyo@samsung.com>2021-01-25 17:23:12 +0900
commitc0a42109c09161535be75967844eedecc1bd7db6 (patch)
tree166c878ce6701a0ffd39143d6fb4ccec766748dd
parente5b45c9604bb3cac27f66ce148e863350a40ec9c (diff)
downloaddevice-common-c0a42109c09161535be75967844eedecc1bd7db6.tar.gz
device-common-c0a42109c09161535be75967844eedecc1bd7db6.tar.bz2
device-common-c0a42109c09161535be75967844eedecc1bd7db6.zip
usb_gadget: apply next HAL architecture
Change-Id: Ie79da201e2aba0c6007b553f142713b3110872ab
-rw-r--r--CMakeLists.txt76
-rw-r--r--hal-backend-device-common.pc.in10
-rw-r--r--include/hal-common-interface.h39
-rw-r--r--packaging/hal-backend-device-common.spec30
-rw-r--r--src/usb_gadget/usb_cfs_client_common.c642
-rw-r--r--src/usb_gadget/usb_client_common.c481
-rw-r--r--src/usb_gadget/usb_gadget.h26
-rw-r--r--src/usb_gadget/usb_gadget_common.c396
8 files changed, 1610 insertions, 90 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e46af6..8fbc524 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,77 +1,29 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
-PROJECT(device-node C)
+PROJECT(hal-backend-device-common C)
-SET(PREFIX ${CMAKE_INSTALL_PREFIX})
-SET(EXEC_PREFIX "${PREFIX}/bin")
-SET(LIBDIR ${LIB_INSTALL_DIR})
-SET(INCLUDEDIR "${PREFIX}/include/${PROJECT_NAME}")
-SET(VERSION 0.1)
-
-SET(INC_DIR include)
-INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
-INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/${INC_DIR})
+SET(SRCS
+ src/usb_gadget/usb_gadget_common.c
+ src/usb_gadget/usb_client_common.c
+ src/usb_gadget/usb_cfs_client_common.c
+)
INCLUDE(FindPkgConfig)
-pkg_check_modules(rpkgs REQUIRED dlog vconf glib-2.0 libsystemd libusbgx libsyscommon)
+pkg_check_modules(hal-backend-device-common_pkgs REQUIRED libusbgx libsyscommon)
-FOREACH(flag ${rpkgs_CFLAGS})
+FOREACH(flag ${hal-backend-device-common_pkgs_CFLAGS})
SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
ENDFOREACH(flag)
-SET(SRCS
- src/device-node.c
- src/device-plugin.c)
-
-SET(HEADERS
- include/device-node.h
- include/devman_plugin_intf.h)
-
-INCLUDE(devices/CMakeLists.txt)
-
SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden")
-SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g")
-SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Werror -Wl,-zdefs")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
-ADD_DEFINITIONS("-DLIBPATH=\"${LIB_INSTALL_DIR}\"")
-
-ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${TARGET_SRCS})
-TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${rpkgs_LDFLAGS} "-ldl")
-SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION})
-INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries)
-
-ADD_LIBRARY(hwcommon SHARED hw/common.c hw/usb_client_common.c hw/usb_cfs_client_common.c hw/usb_gadget_common.c)
-TARGET_LINK_LIBRARIES(hwcommon ${rpkgs_LDFLAGS} "-ldl")
-SET_TARGET_PROPERTIES(hwcommon PROPERTIES SOVERSION ${VERSION})
-INSTALL(TARGETS hwcommon DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries)
-
-CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY)
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
-
-# will be deprecated
-CONFIGURE_FILE(devman_plugin.pc.in devman_plugin.pc @ONLY)
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/devman_plugin.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
-
-FOREACH(hfile ${HEADERS})
- INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${hfile} DESTINATION include/${PROJECT_NAME})
-ENDFOREACH(hfile)
-
-INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/hw/ DESTINATION include/hw
- FILES_MATCHING
- PATTERN "*.h")
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
-CONFIGURE_FILE(hwcommon.pc.in hwcommon.pc @ONLY)
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hwcommon.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${hal-backend-device-common_pkgs_LDFLAGS})
-# dummy mtp responder
-INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mtp-responder-dummy/strs DESTINATION /etc/mtp-responder-dummy)
-INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mtp-responder-dummy/descs DESTINATION /etc/mtp-responder-dummy)
-INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mtp-responder-dummy/mtp-responder-dummy.socket DESTINATION /usr/lib/systemd/system)
-INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mtp-responder-dummy/mtp-responder-dummy.service DESTINATION /usr/lib/systemd/system)
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION /hal/lib/ COMPONENT RuntimeLibraries)
-ADD_SUBDIRECTORY(battery-plugin)
-ADD_DEPENDENCIES(battery-plugin hwcommon)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/hal-common-interface.h DESTINATION include/hal/device)
-IF(BUILD_GTESTS STREQUAL on)
- ADD_SUBDIRECTORY(unittest)
-ENDIF()
+CONFIGURE_FILE(hal-backend-device-common.pc.in hal-backend-device-common.pc @ONLY)
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hal-backend-device-common.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
diff --git a/hal-backend-device-common.pc.in b/hal-backend-device-common.pc.in
index a718e24..08ee2fb 100644
--- a/hal-backend-device-common.pc.in
+++ b/hal-backend-device-common.pc.in
@@ -1,12 +1,6 @@
# Package Information for pkg-config
-prefix=@PREFIX@
-exec_prefix=@EXEC_PREFIX@
-libdir=@LIBDIR@
-includedir=@INCLUDEDIR@
Name: HAL backend device common
Description: Library for HAL backend device modules
-Version: @VERSION@
-Requires.private: glib-2.0
-Libs: -L${libdir} -lhal-backend-device-common
-Cflags: -I${includedir}
+Version: 1.1
+Libs: -L/hal/lib -Wl,-rpath,/hal/lib -lhal-backend-device-common
diff --git a/include/hal-common-interface.h b/include/hal-common-interface.h
new file mode 100644
index 0000000..662efb4
--- /dev/null
+++ b/include/hal-common-interface.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __HAL_COMMON_H_
+#define __HAL_COMMON_H_
+
+#include <hal/device/hal-usb_gadget-interface.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int simple_translator_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs);
+int simple_translator_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs);
+
+int hw_legacy_gadget_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs);
+int hw_legacy_gadget_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs);
+
+int hw_cfs_gadget_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs);
+int hw_cfs_gadget_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __HAL_BOARD_INTERFACE_H__ */
diff --git a/packaging/hal-backend-device-common.spec b/packaging/hal-backend-device-common.spec
index 6bcf9c8..6d6ce69 100644
--- a/packaging/hal-backend-device-common.spec
+++ b/packaging/hal-backend-device-common.spec
@@ -1,5 +1,5 @@
Name: hal-backend-device-common
-Summary: HAL backend device common
+Summary: HAL backend device common
Version: 1
Release: 1
Group: System/API
@@ -7,16 +7,14 @@ License: Apache-2.0
Source0: %{name}-%{version}.tar.gz
Source1: %{name}.manifest
BuildRequires: cmake
-BuildRequires: pkgconfig(vconf)
-BuildRequires: pkgconfig(dlog)
-BuildRequires: pkgconfig(glib-2.0)
-BuildRequires: pkgconfig(gio-2.0)
-BuildRequires: pkgconfig(gmock)
-BuildRequires: pkgconfig(capi-system-info)
-BuildRequires: pkgconfig(libsystemd)
BuildRequires: pkgconfig(libusbgx)
+BuildRequires: pkgconfig(hal-api-device)
+
BuildRequires: pkgconfig(libsyscommon)
+# TODO remove later. It's temporary patch
+BuildRequires: pkgconfig(dlog)
+
%description
Library for HAL backend device modules
@@ -30,29 +28,21 @@ Library for HAL backend device modules (devel)
%prep
%setup -q
cp %{SOURCE1} .
+
%build
+%cmake .
make %{?jobs:-j%jobs}
%install
%make_install
-%post -p /sbin/ldconfig
-%postun -p /sbin/ldconfig
-
%files
%manifest %{name}.manifest
%license LICENSE.Apache-2.0
-%{_libdir}/*.so.*
-%{_libdir}/libbattery-plugin.so
-%{_unitdir}/mtp-responder-dummy.socket
-%{_unitdir}/mtp-responder-dummy.service
-/etc/mtp-responder-dummy/strs
-/etc/mtp-responder-dummy/descs
+/hal/lib/libhal-backend-device-common.so
%files devel
%manifest %{name}.manifest
%license LICENSE.Apache-2.0
-%{_includedir}/device-node/*.h
-%{_includedir}/hw/*.h
-%{_libdir}/*.so
+%{_includedir}/hal/device/hal-common-interface.h
%{_libdir}/pkgconfig/*.pc
diff --git a/src/usb_gadget/usb_cfs_client_common.c b/src/usb_gadget/usb_cfs_client_common.c
new file mode 100644
index 0000000..89ccd32
--- /dev/null
+++ b/src/usb_gadget/usb_cfs_client_common.c
@@ -0,0 +1,642 @@
+/*
+ * libdevice-node
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <linux/limits.h>
+
+#include <usbg/usbg.h>
+#include <usbg/function/net.h>
+
+#include <libsyscommon/dbus-systemd.h>
+#include <hal/device/hal-usb_gadget-interface.h>
+
+#include "usb_gadget.h"
+
+#define CONFIGFS_PATH "/sys/kernel/config"
+
+#define CONFIGFS_GADGET_NAME "hal-gadget"
+#define CONFIGFS_CONFIG_LABEL "hal-config"
+
+#define NAME_INSTANCE_SEP '.'
+#define MAX_INSTANCE_LEN 512
+
+#define USB_FUNCS_PATH "/dev/usb-funcs"
+
+#ifndef EXPORT
+#define EXPORT __attribute__ ((visibility("default")))
+#endif
+
+enum cfs_function_service_operation {
+ CFS_FUNCTION_SERVICE_START,
+ CFS_FUNCTION_SERVICE_STOP,
+ CFS_FUNCTION_SERVICE_POST_STOP,
+};
+
+struct cfs_client {
+ usbg_state *ctx;
+ usbg_gadget *gadget;
+ usbg_udc *udc;
+};
+
+/* Based on values in slp-gadget kernel module */
+struct usbg_gadget_attrs default_g_attrs = {
+ .idVendor = DEFAULT_VID,
+ .idProduct = DEFAULT_PID,
+ .bcdDevice = DEFAULT_BCD_DEVICE,
+};
+
+struct usbg_gadget_strs default_g_strs = {
+ .manufacturer = DEFAULT_MANUFACTURER,
+ .product = DEFAULT_PRODUCT,
+ .serial = DEFAULT_SERIAL,
+};
+
+struct cfs_client *g_cfs_client;
+
+static struct usb_function *cfs_find_usb_function(usbg_function *function)
+{
+ char *sep;
+ char buf[MAX_INSTANCE_LEN];
+ const char *instance = usbg_get_function_instance(function);
+ const char *name = usbg_get_function_type_str(usbg_get_function_type(function));
+
+ /* Ex. name:"ffs", instance: "sdb.default" */
+ if (strcmp(name, usbg_get_function_type_str(USBG_F_FFS)) == 0) {
+ strncpy(buf, instance, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+
+ /* Ex. "sdb.default" ==> "sdb" + "default" */
+ sep = strchr(buf, NAME_INSTANCE_SEP);
+ if (!sep || !sep[1])
+ return NULL;
+ *sep = '\0';
+
+ name = buf;
+ instance = sep + 1;
+ }
+
+ return find_usb_function_by_name_instance(name, instance);
+}
+
+static bool cfs_is_function_supported(struct usb_function *func)
+{
+ char buf[PATH_MAX];
+
+ if (func->is_functionfs) {
+ /* functionfs must have a service */
+ if (!func->service)
+ return false;
+
+ snprintf(buf, sizeof(buf), "/usr/lib/systemd/system/%s.socket", func->service);
+ if (access(buf, F_OK))
+ return false;
+ } else {
+ if (usbg_lookup_function_type(func->name) < 0)
+ return false;
+ }
+
+ return true;
+}
+
+static bool cfs_is_gadget_supported(struct usb_gadget *gadget)
+{
+ int i, j;
+ struct usb_configuration *config;
+
+ if (!gadget || !gadget->configs || !gadget->configs[0] || !gadget->configs[0]->funcs[0])
+ return false;
+
+ if (!gadget->attrs.idVendor || !gadget->attrs.idProduct || !gadget->attrs.bcdDevice)
+ return false;
+
+ /* only strings in US_en are allowed */
+ if (gadget->strs.lang_code != DEFAULT_LANG)
+ return false;
+
+ if (!gadget->strs.manufacturer || !gadget->strs.product || !gadget->strs.serial)
+ return false;
+
+ for (j = 0; gadget->configs[j]; ++j) {
+ config = gadget->configs[j];
+
+ if (!config->funcs)
+ return false;
+
+ for (i = 0; config->funcs[i]; ++i)
+ if (!cfs_is_function_supported(config->funcs[i]))
+ return false;
+ }
+
+ return true;
+}
+
+static int cfs_set_gadget_attrs(struct cfs_client *cfs_client,
+ struct usb_gadget_attrs *attrs)
+{
+ int ret;
+ struct usbg_gadget_attrs gadget_attrs;
+
+ ret = usbg_get_gadget_attrs(cfs_client->gadget, &gadget_attrs);
+ if (ret)
+ return ret;
+
+ gadget_attrs.bDeviceClass = attrs->bDeviceClass;
+ gadget_attrs.bDeviceSubClass = attrs->bDeviceSubClass;
+ gadget_attrs.bDeviceProtocol = attrs->bDeviceProtocol;
+ gadget_attrs.idVendor = attrs->idVendor;
+ gadget_attrs.idProduct = attrs->idProduct;
+ gadget_attrs.bcdDevice = attrs->bcdDevice;
+
+ ret = usbg_set_gadget_attrs(cfs_client->gadget, &gadget_attrs);
+
+ return ret;
+}
+
+static int cfs_set_gadget_strs(struct cfs_client *cfs_client, struct usb_gadget_strings *strs)
+{
+ int ret;
+
+ if (!strs->manufacturer || !strs->product || !strs->serial)
+ return -EINVAL;
+
+ ret = usbg_set_gadget_str(cfs_client->gadget, USBG_STR_MANUFACTURER, strs->lang_code, strs->manufacturer);
+ if (ret)
+ return ret;
+
+ ret = usbg_set_gadget_str(cfs_client->gadget, USBG_STR_PRODUCT, strs->lang_code, strs->product);
+ if (ret)
+ return ret;
+
+ ret = usbg_set_gadget_str(cfs_client->gadget, USBG_STR_SERIAL_NUMBER, strs->lang_code, strs->serial);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cfs_ensure_dir(char *path)
+{
+ if (mkdir(path, 0770) < 0)
+ return (errno == EEXIST) ? 0 : -errno;
+
+ return 0;
+}
+
+
+static int cfs_prep_ffs_service(struct usb_function *usb_func, usbg_function *function)
+{
+ int ret;
+ const char *name;
+ const char *service;
+ const char *instance;
+ const char *dev_name;
+ char buf[MAX_INSTANCE_LEN];
+
+ if (!usb_func || !function)
+ return -EINVAL;
+
+ if (usbg_get_function_type(function) != USBG_F_FFS)
+ return -EINVAL;
+
+ name = usb_func->name;
+ service = usb_func->service;
+ instance = usb_func->instance;
+ dev_name = usbg_get_function_instance(function);
+
+ /* "/dev/usb-funcs" + "/" + "sdb" + "/" + "default" + '0' */
+ if (strlen(USB_FUNCS_PATH) + strlen(name) + strlen(instance) + 3 > sizeof(buf))
+ return -ENAMETOOLONG;
+
+ /* mkdir /dev/usb-funcs */
+ ret = cfs_ensure_dir(USB_FUNCS_PATH);
+ if (ret < 0)
+ return ret;
+
+ /* mkdir /dev/usb-funcs/sdb */
+ snprintf(buf, sizeof(buf), "%s/%s", USB_FUNCS_PATH, name);
+ ret = cfs_ensure_dir(buf);
+ if (ret < 0)
+ goto out_rmdir;
+
+ /* mkdir /dev/usb-funcs/sdb/default */
+ snprintf(buf, sizeof(buf), "%s/%s/%s", USB_FUNCS_PATH, name, instance);
+ ret = cfs_ensure_dir(buf);
+ if (ret < 0)
+ goto out_rmdir;
+
+ /* mount -t functionfs sdb.default /dev/usb-funcs/sdb/default */
+ ret = mount(dev_name, buf, "functionfs", 0, NULL);
+ if (ret < 0)
+ goto out_rmdir;
+
+ /* start sdbd.socket */
+ ret = systemd_start_unit_wait_started(service, ".socket", -1);
+ if (ret < 0)
+ goto out_unmount;
+
+ return 0;
+
+out_unmount:
+ umount(buf);
+
+out_rmdir:
+ snprintf(buf, sizeof(buf), "%s/%s/%s", USB_FUNCS_PATH, name, instance);
+ rmdir(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s", USB_FUNCS_PATH, name);
+ rmdir(buf);
+
+ rmdir(USB_FUNCS_PATH);
+
+ return ret;
+}
+
+static int cfs_cleanup_ffs_service(usbg_function *function)
+{
+ int ret;
+ char buf[MAX_INSTANCE_LEN];
+ struct usb_function *usb_function;
+
+ if (!function)
+ return -EINVAL;
+
+ usb_function = cfs_find_usb_function(function);
+ if (!usb_function)
+ return -ENOENT;
+
+ /* stop .socket first and stop .service later becuase of socket activation */
+ if (usb_function->service) {
+ (void)systemd_stop_unit_wait_stopped(usb_function->service, ".socket", -1);
+ (void)systemd_stop_unit_wait_stopped(usb_function->service, ".service", -1);
+ }
+
+ /* umount /dev/usb-funcs/[sdb|mtp]/default and remove it's directory */
+ ret = snprintf(buf, sizeof(buf), "%s/%s/%s", USB_FUNCS_PATH, usb_function->name, usb_function->instance);
+ if (ret < 0)
+ return ret;
+
+ ret = umount(buf);
+ if (ret < 0)
+ return ret;
+
+ ret = rmdir(buf);
+ if (ret < 0)
+ return ret;
+
+ /* remove /dev/usb-funcs/[sdb|mtp] directory */
+ ret = snprintf(buf, sizeof(buf), "%s/%s", USB_FUNCS_PATH, usb_function->name);
+ if (ret < 0)
+ return ret;
+
+ ret = rmdir(buf);
+ if (ret < 0 && errno != ENOTEMPTY)
+ return ret;
+
+ /* remove /dev/usb-funcs/ directory */
+ ret = rmdir(USB_FUNCS_PATH);
+ if (ret < 0 && errno != ENOTEMPTY)
+ return ret;
+
+ return 0;
+}
+
+
+static int cfs_set_rndis_mac_addr(usbg_gadget *gadget, usbg_function *func)
+{
+ int i, ret;
+ struct ether_addr ethaddr;
+ struct usbg_gadget_strs strs;
+ struct usbg_f_net *nf = usbg_to_net_function(func);
+
+ if (!nf)
+ return -EINVAL;
+
+ ret = usbg_get_gadget_strs(gadget, LANG_US_ENG, &strs);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ ethaddr.ether_addr_octet[i] = 0;
+
+ for (i = 0; (i < 256) && strs.serial[i]; i++) {
+ ethaddr.ether_addr_octet[i % (ETHER_ADDR_LEN - 1) + 1] ^= strs.serial[i];
+ }
+ ethaddr.ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
+ ethaddr.ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
+
+ usbg_free_gadget_strs(&strs);
+
+ /* host_addr changes mac address */
+ ret = usbg_f_net_set_host_addr(nf, &ethaddr);
+
+ return ret;
+}
+
+static int cfs_cleanup_all_config_and_function(struct cfs_client *cfs_client)
+{
+ int ret;
+ usbg_config *config;
+ usbg_function *function;
+
+ /* delete all configs */
+restart_rm_config:
+ usbg_for_each_config(config, cfs_client->gadget) {
+ ret = usbg_rm_config(config, USBG_RM_RECURSE);
+ if (ret)
+ return ret;
+
+ goto restart_rm_config; /* You cannot delete a config directly in an iterator. */
+ }
+
+ /* delete all functions */
+restart_rm_function:
+ usbg_for_each_function(function, cfs_client->gadget) {
+ if (usbg_get_function_type(function) == USBG_F_FFS) {
+ ret = cfs_cleanup_ffs_service(function);
+ if (ret)
+ return ret;
+ }
+
+ ret = usbg_rm_function(function, USBG_RM_RECURSE);
+ if (ret)
+ return ret;
+
+ goto restart_rm_function; /* You cannot delete a function directly in an iterator. */
+ }
+
+ return 0;
+}
+
+static int cfs_set_gadget_config(struct cfs_client *cfs_client, int config_id, struct usb_configuration *usb_config)
+{
+ int i;
+ int ret;
+ int function_type;
+ usbg_config *config;
+ usbg_function *function;
+ struct usb_function *usb_func;
+ char instance[MAX_INSTANCE_LEN];
+ struct usbg_config_attrs cattrs = {
+ .bmAttributes = usb_config->attrs.bmAttributs,
+ .bMaxPower = usb_config->attrs.MaxPower/2,
+ };
+
+ if (!usb_config->funcs || !usb_config->funcs[0])
+ return -EINVAL;
+
+ ret = usbg_create_config(cfs_client->gadget, config_id, CONFIGFS_CONFIG_LABEL, &cattrs, NULL, &config);
+ if (ret)
+ return ret;
+
+ if (usb_config->strs.config_str) {
+ ret = usbg_set_config_string(config, usb_config->strs.lang_code, usb_config->strs.config_str);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; usb_config->funcs[i]; ++i) {
+ usb_func = usb_config->funcs[i];
+
+ /* name("sdb") + NAME_INSTANCE_SEP(".") + instance("default") + '\0' */
+ if (strlen(usb_func->name) + strlen(usb_func->instance) + 2 > sizeof(instance))
+ return -ENAMETOOLONG;
+
+ /* In functionfs, the instance is used in the format "[sdb|mtp].default" instead of "default" */
+ if (usb_func->is_functionfs) {
+ function_type = USBG_F_FFS;
+ snprintf(instance, sizeof(instance), "%s%c%s", usb_func->name, NAME_INSTANCE_SEP, usb_func->instance);
+ } else {
+ function_type = usbg_lookup_function_type(usb_func->name);
+ strncpy(instance, usb_func->instance, sizeof(instance) - 1);
+ instance[sizeof(instance) - 1] = '\0';
+ }
+
+ function = usbg_get_function(cfs_client->gadget, function_type, instance);
+ if (!function) {
+ ret = usbg_create_function(cfs_client->gadget, function_type, instance, NULL, &function);
+ if (ret)
+ return ret;
+
+ /* Setting rndis mac address. This should be done at this point,
+ * since the node host_addr changes to read only after the function
+ * is added to config. */
+ if (usbg_get_function_type(function) == USBG_F_RNDIS)
+ (void)cfs_set_rndis_mac_addr(cfs_client->gadget, function); /* A random value is used if fails */
+
+ if (usbg_get_function_type(function) == USBG_F_FFS) {
+ ret = cfs_prep_ffs_service(usb_func, function);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = usbg_add_config_function(config, NULL, function);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cfs_reconfigure_gadget(struct usb_gadget *gadget)
+{
+ int i;
+ int ret;
+
+ if (!g_cfs_client || !gadget)
+ return -EINVAL;
+
+ /* Verify the gadget and check if function is supported */
+ if (!cfs_is_gadget_supported(gadget))
+ return -ENOTSUP;
+
+ ret = cfs_set_gadget_attrs(g_cfs_client, &gadget->attrs);
+ if (ret)
+ return ret;
+
+ ret = cfs_set_gadget_strs(g_cfs_client, &gadget->strs);
+ if (ret)
+ return ret;
+
+ ret = cfs_cleanup_all_config_and_function(g_cfs_client);
+ if (ret)
+ return ret;
+
+ for (i = 0; gadget->configs[i]; ++i) {
+ ret = cfs_set_gadget_config(g_cfs_client, i + 1, gadget->configs[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cfs_start_stop_service_and_handler(usbg_gadget *gadget, enum cfs_function_service_operation operation)
+{
+ usbg_function *function;
+ struct usb_function *usb_function;
+
+ usbg_for_each_function(function, gadget) {
+ usb_function = cfs_find_usb_function(function);
+ if (!usb_function)
+ continue;
+
+ switch(operation) {
+ case CFS_FUNCTION_SERVICE_START:
+ if (usb_function->handler)
+ usb_function->handler(1);
+
+ /* functionfs service is automatically started by socket activation */
+ if (!usb_function->is_functionfs && usb_function->service)
+ (void)systemd_start_unit_wait_started(usb_function->service, ".service", -1);
+ break;
+
+ case CFS_FUNCTION_SERVICE_STOP:
+ if (!usb_function->is_functionfs && usb_function->service)
+ (void)systemd_stop_unit_wait_stopped(usb_function->service, ".service", -1);
+
+ if (usb_function->handler)
+ usb_function->handler(0);
+ break;
+
+ case CFS_FUNCTION_SERVICE_POST_STOP:
+ if (usb_function->is_functionfs && usb_function->service)
+ (void)systemd_stop_unit_wait_stopped(usb_function->service, ".service", -1);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static int cfs_enable(void)
+{
+ int ret;
+
+ if (!g_cfs_client)
+ return -EINVAL;
+
+ ret = usbg_enable_gadget(g_cfs_client->gadget, g_cfs_client->udc);
+ if (ret)
+ return ret;
+
+ cfs_start_stop_service_and_handler(g_cfs_client->gadget, CFS_FUNCTION_SERVICE_START);
+
+ return 0;
+}
+
+static int cfs_disable(void)
+{
+ int ret;
+
+ if (!g_cfs_client)
+ return -EINVAL;
+
+ cfs_start_stop_service_and_handler(g_cfs_client->gadget, CFS_FUNCTION_SERVICE_STOP);
+
+ ret = usbg_disable_gadget(g_cfs_client->gadget); /* ignore error checking */
+
+ /*
+ * Since functionfs service works with socket activation, you must stop it after disabling gadget.
+ * If usb data may come in after stopping functionfs service and before disabling gadget,
+ * functionfs service wakes up again by socket activation.
+ */
+ cfs_start_stop_service_and_handler(g_cfs_client->gadget, CFS_FUNCTION_SERVICE_POST_STOP);
+
+ return ret;
+}
+
+EXPORT
+int hw_cfs_gadget_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
+{
+ int ret;
+
+ if (!usb_gadget_funcs)
+ return -EINVAL;
+
+ g_cfs_client = calloc(1, sizeof(*g_cfs_client));
+ if (!g_cfs_client)
+ return -ENOMEM;
+
+ ret = usbg_init(CONFIGFS_PATH, &g_cfs_client->ctx);
+ if (ret)
+ goto err_usbg_init;
+
+ g_cfs_client->udc = usbg_get_first_udc(g_cfs_client->ctx);
+ if (!g_cfs_client->udc) {
+ ret = -ENODEV;
+ goto err_no_udc;
+ }
+
+ ret = usbg_create_gadget(g_cfs_client->ctx, CONFIGFS_GADGET_NAME,
+ &default_g_attrs, &default_g_strs, &g_cfs_client->gadget);
+ if (ret)
+ goto err_create_gadget;
+
+ usb_gadget_funcs->enable = cfs_enable;
+ usb_gadget_funcs->disable = cfs_disable;
+ usb_gadget_funcs->reconfigure_gadget = cfs_reconfigure_gadget;
+
+ return 0;
+
+err_create_gadget:
+err_no_udc:
+ usbg_cleanup(g_cfs_client->ctx);
+err_usbg_init:
+ free(g_cfs_client);
+
+ return ret;
+}
+
+EXPORT
+int hw_cfs_gadget_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
+{
+ usbg_function *function;
+ struct usb_function *usb_func;
+
+ if (!usb_gadget_funcs || !g_cfs_client)
+ return -EINVAL;
+
+ usbg_for_each_function(function, g_cfs_client->gadget) {
+ usb_func = cfs_find_usb_function(function);
+ if (!usb_func)
+ continue;
+
+ if (usb_func->is_functionfs && usb_func->service) {
+ (void)systemd_stop_unit_wait_stopped(usb_func->service, ".socket", -1);
+ (void)systemd_stop_unit_wait_stopped(usb_func->service, ".service", -1);
+ }
+ }
+
+ /*
+ * For now we don't check for errors
+ * but we should somehow handle them
+ */
+ usbg_rm_gadget(g_cfs_client->gadget, USBG_RM_RECURSE);
+ usbg_cleanup(g_cfs_client->ctx);
+ free(g_cfs_client);
+
+ return 0;
+}
+
diff --git a/src/usb_gadget/usb_client_common.c b/src/usb_gadget/usb_client_common.c
new file mode 100644
index 0000000..f3f49f1
--- /dev/null
+++ b/src/usb_gadget/usb_client_common.c
@@ -0,0 +1,481 @@
+/*
+ * libdevice-node
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <linux/limits.h>
+
+#include <libsyscommon/dbus-systemd.h>
+#include <hal/device/hal-usb_gadget-interface.h>
+
+#include "usb_gadget.h"
+
+#define MAX_GADGET_STR_LEN 256
+
+#define LEGACY_ROOTPATH "/sys/class/usb_mode/usb0"
+
+/* Device descriptor values */
+#define LEGACY_ID_VENDOR_PATH LEGACY_ROOTPATH"/idVendor"
+#define LEGACY_ID_PRODUCT_PATH LEGACY_ROOTPATH"/idProduct"
+#define LEGACY_BCD_DEVICE_PATH LEGACY_ROOTPATH"/bcdDevice"
+#define LEGACY_CLASS_PATH LEGACY_ROOTPATH"/bDeviceClass"
+#define LEGACY_SUBCLASS_PATH LEGACY_ROOTPATH"/bDeviceSubClass"
+#define LEGACY_PROTOCOL_PATH LEGACY_ROOTPATH"/bDeviceProtocol"
+
+/* Strings */
+#define LEGACY_IMANUFACTURER_PATH LEGACY_ROOTPATH"/iManufacturer"
+#define LEGACY_IPRODUCT_PATH LEGACY_ROOTPATH"/iProduct"
+
+/* Functions in each config */
+#define LEGACY_CONFIG_1_PATH LEGACY_ROOTPATH"/funcs_fconf"
+#define LEGACY_CONFIG_2_PATH LEGACY_ROOTPATH"/funcs_sconf"
+
+/* should be single char */
+#define LEGACY_FUNC_SEP ","
+
+/* ON/OFF switch */
+#define LEGACY_ENABLE_PATH LEGACY_ROOTPATH"/enable"
+#define LEGACY_ENABLE "1"
+#define LEGACY_DISABLE "0"
+
+#ifndef EXPORT
+#define EXPORT __attribute__ ((visibility("default")))
+#endif
+
+// TODO move to common code
+#if 1
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define SHARED_H_BUF_MAX 255
+
+static inline int sys_read_buf(char *file, char *buf, int len)
+{
+ int fd, r;
+
+ if (!file || !buf || len < 0)
+ return -EINVAL;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -ENOENT;
+
+ r = read(fd, buf, len);
+ close(fd);
+ if ((r >= 0) && (r < len))
+ buf[r] = '\0';
+ else
+ return -EIO;
+
+ return 0;
+}
+
+static inline int sys_write_buf(char *file, char *buf)
+{
+ int fd, r;
+
+ if (!file || !buf)
+ return -EINVAL;
+
+ fd = open(file, O_WRONLY);
+ if (fd == -1)
+ return -EPERM;
+
+ r = write(fd, buf, strlen(buf));
+ close(fd);
+ if (r < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static inline int sys_get_str(char *fname, char *str, int len)
+{
+ int r;
+
+ if (!fname || !str || len < 0)
+ return -EINVAL;
+
+ r = sys_read_buf(fname, str, len);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static inline int sys_set_str(char *fname, char *val)
+{
+ int r;
+
+ if (!fname || !val)
+ return -EINVAL;
+
+ r = sys_write_buf(fname, val);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static inline int sys_get_int(char *fname, int *val)
+{
+ char buf[SHARED_H_BUF_MAX];
+ int r;
+
+ if (!fname || !val)
+ return -EINVAL;
+
+ r = sys_read_buf(fname, buf, sizeof(buf));
+ if (r < 0)
+ return r;
+
+ *val = atoi(buf);
+ return 0;
+}
+
+static inline int sys_set_int(char *fname, int val)
+{
+ char buf[SHARED_H_BUF_MAX];
+ int r;
+
+ if (!fname)
+ return -EINVAL;
+
+ snprintf(buf, sizeof(buf), "%d", val);
+ r = sys_write_buf(fname, buf);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+#endif
+
+static bool legacy_is_function_supported(struct usb_function *func)
+{
+ char buf[PATH_MAX];
+
+ snprintf (buf, sizeof(buf), "%s/f_%s", LEGACY_ROOTPATH, func->name);
+ if (access(buf, F_OK))
+ return false;
+
+ return true;
+}
+
+static bool legacy_is_gadget_supported(struct usb_gadget *gadget)
+{
+ int i, j;
+ struct usb_configuration *config;
+
+ if (!gadget || !gadget->configs || !gadget->configs[0] || !gadget->configs[0]->funcs[0])
+ return false;
+
+ if (!gadget->attrs.idVendor || !gadget->attrs.idProduct || !gadget->attrs.bcdDevice)
+ return false;
+
+ /* only strings in US_en are allowed */
+ if (gadget->strs.lang_code != DEFAULT_LANG)
+ return false;
+
+ if (!gadget->strs.manufacturer || !gadget->strs.product)
+ return false;
+
+ for (j = 0; gadget->configs[j]; ++j) {
+ config = gadget->configs[j];
+
+ if (!config->funcs)
+ return false;
+
+ for (i = 0; config->funcs[i]; ++i)
+ if (!legacy_is_function_supported(config->funcs[i]))
+ return false;
+ }
+
+ return true;
+}
+
+/* TODO. Maybe move this to sys ? */
+static int legacy_set_int_hex(char *path, int val)
+{
+ int r;
+ char buf[MAX_GADGET_STR_LEN];
+
+ if (!path)
+ return -EINVAL;
+
+ snprintf(buf, sizeof(buf), "%x", val);
+ r = sys_set_str(path, buf);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int legacy_set_gadget_attrs(struct usb_gadget_attrs *attrs)
+{
+ int ret;
+
+ ret = sys_set_int(LEGACY_CLASS_PATH, attrs->bDeviceClass);
+ if (ret)
+ return ret;
+
+ ret = sys_set_int(LEGACY_SUBCLASS_PATH, attrs->bDeviceSubClass);
+ if (ret)
+ return ret;
+
+ ret = sys_set_int(LEGACY_PROTOCOL_PATH, attrs->bDeviceProtocol);
+ if (ret)
+ return ret;
+
+ ret = legacy_set_int_hex(LEGACY_ID_VENDOR_PATH, attrs->idVendor);
+ if (ret)
+ return ret;
+
+ ret = legacy_set_int_hex(LEGACY_ID_PRODUCT_PATH, attrs->idProduct);
+ if (ret)
+ return ret;
+
+ ret = legacy_set_int_hex(LEGACY_BCD_DEVICE_PATH, attrs->bcdDevice);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int legacy_set_gadget_strs(struct usb_gadget_strings *strs)
+{
+ int ret;
+
+ if (!strs->manufacturer || !strs->product)
+ return -EINVAL;
+
+ ret = sys_set_str(LEGACY_IMANUFACTURER_PATH, strs->manufacturer);
+ if (ret)
+ return ret;
+
+ ret = sys_set_str(LEGACY_IPRODUCT_PATH, strs->product);
+ if (ret)
+ return ret;
+
+ /* The serial is written by the slp gadget kernel driver */
+
+ return 0;
+}
+
+static int legacy_set_gadget_config(char *cpath,
+ struct usb_configuration *config)
+{
+ char buf[MAX_GADGET_STR_LEN];
+ int left = sizeof(buf);
+ char *pos = buf;
+ int ret;
+ int i;
+
+ if (!config) {
+ buf[0] = '\n';
+ buf[1] = '\0';
+ goto empty_config;
+ }
+
+ for (i = 0; config->funcs[i]; ++i) {
+ ret = snprintf(pos, left, "%s" LEGACY_FUNC_SEP,
+ config->funcs[i]->name);
+ if (ret >= left)
+ return -EOVERFLOW;
+
+ pos += ret;
+ left -= ret;
+ }
+
+ /* eliminate last separator */
+ *(pos - 1) = '\0';
+
+empty_config:
+ return sys_set_str(cpath, buf);
+}
+
+static int legacy_reconfigure_gadget(struct usb_gadget *gadget)
+{
+ int ret;
+
+ if (!gadget)
+ return -EINVAL;
+
+ /* Verify the gadget and check if function is supported */
+ if (!legacy_is_gadget_supported(gadget))
+ return -ENOTSUP;
+
+ ret = legacy_set_gadget_attrs(&gadget->attrs);
+ if (ret)
+ return ret;
+
+ ret = legacy_set_gadget_strs(&gadget->strs);
+ if (ret)
+ return ret;
+
+ ret = legacy_set_gadget_config(LEGACY_CONFIG_1_PATH, gadget->configs[0]);
+ if (ret)
+ return ret;
+
+ ret = legacy_set_gadget_config(LEGACY_CONFIG_2_PATH, gadget->configs[1]);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void legacy_start_stop_service_and_handler(bool start)
+{
+ int i;
+ int ret;
+ int n_func = 0;
+ char *ptr;
+ char *begin;
+ char *fname;
+ char *sep = LEGACY_FUNC_SEP;
+ char buf[MAX_GADGET_STR_LEN];
+ struct usb_function *func;
+ struct usb_function *funcs[USB_FUNCTION_IDX_MAX];
+
+ /* SLP gadget uses two USB configuration.
+ * (/sys/class/usb_mode/usb0/funcs_fconf and /sys/class/usb_mode/usb0/funcs_sconf)
+ *
+ * One usb function can be included in two configurations simultaneously.
+ * In this situation, a handler associated with function can be called twice for a usb function.
+ * To prevent duplicate calls,
+ * Collect all functions and remove duplicates and process them.
+ */
+
+ /* First configuration */
+ ret = sys_get_str(LEGACY_CONFIG_1_PATH, buf, sizeof(buf));
+ if (ret)
+ goto second_configuration;
+
+ /* Caution: buf ends with '\n' */
+ ptr = strchr(buf, '\n');
+ if (ptr)
+ *ptr = 0;
+
+ begin = buf;
+ for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
+ func = find_usb_function_by_name(fname);
+ if (!func)
+ continue;
+
+ for (i = 0; i < n_func; i++)
+ if (funcs[i] == func)
+ continue;
+
+ if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
+ break;
+
+ funcs[n_func] = func;
+ n_func++;
+ }
+
+ /* Second configuration */
+second_configuration:
+ ret = sys_get_str(LEGACY_CONFIG_2_PATH, buf, sizeof(buf));
+ if (ret)
+ return;
+
+ /* Caution: buf ends with '\n' */
+ ptr = strchr(buf, '\n');
+ if (ptr)
+ *ptr = 0;
+
+ begin = buf;
+ for (fname = strsep(&begin, sep); fname; fname = strsep(&begin, sep)) {
+ func = find_usb_function_by_name(fname);
+ if (!func)
+ continue;
+
+ for (i = 0; i < n_func; i++)
+ if (funcs[i] == func)
+ continue;
+
+ if(n_func >= USB_FUNCTION_IDX_MAX) /* What happen */
+ break;
+
+ funcs[n_func] = func;
+ n_func++;
+ }
+
+ for (i = 0; i < n_func; i++) {
+ if (start) {
+ if (funcs[i]->handler)
+ funcs[i]->handler(1);
+
+ if (funcs[i]->service)
+ (void)systemd_start_unit_wait_started(funcs[i]->service, ".service", -1);
+ } else {
+ if (funcs[i]->service)
+ (void)systemd_stop_unit_wait_stopped(funcs[i]->service, ".service", -1);
+
+ if (funcs[i]->handler)
+ funcs[i]->handler(0);
+ }
+ }
+}
+
+static int legacy_enable(void)
+{
+ int ret;
+
+ ret = sys_set_str(LEGACY_ENABLE_PATH, LEGACY_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ legacy_start_stop_service_and_handler(true);
+
+ return 0;
+}
+
+static int legacy_disable(void)
+{
+ legacy_start_stop_service_and_handler(false);
+
+ return sys_set_str(LEGACY_ENABLE_PATH, LEGACY_DISABLE);
+}
+
+EXPORT
+int hw_legacy_gadget_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
+{
+ if (!usb_gadget_funcs)
+ return -EINVAL;
+
+ /* check if slp usb gadget exists */
+ if (access("/sys/class/usb_mode/usb0/enable", F_OK))
+ return -ENOENT;
+
+ usb_gadget_funcs->enable = legacy_enable;
+ usb_gadget_funcs->enable = legacy_disable;
+ usb_gadget_funcs->reconfigure_gadget = legacy_reconfigure_gadget;
+
+ return 0;
+}
+
+EXPORT
+int hw_legacy_gadget_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
+{
+ return 0;
+}
diff --git a/src/usb_gadget/usb_gadget.h b/src/usb_gadget/usb_gadget.h
new file mode 100644
index 0000000..ac14373
--- /dev/null
+++ b/src/usb_gadget/usb_gadget.h
@@ -0,0 +1,26 @@
+/*
+ * libdevice-node
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __HW_USB_GADGET_H__
+#define __HW_USB_GADGET_H__
+
+struct usb_function *find_usb_function_by_id(int id);
+struct usb_function *find_usb_function_by_name(const char *name);
+struct usb_function *find_usb_function_by_name_instance(const char *name, const char *instance);
+
+#endif
diff --git a/src/usb_gadget/usb_gadget_common.c b/src/usb_gadget/usb_gadget_common.c
new file mode 100644
index 0000000..734f933
--- /dev/null
+++ b/src/usb_gadget/usb_gadget_common.c
@@ -0,0 +1,396 @@
+/*
+ * libdevice-node
+ *
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+//include <hw/board.h> TODO move to common
+#include <libsyscommon/dbus-systemd.h>
+#include <hal/device/hal-usb_gadget-interface.h>
+
+#include "usb_gadget.h"
+
+#ifndef EXPORT
+#define EXPORT __attribute__ ((visibility("default")))
+#endif
+
+static struct usb_function *_available_funcs[];
+
+struct usb_function *find_usb_function_by_id(int id);
+
+static void simple_cleanup_config(struct usb_configuration *config)
+{
+ if (!config)
+ return;
+
+ free(config->strs.config_str);
+
+ if (config->funcs)
+ free(config->funcs);
+
+ free(config);
+}
+
+static void cleanup_gadget(struct usb_gadget *gadget)
+{
+ int i;
+
+ if (!gadget)
+ return;
+
+ free(gadget->strs.manufacturer);
+ free(gadget->strs.product);
+ free(gadget->strs.serial);
+
+ if (gadget->configs) {
+ for (i = 0; gadget->configs[i]; ++i)
+ simple_cleanup_config(gadget->configs[i]);
+
+ free(gadget->configs);
+ }
+
+ free(gadget);
+}
+
+static int alloc_default_config(struct usb_configuration **_config)
+{
+ struct usb_configuration *config;
+
+ config = calloc(1, sizeof(*config));
+ if (!config)
+ return -ENOMEM;
+
+ config->attrs.bmAttributs = DEFAULT_BMATTRIBUTES;
+ config->attrs.MaxPower = DEFAULT_MAX_POWER;
+
+ /* TODO. Here is where to set the string used in config of configfs */
+
+ *_config = config;
+
+ return 0;
+}
+
+static int get_device_serial(char **out)
+{
+#if 0 // TODO fix build error
+ struct hw_info *info;
+ struct hw_common *common;
+ struct hw_board *board;
+ int ret;
+
+ if (hw_get_info(BOARD_HARDWARE_DEVICE_ID, (const struct hw_info **)&info))
+ return -ENOENT;
+
+ ret = info->open(info, NULL, &common);
+ if (ret < 0)
+ return ret;
+
+ board = container_of(common, struct hw_board, common);
+ return board->get_device_serial(out);
+#else
+ return -1;
+#endif
+}
+
+static int alloc_default_gadget(struct usb_gadget **_gadget)
+{
+ struct usb_gadget *gadget;
+ struct usb_configuration **configs;
+
+ gadget = calloc(1, sizeof(*gadget));
+ if (!gadget)
+ goto out;
+
+ gadget->attrs.idVendor = DEFAULT_VID;
+ gadget->attrs.idProduct = DEFAULT_PID;
+ gadget->attrs.bcdDevice = DEFAULT_BCD_DEVICE;
+
+ gadget->strs.lang_code = DEFAULT_LANG;
+ gadget->strs.manufacturer = strdup(DEFAULT_MANUFACTURER);
+ gadget->strs.product = strdup(DEFAULT_PRODUCT);
+ if (get_device_serial(&gadget->strs.serial) < 0)
+ gadget->strs.serial = strdup(DEFAULT_SERIAL);
+
+ if (!gadget->strs.manufacturer || !gadget->strs.product || !gadget->strs.serial)
+ goto free_strs;
+
+ /* slp-gadget use max 2 confiuration and NULL termination */
+ configs = calloc(3, sizeof(*configs));
+ if (!configs)
+ goto free_strs;
+
+ gadget->configs = configs;
+ *_gadget = gadget;
+
+ return 0;
+
+free_strs:
+ free(gadget->strs.manufacturer);
+ free(gadget->strs.product);
+ free(gadget->strs.serial);
+ free(gadget);
+out:
+ return -ENOMEM;
+}
+
+static int id_to_gadget(struct usb_gadget_id *gadget_id, struct usb_gadget **_gadget)
+{
+ int ret;
+ int i, j;
+ int n_configs;
+ struct usb_gadget *gadget;
+ int functions[2][sizeof(gadget_id->function_mask)*8]; /* zero terminates */
+
+ if (!gadget_id || !_gadget)
+ return -EINVAL;
+
+ ret = alloc_default_gadget(&gadget);
+ if (ret)
+ goto out;
+
+ /*
+ * Currently all gadgets use inly single configuration but
+ * slp-gadget is capable to handle two of them
+ *
+ * Order of interfaces in configuration is significant
+ * so in this switch we sort our functions in a correct order
+ */
+ switch (gadget_id->function_mask) {
+ /* MTP, ACM, SDB */
+ case USB_FUNCTION_MTP | USB_FUNCTION_ACM:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_MTP;
+ functions[0][1] = USB_FUNCTION_ACM;
+ functions[0][2] = 0;
+ gadget->attrs.idProduct = 0x6860;
+ break;
+
+ case USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_MTP;
+ functions[0][1] = USB_FUNCTION_ACM;
+ functions[0][2] = USB_FUNCTION_SDB;
+ functions[0][3] = 0;
+ gadget->attrs.idProduct = 0x6860;
+ break;
+
+ /* DIAG */
+ case USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_DIAG:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_MTP;
+ functions[0][1] = USB_FUNCTION_ACM;
+ functions[0][2] = USB_FUNCTION_SDB;
+ functions[0][3] = USB_FUNCTION_DIAG;
+ functions[0][4] = 0;
+ gadget->attrs.idProduct = 0x6860;
+ break;
+
+ /* RNDIS */
+ case USB_FUNCTION_RNDIS:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_RNDIS;
+ functions[0][1] = 0;
+ gadget->attrs.idProduct = 0x6863;
+ break;
+
+ case USB_FUNCTION_RNDIS | USB_FUNCTION_DIAG:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_RNDIS;
+ functions[0][1] = USB_FUNCTION_DIAG;
+ functions[0][2] = 0;
+ gadget->attrs.idProduct = 0x6864;
+ break;
+
+ case USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_RNDIS:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_RNDIS;
+ functions[0][1] = USB_FUNCTION_ACM;
+ functions[0][2] = USB_FUNCTION_SDB;
+ functions[0][3] = 0;
+ gadget->attrs.idProduct = 0x6864;
+ break;
+
+ /* RMNET */
+ case USB_FUNCTION_DIAG | USB_FUNCTION_RMNET:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_DIAG;
+ functions[0][1] = USB_FUNCTION_RMNET;
+ functions[0][2] = 0;
+ gadget->attrs.idProduct = 0x685d;
+ break;
+
+ /* DM */
+ case USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_DM:
+ n_configs = 1;
+ functions[0][0] = USB_FUNCTION_ACM;
+ functions[0][1] = USB_FUNCTION_SDB;
+ functions[0][2] = USB_FUNCTION_DM;
+ functions[0][3] = 0;
+ gadget->attrs.idProduct = 0x6860;
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto free_gadget;
+ };
+
+ for (j = 0; j < n_configs; ++j) {
+ int n_funcs_in_config;
+ struct usb_configuration *config;
+
+ for (i = 0; functions[j][i]; ++i);
+ n_funcs_in_config = i;
+
+ ret = alloc_default_config(&config);
+ if (ret)
+ goto free_configs;
+
+ gadget->configs[j] = config;
+ config->funcs = calloc(n_funcs_in_config + 1, sizeof(void *));
+ if (!config->funcs)
+ goto free_configs;
+
+ for (i = 0; functions[j][i]; ++i) {
+ config->funcs[i] = find_usb_function_by_id(functions[j][i]);
+ if (!config->funcs[i])
+ goto free_configs;
+ }
+ }
+
+ *_gadget = gadget;
+
+ return 0;
+
+free_configs:
+free_gadget:
+ cleanup_gadget(gadget);
+out:
+ return ret;
+}
+
+void rndis_handler(int enable)
+{
+ if (enable)
+ (void)systemd_start_unit_wait_started("rndis.service", NULL, -1);
+ else
+ (void)systemd_stop_unit_wait_stopped("rndis.service", NULL, -1);
+}
+
+#define DEFINE_USB_FUNCTION(_id, _name, _is_functionfs, _service, _handler) \
+ static struct usb_function _##_name##_function = { \
+ .id = _id, \
+ .name = #_name, \
+ .instance = "default", \
+ .is_functionfs = _is_functionfs, \
+ .service = _service, \
+ .handler = _handler, \
+ }
+
+DEFINE_USB_FUNCTION(USB_FUNCTION_MTP, mtp, 1, "mtp-responder", NULL);
+DEFINE_USB_FUNCTION(USB_FUNCTION_ACM, acm, 0, "data-router", NULL);
+DEFINE_USB_FUNCTION(USB_FUNCTION_SDB, sdb, 1, "sdbd", NULL);
+DEFINE_USB_FUNCTION(USB_FUNCTION_RNDIS, rndis, 0, "sshd", rndis_handler);
+DEFINE_USB_FUNCTION(USB_FUNCTION_DIAG, diag, 0, NULL, NULL);
+DEFINE_USB_FUNCTION(USB_FUNCTION_CONN_GADGET, conn_gadget, 0, NULL, NULL);
+DEFINE_USB_FUNCTION(USB_FUNCTION_DM, dm, 0, NULL, NULL);
+DEFINE_USB_FUNCTION(USB_FUNCTION_RMNET, rmnet, 0, NULL, NULL);
+
+#undef DEFINE_USB_FUNCTION
+
+/* Caution: index order of arrary is important, because simple_translator_open() uses it. */
+static struct usb_function *_available_funcs[] = {
+ [USB_FUNCTION_IDX_MTP] = &_mtp_function,
+ [USB_FUNCTION_IDX_ACM] = &_acm_function,
+ [USB_FUNCTION_IDX_SDB] = &_sdb_function,
+ [USB_FUNCTION_IDX_RNDIS] = &_rndis_function,
+ [USB_FUNCTION_IDX_DIAG] = &_diag_function,
+ [USB_FUNCTION_IDX_CONN_GADGET] = &_conn_gadget_function,
+ [USB_FUNCTION_IDX_DM] = &_dm_function,
+ [USB_FUNCTION_IDX_RMNET] = &_rmnet_function,
+ [USB_FUNCTION_IDX_MAX] = NULL /* An indicator to end the array */
+};
+
+struct usb_function *find_usb_function_by_id(int id)
+{
+ int i;
+
+ for (i = 0; _available_funcs[i]; i++)
+ if (_available_funcs[i]->id == id)
+ return _available_funcs[i];
+
+ return NULL;
+}
+
+struct usb_function *find_usb_function_by_name(const char *name)
+{
+ int i;
+
+ if(!name || !name[0])
+ return NULL;
+
+ for (i = 0; _available_funcs[i]; i++)
+ if (!strcmp(name, _available_funcs[i]->name))
+ return _available_funcs[i];
+
+ return NULL;
+}
+
+struct usb_function *find_usb_function_by_name_instance(const char *name, const char *instance)
+{
+ int i;
+
+ if(!name || !name[0] || !instance || !instance[0])
+ return NULL;
+
+ for (i = 0; _available_funcs[i]; ++i)
+ if (!strcmp(name, _available_funcs[i]->name) && !strcmp(instance, _available_funcs[i]->instance))
+ return _available_funcs[i];
+
+ return NULL;
+}
+
+EXPORT
+int simple_translator_open(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
+{
+ if (!usb_gadget_funcs)
+ return -EINVAL;
+
+ usb_gadget_funcs->id_to_gadget = id_to_gadget;
+ usb_gadget_funcs->cleanup_gadget = cleanup_gadget;
+
+ /* Use mtp-responder-dummy.socket when there is no mtp-responser.socket.
+ *
+ * The mtp-responder.socket is special in the configfs environment.
+ * If mtp-responder.socket is missing, gadget configuration will fail.
+ * As a result, all usb operations do not work properly.
+ * So in environments that mtp doesn't support, use dummy mtp.
+ */
+ if (access("/usr/lib/systemd/system/mtp-responder.socket", F_OK)) {
+ _available_funcs[USB_FUNCTION_IDX_MTP]->service = "mtp-responder-dummy";
+ }
+
+ return 0;
+}
+
+EXPORT
+int simple_translator_close(hal_backend_usb_gadget_funcs *usb_gadget_funcs)
+{
+ return 0;
+}