[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[tyndur-devel] [PATCH 17/22] cdi/usb: Implementierung der CDI-Bibliothek
+ Implementierung der CDI-Bibliothek für CDI.usb für týndur
Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
src/modules/cdi/lib/cdi.c | 81 +++++++++
src/modules/cdi/lib/usb.c | 403 +++++++++++++++++++++++++++++++++++++++++
src/modules/cdi/lib/usb_dd.c | 173 ++++++++++++++++++
src/modules/cdi/lib/usb_hcd.c | 413 ++++++++++++++++++++++++++++++++++++++++++
src/modules/include/usb-ipc.h | 103 +++++++++++
5 files changed, 1173 insertions(+)
create mode 100644 src/modules/cdi/lib/usb.c
create mode 100644 src/modules/cdi/lib/usb_dd.c
create mode 100644 src/modules/cdi/lib/usb_hcd.c
create mode 100644 src/modules/include/usb-ipc.h
diff --git a/src/modules/cdi/lib/cdi.c b/src/modules/cdi/lib/cdi.c
index 11b3009..fa8a217 100644
--- a/src/modules/cdi/lib/cdi.c
+++ b/src/modules/cdi/lib/cdi.c
@@ -23,11 +23,20 @@
#include "cdi/pci.h"
#include "cdi/scsi.h"
#include "cdi/storage.h"
+#include "cdi/usb.h"
+#include "cdi/usb_hcd.h"
extern void cdi_storage_driver_register(struct cdi_storage_driver* driver);
extern void cdi_audio_driver_register(struct cdi_audio_driver* driver);
+extern void cdi_usb_driver_register(struct cdi_usb_driver* driver);
extern void cdi_tyndur_net_device_init(struct cdi_device* device);
+extern void cdi_osdep_handle_usb_device(struct cdi_usb_bus_device_pattern* p);
+
+extern void cdi_osdep_provide_hc(struct cdi_usb_hc* hc);
+
+extern void cdi_osdep_set_up_usb_dd_ipc(struct cdi_driver* drv);
+
void cdi_osdep_new_device(struct cdi_driver* drv, struct cdi_device* dev);
static list_t* drivers = NULL;
@@ -219,6 +228,10 @@ static void cdi_tyndur_run_drivers(void)
}
}
+ if (driver->bus == CDI_USB) {
+ cdi_osdep_set_up_usb_dd_ipc(driver);
+ }
+
/* Netzwerk ist schon initialisiert */
if (driver->type != CDI_NETWORK) {
init_service_register((char*) driver->name);
@@ -270,6 +283,10 @@ void cdi_driver_register(struct cdi_driver* driver)
cdi_audio_driver_register((struct cdi_audio_driver*) driver);
break;
+ case CDI_USB:
+ cdi_usb_driver_register((struct cdi_usb_driver*) driver);
+ break;
+
default:
break;
}
@@ -281,6 +298,66 @@ list_t* cdi_tyndur_get_drivers(void)
return drivers;
}
+void cdi_handle_bus_device(struct cdi_driver* drv,
+ struct cdi_bus_device_pattern* pattern)
+{
+ (void)drv;
+
+ switch ((int)pattern->bus_type) {
+ case CDI_USB:
+ cdi_osdep_handle_usb_device(
+ CDI_UPCAST(pattern, struct cdi_usb_bus_device_pattern,
+ pattern));
+ break;
+ }
+}
+
+extern void cdi_osdep_provide_usb_device_to(struct cdi_usb_device* dev,
+ pid_t pid);
+
+int cdi_provide_device(struct cdi_bus_data* device)
+{
+ if (device->bus_type != CDI_USB) {
+ return -1;
+ }
+
+ struct cdi_usb_device* usb_dev = CDI_UPCAST(device, struct cdi_usb_device,
+ bus_data);
+ pid_t pid;
+ char name[32];
+
+ sprintf(name, "usb-%04x-%04x", usb_dev->vendor_id, usb_dev->product_id);
+ pid = init_service_get(name);
+ if (pid > 0) {
+ cdi_osdep_provide_usb_device_to(usb_dev, pid);
+ return 0;
+ }
+
+ sprintf(name, "usb-%02x-%02x-%02x", usb_dev->class_id, usb_dev->subclass_id,
+ usb_dev->protocol_id);
+ pid = init_service_get(name);
+ if (pid > 0) {
+ cdi_osdep_provide_usb_device_to(usb_dev, pid);
+ return 0;
+ }
+
+ sprintf(name, "usb-%02x-%02x-?", usb_dev->class_id, usb_dev->subclass_id);
+ pid = init_service_get(name);
+ if (pid > 0) {
+ cdi_osdep_provide_usb_device_to(usb_dev, pid);
+ return 0;
+ }
+
+ sprintf(name, "usb-%02x-?-?", usb_dev->class_id);
+ pid = init_service_get(name);
+ if (pid > 0) {
+ cdi_osdep_provide_usb_device_to(usb_dev, pid);
+ return 0;
+ }
+
+ return -1;
+}
+
void cdi_osdep_new_device(struct cdi_driver* drv, struct cdi_device* dev)
{
if (!dev) {
@@ -294,6 +371,10 @@ void cdi_osdep_new_device(struct cdi_driver* drv, struct cdi_device* dev)
case CDI_SCSI:
cdi_scsi_device_init(CDI_UPCAST(dev, struct cdi_scsi_device, dev));
break;
+
+ case CDI_USB_HCD:
+ cdi_osdep_provide_hc(CDI_UPCAST(dev, struct cdi_usb_hc, dev));
+ break;
}
}
diff --git a/src/modules/cdi/lib/usb.c b/src/modules/cdi/lib/usb.c
new file mode 100644
index 0000000..69da43f
--- /dev/null
+++ b/src/modules/cdi/lib/usb.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2015 Max Reitz
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+ */
+
+#include <assert.h>
+#include <collections.h>
+#include <rpc.h>
+#include <services.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <usb-ipc.h>
+
+#include "cdi.h"
+#include "cdi/lists.h"
+#include "cdi/misc.h"
+#include "cdi/usb.h"
+#include "cdi/usb_hcd.h"
+
+
+static struct cdi_usb_driver* usb_drv;
+static struct cdi_usb_device** dev_list;
+static size_t dev_list_size;
+
+extern void cdi_osdep_new_device(struct cdi_driver* drv,
+ struct cdi_device* dev);
+
+#define RPC(name) \
+ static void name(pid_t src, uint32_t corr_id, size_t length, void* data)
+
+#define rpc_int_resp(val) rpc_send_int_response(src, corr_id, val)
+
+
+struct transmission {
+ cdi_usb_hc_transaction_t ta;
+
+ void* buffer;
+ size_t size;
+
+ cdi_usb_transmission_result_t* result_ptr;
+
+ struct {
+ cdi_usb_transmission_result_t result;
+ char data[];
+ } *shm;
+ uint32_t shmid;
+};
+
+struct ipc_usb_hc {
+ struct cdi_usb_hc cdi;
+ pid_t pid;
+ int id;
+
+ list_t* open_transmissions;
+};
+
+
+RPC(hc_found);
+RPC(get_endpoint_descriptor);
+RPC(control_transfer);
+RPC(bulk_transfer);
+
+
+void cdi_usb_driver_register(struct cdi_usb_driver* drv)
+{
+ assert(!usb_drv);
+ usb_drv = drv;
+
+ // Wait for all USB device drivers
+ servmgr_need("usb-storage");
+
+ register_message_handler(USB_IPC_HC_FOUND, hc_found);
+ register_message_handler(USB_IPC_GET_ENDPOINT_DESCRIPTOR,
+ get_endpoint_descriptor);
+ register_message_handler(USB_IPC_CONTROL_TRANSFER, control_transfer);
+ register_message_handler(USB_IPC_BULK_TRANSFER, bulk_transfer);
+}
+
+
+static void rh_port_down(struct cdi_usb_hub* hub, int index)
+{
+ struct cdi_usb_hc* hc = CDI_UPCAST(hub, struct cdi_usb_hc, rh);
+ struct ipc_usb_hc* ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct usb_ipc_hc_port_param par = {
+ .hc = ipc_hc->id,
+ .index = index
+ };
+
+ rpc_get_int(ipc_hc->pid, USB_IPC_RH_PORT_DISABLE, sizeof(par),
+ (char*) &par);
+}
+
+
+static void rh_port_up(struct cdi_usb_hub* hub, int index)
+{
+ struct cdi_usb_hc* hc = CDI_UPCAST(hub, struct cdi_usb_hc, rh);
+ struct ipc_usb_hc* ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct usb_ipc_hc_port_param par = {
+ .hc = ipc_hc->id,
+ .index = index
+ };
+
+ rpc_get_int(ipc_hc->pid, USB_IPC_RH_PORT_RESET_ENABLE, sizeof(par),
+ (char*) &par);
+}
+
+
+static cdi_usb_port_status_t rh_port_status(struct cdi_usb_hub* hub, int index)
+{
+ struct cdi_usb_hc* hc = CDI_UPCAST(hub, struct cdi_usb_hc, rh);
+ struct ipc_usb_hc* ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct usb_ipc_hc_port_param par = {
+ .hc = ipc_hc->id,
+ .index = index
+ };
+
+ return rpc_get_dword(ipc_hc->pid, USB_IPC_RH_PORT_STATUS, sizeof(par),
+ (char*) &par);
+}
+
+
+static cdi_usb_hc_transaction_t
+ hc_create_transaction(struct cdi_usb_hc* hc,
+ const struct cdi_usb_hc_ep_info* target)
+{
+ struct ipc_usb_hc* ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct usb_ipc_hc_create_transaction_param par = {
+ .hc = ipc_hc->id,
+ .target = *target
+ };
+
+ response_t* resp = rpc_get_response(ipc_hc->pid,
+ USB_IPC_HC_CREATE_TRANSACTION,
+ sizeof(par), (char*) &par);
+ if (resp->data_length != sizeof(cdi_usb_hc_transaction_t)) {
+ free(resp->data);
+ free(resp);
+ return NULL;
+ }
+
+ cdi_usb_hc_transaction_t ta = *(cdi_usb_hc_transaction_t*) resp->data;
+ free(resp->data);
+ free(resp);
+
+ return ta;
+}
+
+
+static void hc_enqueue(struct cdi_usb_hc* hc,
+ const struct cdi_usb_hc_transmission* trans)
+{
+ struct ipc_usb_hc* ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct transmission* int_trans = calloc(1, sizeof(*trans));
+
+ int_trans->ta = trans->ta;
+ int_trans->result_ptr = trans->result;
+
+ int_trans->shmid = create_shared_memory(trans->size
+ + sizeof(*trans->result));
+ int_trans->shm = open_shared_memory(int_trans->shmid);
+
+ if (trans->token == CDI_USB_IN) {
+ int_trans->buffer = trans->buffer;
+ int_trans->size = trans->size;
+ } else {
+ memcpy(int_trans->shm->data, trans->buffer, trans->size);
+ }
+
+ list_push(ipc_hc->open_transmissions, int_trans);
+
+ struct usb_ipc_hc_enqueue_param par = {
+ .hc = ipc_hc->id,
+ .trans = *trans,
+ .shm = int_trans->shmid
+ };
+
+ rpc_get_int(ipc_hc->pid, USB_IPC_HC_ENQUEUE, sizeof(par), (char*) &par);
+}
+
+
+static void hc_start_transaction(struct cdi_usb_hc* hc,
+ cdi_usb_hc_transaction_t ta)
+{
+ struct ipc_usb_hc *ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct usb_ipc_hc_start_transaction_param par = {
+ .hc = ipc_hc->id,
+ .ta = ta
+ };
+
+ rpc_get_int(ipc_hc->pid, USB_IPC_HC_START_TRANSACTION,
+ sizeof(par), (char*) &par);
+}
+
+
+static void hc_wait_transaction(struct cdi_usb_hc* hc,
+ cdi_usb_hc_transaction_t ta)
+{
+ struct ipc_usb_hc* ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct usb_ipc_hc_wait_transaction_param par = {
+ .hc = ipc_hc->id,
+ .ta = ta
+ };
+
+ rpc_get_int(ipc_hc->pid, USB_IPC_HC_WAIT_TRANSACTION, sizeof(par),
+ (char*) &par);
+
+ int i = 0;
+ struct transmission* int_trans;
+ while ((int_trans = list_get_element_at(ipc_hc->open_transmissions, i++))) {
+ if (int_trans->ta == ta) {
+ list_remove(ipc_hc->open_transmissions, --i);
+
+ if (int_trans->result_ptr) {
+ *int_trans->result_ptr = int_trans->shm->result;
+ }
+
+ if (int_trans->buffer) {
+ memcpy(int_trans->buffer, int_trans->shm->data,
+ int_trans->size);
+ }
+
+ close_shared_memory(int_trans->shmid);
+ free(int_trans);
+ }
+ }
+}
+
+
+static void hc_destroy_transaction(struct cdi_usb_hc* hc,
+ cdi_usb_hc_transaction_t ta)
+{
+ struct ipc_usb_hc* ipc_hc = CDI_UPCAST(hc, struct ipc_usb_hc, cdi);
+ struct usb_ipc_hc_wait_transaction_param par = {
+ .hc = ipc_hc->id,
+ .ta = ta
+ };
+
+ rpc_get_int(ipc_hc->pid, USB_IPC_HC_DESTROY_TRANSACTION, sizeof(par),
+ (char*) &par);
+
+ int i = 0;
+ struct transmission* int_trans;
+ while ((int_trans = list_get_element_at(ipc_hc->open_transmissions, i++))) {
+ if (int_trans->ta == ta) {
+ list_remove(ipc_hc->open_transmissions, --i);
+ close_shared_memory(int_trans->shmid);
+ free(int_trans);
+ }
+ }
+}
+
+
+static struct cdi_usb_hcd wrapper_hcd = {
+ .drv = {
+ .name = "ipc-hci",
+ .type = CDI_USB_HCD,
+ },
+
+ .rh_drv = {
+ .port_disable = rh_port_down,
+ .port_reset_enable = rh_port_up,
+ .port_status = rh_port_status,
+ },
+
+ .create_transaction = hc_create_transaction,
+ .enqueue = hc_enqueue,
+ .start_transaction = hc_start_transaction,
+ .wait_transaction = hc_wait_transaction,
+ .destroy_transaction = hc_destroy_transaction,
+};
+
+
+RPC(hc_found)
+{
+ struct usb_ipc_hc_found_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct cdi_usb_hcd* hcd = malloc(sizeof(*hcd));
+ *hcd = wrapper_hcd;
+ hcd->drv.name = strdup(par->drv_name);
+
+ struct ipc_usb_hc* hc = calloc(1, sizeof(*hc));
+ hc->pid = src;
+ hc->id = par->id;
+ hc->open_transmissions = list_create();
+ hc->cdi.dev.driver = &hcd->drv;
+ hc->cdi.dev.name = strdup(par->hc_name);
+ hc->cdi.rh = par->rh;
+
+ struct cdi_usb_hc_bus* hcb = calloc(1, sizeof(*hcb));
+ hcb->bus_data.bus_type = CDI_USB_HCD;
+ hcb->hc = &hc->cdi;
+
+ cdi_osdep_new_device(&usb_drv->drv,
+ usb_drv->drv.init_device(&hcb->bus_data));
+
+ rpc_int_resp(1);
+}
+
+
+RPC(get_endpoint_descriptor)
+{
+ struct usb_ipc_get_endpoint_descriptor_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->dev < 0 || par->dev >= (int)dev_list_size || !dev_list[par->dev]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct cdi_usb_device* dev = dev_list[par->dev];
+ struct cdi_usb_endpoint_descriptor desc;
+
+ usb_drv->get_endpoint_descriptor(dev, par->ep, &desc);
+ rpc_send_response(src, corr_id, sizeof(desc), (char*) &desc);
+}
+
+
+RPC(control_transfer)
+{
+ struct usb_ipc_control_transfer_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_send_dword_response(src, corr_id, CDI_USB_DRIVER_ERROR);
+ return;
+ }
+
+ if (par->dev < 0 || par->dev >= (int)dev_list_size || !dev_list[par->dev]) {
+ rpc_send_dword_response(src, corr_id, CDI_USB_DRIVER_ERROR);
+ return;
+ }
+
+ struct cdi_usb_device* dev = dev_list[par->dev];
+ void* shm = open_shared_memory(par->shm);
+
+ cdi_usb_transmission_result_t res;
+ res = usb_drv->control_transfer(dev, par->ep, &par->setup, shm);
+
+ close_shared_memory(par->shm);
+
+ rpc_send_dword_response(src, corr_id, res);
+}
+
+
+RPC(bulk_transfer)
+{
+ struct usb_ipc_bulk_transfer_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_send_dword_response(src, corr_id, CDI_USB_DRIVER_ERROR);
+ return;
+ }
+
+ if (par->dev < 0 || par->dev >= (int)dev_list_size || !dev_list[par->dev]) {
+ rpc_send_dword_response(src, corr_id, CDI_USB_DRIVER_ERROR);
+ return;
+ }
+
+ struct cdi_usb_device* dev = dev_list[par->dev];
+ void* shm = open_shared_memory(par->shm);
+
+ cdi_usb_transmission_result_t res;
+ res = usb_drv->bulk_transfer(dev, par->ep, shm, par->size);
+
+ close_shared_memory(par->shm);
+
+ rpc_send_dword_response(src, corr_id, res);
+}
+
+
+void cdi_osdep_provide_usb_device_to(struct cdi_usb_device* usb_dev, pid_t pid);
+
+void cdi_osdep_provide_usb_device_to(struct cdi_usb_device* usb_dev, pid_t pid)
+{
+ int idx;
+ for (idx = 0; idx < (int)dev_list_size && !dev_list[idx]; idx++);
+
+ if (idx == (int)dev_list_size) {
+ dev_list_size = (dev_list_size + 4) * 3 / 2;
+ dev_list = realloc(dev_list, dev_list_size * sizeof(*dev_list));
+ memset(dev_list + idx, 0, (dev_list_size - idx) * sizeof(*dev_list));
+ }
+
+ dev_list[idx] = usb_dev;
+ dev_list[idx]->meta.pid = getpid();
+ dev_list[idx]->meta.id = idx;
+
+ rpc_get_int(pid, USB_IPC_DEVICE_FOUND,
+ sizeof(*dev_list[idx]), (char*) dev_list[idx]);
+}
diff --git a/src/modules/cdi/lib/usb_dd.c b/src/modules/cdi/lib/usb_dd.c
new file mode 100644
index 0000000..e1a379d
--- /dev/null
+++ b/src/modules/cdi/lib/usb_dd.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2015 Max Reitz
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+ */
+
+#include <assert.h>
+#include <init.h>
+#include <rpc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <usb-ipc.h>
+
+#include "cdi.h"
+#include "cdi/misc.h"
+#include "cdi/usb.h"
+
+
+extern void cdi_osdep_new_device(struct cdi_driver* drv,
+ struct cdi_device* dev);
+
+static struct cdi_driver* usb_dd;
+
+#define RPC(name) \
+ static void name(pid_t src, uint32_t corr_id, size_t length, void* data)
+
+#define rpc_int_resp(val) rpc_send_int_response(src, corr_id, val)
+
+
+RPC(device_found)
+{
+ struct cdi_usb_device* dev = data;
+ if (length != sizeof(*dev)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ cdi_osdep_new_device(usb_dd, usb_dd->init_device(&dev->bus_data));
+
+ rpc_int_resp(0);
+}
+
+
+void cdi_osdep_set_up_usb_dd_ipc(struct cdi_driver* drv);
+
+void cdi_osdep_set_up_usb_dd_ipc(struct cdi_driver* drv)
+{
+ assert(!usb_dd);
+ usb_dd = drv;
+
+ register_message_handler(USB_IPC_DEVICE_FOUND, device_found);
+}
+
+
+void cdi_osdep_handle_usb_device(struct cdi_usb_bus_device_pattern* p);
+
+void cdi_osdep_handle_usb_device(struct cdi_usb_bus_device_pattern* p)
+{
+ char alias[32];
+
+ if (p->vendor_id < 0 && p->product_id < 0) {
+ assert(p->class_id >= 0);
+ if (p->protocol_id < 0) {
+ assert(p->subclass_id < 0);
+ snprintf(alias, 32, "usb-%02x-?-?", p->class_id);
+ } else if (p->subclass_id < 0) {
+ snprintf(alias, 32, "usb-%02x-%02x-?", p->class_id, p->subclass_id);
+ } else {
+ snprintf(alias, 32, "usb-%02x-%02x-%02x", p->class_id,
+ p->subclass_id, p->protocol_id);
+ }
+ } else if (p->class_id < 0 && p->subclass_id < 0 && p->protocol_id < 0) {
+ assert(p->vendor_id >= 0 && p->product_id >= 0);
+ snprintf(alias, 32, "usb-%04x-%04x", p->vendor_id, p->product_id);
+ } else {
+ assert(0);
+ }
+
+ init_service_register(alias);
+}
+
+
+void cdi_usb_get_endpoint_descriptor(struct cdi_usb_device* dev, int ep,
+ struct cdi_usb_endpoint_descriptor* desc)
+{
+ struct usb_ipc_get_endpoint_descriptor_param par = {
+ .dev = dev->meta.id,
+ .ep = ep
+ };
+
+ response_t* resp = rpc_get_response(dev->meta.pid,
+ USB_IPC_GET_ENDPOINT_DESCRIPTOR,
+ sizeof(par), (char*) &par);
+ assert(resp->data_length == sizeof(*desc));
+
+ memcpy(desc, resp->data, sizeof(*desc));
+ free(resp->data);
+ free(resp);
+}
+
+
+cdi_usb_transmission_result_t
+ cdi_usb_control_transfer(struct cdi_usb_device* dev, int ep,
+ const struct cdi_usb_setup_packet* setup,
+ void* data)
+{
+ uint32_t shmid = 0;
+ void* shm = NULL;
+
+ if (setup->w_length) {
+ shmid = create_shared_memory(setup->w_length);
+ shm = open_shared_memory(shmid);
+ if (setup->bm_request_type & CDI_USB_CREQ_OUT) {
+ memcpy(shm, data, setup->w_length);
+ }
+ }
+
+ struct usb_ipc_control_transfer_param par = {
+ .dev = dev->meta.id,
+ .ep = ep,
+ .setup = *setup,
+ .shm = shmid
+ };
+
+ cdi_usb_transmission_result_t res;
+ res = rpc_get_dword(dev->meta.pid, USB_IPC_CONTROL_TRANSFER,
+ sizeof(par), (char*) &par);
+
+ if (setup->w_length) {
+ if (setup->bm_request_type & CDI_USB_CREQ_IN) {
+ memcpy(data, shm, setup->w_length);
+ }
+ close_shared_memory(shmid);
+ }
+
+ return res;
+}
+
+
+cdi_usb_transmission_result_t cdi_usb_bulk_transfer(struct cdi_usb_device* dev,
+ int ep, void* data,
+ size_t size)
+{
+ uint32_t shmid = create_shared_memory(size);
+ void* shm = open_shared_memory(shmid);
+
+ // FIXME (We don't know which direction the transfer is going)
+ memcpy(shm, data, size);
+
+ struct usb_ipc_bulk_transfer_param par = {
+ .dev = dev->meta.id,
+ .ep = ep,
+ .size = size,
+ .shm = shmid
+ };
+
+ cdi_usb_transmission_result_t res;
+ res = rpc_get_dword(dev->meta.pid, USB_IPC_BULK_TRANSFER,
+ sizeof(par), (char*) &par);
+
+ // FIXME (Same as above)
+ memcpy(data, shm, size);
+
+ close_shared_memory(shmid);
+
+ return res;
+}
diff --git a/src/modules/cdi/lib/usb_hcd.c b/src/modules/cdi/lib/usb_hcd.c
new file mode 100644
index 0000000..b06722c
--- /dev/null
+++ b/src/modules/cdi/lib/usb_hcd.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2015 Max Reitz
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+ */
+
+#include <collections.h>
+#include <init.h>
+#include <rpc.h>
+#include <services.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <usb-ipc.h>
+
+#include "cdi/misc.h"
+#include "cdi/usb_hcd.h"
+
+
+extern void cdi_osdep_device_found(void);
+
+
+struct transmission {
+ uint32_t shmid;
+ void* mapping;
+};
+
+struct transaction {
+ cdi_usb_hc_transaction_t ta;
+ list_t* transmissions;
+ bool started;
+};
+
+struct usb_hc {
+ struct cdi_usb_hc* cdi;
+
+ struct transaction** transactions;
+ size_t transactions_size;
+ size_t transactions_first_free;
+};
+
+static struct usb_hc** hc_list;
+static size_t hc_list_size, hc_list_elements;
+
+#define RPC(name) \
+ static void name(pid_t src, uint32_t corr_id, size_t length, void* data)
+
+#define rpc_int_resp(val) rpc_send_int_response(src, corr_id, val)
+
+
+RPC(hc_create_transaction);
+RPC(hc_enqueue);
+RPC(hc_start_transaction);
+RPC(hc_wait_transaction);
+RPC(hc_destroy_transaction);
+
+RPC(rh_port_disable);
+RPC(rh_port_reset_enable);
+RPC(rh_port_status);
+
+
+static void set_up_hc_ipc(void)
+{
+ static bool ipc_set_up;
+
+ if (ipc_set_up) {
+ return;
+ }
+ ipc_set_up = true;
+
+ register_message_handler(USB_IPC_HC_CREATE_TRANSACTION,
+ hc_create_transaction);
+ register_message_handler(USB_IPC_HC_ENQUEUE, hc_enqueue);
+ register_message_handler(USB_IPC_HC_START_TRANSACTION,
+ hc_start_transaction);
+ register_message_handler(USB_IPC_HC_WAIT_TRANSACTION, hc_wait_transaction);
+ register_message_handler(USB_IPC_HC_DESTROY_TRANSACTION,
+ hc_destroy_transaction);
+
+ register_message_handler(USB_IPC_RH_PORT_DISABLE, rh_port_disable);
+ register_message_handler(USB_IPC_RH_PORT_RESET_ENABLE,
+ rh_port_reset_enable);
+ register_message_handler(USB_IPC_RH_PORT_STATUS, rh_port_status);
+}
+
+
+RPC(hc_create_transaction)
+{
+ struct usb_ipc_hc_create_transaction_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct usb_hc* hc = hc_list[par->hc];
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->cdi->dev.driver,
+ struct cdi_usb_hcd, drv);
+ struct transaction* ta = malloc(sizeof(*ta));
+
+ *ta = (struct transaction){
+ .ta = hcd->create_transaction(hc->cdi, &par->target),
+ .transmissions = list_create()
+ };
+
+ size_t idx;
+ for (idx = hc->transactions_first_free;
+ idx < hc->transactions_size && hc->transactions[idx]; idx++);
+
+ if (idx >= hc->transactions_size) {
+ size_t old_ts = hc->transactions_size;
+ hc->transactions_size = (hc->transactions_size + 4) * 2 / 3;
+ hc->transactions = realloc(hc->transactions,
+ hc->transactions_size
+ * sizeof(*hc->transactions));
+ memset(hc->transactions + old_ts, 0,
+ (hc->transactions_size - old_ts) * sizeof(*hc->transactions));
+ }
+ hc->transactions[idx] = ta;
+ hc->transactions_first_free = idx + 1;
+
+ cdi_usb_hc_transaction_t ipc_ta = (cdi_usb_hc_transaction_t)(uintptr_t)idx;
+ rpc_send_response(src, corr_id, sizeof(ipc_ta), (char*) &ipc_ta);
+}
+
+
+RPC(hc_enqueue)
+{
+ struct usb_ipc_hc_enqueue_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct usb_hc* hc = hc_list[par->hc];
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->cdi->dev.driver,
+ struct cdi_usb_hcd, drv);
+
+ if ((uintptr_t)par->trans.ta > hc->transactions_size ||
+ !hc->transactions[(uintptr_t)par->trans.ta])
+ {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct transaction* ta = hc->transactions[(uintptr_t)par->trans.ta];
+
+ if (ta->started) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct transmission* tm = malloc(sizeof(*tm));
+ *tm = (struct transmission) {
+ .shmid = par->shm,
+ .mapping = open_shared_memory(par->shm)
+ };
+
+ par->trans.ta = ta->ta;
+ par->trans.result = tm->mapping;
+ par->trans.buffer = par->trans.result + 1;
+
+ hcd->enqueue(hc->cdi, &par->trans);
+
+ list_push(ta->transmissions, tm);
+
+ rpc_int_resp(0);
+}
+
+
+RPC(hc_start_transaction)
+{
+ struct usb_ipc_hc_start_transaction_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct usb_hc* hc = hc_list[par->hc];
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->cdi->dev.driver,
+ struct cdi_usb_hcd, drv);
+
+ if ((uintptr_t)par->ta > hc->transactions_size ||
+ !hc->transactions[(uintptr_t)par->ta])
+ {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct transaction* ta = hc->transactions[(uintptr_t)par->ta];
+ ta->started = true;
+
+ hcd->start_transaction(hc->cdi, ta->ta);
+
+ rpc_int_resp(0);
+}
+
+
+RPC(hc_wait_transaction)
+{
+ struct usb_ipc_hc_wait_transaction_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct usb_hc* hc = hc_list[par->hc];
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->cdi->dev.driver,
+ struct cdi_usb_hcd, drv);
+
+ if ((uintptr_t)par->ta > hc->transactions_size ||
+ !hc->transactions[(uintptr_t)par->ta])
+ {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct transaction* ta = hc->transactions[(uintptr_t)par->ta];
+
+ if (!ta->started) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ hcd->wait_transaction(hc->cdi, ta->ta);
+
+ struct transmission* tm;
+ while ((tm = list_pop(ta->transmissions))) {
+ close_shared_memory(tm->shmid);
+ free(tm);
+ }
+
+ rpc_int_resp(0);
+}
+
+
+RPC(hc_destroy_transaction)
+{
+ struct usb_ipc_hc_wait_transaction_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct usb_hc* hc = hc_list[par->hc];
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->cdi->dev.driver,
+ struct cdi_usb_hcd, drv);
+
+ if ((uintptr_t)par->ta > hc->transactions_size ||
+ !hc->transactions[(uintptr_t)par->ta])
+ {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct transaction* ta = hc->transactions[(uintptr_t)par->ta];
+
+ hcd->destroy_transaction(hc->cdi, ta->ta);
+
+ hc->transactions[(uintptr_t)par->ta] = NULL;
+ if (hc->transactions_first_free > (uintptr_t)par->ta) {
+ hc->transactions_first_free = (uintptr_t)par->ta;
+ }
+
+ struct transmission* tm;
+ while ((tm = list_pop(ta->transmissions))) {
+ close_shared_memory(tm->shmid);
+ free(tm);
+ }
+
+ list_destroy(ta->transmissions);
+ free(ta);
+
+ rpc_int_resp(0);
+}
+
+
+RPC(rh_port_disable)
+{
+ struct usb_ipc_hc_port_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct cdi_usb_hc* hc = hc_list[par->hc]->cdi;
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->dev.driver, struct cdi_usb_hcd,
+ drv);
+
+ hcd->rh_drv.port_disable(&hc->rh, par->index);
+
+ rpc_int_resp(0);
+}
+
+
+RPC(rh_port_reset_enable)
+{
+ struct usb_ipc_hc_port_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_int_resp(0);
+ return;
+ }
+
+ struct cdi_usb_hc* hc = hc_list[par->hc]->cdi;
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->dev.driver, struct cdi_usb_hcd,
+ drv);
+
+ hcd->rh_drv.port_reset_enable(&hc->rh, par->index);
+
+ rpc_int_resp(0);
+}
+
+
+RPC(rh_port_status)
+{
+ struct usb_ipc_hc_port_param* par = data;
+ if (length != sizeof(*par)) {
+ rpc_send_dword_response(src, corr_id, 0);
+ return;
+ }
+
+ if (par->hc < 0 || par->hc > (int)hc_list_elements || !hc_list[par->hc]) {
+ rpc_send_dword_response(src, corr_id, 0);
+ return;
+ }
+
+ struct cdi_usb_hc* hc = hc_list[par->hc]->cdi;
+ struct cdi_usb_hcd* hcd = CDI_UPCAST(hc->dev.driver, struct cdi_usb_hcd,
+ drv);
+
+ rpc_send_dword_response(src, corr_id,
+ hcd->rh_drv.port_status(&hc->rh, par->index));
+}
+
+
+void cdi_osdep_provide_hc(struct cdi_usb_hc* cdi_hc);
+
+void cdi_osdep_provide_hc(struct cdi_usb_hc* cdi_hc)
+{
+ struct usb_hc* hc = calloc(1, sizeof(*hc));
+ hc->cdi = cdi_hc;
+
+ set_up_hc_ipc();
+
+ if (hc_list_size == hc_list_elements) {
+ size_t old_hls = hc_list_size;
+ hc_list_size = (hc_list_size + 4) * 2 / 3;
+ hc_list = realloc(hc_list, hc_list_size * sizeof(*hc_list));
+ memset(hc_list + old_hls, 0,
+ (hc_list_size - old_hls) * sizeof(*hc_list));
+ }
+
+ int id = hc_list_elements;
+ hc_list[hc_list_elements++] = hc;
+
+ static pid_t usb_pid;
+ if (!usb_pid) {
+ servmgr_need("usb");
+ usb_pid = init_service_get("usb");
+ }
+
+ struct usb_ipc_hc_found_param par;
+
+ strncpy(par.drv_name, cdi_hc->dev.driver->name, 31);
+ par.drv_name[31] = 0;
+
+ strncpy(par.hc_name, cdi_hc->dev.name, 31);
+ par.hc_name[31] = 0;
+
+ par.id = id;
+ par.rh = cdi_hc->rh;
+
+ rpc_get_int(usb_pid, USB_IPC_HC_FOUND, sizeof(par), (char*) &par);
+}
diff --git a/src/modules/include/usb-ipc.h b/src/modules/include/usb-ipc.h
new file mode 100644
index 0000000..ae6ccf0
--- /dev/null
+++ b/src/modules/include/usb-ipc.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015 Max Reitz
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+ */
+
+#ifndef _USB_IPC_H
+#define _USB_IPC_H
+
+#include <stdint.h>
+
+#include <cdi/usb.h>
+#include <cdi/usb_hcd.h>
+
+
+// Provided by HCDs
+#define USB_IPC_HC_CREATE_TRANSACTION "HCCRTTA"
+#define USB_IPC_HC_ENQUEUE "HCNQXFR"
+#define USB_IPC_HC_START_TRANSACTION "HCSTTTA"
+#define USB_IPC_HC_WAIT_TRANSACTION "HCWAITA"
+#define USB_IPC_HC_DESTROY_TRANSACTION "HCDTYTA"
+
+#define USB_IPC_RH_PORT_DISABLE "RHPRTDN"
+#define USB_IPC_RH_PORT_RESET_ENABLE "RHPRTUP"
+#define USB_IPC_RH_PORT_STATUS "RHPRTST"
+
+
+struct usb_ipc_hc_create_transaction_param {
+ int hc;
+ struct cdi_usb_hc_ep_info target;
+};
+
+struct usb_ipc_hc_enqueue_param {
+ int hc;
+ // @buffer and @result are ignored; @result is put at the start of the SHM
+ // provided, @buffer has to be placed directly afterwards
+ struct cdi_usb_hc_transmission trans;
+ uint32_t shm;
+};
+
+struct usb_ipc_hc_start_transaction_param {
+ int hc;
+ cdi_usb_hc_transaction_t ta;
+};
+
+struct usb_ipc_hc_wait_transaction_param {
+ int hc;
+ cdi_usb_hc_transaction_t ta;
+};
+
+struct usb_ipc_hc_destroy_transaction_param {
+ int hc;
+ cdi_usb_hc_transaction_t ta;
+};
+
+struct usb_ipc_hc_port_param {
+ int hc, index;
+};
+
+
+// Provided by the bus driver
+#define USB_IPC_HC_FOUND "HCFOUND"
+
+#define USB_IPC_GET_ENDPOINT_DESCRIPTOR "GEPDESC"
+
+#define USB_IPC_CONTROL_TRANSFER "CTRLXFR"
+#define USB_IPC_BULK_TRANSFER "BULKXFR"
+
+
+struct usb_ipc_hc_found_param {
+ char drv_name[32], hc_name[32];
+ int id;
+ struct cdi_usb_hub rh;
+};
+
+struct usb_ipc_get_endpoint_descriptor_param {
+ int dev, ep;
+};
+
+struct usb_ipc_control_transfer_param {
+ int dev, ep;
+ struct cdi_usb_setup_packet setup;
+ uint32_t shm; // @buffer
+};
+
+struct usb_ipc_bulk_transfer_param {
+ int dev, ep;
+ size_t size;
+ uint32_t shm; // @buffer
+};
+
+
+// Provided by device drivers
+#define USB_IPC_DEVICE_FOUND "USBPLUG"
+
+
+// The parameter for device_found() is a struct cdi_usb_device.
+
+#endif
--
2.6.3