diff options
-rw-r--r-- | hw/usb_gadget.h | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/hw/usb_gadget.h b/hw/usb_gadget.h new file mode 100644 index 0000000..2db9566 --- /dev/null +++ b/hw/usb_gadget.h @@ -0,0 +1,298 @@ +/* + * 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__ + +#include <hw/common.h> + +#include <stddef.h> +#include <stdlib.h> +#include <errno.h> + +/** + * The id of this device + */ +#define USB_GADGET_DEVICE_ID "usb_gadget" + +/** + * The version of this device + */ +#define USB_GADGET_DEVICE_VERSION MAKE_VERSION(0,1) + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +#define _HELPER_Y(x) ((x) & -(x)) + +/* Count number of trailing zeros using Dean Gaudet's algorithm */ +#define _HELPER_CTZ(mask) \ + ((_HELPER_Y(mask) ? 0 : 1) + \ + ((_HELPER_Y(mask) & 0x0000FFFF) ? 0 : 16) + \ + ((_HELPER_Y(mask) & 0x00FF00FF) ? 0 : 8) + \ + ((_HELPER_Y(mask) & 0x0F0F0F0F) ? 0 : 4) + \ + ((_HELPER_Y(mask) & 0x33333333) ? 0 : 2) + \ + ((_HELPER_Y(mask) & 0x55555555) ? 0 : 1)) + +/* Function IDX in array is number of trailing zeros */ +#define FUNC_IDX_FROM_MASK(mask) _HELPER_CTZ(mask) + +typedef enum { + USB_FUNCTION_GROUP_SIMPLE, + USB_FUNCTION_GROUP_WITH_SERVICE, +} usb_function_group_e; + +struct usb_function { + int function_group; + int id; + char *name; + char *instance; + + int (*clone)(struct usb_function *func, struct usb_function **_clone); + void (*free_func)(struct usb_function *func); +}; + +struct usb_function_with_service { + struct usb_function func; + const char *service; +}; + +struct usb_configuration_attributes { + uint8_t bmAttributs; + int MaxPower; +}; + +struct usb_configuration_strings { + uint16_t lang_code; + char *config_str; +}; + +struct usb_configuration { + struct usb_configuration_attributes attrs; + struct usb_configuration_strings *strs; + struct usb_function **funcs; +}; + +struct usb_gadget_attrs { + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; +}; + +struct usb_gadget_strings { + uint16_t lang_code; + char *manufacturer; + char *product; + char *serial; +}; + +struct usb_gadget { + struct usb_gadget_attrs attrs; + struct usb_gadget_strings *strs; + struct usb_function **funcs; + struct usb_configuration **configs; +}; + +typedef enum { + USB_FUNCTION_NONE = 0, + USB_FUNCTION_MTP = 1 << 0, + USB_FUNCTION_ACM = 1 << 1, + USB_FUNCTION_SDB = 1 << 2, + USB_FUNCTION_RNDIS = 1 << 3, + USB_FUNCTION_DIAG = 1 << 4, + USB_FUNCTION_CONN_GADGET = 1 << 5, + USB_FUNCTION_DM = 1 << 6, + USB_FUNCTION_RMNET = 1 << 7, +} usb_function_e; + +static void free_simple_func_content(struct usb_function *func) +{ + free(func->name); + free(func->instance); +} + +static void free_simple_func(struct usb_function *func) +{ + free_simple_func_content(func); + free(func); +} + +static int clone_simple_func_to(struct usb_function *func, + struct usb_function *other) +{ + *other = *func; + other->name = strdup(func->name); + other->instance = strdup(func->instance); + + if (!other->name || !other->instance) + goto free_strs; + + return 0; +free_strs: + free(other->name); + free(other->instance); + return -ENOMEM; +} + +static int clone_simple_func(struct usb_function *func, + struct usb_function **clone) +{ + struct usb_function *other; + int ret; + + if (!func || !clone) + return -EINVAL; + + other = malloc(sizeof(*other)); + if (!other) + goto out; + + ret = clone_simple_func_to(func, other); + if (ret) + goto free_other; + + *clone = other; + return 0; +free_other: + free(other); +out: + return -ENOMEM; +} + +#define DEFINE_SIMPLE_USB_FUNCTION(_id, _name) \ + static struct usb_function _##_name##_function = { \ + .function_group = USB_FUNCTION_GROUP_SIMPLE, \ + .id = _id, \ + .name = #_name, \ + .instance = "default", \ + .free_func = free_simple_func, \ + .clone = clone_simple_func, \ + } + +DEFINE_SIMPLE_USB_FUNCTION(USB_FUNCTION_ACM, acm); +DEFINE_SIMPLE_USB_FUNCTION(USB_FUNCTION_RNDIS, rndis); +DEFINE_SIMPLE_USB_FUNCTION(USB_FUNCTION_DIAG, diag); +DEFINE_SIMPLE_USB_FUNCTION(USB_FUNCTION_RMNET, rmnet); +DEFINE_SIMPLE_USB_FUNCTION(USB_FUNCTION_DM, dm); +DEFINE_SIMPLE_USB_FUNCTION(USB_FUNCTION_CONN_GADGET, conn_gadget); + +#undef DEFINE_SIMPLE_USB_FUNCTION + +static void free_func_with_service(struct usb_function *func) +{ + struct usb_function_with_service *fws; + + fws = container_of(func, struct usb_function_with_service, func); + + free_simple_func_content(func); + free(fws->service); + free(fws); +} + +static int clone_func_with_service(struct usb_function *func, + struct usb_function **clone) +{ + struct usb_function_with_service *fws; + struct usb_function_with_service *other; + int ret; + + if (!func || !clone) + return -EINVAL; + + other = malloc(sizeof(*other)); + if (!other) + goto out; + + ret = clone_simple_func_to(func, &other->func); + if (ret) + goto free_other; + + fws = container_of(func, struct usb_function_with_service, func); + if (fws->service) { + other->service = strdup(fws->service); + if (!other->service) + goto free_content; + } else { + other->service = NULL; + } + + *clone = &other->func; + return 0; +free_content: + free_simple_func_content(&other->func); +free_other: + free(other); +out: + return -ENOMEM; +} + +#define DEFINE_USB_FUNCTION_WITH_SERVICE(_id, _name) \ + static struct usb_function_with_service _##_name##_function = { \ + .func = { \ + .function_group = USB_FUNCTION_GROUP_WITH_SERVICE, \ + .id = _id, \ + .name = #_name, \ + .instance = "default", \ + .free_func = free_func_with_service, \ + .clone = clone_func_with_service, \ + }, \ + .service = NULL, \ + } + +DEFINE_USB_FUNCTION_WITH_SERVICE(USB_FUNCTION_SDB, sdb); +DEFINE_USB_FUNCTION_WITH_SERVICE(USB_FUNCTION_MTP, mtp); + +#define MAKE_FUNC_AVAILABLE(_name, _vname) \ + [FUNC_IDX_FROM_MASK(USB_FUNCTION_##_name)] = &_##_vname##_function + +#define MAKE_FUNC_WS_AVAILABLE(_name, _vname) \ + [FUNC_IDX_FROM_MASK(USB_FUNCTION_##_name)] = &_##_vname##_function.func + +static struct usb_function *_available_funcs[] = { + MAKE_FUNC_WS_AVAILABLE(MTP, mtp), + MAKE_FUNC_AVAILABLE(ACM, acm), + MAKE_FUNC_WS_AVAILABLE(SDB, sdb), + MAKE_FUNC_AVAILABLE(RNDIS, rndis), + MAKE_FUNC_AVAILABLE(DIAG, diag), + MAKE_FUNC_AVAILABLE(CONN_GADGET, conn_gadget), + MAKE_FUNC_AVAILABLE(DM, dm), + MAKE_FUNC_AVAILABLE(RMNET, rmnet), +}; + +#undef MAKE_FUNC_AVAILABLE + +struct usb_gadget_id { + unsigned int function_mask; +}; + +struct usb_gadget_translator { + struct hw_common common; + + int (*id_to_gadget)(struct usb_gadget_id *gadget_id, + struct usb_gadget **gadget); + + void (*cleanup_gadget)(struct usb_gadget *gadget); +}; + +#endif |