| // Copyright 2017 The Fuchsia Authors. All riusghts reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <ddk/binding.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/protocol/usb.h> |
| #include <ddk/protocol/usb-dci.h> |
| |
| #include <zircon/types.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <threads.h> |
| |
| #include "usb-virtual-bus.h" |
| |
| typedef struct usb_virtual_device { |
| zx_device_t* mxdev; |
| usb_virtual_bus_t* bus; |
| usb_dci_interface_t dci_intf; |
| } usb_virtual_device_t; |
| |
| void usb_virtual_device_control(usb_virtual_device_t* device, iotxn_t* txn) { |
| usb_protocol_data_t* data = iotxn_pdata(txn, usb_protocol_data_t); |
| usb_setup_t* setup = &data->setup; |
| zx_status_t status; |
| size_t length = le16toh(setup->wLength); |
| size_t actual = 0; |
| |
| printf("usb_virtual_device_control type: 0x%02X req: %d value: %d index: %d length: %zu\n", |
| setup->bmRequestType, setup->bRequest, le16toh(setup->wValue), le16toh(setup->wIndex), |
| length); |
| |
| if (device->dci_intf.ops) { |
| void* buffer = NULL; |
| |
| if (length > 0) { |
| iotxn_mmap(txn, &buffer); |
| } |
| |
| status = usb_dci_control(&device->dci_intf, setup, buffer, length, &actual); |
| } else { |
| status = ZX_ERR_UNAVAILABLE; |
| } |
| |
| iotxn_complete(txn, status, actual); |
| } |
| |
| static zx_status_t device_set_interface(void* ctx, usb_dci_interface_t* dci_intf) { |
| usb_virtual_device_t* device = ctx; |
| memcpy(&device->dci_intf, dci_intf, sizeof(device->dci_intf)); |
| return ZX_OK; |
| } |
| |
| static zx_status_t device_config_ep(void* ctx, usb_endpoint_descriptor_t* ep_desc, |
| usb_ss_ep_comp_descriptor_t* ss_comp_desc) { |
| return ZX_OK; |
| } |
| |
| static zx_status_t device_disable_ep(void* ctx, uint8_t ep_addr) { |
| return ZX_OK; |
| } |
| |
| static zx_status_t device_set_enabled(void* ctx, bool enabled) { |
| usb_virtual_device_t* device = ctx; |
| return usb_virtual_bus_set_device_enabled(device->bus, enabled); |
| } |
| |
| static zx_status_t device_ep_set_stall(void* ctx, uint8_t ep_address) { |
| usb_virtual_device_t* device = ctx; |
| return usb_virtual_bus_set_stall(device->bus, ep_address, true); |
| } |
| |
| static zx_status_t device_ep_clear_stall(void* ctx, uint8_t ep_address) { |
| usb_virtual_device_t* device = ctx; |
| return usb_virtual_bus_set_stall(device->bus, ep_address, false); |
| } |
| |
| usb_dci_protocol_ops_t virtual_device_protocol = { |
| .set_interface = device_set_interface, |
| .config_ep = device_config_ep, |
| .disable_ep = device_disable_ep, |
| .set_enabled = device_set_enabled, |
| .ep_set_stall = device_ep_set_stall, |
| .ep_clear_stall = device_ep_clear_stall, |
| }; |
| |
| static zx_status_t virt_device_open(void* ctx, zx_device_t** dev_out, uint32_t flags) { |
| printf("device_open\n"); |
| return ZX_OK; |
| } |
| |
| static void virt_device_iotxn_queue(void* ctx, iotxn_t* txn) { |
| usb_virtual_device_t* device = ctx; |
| iotxn_queue(device->bus->mxdev, txn); |
| } |
| |
| static void virt_device_unbind(void* ctx) { |
| printf("virt_device_unbind\n"); |
| usb_virtual_device_t* device = ctx; |
| device_remove(device->mxdev); |
| } |
| |
| static void virt_device_release(void* ctx) { |
| usb_virtual_device_t* device = ctx; |
| free(device); |
| } |
| |
| static zx_protocol_device_t usb_virtual_device_device_proto = { |
| .version = DEVICE_OPS_VERSION, |
| .open = virt_device_open, |
| .iotxn_queue = virt_device_iotxn_queue, |
| .unbind = virt_device_unbind, |
| .release = virt_device_release, |
| }; |
| |
| zx_status_t usb_virtual_device_add(usb_virtual_bus_t* bus, usb_virtual_device_t** out_device) { |
| usb_virtual_device_t* device = calloc(1, sizeof(usb_virtual_device_t)); |
| if (!device) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| device->bus = bus; |
| |
| device_add_args_t args = { |
| .version = DEVICE_ADD_ARGS_VERSION, |
| .name = "usb-virtual-device", |
| .ctx = device, |
| .ops = &usb_virtual_device_device_proto, |
| .proto_id = ZX_PROTOCOL_USB_DCI, |
| .proto_ops = &virtual_device_protocol, |
| }; |
| |
| zx_status_t status = device_add(device->bus->mxdev, &args, &device->mxdev); |
| |
| if (status != ZX_OK) { |
| free(device); |
| return status; |
| } |
| |
| *out_device = device; |
| return ZX_OK; |
| } |
| |
| void usb_virtual_device_release(usb_virtual_device_t* device) { |
| device_remove(device->mxdev); |
| } |