/* * 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 libusbvirt * @{ */ /** @file * Standard control request handlers. */ #include "private.h" #include #include #include /** Helper for replying to control read transfer from virtual USB device. * * This function takes care of copying data to answer buffer taking care * of buffer sizes properly. * * @param setup_packet The setup packet. * @param data Data buffer to write to. * @param act_size Where to write actual size of returned data. * @param actual_data Data to be returned. * @param actual_data_size Size of answer data (@p actual_data) in bytes. */ void usbvirt_control_reply_helper(const usb_device_request_setup_packet_t *setup_packet, uint8_t *data, size_t *act_size, void *actual_data, size_t actual_data_size) { size_t expected_size = setup_packet->length; if (expected_size < actual_data_size) { actual_data_size = expected_size; } memcpy(data, actual_data, actual_data_size); if (act_size != NULL) { *act_size = actual_data_size; } } /** GET_DESCRIPTOR handler. */ static int req_get_descriptor(usbvirt_device_t *device, const usb_device_request_setup_packet_t *setup_packet, uint8_t *data, size_t *act_size) { uint8_t type = setup_packet->value_high; uint8_t index = setup_packet->value_low; /* * Standard device descriptor. */ if ((type == USB_DESCTYPE_DEVICE) && (index == 0)) { if (device->descriptors && device->descriptors->device) { usbvirt_control_reply_helper(setup_packet, data, act_size, device->descriptors->device, device->descriptors->device->length); return EOK; } else { return EFORWARD; } } /* * Configuration descriptor together with interface, endpoint and * class-specific descriptors. */ if (type == USB_DESCTYPE_CONFIGURATION) { if (!device->descriptors) { return EFORWARD; } if (index >= device->descriptors->configuration_count) { return EFORWARD; } /* Copy the data. */ usbvirt_device_configuration_t *config = &device->descriptors ->configuration[index]; uint8_t *all_data = malloc(config->descriptor->total_length); if (all_data == NULL) { return ENOMEM; } uint8_t *ptr = all_data; memcpy(ptr, config->descriptor, config->descriptor->length); ptr += config->descriptor->length; size_t i; for (i = 0; i < config->extra_count; i++) { usbvirt_device_configuration_extras_t *extra = &config->extra[i]; memcpy(ptr, extra->data, extra->length); ptr += extra->length; } usbvirt_control_reply_helper(setup_packet, data, act_size, all_data, config->descriptor->total_length); free(all_data); return EOK; } return EFORWARD; } static int req_set_address(usbvirt_device_t *device, const usb_device_request_setup_packet_t *setup_packet, uint8_t *data, size_t *act_size) { uint16_t new_address = setup_packet->value; uint16_t zero1 = setup_packet->index; uint16_t zero2 = setup_packet->length; if ((zero1 != 0) || (zero2 != 0)) { return EINVAL; } if (new_address > 127) { return EINVAL; } device->address = new_address; return EOK; } static int req_set_configuration(usbvirt_device_t *device, const usb_device_request_setup_packet_t *setup_packet, uint8_t *data, size_t *act_size) { uint16_t configuration_value = setup_packet->value; uint16_t zero1 = setup_packet->index; uint16_t zero2 = setup_packet->length; if ((zero1 != 0) || (zero2 != 0)) { return EINVAL; } /* * Configuration value is 1 byte information. */ if (configuration_value > 255) { return EINVAL; } /* * Do nothing when in default state. According to specification, * this is not specified. */ if (device->state == USBVIRT_STATE_DEFAULT) { return EOK; } usbvirt_device_state_t new_state; if (configuration_value == 0) { new_state = USBVIRT_STATE_ADDRESS; } else { // FIXME: check that this configuration exists new_state = USBVIRT_STATE_CONFIGURED; } if (device->ops && device->ops->state_changed) { device->ops->state_changed(device, device->state, new_state); } device->state = new_state; return EOK; } /** Standard request handlers. */ usbvirt_control_request_handler_t library_handlers[] = { { .req_direction = USB_DIRECTION_OUT, .req_recipient = USB_REQUEST_RECIPIENT_DEVICE, .req_type = USB_REQUEST_TYPE_STANDARD, .request = USB_DEVREQ_SET_ADDRESS, .name = "SetAddress", .callback = req_set_address }, { .req_direction = USB_DIRECTION_IN, .req_recipient = USB_REQUEST_RECIPIENT_DEVICE, .req_type = USB_REQUEST_TYPE_STANDARD, .request = USB_DEVREQ_GET_DESCRIPTOR, .name = "GetDescriptor", .callback = req_get_descriptor }, { .req_direction = USB_DIRECTION_OUT, .req_recipient = USB_REQUEST_RECIPIENT_DEVICE, .req_type = USB_REQUEST_TYPE_STANDARD, .request = USB_DEVREQ_SET_CONFIGURATION, .name = "SetConfiguration", .callback = req_set_configuration }, { .callback = NULL } }; /** * @} */