blob: 3b322872e665a2f35e87883cef9dddecfcc9a7bc [file] [log] [blame]
// 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/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/usb-bus.h>
#include <ddk/protocol/usb-hci.h>
#include <ddk/protocol/usb.h>
#include <zircon/listnode.h>
#include <zircon/types.h>
#include <sync/completion.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include "usb-virtual-bus.h"
#define CLIENT_SLOT_ID 0
#define CLIENT_HUB_ID 0
#define CLIENT_SPEED USB_SPEED_HIGH
typedef struct usb_virtual_host {
zx_device_t* zxdev;
usb_virtual_bus_t* bus;
usb_bus_interface_t bus_intf;
mtx_t lock;
completion_t completion;
bool connected;
} usb_virtual_host_t;
static void virt_host_request_queue(void* ctx, usb_request_t* req) {
usb_virtual_host_t* host = ctx;
usb_virtual_bus_host_queue(host->bus, req);
}
static void virt_host_set_bus_interface(void* ctx, usb_bus_interface_t* bus_intf) {
usb_virtual_host_t* host = ctx;
if (bus_intf) {
memcpy(&host->bus_intf, bus_intf, sizeof(host->bus_intf));
mtx_lock(&host->lock);
bool connected = host->connected;
mtx_unlock(&host->lock);
if (connected) {
usb_bus_add_device(&host->bus_intf, CLIENT_SLOT_ID, CLIENT_HUB_ID, CLIENT_SPEED);
}
} else {
memset(&host->bus_intf, 0, sizeof(host->bus_intf));
}
}
static size_t virt_host_get_max_device_count(void* ctx) {
return 1;
}
static zx_status_t virt_host_enable_ep(void* ctx, uint32_t device_id,
usb_endpoint_descriptor_t* ep_desc,
usb_ss_ep_comp_descriptor_t* ss_comp_desc, bool enable) {
return ZX_OK;
}
static uint64_t virt_host_get_frame(void* ctx) {
return 0;
}
zx_status_t virt_host_config_hub(void* ctx, uint32_t device_id, usb_speed_t speed, bool multi_tt,
usb_hub_descriptor_t* descriptor) {
return ZX_OK;
}
zx_status_t virt_host_hub_device_added(void* ctx, uint32_t hub_address, int port,
usb_speed_t speed) {
return ZX_OK;
}
zx_status_t virt_host_hub_device_removed(void* ctx, uint32_t hub_address, int port) {
return ZX_OK;
}
zx_status_t virt_host_reset_endpoint(void* ctx, uint32_t device_id, uint8_t ep_address) {
return ZX_ERR_NOT_SUPPORTED;
}
size_t virt_host_get_max_transfer_size(void* ctx, uint32_t device_id, uint8_t ep_address) {
return 65536;
}
static usb_hci_protocol_ops_t virtual_host_protocol = {
.request_queue = virt_host_request_queue,
.set_bus_interface = virt_host_set_bus_interface,
.get_max_device_count = virt_host_get_max_device_count,
.enable_endpoint = virt_host_enable_ep,
.get_current_frame = virt_host_get_frame,
.configure_hub = virt_host_config_hub,
.hub_device_added = virt_host_hub_device_added,
.hub_device_removed = virt_host_hub_device_removed,
.reset_endpoint = virt_host_reset_endpoint,
.get_max_transfer_size = virt_host_get_max_transfer_size,
};
static void virt_host_unbind(void* ctx) {
printf("virt_host_unbind\n");
usb_virtual_host_t* host = ctx;
device_remove(host->zxdev);
}
static void virt_host_release(void* ctx) {
printf("host_release\n");
usb_virtual_host_t* host = ctx;
free(host);
}
static zx_protocol_device_t virt_host_device_proto = {
.version = DEVICE_OPS_VERSION,
.unbind = virt_host_unbind,
.release = virt_host_release,
};
zx_status_t usb_virtual_host_add(usb_virtual_bus_t* bus, usb_virtual_host_t** out_host) {
usb_virtual_host_t* host = calloc(1, sizeof(usb_virtual_host_t));
if (!host) {
return ZX_ERR_NO_MEMORY;
}
mtx_init(&host->lock, mtx_plain);
completion_reset(&host->completion);
host->bus = bus;
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = "usb-virtual-host",
.ctx = host,
.ops = &virt_host_device_proto,
.proto_id = ZX_PROTOCOL_USB_HCI,
.proto_ops = &virtual_host_protocol,
};
zx_status_t status = device_add(host->bus->zxdev, &args, &host->zxdev);
if (status != ZX_OK) {
free(host);
return status;
}
*out_host = host;
return ZX_OK;
}
void usb_virtual_host_release(usb_virtual_host_t* host) {
device_remove(host->zxdev);
}
void usb_virtual_host_set_connected(usb_virtual_host_t* host, bool connected) {
mtx_lock(&host->lock);
bool connect = connected && !host->connected;
bool disconnect = !connected && host->connected;
host->connected = connected;
mtx_unlock(&host->lock);
if (host->bus_intf.ops) {
if (connect) {
usb_bus_add_device(&host->bus_intf, CLIENT_SLOT_ID, CLIENT_HUB_ID, CLIENT_SPEED);
} else if (disconnect) {
usb_bus_remove_device(&host->bus_intf, CLIENT_SLOT_ID);
}
}
}