/* * Copyright (c) 2010 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 drvusbvhc * @{ */ /** @file * @brief Virtual device management (implementation). */ #include #include #include #include #include #include #include #include #include #include "devices.h" #include "hub.h" #include "hub/virthub.h" #include "vhcd.h" #define list_foreach(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) static LIST_INITIALIZE(devices); /** Create virtual device. * * @param phone Callback phone. * @param id Device id. * @return New device. * @retval NULL Out of memory. */ virtdev_connection_t *virtdev_add_device(int phone, sysarg_t id) { virtdev_connection_t *dev = (virtdev_connection_t *) malloc(sizeof(virtdev_connection_t)); if (dev == NULL) { return NULL; } dev->phone = phone; dev->id = id; list_append(&dev->link, &devices); virthub_connect_device(&virtual_hub_device, dev); return dev; } /** Find virtual device by id. * * @param id Device id. * @return Device with given id. * @retval NULL No such device. */ virtdev_connection_t *virtdev_find(sysarg_t id) { link_t *pos; list_foreach(pos, &devices) { virtdev_connection_t *dev = list_get_instance(pos, virtdev_connection_t, link); if (dev->id == id) { return dev; } } return NULL; } /** Destroy virtual device. */ void virtdev_destroy_device(virtdev_connection_t *dev) { virthub_disconnect_device(&virtual_hub_device, dev); list_remove(&dev->link); free(dev); } /** Send data to all connected devices. * * @param transaction Transaction to be sent over the bus. */ usb_transaction_outcome_t virtdev_send_to_all(transaction_t *transaction) { /* For easier debugging. */ switch (transaction->type) { case USBVIRT_TRANSACTION_SETUP: case USBVIRT_TRANSACTION_OUT: transaction->actual_len = transaction->len; break; case USBVIRT_TRANSACTION_IN: transaction->actual_len = 0; break; default: assert(false && "unreachable branch in switch()"); } usb_transaction_outcome_t outcome = USB_OUTCOME_BABBLE; link_t *pos; list_foreach(pos, &devices) { virtdev_connection_t *dev = list_get_instance(pos, virtdev_connection_t, link); if (!virthub_is_device_enabled(&virtual_hub_device, dev)) { continue; } ipc_call_t answer_data; sysarg_t answer_rc; aid_t req; int rc = EOK; int method = IPC_M_USBVIRT_TRANSACTION_SETUP; switch (transaction->type) { case USBVIRT_TRANSACTION_SETUP: method = IPC_M_USBVIRT_TRANSACTION_SETUP; break; case USBVIRT_TRANSACTION_IN: method = IPC_M_USBVIRT_TRANSACTION_IN; break; case USBVIRT_TRANSACTION_OUT: method = IPC_M_USBVIRT_TRANSACTION_OUT; break; } req = async_send_3(dev->phone, method, transaction->target.address, transaction->target.endpoint, transaction->len, &answer_data); if (transaction->len > 0) { if (transaction->type == USBVIRT_TRANSACTION_IN) { rc = async_data_read_start(dev->phone, transaction->buffer, transaction->len); } else { rc = async_data_write_start(dev->phone, transaction->buffer, transaction->len); } } if (rc != EOK) { async_wait_for(req, NULL); } else { async_wait_for(req, &answer_rc); transaction->actual_len = IPC_GET_ARG1(answer_data); rc = (int)answer_rc; } /* * If at least one device was able to accept this * transaction and process it, we can announce success. */ if (rc == EOK) { outcome = USB_OUTCOME_OK; } } /* * Send the data to the virtual hub as well * (if the address matches). */ if (virtual_hub_device.address == transaction->target.address) { size_t tmp; dprintf(1, "sending `%s' transaction to hub", usbvirt_str_transaction_type(transaction->type)); switch (transaction->type) { case USBVIRT_TRANSACTION_SETUP: virtual_hub_device.transaction_setup( &virtual_hub_device, transaction->target.endpoint, transaction->buffer, transaction->len); break; case USBVIRT_TRANSACTION_IN: virtual_hub_device.transaction_in( &virtual_hub_device, transaction->target.endpoint, transaction->buffer, transaction->len, &tmp); transaction->actual_len = tmp; break; case USBVIRT_TRANSACTION_OUT: virtual_hub_device.transaction_out( &virtual_hub_device, transaction->target.endpoint, transaction->buffer, transaction->len); break; } dprintf(4, "transaction on hub processed..."); outcome = USB_OUTCOME_OK; } /* * TODO: maybe screw some transactions to get more * real-life image. */ return outcome; } /** * @} */