/* * Copyright (c) 2011 Vojtech Horky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup drvusbmid * @{ */ /** * @file * Helper functions. */ #include #include #include #include #include #include #include #include "usbmid.h" /** Get host controller handle by calling the parent usb_device_t. * * @param[in] fun Device function the operation is running on. * @param[out] handle Storage for the host controller handle. * @return Error code. */ static int usb_iface_device_hc_handle(ddf_fun_t *fun, devman_handle_t *handle) { assert(handle); assert(fun); usb_device_t *usb_dev = usb_device_get(ddf_fun_get_dev(fun)); assert(usb_dev); *handle = usb_device_hc_handle(usb_dev); return EOK; } static int usb_iface_device_handle(ddf_fun_t *fun, devman_handle_t *handle) { assert(fun); assert(handle); usb_device_t *usb_dev = usb_device_get(ddf_fun_get_dev(fun)); assert(usb_dev); *handle = usb_device_get_devman_handle(usb_dev); return EOK; } /** Get USB device address by calling the parent usb_device_t. * * @param[in] fun Device function the operation is running on. * @param[in] handle Devman handle of USB device we want address of. * @param[out] address Storage for USB address of device with handle @p handle. * @return Error code. */ static int usb_iface_device_address(ddf_fun_t *fun, usb_address_t *address) { assert(address); assert(fun); usb_device_t *usb_dev = usb_device_get(ddf_fun_get_dev(fun)); assert(usb_dev); *address = usb_device_address(usb_dev); return EOK; } /** Callback for DDF USB interface. */ static int usb_iface_iface(ddf_fun_t *fun, int *iface_no) { usbmid_interface_t *iface = ddf_fun_data_get(fun); assert(iface); if (iface_no != NULL) { *iface_no = iface->interface_no; } return EOK; } static int usb_iface_register_endpoint(ddf_fun_t *fun, usb_endpoint_t ep, usb_transfer_type_t type, usb_direction_t dir, size_t mps, unsigned inter) { usb_device_t *usb_dev = usb_device_get(ddf_fun_get_dev(fun)); assert(usb_dev); async_exch_t *exch = usb_device_bus_exchange_begin(usb_dev); if (!exch) return ENOMEM; const int ret = usb_register_endpoint(exch, ep, type, dir, mps, inter); usb_device_bus_exchange_end(exch); return ret; } static int usb_iface_unregister_endpoint(ddf_fun_t *fun, usb_endpoint_t ep, usb_direction_t dir) { usb_device_t *usb_dev = usb_device_get(ddf_fun_get_dev(fun)); assert(usb_dev); async_exch_t *exch = usb_device_bus_exchange_begin(usb_dev); if (!exch) return ENOMEM; const int ret = usb_unregister_endpoint(exch, ep, dir); usb_device_bus_exchange_end(exch); return ret; } /** DDF interface of the child - interface function. */ static usb_iface_t child_usb_iface = { .get_hc_handle = usb_iface_device_hc_handle, .get_my_address = usb_iface_device_address, .get_device_handle = usb_iface_device_handle, .get_my_interface = usb_iface_iface, .register_endpoint = usb_iface_register_endpoint, .unregister_endpoint = usb_iface_unregister_endpoint, }; /** Operations for children - interface functions. */ static ddf_dev_ops_t child_device_ops = { .interfaces[USB_DEV_IFACE] = &child_usb_iface }; int usbmid_interface_destroy(usbmid_interface_t *mid_iface) { assert(mid_iface); assert_link_not_used(&mid_iface->link); const int ret = ddf_fun_unbind(mid_iface->fun); if (ret != EOK) { return ret; } ddf_fun_destroy(mid_iface->fun); return EOK; } /** Spawn new child device from one interface. * * @param parent Parent MID device. * @param iface Interface information. * @param device_descriptor Device descriptor. * @param interface_descriptor Interface descriptor. * @return Error code. */ int usbmid_spawn_interface_child(usb_device_t *parent, usbmid_interface_t **iface_ret, const usb_standard_device_descriptor_t *device_descriptor, const usb_standard_interface_descriptor_t *interface_descriptor) { ddf_fun_t *child = NULL; char *child_name = NULL; int rc; /* * Name is class name followed by interface number. * The interface number shall provide uniqueness while the * class name something humanly understandable. */ rc = asprintf(&child_name, "%s%hhu", usb_str_class(interface_descriptor->interface_class), interface_descriptor->interface_number); if (rc < 0) { return ENOMEM; } /* Create the device. */ child = usb_device_ddf_fun_create(parent, fun_inner, child_name); free(child_name); if (child == NULL) { return ENOMEM; } match_id_list_t match_ids; init_match_ids(&match_ids); rc = usb_device_create_match_ids_from_interface(device_descriptor, interface_descriptor, &match_ids); if (rc != EOK) { ddf_fun_destroy(child); return rc; } list_foreach(match_ids.ids, link) { match_id_t *match_id = list_get_instance(link, match_id_t, link); rc = ddf_fun_add_match_id(child, match_id->id, match_id->score); if (rc != EOK) { clean_match_ids(&match_ids); ddf_fun_destroy(child); return rc; } } clean_match_ids(&match_ids); ddf_fun_set_ops(child, &child_device_ops); usbmid_interface_t *iface = ddf_fun_data_alloc(child, sizeof(*iface)); iface->fun = child; iface->interface_no = interface_descriptor->interface_number; link_initialize(&iface->link); rc = ddf_fun_bind(child); if (rc != EOK) { /* This takes care of match_id deallocation as well. */ ddf_fun_destroy(child); return rc; } *iface_ret = iface; return EOK; } /** * @} */