diff options
author | Krzysztof Opasiak <k.opasiak@samsung.com> | 2016-12-16 13:10:30 +0100 |
---|---|---|
committer | Krzysztof Opasiak <k.opasiak@samsung.com> | 2017-03-03 14:02:27 +0100 |
commit | b656ce0845abba50b71f5b5fad989c82bf02cd16 (patch) | |
tree | 3be682dcb5f911410a8dc63d181df2afcbf7d97a | |
parent | 784e693fe5b397516b8b8521386205512e989c70 (diff) | |
download | libusbg-b656ce0845abba50b71f5b5fad989c82bf02cd16.tar.gz libusbg-b656ce0845abba50b71f5b5fad989c82bf02cd16.tar.bz2 libusbg-b656ce0845abba50b71f5b5fad989c82bf02cd16.zip |
libusbgx: Add support for HID function
HID function has been ported to ConfigFS interface quite long
time ago so let's add support for it also in libusbgx.
Signed-off-by: Krzysztof Opasiak <k.opasiak@samsung.com>
-rw-r--r-- | include/usbg/function/hid.h | 300 | ||||
-rw-r--r-- | include/usbg/usbg.h | 1 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/function/hid.c | 349 | ||||
-rw-r--r-- | src/usbg.c | 2 |
5 files changed, 653 insertions, 1 deletions
diff --git a/include/usbg/function/hid.h b/include/usbg/function/hid.h new file mode 100644 index 0000000..83ae51a --- /dev/null +++ b/include/usbg/function/hid.h @@ -0,0 +1,300 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef USBG_FUNCTION_HID__ +#define USBG_FUNCTION_HID__ + +#include <usbg/usbg.h> + +#include <malloc.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct usbg_f_hid; +typedef struct usbg_f_hid usbg_f_hid; + +struct usbg_f_hid_report_desc { + char *desc; + unsigned int len; +}; + +struct usbg_f_hid_attrs { + dev_t dev; + unsigned int protocol; + struct usbg_f_hid_report_desc report_desc; + unsigned int report_length; + unsigned int subclass; +}; + +enum usbg_f_hid_attr { + USBG_F_HID_ATTR_MIN = 0, + USBG_F_HID_DEV = USBG_F_HID_ATTR_MIN, + USBG_F_HID_PROTOCOL, + USBG_F_HID_REPORT_DESC, + USBG_F_HID_REPORT_LENGTH, + USBG_F_HID_SUBCLASS, + USBG_F_HID_ATTR_MAX +}; + +union usbg_f_hid_attr_val { + dev_t dev; + unsigned int protocol; + struct usbg_f_hid_report_desc report_desc; + unsigned int report_length; + unsigned int subclass; +}; + +/** + * @brief Cast from generic function to hid function + * @param[in] f function to be converted to hid funciton. + * Function should be one of type hid. + * @return Converted hid function or NULL if function hasn't suitable type + */ +usbg_f_hid *usbg_to_hid_function(usbg_function *f); + +/** + * @brief Cast form hid function to generic one + * @param[in] hf function to be converted to generic one + * @return Generic usbg function + */ +usbg_function *usbg_from_hid_function(usbg_f_hid *hf); + +/** + * @brief Get attributes of given hid function + * @param[in] hf Pointer to hid function + * @param[out] attrs Structure to be filled with data + * @return 0 on success usbg_error if error occurred. + */ +int usbg_f_hid_get_attrs(usbg_f_hid *hf, + struct usbg_f_hid_attrs *attrs); + +/** + * @brief Set attributes of given hid function + * @param[in] hf Pointer to hid function + * @param[in] attrs to be set + * @return 0 on success usbg_error if error occurred. + */ +int usbg_f_hid_set_attrs(usbg_f_hid *hf, + const struct usbg_f_hid_attrs *attrs); + + +/** + * @brief Cleanup HID report descriptor structure after usage + * @param[in] report_desc to be cleaned up + */ +static inline void usbg_f_hid_cleanup_report_desc( + struct usbg_f_hid_report_desc *report_desc) +{ + if (report_desc->desc) { + free(report_desc->desc); + /* Just for safety */ + report_desc->desc = NULL; + } +} + +/** + * @brief Cleanup attributes structure after usage + * @param[in] attrs to be cleaned up + */ +static inline void usbg_f_hid_cleanup_attrs(struct usbg_f_hid_attrs *attrs) +{ + if (attrs) + usbg_f_hid_cleanup_report_desc(&attrs->report_desc); +} + +/** + * @brief Get the value of single attribute + * @param[in] hf Pointer to hid function + * @param[in] attr Code of attribute which value should be taken + * @param[out] val Current value of this attribute + * @return 0 on success usbg_error if error occurred. + */ +int usbg_f_hid_get_attr_val(usbg_f_hid *hf, enum usbg_f_hid_attr attr, + union usbg_f_hid_attr_val *val); + +/** + * @brief Set the value of single attribute + * @param[in] hf Pointer to hid function + * @param[in] attr Code of attribute which value should be set + * @param[in] val Value of attribute which should be set + * @return 0 on success usbg_error if error occurred. + */ +int usbg_f_hid_set_attr_val(usbg_f_hid *hf, enum usbg_f_hid_attr attr, + union usbg_f_hid_attr_val val); + +/** + * @brief Get the minor and major of corresponding character device + * @param[in] hf Pointer to hid function + * @param[out] dev Minor and major of corresponding + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_get_dev(usbg_f_hid *hf, dev_t *dev) +{ + return usbg_f_hid_get_attr_val(hf, USBG_F_HID_DEV, + (union usbg_f_hid_attr_val *)dev); +} + +/** + * @brief Get HID protocol code + * @param[in] hf Pointer to hid function + * @param[out] protocol code + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_get_protocol(usbg_f_hid *hf, + unsigned int *protocol) +{ + return usbg_f_hid_get_attr_val(hf, USBG_F_HID_PROTOCOL, + (union usbg_f_hid_attr_val *)protocol); +} + +/** + * @brief Set HID protocol code + * @param[in] hf Pointer to hid function + * @param[out] protocol code + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_set_protocol(usbg_f_hid *hf, + unsigned int protocol) +{ + return usbg_f_hid_set_attr_val(hf, USBG_F_HID_PROTOCOL, + (union usbg_f_hid_attr_val)protocol); +} + +/** + * @brief Get HID report descriptor + * @param[in] hf Pointer to hid function + * @param[out] report_desc Report descriptor + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_get_report_desc(usbg_f_hid *hf, + struct usbg_f_hid_report_desc *report_desc) +{ + return usbg_f_hid_get_attr_val(hf, USBG_F_HID_REPORT_DESC, + (union usbg_f_hid_attr_val *)report_desc); +} + +/** + * @brief Set HID report descriptor + * @param[in] hf Pointer to hid function + * @param[out] report_desc Report descriptor + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_set_report_desc(usbg_f_hid *hf, + struct usbg_f_hid_report_desc report_desc) +{ + return usbg_f_hid_set_attr_val(hf, USBG_F_HID_REPORT_DESC, + (union usbg_f_hid_attr_val)report_desc); +} + +/** + * @brief Get HID report descriptor + * @param[in] hf Pointer to hid function + * @param[out] report_desc Report descriptor + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_get_report_desc_raw(usbg_f_hid *hf, + char **desc, + unsigned int *len) +{ + struct usbg_f_hid_report_desc report_desc; + int ret; + + ret = usbg_f_hid_get_attr_val(hf, USBG_F_HID_REPORT_DESC, + (union usbg_f_hid_attr_val *)&report_desc); + if (ret != USBG_SUCCESS) + return ret; + + *desc = report_desc.desc; + *len = report_desc.len; + + return USBG_SUCCESS; +} + +/** + * @brief Set HID report descriptor + * @param[in] hf Pointer to hid function + * @param[out] report_desc Report descriptor + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_set_report_desc_raw(usbg_f_hid *hf, + char *desc, + unsigned int len) +{ + struct usbg_f_hid_report_desc report_desc = { + .desc = desc, + .len = len, + }; + + return usbg_f_hid_set_attr_val(hf, USBG_F_HID_REPORT_DESC, + (union usbg_f_hid_attr_val)report_desc); +} + +/** + * @brief Get HID report length + * @param[in] hf Pointer to hid function + * @param[out] report_length Length of HID report + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_get_report_length(usbg_f_hid *hf, + unsigned int *report_length) +{ + return usbg_f_hid_get_attr_val(hf, USBG_F_HID_REPORT_LENGTH, + (union usbg_f_hid_attr_val *)report_length); +} + +/** + * @brief Set HID report length + * @param[in] hf Pointer to hid function + * @param[out] report_length Length of HID report + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_set_report_length(usbg_f_hid *hf, + unsigned int report_length) +{ + return usbg_f_hid_set_attr_val(hf, USBG_F_HID_REPORT_LENGTH, + (union usbg_f_hid_attr_val)report_length); +} + +/** + * @brief Get HID subclass code + * @param[in] hf Pointer to hid function + * @param[out] subclass code + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_get_subclass(usbg_f_hid *hf, + unsigned int *subclass) +{ + return usbg_f_hid_get_attr_val(hf, USBG_F_HID_SUBCLASS, + (union usbg_f_hid_attr_val *)subclass); +} + +/** + * @brief Set HID subclass code + * @param[in] hf Pointer to hid function + * @param[out] subclass code + * @return 0 on success usbg_error if error occurred. + */ +static inline int usbg_f_hid_set_subclass(usbg_f_hid *hf, + unsigned int subclass) +{ + return usbg_f_hid_set_attr_val(hf, USBG_F_HID_SUBCLASS, + (union usbg_f_hid_attr_val)subclass); +} + +#ifdef __cplusplus +} +#endif + +#endif /* USBG_FUNCTION_MIDI__ */ diff --git a/include/usbg/usbg.h b/include/usbg/usbg.h index 31d9862..692c188 100644 --- a/include/usbg/usbg.h +++ b/include/usbg/usbg.h @@ -190,6 +190,7 @@ typedef enum USBG_F_MASS_STORAGE, USBG_F_MIDI, USBG_F_LOOPBACK, + USBG_F_HID, USBG_FUNCTION_TYPE_MAX, } usbg_function_type; diff --git a/src/Makefile.am b/src/Makefile.am index 3f18281..98aebcd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ AUTOMAKE_OPTIONS = std-options subdir-objects lib_LTLIBRARIES = libusbgx.la -libusbgx_la_SOURCES = usbg.c usbg_error.c usbg_common.c function/ether.c function/ffs.c function/midi.c function/ms.c function/phonet.c function/serial.c function/loopback.c +libusbgx_la_SOURCES = usbg.c usbg_error.c usbg_common.c function/ether.c function/ffs.c function/midi.c function/ms.c function/phonet.c function/serial.c function/loopback.c function/hid.c if TEST_GADGET_SCHEMES libusbgx_la_SOURCES += usbg_schemes_libconfig.c else diff --git a/src/function/hid.c b/src/function/hid.c new file mode 100644 index 0000000..a60333a --- /dev/null +++ b/src/function/hid.c @@ -0,0 +1,349 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include "usbg/usbg.h" +#include "usbg/usbg_internal.h" +#include "usbg/function/hid.h" + +#include <malloc.h> +#include <assert.h> +#ifdef HAS_LIBCONFIG +#include <libconfig.h> +#endif + +struct usbg_f_hid { + struct usbg_function func; +}; + +#define HID_DEC_ATTR(_name) \ + { \ + .name = #_name, \ + .ro = false, \ + .offset = offsetof(struct usbg_f_hid_attrs, _name), \ + .get = usbg_get_dec, \ + .set = usbg_set_dec, \ + .import = usbg_get_config_node_int, \ + .export = usbg_set_config_node_int, \ + } + +#define HID_DEV_ATTR_RO(_name) \ + { \ + .name = #_name, \ + .ro = true, \ + .offset = offsetof(struct usbg_f_hid_attrs, _name), \ + .get = usbg_get_dev, \ + .export = usbg_set_config_node_dev, \ + } + +static int hid_get_report(const char *path, const char *name, const char *attr, + void *val) +{ + struct usbg_f_hid_report_desc *report_desc = val; + char buf[USBG_MAX_FILE_SIZE]; + int ret; + + ret = usbg_read_buf(path, name, attr, buf); + if (ret < 0) + return ret; + + report_desc->len = ret; + if (ret == 0) { + report_desc->desc = NULL; + } else { + report_desc->desc = malloc(report_desc->len); + if (!report_desc->desc) + return USBG_ERROR_NO_MEM; + memcpy(report_desc->desc, buf, report_desc->len); + } + + return 0; +} + +static int hid_set_report(const char *path, const char *name, const char *attr, + void *val) +{ + struct usbg_f_hid_report_desc *report_desc = val; + char *buf = report_desc->desc; + int len = report_desc->len; + int ret; + + if (len == 0) { + buf = ""; + len = 1; + } + + ret = usbg_write_buf(path, name, attr, buf, len); + if (ret > 0) + ret = USBG_SUCCESS; + + return ret; +} + +static int hid_get_config_node_report(config_setting_t *root, + const char *node_name, void *val) +{ + struct usbg_f_hid_report_desc *report_desc = val; + char *buf; + int tmp, i; + config_setting_t *list_node, *node; + int len; + int ret; + + list_node = config_setting_get_member(root, node_name); + if (!list_node) + return 0; + + if (!config_setting_is_list(list_node)) + return USBG_ERROR_INVALID_TYPE; + + len = config_setting_length(list_node); + + buf = malloc(len * sizeof(char)); + if (!buf) + return USBG_ERROR_NO_MEM; + + for (i = 0; i < len; ++i) { + node = config_setting_get_elem(list_node, i); + assert(node); + + if (!usbg_config_is_int(node)) { + ret = USBG_ERROR_INVALID_TYPE; + goto free_buf; + } + + tmp = config_setting_get_int(node); + if (tmp < 0 || tmp > 255) { + ret = USBG_ERROR_INVALID_VALUE; + goto free_buf; + } + buf[i] = (char)tmp; + } + + report_desc->desc = buf; + report_desc->len = len; + + return 1; + +free_buf: + free(buf); + return ret; +} + +static int hid_set_config_node_report(config_setting_t *root, + const char *node_name, void *val) +{ + struct usbg_f_hid_report_desc *report_desc = val; + config_setting_t *node; + int i; + int ret = 0; + + node = config_setting_add(root, node_name, CONFIG_TYPE_LIST); + if (!node) + return USBG_ERROR_NO_MEM; + + for (i = 0; i < report_desc->len; ++i) { + int tmp = report_desc->desc[i]; + ret = usbg_set_config_node_int_hex(node, NULL, &tmp); + if (ret) + return ret; + } + + return 0; +} + +#define HID_RD_ATTR(_name) \ + { \ + .name = #_name, \ + .ro = false, \ + .offset = offsetof(struct usbg_f_hid_attrs, _name), \ + .get = hid_get_report, \ + .set = hid_set_report, \ + .import = hid_get_config_node_report, \ + .export = hid_set_config_node_report, \ + } + +struct { + const char *name; + bool ro; + size_t offset; + usbg_attr_get_func get; + usbg_attr_set_func set; + usbg_import_node_func import; + usbg_export_node_func export; +} hid_attr[USBG_F_HID_ATTR_MAX] = { + [USBG_F_HID_DEV] = HID_DEV_ATTR_RO(dev), + [USBG_F_HID_PROTOCOL] = HID_DEC_ATTR(protocol), + [USBG_F_HID_REPORT_DESC] = HID_RD_ATTR(report_desc), + [USBG_F_HID_REPORT_LENGTH] = HID_DEC_ATTR(report_length), + [USBG_F_HID_SUBCLASS] = HID_DEC_ATTR(subclass), +}; + +#undef HID_DEC_ATTR +#undef HID_DEV_ATTR_RO +#undef HID_RD_ATTR + +GENERIC_ALLOC_INST(hid, struct usbg_f_hid, func); + +GENERIC_FREE_INST(hid, struct usbg_f_hid, func); + +static int hid_set_attrs(struct usbg_function *f, void *f_attrs) +{ + return usbg_f_hid_set_attrs(usbg_to_hid_function(f), f_attrs); +} + +static int hid_get_attrs(struct usbg_function *f, void *f_attrs) +{ + return usbg_f_hid_get_attrs(usbg_to_hid_function(f), f_attrs); +} + +static void hid_cleanup_attrs(struct usbg_function *f, void *f_attrs) +{ + usbg_f_hid_cleanup_attrs(f_attrs); +} + +#ifdef HAS_LIBCONFIG + +static int hid_libconfig_import(struct usbg_function *f, + config_setting_t *root) +{ + struct usbg_f_hid *hf = usbg_to_hid_function(f); + union usbg_f_hid_attr_val val; + int i; + int ret = 0; + + for (i = USBG_F_HID_ATTR_MIN; i < USBG_F_HID_ATTR_MAX; ++i) { + if (hid_attr[i].ro) + continue; + + ret = hid_attr[i].import(root, hid_attr[i].name, &val); + /* node not found */ + if (ret == 0) + continue; + /* error */ + if (ret < 0) + break; + + ret = usbg_f_hid_set_attr_val(hf, i, val); + if (ret) + break; + } + + return ret; +} + +static int hid_libconfig_export(struct usbg_function *f, + config_setting_t *root) +{ + struct usbg_f_hid *hf = usbg_to_hid_function(f); + union usbg_f_hid_attr_val val; + int i; + int ret = 0; + + for (i = USBG_F_HID_ATTR_MIN; i < USBG_F_HID_ATTR_MAX; ++i) { + ret = usbg_f_hid_get_attr_val(hf, i, &val); + if (ret) + break; + + ret = hid_attr[i].export(root, hid_attr[i].name, &val); + if (ret) + break; + } + + return ret; +} + +#endif /* HAS_LIBCONFIG */ + +struct usbg_function_type usbg_f_type_hid = { + .name = "hid", + .alloc_inst = hid_alloc_inst, + .free_inst = hid_free_inst, + .set_attrs = hid_set_attrs, + .get_attrs = hid_get_attrs, + .cleanup_attrs = hid_cleanup_attrs, + +#ifdef HAS_LIBCONFIG + .import = hid_libconfig_import, + .export = hid_libconfig_export, +#endif +}; + +/* API implementation */ + +usbg_f_hid *usbg_to_hid_function(usbg_function *f) +{ + return f->ops == &usbg_f_type_hid ? + container_of(f, struct usbg_f_hid, func) : NULL; +} + +usbg_function *usbg_from_hid_function(usbg_f_hid *hf) +{ + return &hf->func; +} + +int usbg_f_hid_get_attrs(usbg_f_hid *hf, + struct usbg_f_hid_attrs *attrs) +{ + int i; + int ret = 0; + + for (i = USBG_F_HID_ATTR_MIN; i < USBG_F_HID_ATTR_MAX; ++i) { + ret = usbg_f_hid_get_attr_val(hf, i, + (union usbg_f_hid_attr_val *) + ((char *)attrs + + hid_attr[i].offset)); + if (ret) + break; + } + + return ret; + +} + +int usbg_f_hid_set_attrs(usbg_f_hid *hf, + const struct usbg_f_hid_attrs *attrs) +{ + int i; + int ret = 0; + + for (i = USBG_F_HID_ATTR_MIN; i < USBG_F_HID_ATTR_MAX; ++i) { + if (hid_attr[i].ro) + continue; + + ret = usbg_f_hid_set_attr_val(hf, i, + *(union usbg_f_hid_attr_val *) + ((char *)attrs + + hid_attr[i].offset)); + if (ret) + break; + } + + return ret; + +} + +int usbg_f_hid_get_attr_val(usbg_f_hid *hf, enum usbg_f_hid_attr attr, + union usbg_f_hid_attr_val *val) +{ + return hid_attr[attr].get(hf->func.path, hf->func.name, + hid_attr[attr].name, val); +} + +int usbg_f_hid_set_attr_val(usbg_f_hid *hf, enum usbg_f_hid_attr attr, + union usbg_f_hid_attr_val val) +{ + return hid_attr[attr].ro ? + USBG_ERROR_INVALID_PARAM : + hid_attr[attr].set(hf->func.path, hf->func.name, + hid_attr[attr].name, &val); +} + @@ -50,6 +50,7 @@ extern struct usbg_function_type usbg_f_type_midi; extern struct usbg_function_type usbg_f_type_ms; extern struct usbg_function_type usbg_f_type_phonet; extern struct usbg_function_type usbg_f_type_loopback; +extern struct usbg_function_type usbg_f_type_hid; /** * @var function_types @@ -69,6 +70,7 @@ struct usbg_function_type* function_types[] = { [USBG_F_MASS_STORAGE] = &usbg_f_type_ms, [USBG_F_PHONET] = &usbg_f_type_phonet, [USBG_F_LOOPBACK] = &usbg_f_type_loopback, + [USBG_F_HID] = &usbg_f_type_hid, }; ARRAY_SIZE_SENTINEL(function_types, USBG_FUNCTION_TYPE_MAX); |