diff --git a/repos/dde_linux/lib/mk/usb.inc b/repos/dde_linux/lib/mk/usb.inc index 91758453f..04d1498b0 100644 --- a/repos/dde_linux/lib/mk/usb.inc +++ b/repos/dde_linux/lib/mk/usb.inc @@ -4,8 +4,8 @@ LIB_INC_DIR = $(LIB_DIR)/include # FIXME should we *really* dde_kit to this shared library? LIBS += dde_kit libc-setjmp config SRC_CC += main.cc lx_emul.cc irq.cc timer.cc event.cc storage.cc \ - input_component.cc nic.cc -SRC_C += dummies.c scsi.c evdev.c + input_component.cc nic.cc raw.cc +SRC_C += dummies.c scsi.c evdev.c raw_driver.c LX_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/lib/usb DRIVERS_DIR := $(LX_CONTRIB_DIR)/drivers @@ -110,6 +110,8 @@ vpath %.cc $(LIB_DIR)/input vpath %.cc $(LIB_DIR)/storage vpath %.c $(LIB_DIR)/storage vpath %.cc $(LIB_DIR)/nic +vpath %.cc $(LIB_DIR)/raw +vpath %.c $(LIB_DIR)/raw vpath lib/int_sqrt.c $(LX_CONTRIB_DIR) # vi: set ft=make : diff --git a/repos/dde_linux/src/drivers/usb/main.cc b/repos/dde_linux/src/drivers/usb/main.cc index 0ec22689c..4a4dba00a 100644 --- a/repos/dde_linux/src/drivers/usb/main.cc +++ b/repos/dde_linux/src/drivers/usb/main.cc @@ -4,6 +4,13 @@ * \date 2013-02-20 */ +/* + * Copyright (C) 2013-2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + #include extern void start_usb_driver(Server::Entrypoint &e); diff --git a/repos/dde_linux/src/drivers/usb_net_stat/main.cc b/repos/dde_linux/src/drivers/usb_net_stat/main.cc index 40c156903..5679e7d75 100644 --- a/repos/dde_linux/src/drivers/usb_net_stat/main.cc +++ b/repos/dde_linux/src/drivers/usb_net_stat/main.cc @@ -4,6 +4,13 @@ * \date 2013-02-20 */ +/* + * Copyright (C) 2013-2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + int main() { extern void start_usb_driver(); diff --git a/repos/dde_linux/src/lib/usb/arm/platform_arndale/platform.cc b/repos/dde_linux/src/lib/usb/arm/platform_arndale/platform.cc index 0bda90e47..4a67a4764 100644 --- a/repos/dde_linux/src/lib/usb/arm/platform_arndale/platform.cc +++ b/repos/dde_linux/src/lib/usb/arm/platform_arndale/platform.cc @@ -22,7 +22,9 @@ /* Emulation */ #include +#include #include +#include #include /* Linux */ diff --git a/repos/dde_linux/src/lib/usb/arm/platform_panda/platform.cc b/repos/dde_linux/src/lib/usb/arm/platform_panda/platform.cc index 90532064d..890283b91 100644 --- a/repos/dde_linux/src/lib/usb/arm/platform_panda/platform.cc +++ b/repos/dde_linux/src/lib/usb/arm/platform_panda/platform.cc @@ -19,7 +19,10 @@ #include #include +#include #include +#include + #include using namespace Genode; diff --git a/repos/dde_linux/src/lib/usb/arm/platform_rpi/platform.cc b/repos/dde_linux/src/lib/usb/arm/platform_rpi/platform.cc index 27a8343d4..ae7654f00 100644 --- a/repos/dde_linux/src/lib/usb/arm/platform_rpi/platform.cc +++ b/repos/dde_linux/src/lib/usb/arm/platform_rpi/platform.cc @@ -19,7 +19,10 @@ /* emulation */ #include #include + +#include #include +#include /* dwc-otg */ #define new new_ diff --git a/repos/dde_linux/src/lib/usb/dummies.c b/repos/dde_linux/src/lib/usb/dummies.c index dbcf117ff..fdde85cad 100644 --- a/repos/dde_linux/src/lib/usb/dummies.c +++ b/repos/dde_linux/src/lib/usb/dummies.c @@ -107,14 +107,6 @@ char *kstrdup(const char *s, gfp_t gfp) { TRACE; return 0; } char *strstr(const char *h, const char *n) { TRACE; return 0; } -/***************** - ** linux/nls.h ** - *****************/ - -int utf16s_to_utf8s(const wchar_t *pwcs, int len, - enum utf16_endian endian, u8 *s, int maxlen) { TRACE; return 0; } - - /******************* ** linux/ctype.h ** *******************/ @@ -344,7 +336,6 @@ void device_unregister(struct device *dev) { TRACE; } void device_lock(struct device *dev) { TRACE; } int device_trylock(struct device *dev) { TRACE; return 0; } void device_unlock(struct device *dev) { TRACE; } -void device_del(struct device *dev) { TRACE; } void device_initialize(struct device *dev) { TRACE; } int device_attach(struct device *dev) { TRACE; return 0; } int device_is_registered(struct device *dev) { TRACE; return 0; } diff --git a/repos/dde_linux/src/lib/usb/include/extern_c_begin.h b/repos/dde_linux/src/lib/usb/include/extern_c_begin.h new file mode 100644 index 000000000..8fadabf14 --- /dev/null +++ b/repos/dde_linux/src/lib/usb/include/extern_c_begin.h @@ -0,0 +1,26 @@ +/* + * \brief Include before including Linux headers in C++ + * \author Christian Helmuth + * \date 2014-08-21 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#define extern_c_begin + +extern "C" { + +/* some warnings should only be switched of for Linux headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-arith" +#pragma GCC diagnostic ignored "-Wsign-compare" + +/* deal with C++ keywords used for identifiers etc. */ +#define private private_ +#define class class_ +#define new new_ diff --git a/repos/dde_linux/src/lib/usb/include/extern_c_end.h b/repos/dde_linux/src/lib/usb/include/extern_c_end.h new file mode 100644 index 000000000..ac6b23f8d --- /dev/null +++ b/repos/dde_linux/src/lib/usb/include/extern_c_end.h @@ -0,0 +1,20 @@ +/* + * \brief Include after including Linux headers in C++ + * \author Christian Helmuth + * \date 2014-08-21 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#undef new +#undef class +#undef private + +#pragma GCC diagnostic pop + +} /* extern "C" */ diff --git a/repos/dde_linux/src/lib/usb/include/lx_emul.h b/repos/dde_linux/src/lib/usb/include/lx_emul.h index 2367c6f49..5393113e6 100644 --- a/repos/dde_linux/src/lib/usb/include/lx_emul.h +++ b/repos/dde_linux/src/lib/usb/include/lx_emul.h @@ -18,10 +18,6 @@ #ifndef _LX_EMUL_H_ #define _LX_EMUL_H_ -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - /* DDE Kit includes */ #include #include @@ -1583,11 +1579,6 @@ bool device_can_wakeup(struct device *dev); ** linux/device.h ** ********************/ -#ifdef __cplusplus -#define class device_class -#endif - - #define dev_info(dev, format, arg...) dde_kit_printf("dev_info: " format, ## arg) #define dev_warn(dev, format, arg...) dde_kit_printf("dev_warn: " format, ## arg) #define dev_WARN(dev, format, arg...) dde_kit_printf("dev_WARN: " format, ## arg) @@ -1807,10 +1798,6 @@ void devm_kfree(struct device *dev, void *p); void *dev_get_platdata(const struct device *dev); -#ifdef __cplusplus -#undef class -#endif - /***************************** ** linux/platform_device.h ** @@ -2389,10 +2376,6 @@ typedef enum { PCI_D0 = 0 } pci_power_t; /* * Deal with C++ keyword used as member name of 'pci_dev' */ -#ifdef __cplusplus -#define class device_class -#endif /* __cplusplus */ - struct msix_entry { u32 vector; @@ -2436,9 +2419,6 @@ struct pci_fixup { unsigned int class_shift; /* should be 0, 8, 16 */ void (*hook)(struct pci_dev *dev); }; -#ifdef __cplusplus -#undef class -#endif /* __cplusplus */ /* quirks */ @@ -2780,9 +2760,7 @@ enum { HID_DEBUG_BUFSIZE=512 }; ** linux/list.h ** ******************/ -#define new _new #include -#undef new /******************** @@ -3906,8 +3884,4 @@ static inline void trace_xhci_dbg_reset_ep(struct va_format *v) { } static inline void trace_xhci_dbg_quirks(struct va_format *v) { } static inline void trace_xhci_dbg_address(struct va_format *v) { } -#ifdef __cplusplus -} -#endif /* __cplusplus */ - #endif /* _LX_EMUL_H_ */ diff --git a/repos/dde_linux/src/lib/usb/include/platform.h b/repos/dde_linux/src/lib/usb/include/platform.h index 10800b627..ac28760b4 100644 --- a/repos/dde_linux/src/lib/usb/include/platform.h +++ b/repos/dde_linux/src/lib/usb/include/platform.h @@ -23,26 +23,24 @@ struct Services { /* USB profiles */ - bool hid; - bool stor; - bool nic; + bool hid = false; + bool stor = false; + bool nic = false; + bool raw = false; /* Controller types */ - bool uhci; /* 1.0 */ - bool ehci; /* 2.0 */ - bool xhci; /* 3.0 */ + bool uhci = false; /* 1.0 */ + bool ehci = false; /* 2.0 */ + bool xhci = false; /* 3.0 */ /* * Screen resolution used by touch devices to convert touchscreen * absolute coordinates to screen absolute coordinates */ - unsigned long screen_x; - unsigned long screen_y; + unsigned long screen_x = 0; + unsigned long screen_y = 0; Services() - : hid(false), stor(false), nic(false), - uhci(false), ehci(false), xhci(false), - screen_x(0), screen_y(0) { using namespace Genode; @@ -76,6 +74,13 @@ struct Services PDBG("No config node found - not starting the USB Nic (Network) service"); } + try { + config()->xml_node().sub_node("raw"); + raw = true; + } catch (Xml_node::Nonexistent_sub_node) { + PDBG("No config node found - not starting external USB service"); + } + try { if (!config()->xml_node().attribute("uhci").has_value("yes")) throw -1; diff --git a/repos/dde_linux/src/lib/usb/include/routine.h b/repos/dde_linux/src/lib/usb/include/routine.h index 8726d7020..37ca4a0ab 100644 --- a/repos/dde_linux/src/lib/usb/include/routine.h +++ b/repos/dde_linux/src/lib/usb/include/routine.h @@ -170,24 +170,25 @@ class Routine : public Genode::List::Element /** * Add an object */ - static void add(int (*func)(void *), void *arg, char const *name, - bool started = false) + static Routine *add(int (*func)(void *), void *arg, char const *name, + bool started = false) { - _list()->insert(new (Genode::env()->heap()) - Routine(func, arg, name, started)); + Routine *r = new (Genode::env()->heap()) Routine(func, arg, name, started); + _list()->insert(r); + return r; } /** * Remove this object */ - static void remove() + static void remove(Routine *r = nullptr) { - if (!_current) + if (!_current && !r) return; - _dead = _current; + _dead = r ? r : _current; - schedule(); + schedule_main(); } static void main() diff --git a/repos/dde_linux/src/lib/usb/include/signal.h b/repos/dde_linux/src/lib/usb/include/signal.h index ca37c2415..38faafd73 100644 --- a/repos/dde_linux/src/lib/usb/include/signal.h +++ b/repos/dde_linux/src/lib/usb/include/signal.h @@ -98,4 +98,9 @@ namespace Nic void init(Server::Entrypoint &ep); } +namespace Raw +{ + void init(Server::Entrypoint &ep); +} + #endif /* _SIGNAL_H_ */ diff --git a/repos/dde_linux/src/lib/usb/input/input_component.cc b/repos/dde_linux/src/lib/usb/input/input_component.cc index 1e9a75110..07c40c5bf 100644 --- a/repos/dde_linux/src/lib/usb/input/input_component.cc +++ b/repos/dde_linux/src/lib/usb/input/input_component.cc @@ -18,7 +18,9 @@ #include #include +#include #include +#include #undef RELEASE diff --git a/repos/dde_linux/src/lib/usb/lx_emul.cc b/repos/dde_linux/src/lib/usb/lx_emul.cc index 1571c8a1e..eedf9bab8 100644 --- a/repos/dde_linux/src/lib/usb/lx_emul.cc +++ b/repos/dde_linux/src/lib/usb/lx_emul.cc @@ -22,9 +22,12 @@ /* Local includes */ #include "routine.h" #include "signal.h" -#include "lx_emul.h" #include "platform/lx_mem.h" +#include +#include "lx_emul.h" +#include + /* DDE kit includes */ extern "C" { #include @@ -770,8 +773,8 @@ class Driver : public Genode::List::Element return false; bool ret = _drv->bus->match ? _drv->bus->match(dev, _drv) : true; - dde_kit_log(DEBUG_DRIVER, "MATCH: %s ret: %u match: %p", - _drv->name, ret, _drv->bus->match); + dde_kit_log(DEBUG_DRIVER, "MATCH: %s ret: %u match: %p %p", + _drv->name, ret, _drv->bus->match, _drv->probe); return ret; } @@ -786,7 +789,7 @@ class Driver : public Genode::List::Element dde_kit_log(DEBUG_DRIVER, "Probing device bus %p", dev->bus->probe); return dev->bus->probe(dev); } else if (_drv->probe) { - dde_kit_log(DEBUG_DRIVER, "Probing driver: %s", _drv->name); + dde_kit_log(DEBUG_DRIVER, "Probing driver: %s %p", _drv->name, _drv->probe); return _drv->probe(dev); } @@ -822,6 +825,14 @@ int device_add(struct device *dev) } +void device_del(struct device *dev) +{ + dde_kit_log(DEBUG_DRIVER, "Remove device %p", dev); + if (dev->driver && dev->driver->remove) + dev->driver->remove(dev); +} + + int device_register(struct device *dev) { //XXX: initialize DMA pools (see device_initialize) @@ -870,7 +881,6 @@ void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp) void *dev_get_platdata(const struct device *dev) { - PDBG("called"); return (void *)dev->platform_data; } @@ -1312,3 +1322,23 @@ int waitqueue_active(wait_queue_head_t *q) { return q->q ? 1 : 0; } + + +/***************** + ** linux/nls.h ** + *****************/ + +int utf16s_to_utf8s(const wchar_t *pwcs, int len, + enum utf16_endian endian, u8 *s, int maxlen) +{ + /* + * We do not convert to char, we simply copy the UTF16 plane 0 values + */ + u16 *out = (u16 *)s; + u16 *in = (u16 *)pwcs; + int length = Genode::min(len, maxlen / 2); + for (int i = 0; i < length; i++) + out[i] = in[i]; + + return 2 * length; +} diff --git a/repos/dde_linux/src/lib/usb/main.cc b/repos/dde_linux/src/lib/usb/main.cc index 7e8c09541..65e175492 100644 --- a/repos/dde_linux/src/lib/usb/main.cc +++ b/repos/dde_linux/src/lib/usb/main.cc @@ -42,6 +42,7 @@ extern "C" void module_usb_storage_driver_init(); extern "C" void module_wacom_driver_init(); extern "C" void module_ch_driver_init(); extern "C" void module_mt_driver_init(); +extern "C" void module_raw_driver_init(); extern "C" void start_input_service(void *ep, unsigned long, unsigned long); @@ -81,6 +82,10 @@ static void init(Services *services) /* storage */ if (services->stor) module_usb_storage_driver_init(); + + if (services->raw) + /* low level interface */ + module_raw_driver_init(); } @@ -97,6 +102,9 @@ void start_usb_driver(Server::Entrypoint &ep) Storage::init(ep); Nic::init(ep); + if (services.raw) + Raw::init(ep); + Routine::add(0, 0, "Main", true); Routine::make_main_current(); init(&services); diff --git a/repos/dde_linux/src/lib/usb/nic/nic.cc b/repos/dde_linux/src/lib/usb/nic/nic.cc index 99f2b9cf2..5fd7cc3d1 100644 --- a/repos/dde_linux/src/lib/usb/nic/nic.cc +++ b/repos/dde_linux/src/lib/usb/nic/nic.cc @@ -19,15 +19,15 @@ #include #include +#include #include +#include +#include +#include #include #include "signal.h" -extern "C" { -#include -#include -} static Signal_helper *_signal = 0; diff --git a/repos/dde_linux/src/lib/usb/pci_driver.cc b/repos/dde_linux/src/lib/usb/pci_driver.cc index 8af6f8867..09e0508a1 100644 --- a/repos/dde_linux/src/lib/usb/pci_driver.cc +++ b/repos/dde_linux/src/lib/usb/pci_driver.cc @@ -18,7 +18,9 @@ #include /* Linux includes */ +#include #include +#include #include struct bus_type pci_bus_type; @@ -56,7 +58,7 @@ class Pci_driver _dev->vendor = client.vendor_id(); _dev->device = client.device_id(); - _dev->device_class = client.class_code(); + _dev->class_ = client.class_code(); _dev->revision = client.config_read(REV, Device::ACCESS_8BIT); _dev->dev.driver = &_drv->driver; @@ -236,15 +238,15 @@ int pci_register_driver(struct pci_driver *drv) bool found = false; - while (id->device_class || id->class_mask || id->device_class) { + while (id->class_ || id->class_mask || id->class_) { - if (id->device_class == (unsigned)PCI_ANY_ID) { + if (id->class_ == (unsigned)PCI_ANY_ID) { dde_kit_log(DEBUG_PCI, "Skipping PCI_ANY_ID device class"); id++; continue; } - Pci::Device_capability cap = pci.first_device(id->device_class, + Pci::Device_capability cap = pci.first_device(id->class_, id->class_mask); while (cap.valid()) { @@ -278,7 +280,7 @@ int pci_register_driver(struct pci_driver *drv) } Pci::Device_capability free_up = cap; - cap = pci.next_device(cap, id->device_class, id->class_mask); + cap = pci.next_device(cap, id->class_, id->class_mask); if (!pci_drv) pci.release_device(free_up); } diff --git a/repos/dde_linux/src/lib/usb/raw/raw.cc b/repos/dde_linux/src/lib/usb/raw/raw.cc new file mode 100644 index 000000000..92de72fc4 --- /dev/null +++ b/repos/dde_linux/src/lib/usb/raw/raw.cc @@ -0,0 +1,694 @@ +/** + * \brief Server side USB session implementation + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "raw.h" +#include +#include + +using namespace Genode; + +extern "C" int usb_set_configuration(struct usb_device *dev, int configuration); + +constexpr bool verbose_raw = false; + + +namespace Usb { + class Session_component; + class Root; + class Worker; +} + +/** + * Keep track of all registered USB devices (via raw driver) + */ +struct Device : List::Element +{ + usb_device *udev; + + Device(usb_device *udev) : udev(udev) + { + list()->insert(this); + } + + ~Device() { list()->remove(this); } + + static List *list() + { + static List _l; + return &_l; + } + + static Device * device(uint16_t vendor, uint16_t product) + { + for (Device *d = list()->first(); d; d = d->next()) { + if (d->udev->descriptor.idVendor == vendor && d->udev->descriptor.idProduct == product) + return d; + } + + return nullptr; + } + + usb_interface *interface(unsigned index) + { + usb_interface *iface = udev->actconfig->interface[index]; + return iface; + } + + usb_host_endpoint *endpoint(usb_interface *iface, unsigned alt_setting, + unsigned endpoint_num) + { + return &iface->altsetting[alt_setting].endpoint[endpoint_num]; + } +}; + + +/** + * Handle packet stream request, this way the entrypoint always returns to it's + * server loop + */ +class Usb::Worker +{ + private: + + completion _packet_avail; + + Session::Tx::Sink *_sink; + Device *_device = nullptr; + Signal_context_capability _sigh_ready; + Routine *_routine = nullptr; + unsigned _p_in_flight = 0; + + void _ack_packet(Packet_descriptor &p) + { + _sink->acknowledge_packet(p); + _p_in_flight--; + } + + /** + * Retrieve string descriptor at index given in packet + */ + void _retrieve_string(Packet_descriptor &p) + { + char *buffer = _sink->packet_content(p); + int length; + + if ((length = usb_string(_device->udev, p.string.index, buffer, p.size())) < 0) { + PWRN("Could not read string descriptor index: %u", p.string.index); + p.string.length = 0; + } else { + /* returned length is in bytes (char) */ + p.string.length = length / 2; + p.succeded = true; + } + } + + /** + * Read control transfer + */ + void _ctrl_in(Packet_descriptor &p) + { + void *buf = kmalloc(4096, GFP_NOIO); + + int err = usb_control_msg(_device->udev, usb_rcvctrlpipe(_device->udev, 0), + p.control.request, p.control.request_type, + p.control.value, p.control.index, buf, + p.size(), p.control.timeout); + + if (err > 0 && p.size()) + Genode::memcpy(_sink->packet_content(p), buf, err); + + kfree(buf); + + if (err < 0 && err != -EPIPE) { + p.succeded = false; + return; + } + + p.succeded = true; + } + + /** + * Write control transfer + */ + void _ctrl_out(Packet_descriptor &p) + { + void *buf = kmalloc(4096, GFP_NOIO); + + if (p.size()) + Genode::memcpy(buf, _sink->packet_content(p), p.size()); + + int err = usb_control_msg(_device->udev, usb_sndctrlpipe(_device->udev, 0), + p.control.request, p.control.request_type, + p.control.value, p.control.index, buf, p.size(), + p.control.timeout); + if (err >= 0 || err== -EPIPE) + p.succeded = true; + + kfree(buf); + } + + /** + * Asynchronous transfer helpers + */ + struct Complete_data + { + Worker *worker; + Packet_descriptor packet; + }; + + void _async_finish(Packet_descriptor &p, urb *urb, bool read) + { + p.transfer.actual_size = urb->actual_length; + p.succeded = true; + + if (read) + Genode::memcpy(_sink->packet_content(p), urb->transfer_buffer, + urb->actual_length); + + _ack_packet(p); + } + + static void _async_complete(urb *urb) + { + Complete_data *data = (Complete_data *)urb->context; + + data->worker->_async_finish(data->packet, urb, + !!(data->packet.transfer.ep & USB_DIR_IN)); + kfree (data); + kfree (urb->transfer_buffer); + usb_free_urb(urb); + } + + /** + * Bulk transfer + */ + bool _bulk(Packet_descriptor &p, bool read) + { + unsigned pipe; + void *buf = kmalloc(p.size(), GFP_NOIO); + + if (read) + pipe = usb_rcvbulkpipe(_device->udev, p.transfer.ep); + else { + pipe = usb_sndbulkpipe(_device->udev, p.transfer.ep); + Genode::memcpy(buf, _sink->packet_content(p), p.size()); + } + + urb *bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!bulk_urb) { + PERR("Failed to allocate bulk URB"); + kfree(buf); + return false; + } + + Complete_data *data = (Complete_data *)kmalloc(sizeof(Complete_data), GFP_KERNEL); + data->packet = p; + data->worker = this; + + usb_fill_bulk_urb(bulk_urb, _device->udev, pipe, buf, p.size(), + _async_complete, data); + + if (usb_submit_urb(bulk_urb, GFP_KERNEL)) + PERR("Failed to submit URB"); + + return true; + } + + /** + * IRQ transfer + */ + bool _irq(Packet_descriptor &p, bool read) + { + unsigned pipe; + void *buf = kmalloc(p.size(), GFP_NOIO); + + if (read) + pipe = usb_rcvintpipe(_device->udev, p.transfer.ep); + else { + pipe = usb_sndintpipe(_device->udev, p.transfer.ep); + Genode::memcpy(buf, _sink->packet_content(p), p.size()); + } + + urb *irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!irq_urb) { + PERR("Failed to allocate interrupt URB"); + kfree(buf); + return false; + } + + Complete_data *data = (Complete_data *)kmalloc(sizeof(Complete_data), GFP_KERNEL); + data->packet = p; + data->worker = this; + + usb_fill_int_urb(irq_urb, _device->udev, pipe, buf, p.size(), + _async_complete, data, p.transfer.timeout); + + if (usb_submit_urb(irq_urb, GFP_KERNEL)) + PERR("Failed to submit URB"); + + return true; + } + + /** + * Change alternate settings for device + */ + void _alt_setting(Packet_descriptor &p) + { + int err = usb_set_interface(_device->udev, p.interface.number, + p.interface.alt_setting); + if (!err) + p.succeded = true; + } + + /** + * Set configuration + */ + void _config(Packet_descriptor &p) + { + usb_host_config *config = _device->udev->actconfig; + + if (!config) + return; + + for (unsigned i = 0; i < config->desc.bNumInterfaces; i++) { + if (usb_interface_claimed(config->interface[i])) { + PERR("There are interfaces claimed, won't set configuration"); + return; + } + } + + int err = usb_set_configuration(_device->udev, p.number); + + if (!err) + p.succeded = true; + } + + /** + * Release interface + */ + void _release_interface(Packet_descriptor &p) + { + usb_interface *iface = _device->interface(p.number); + + if (!iface) + return; + + usb_driver_release_interface(&raw_intf_driver, iface); + p.succeded = true; + } + + /** + * Dispatch incoming packet types + */ + void _dispatch() + { + /* + * Get packets until there are no more free ack slots or avaiable + * packets + */ + while (_p_in_flight < _sink->ack_slots_free() && _sink->packet_avail()) + { + Packet_descriptor p = _sink->get_packet(); + + if (verbose_raw) + PDBG("PACKET: %u first value: %x", p.type, p.number); + + _p_in_flight++; + + if (!_device || !_device->udev) { + _ack_packet(p); + continue; + } + + switch (p.type) { + + case Packet_descriptor::STRING: + _retrieve_string(p); + break; + + case Packet_descriptor::CTRL: + if (p.control.request_type & Usb::ENDPOINT_IN) + _ctrl_in(p); + else + _ctrl_out(p); + break; + + case Packet_descriptor::BULK: + if (_bulk(p, !!(p.transfer.ep & USB_DIR_IN))) + continue; + + case Packet_descriptor::IRQ: + if (_irq(p, !!(p.transfer.ep & USB_DIR_IN))) + continue; + + case Packet_descriptor::ALT_SETTING: + _alt_setting(p); + break; + + case Packet_descriptor::CONFIG: + _config(p); + break; + + case Packet_descriptor::RELEASE_IF: + _release_interface(p); + break; + } + + _ack_packet(p); + } + } + + void _wait_for_device() + { + _wait_event(_device); + _wait_event(_device->udev->actconfig); + + /* set raw driver */ + _device->udev->dev.driver = &raw_driver.drvwrap.driver; + + if (_sigh_ready.valid()) + Signal_transmitter(_sigh_ready).submit(1); + } + + /** + * Wait for packets + */ + void _wait() + { + /* wait for device to become ready */ + init_completion(&_packet_avail); + + _wait_for_device(); + + while (true) { + wait_for_completion(&_packet_avail); + + _dispatch(); + Routine::schedule_all(); + } + } + + public: + + static int run(void *worker) + { + Worker *w = static_cast(worker); + w->_wait(); + return 0; + } + + Worker(Session::Tx::Sink *sink) + : _sink(sink) + { } + + void start() + { + if (!_routine) + _routine = Routine::add(run, this, "worker"); + } + + void stop() + { + if (_routine) + Routine::remove(_routine); + _routine = nullptr; + } + + void packet_avail() { ::complete(&_packet_avail); } + + void device(Device *device, Signal_context_capability sigh_ready = Signal_context_capability()) + { + _device = device; + _sigh_ready = sigh_ready; + } +}; + + +/***************** + ** USB session ** + *****************/ + +class Usb::Session_component : public Session_rpc_object, + public List::Element +{ + private: + + Server::Entrypoint &_ep; + unsigned long _vendor; + unsigned long _product; + Device *_device = nullptr; + Signal_context_capability _sigh_state_change; + Signal_rpc_member _packet_avail; + Signal_rpc_member _ready_ack; + Worker _worker; + + + void _signal_state_change() + { + if (_sigh_state_change.valid()) + Signal_transmitter(_sigh_state_change).submit(1); + } + + void _receive(unsigned) + { + _worker.packet_avail(); + } + + public: + + enum State { + DEVICE_ADD, + DEVICE_REMOVE, + }; + + Session_component(Genode::Dataspace_capability tx_ds, Server::Entrypoint &ep, + unsigned long vendor, unsigned long product) + : Session_rpc_object(tx_ds, ep.rpc_ep()), + _ep(ep), + _vendor(vendor), _product(product), + _packet_avail(ep, *this, &Session_component::_receive), + _ready_ack(ep, *this, &Session_component::_receive), + _worker(sink()) + { + _device = Device::device(_vendor, _product); + if (_device) + PDBG("Found device"); + + /* register signal handlers */ + _tx.sigh_packet_avail(_packet_avail); + } + + /*********************** + ** Session interface ** + ***********************/ + + bool plugged() { return _device != nullptr; } + + void claim_interface(unsigned interface_num) override + { + usb_interface *iface = _device->interface(interface_num); + if (!iface) + throw Interface_not_found(); + + if (usb_driver_claim_interface(&raw_intf_driver, iface, nullptr)) + throw Interface_already_claimed(); + } + + void config_descriptor(Device_descriptor *device_descr, + Config_descriptor *config_descr) override + { + Genode::memcpy(device_descr, &_device->udev->descriptor, sizeof(usb_device_descriptor)); + Genode::memcpy(config_descr, &_device->udev->actconfig->desc, sizeof(usb_config_descriptor)); + + device_descr->bus = _device->udev->bus->busnum; + device_descr->num = _device->udev->devnum; + device_descr->speed = _device->udev->speed; + } + + unsigned alt_settings(unsigned index) override + { + return _device->interface(index)->num_altsetting; + } + + void interface_descriptor(unsigned index, unsigned alt_setting, + Interface_descriptor *interface_descr) override + { + if (!_device) + throw Device_not_found(); + + if (index >= _device->udev->actconfig->desc.bNumInterfaces) + throw Interface_not_found(); + + usb_interface *iface = _device->interface(index); + Genode::memcpy(interface_descr, &iface->altsetting[alt_setting].desc, + sizeof(usb_interface_descriptor)); + + if (&iface->altsetting[alt_setting] == iface->cur_altsetting) + interface_descr->active = true; + } + + void endpoint_descriptor(unsigned interface_num, + unsigned alt_setting, + unsigned endpoint_num, + Endpoint_descriptor *endpoint_descr) override + { + if (!_device) + throw Device_not_found(); + + if (interface_num >= _device->udev->actconfig->desc.bNumInterfaces) + throw Interface_not_found(); + + usb_interface *iface = usb_ifnum_to_if(_device->udev, interface_num); + Genode::memcpy(endpoint_descr, &_device->endpoint(iface, alt_setting, + endpoint_num)->desc, sizeof(usb_endpoint_descriptor)); + } + + /********************* + ** Local interface ** + *********************/ + + bool session_device(Device *device) + { + usb_device_descriptor *descr = &device->udev->descriptor; + return (descr->idVendor == _vendor && descr->idProduct == _product) ? true : false; + } + + bool state_change(State state, Device *device) + { + switch (state) { + case DEVICE_ADD: + if (!session_device(device)) + return false; + + if (_device) + PWRN("Device type already present (vendor: %x product: %x). Overwrite!", + device->udev->descriptor.idVendor, + device->udev->descriptor.idProduct); + + _device = device; + _worker.device(_device, _sigh_state_change); + _worker.start(); + return true; + + case DEVICE_REMOVE: + if (!session_device(device)) + return false; + _device = nullptr; + _worker.stop(); + _signal_state_change(); + return true; + } + + return false; + } + + void sigh_state_change(Signal_context_capability sigh) { _sigh_state_change = sigh; } +}; + + +struct Session : public List +{ + static Session *list() + { + static Session _l; + return &_l; + } + + void state_change(Usb::Session_component::State state, Device *device) + { + for (Usb::Session_component *session = list()->first(); session; session = session->next()) + if (session->state_change(state, device)) + return; + } +}; + + +class Usb::Root : public Genode::Root_component +{ + private: + + Server::Entrypoint &_ep; + + protected: + + Session_component *_create_session(const char *args) + { + using namespace Genode; + + size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); + size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0); + + unsigned long vendor = Arg_string::find_arg(args, "vendor").ulong_value(0); + unsigned long product = Arg_string::find_arg(args, "product").ulong_value(0); + + /* check session quota */ + size_t session_size = max(4096, sizeof(Session_component)); + if (ram_quota < session_size) + throw Root::Quota_exceeded(); + + if (tx_buf_size > ram_quota - session_size) { + PERR("Insufficient 'ram_quota',got %zu, need %zu", + ram_quota, tx_buf_size + session_size); + throw Root::Quota_exceeded(); + } + + Dataspace_capability tx_ds = env()->ram_session()->alloc(tx_buf_size); + Session_component *session = new (md_alloc()) + Session_component(tx_ds, _ep, vendor, product); + ::Session::list()->insert(session); + return session; + } + + public: + + Root(Server::Entrypoint &session_ep, + Genode::Allocator *md_alloc) + : Genode::Root_component(&session_ep.rpc_ep(), md_alloc), + _ep(session_ep) { } +}; + + +void Raw::init(Server::Entrypoint &ep) +{ + static Usb::Root root(ep, env()->heap()); + Genode::env()->parent()->announce(ep.rpc_ep().manage(&root)); +} + + +/***************** + ** C interface ** + *****************/ + +void raw_register_device(struct usb_device *udev) +{ + ::Session::list()->state_change(Usb::Session_component::DEVICE_ADD, + new (env()->heap()) Device(udev)); +} + + +void raw_unregister_device(struct usb_device *udev) +{ + Device *dev = Device::device(udev->descriptor.idVendor, + udev->descriptor.idProduct); + if (dev) + ::Session::list()->state_change(Usb::Session_component::DEVICE_REMOVE, dev); +} + diff --git a/repos/dde_linux/src/lib/usb/raw/raw.h b/repos/dde_linux/src/lib/usb/raw/raw.h new file mode 100644 index 000000000..041ce439b --- /dev/null +++ b/repos/dde_linux/src/lib/usb/raw/raw.h @@ -0,0 +1,14 @@ +#ifndef _RAW_H_ +#define _RAW_H_ + +struct usb_device; +struct usb_driver; + +extern struct usb_device_driver raw_driver; +extern struct usb_driver raw_intf_driver; + +void raw_register_device(struct usb_device *udev); +void raw_unregister_device(struct usb_device *udev); + + +#endif /* _RAW_H_ */ diff --git a/repos/dde_linux/src/lib/usb/raw/raw_driver.c b/repos/dde_linux/src/lib/usb/raw/raw_driver.c new file mode 100644 index 000000000..8c3e3fa7a --- /dev/null +++ b/repos/dde_linux/src/lib/usb/raw/raw_driver.c @@ -0,0 +1,87 @@ +/** + * \brief Low level USB access driver + * \author Sebastian Sumpf + * \date 2014-11-11 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include "raw.h" + +static int raw_probe(struct usb_device *udev) +{ + printk("RAW: vendor: %x product: %x dev %p\n", + udev->descriptor.idVendor, udev->descriptor.idProduct, udev); + + raw_register_device(udev); + + return -ENODEV; +} + +static void raw_disconnect(struct usb_device *udev) +{ + printk("driver disconnect called\n"); + raw_unregister_device(udev); +} + + +struct usb_device_driver raw_driver = +{ + .name = "raw", + .probe = raw_probe, + .disconnect = raw_disconnect, + .supports_autosuspend = 0, +}; + + +static int raw_intf_probe(struct usb_interface *intf, + struct usb_device_id const *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + + printk("RAW_INTF: vendor: %04x product: %04x\n", udev->descriptor.idVendor, + udev->descriptor.idProduct); + + return -ENODEV; +} + +void raw_intf_disconnect(struct usb_interface *intf) { } + +static const struct usb_device_id raw_intf_id_table[] = { + { .driver_info = 1 } +}; + + +struct usb_driver raw_intf_driver = +{ + .name = "rawintf", + .probe = raw_intf_probe, + .disconnect = raw_intf_disconnect, + .supports_autosuspend = 0, +}; + + +static int raw_driver_init(void) +{ + int err; + + if ((err = usb_register_device_driver(&raw_driver, THIS_MODULE))) + return err; + + printk("RAW: driver registered\n"); + + if ((err = usb_register(&raw_intf_driver))) + return err; + + printk("RAW: interface driver registered\n"); + + return 0; +} + +module_init(raw_driver_init); diff --git a/repos/dde_linux/src/lib/usb/signal/event.cc b/repos/dde_linux/src/lib/usb/signal/event.cc index fb8a03f61..eb5984379 100644 --- a/repos/dde_linux/src/lib/usb/signal/event.cc +++ b/repos/dde_linux/src/lib/usb/signal/event.cc @@ -12,7 +12,9 @@ */ #include +#include #include +#include static Signal_helper *_signal = 0; diff --git a/repos/dde_linux/src/lib/usb/signal/irq.cc b/repos/dde_linux/src/lib/usb/signal/irq.cc index 02e8fbc5a..18aebba4f 100644 --- a/repos/dde_linux/src/lib/usb/signal/irq.cc +++ b/repos/dde_linux/src/lib/usb/signal/irq.cc @@ -12,7 +12,10 @@ */ #include + +#include #include +#include extern "C" { #include diff --git a/repos/dde_linux/src/lib/usb/signal/timer.cc b/repos/dde_linux/src/lib/usb/signal/timer.cc index fad9baaf2..907c58dc0 100644 --- a/repos/dde_linux/src/lib/usb/signal/timer.cc +++ b/repos/dde_linux/src/lib/usb/signal/timer.cc @@ -14,7 +14,9 @@ #include #include +#include #include +#include #include "signal.h" static void handler(void *timer); diff --git a/repos/dde_linux/src/lib/usb/storage/storage.cc b/repos/dde_linux/src/lib/usb/storage/storage.cc index dbc0bc697..ed950a8a3 100644 --- a/repos/dde_linux/src/lib/usb/storage/storage.cc +++ b/repos/dde_linux/src/lib/usb/storage/storage.cc @@ -17,10 +17,12 @@ #include #include +#include #include +#include +#include #include -#include #include "signal.h" static Signal_helper *_signal = 0; diff --git a/repos/os/include/usb/packet_handler.h b/repos/os/include/usb/packet_handler.h new file mode 100644 index 000000000..e3d719f7e --- /dev/null +++ b/repos/os/include/usb/packet_handler.h @@ -0,0 +1,132 @@ +/** + * \brief Packet stream helper + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ +#ifndef _INCLUDE__USB__PACKET_HANDLER_ +#define _INCLUDE__USB__PACKET_HANDLER_ + +#include +#include + +namespace Usb { + class Packet_handler; +} + +class Usb::Packet_handler +{ + private: + + Usb::Connection &_connection; + Signal_rpc_member _rpc_ack_avail; + Signal_rpc_member _rpc_ready_submit; + + bool _ready_submit = true; + + void _packet_handler(unsigned) + { + if (!_ready_submit) + return; + + while (packet_avail()) { + Packet_descriptor p = _connection.source()->get_acked_packet(); + + if (p.completion) + p.completion->complete(p); + else + release(p); + } + } + + void _ready_handler(unsigned) + { + _ready_submit = true; + }; + + public: + + Packet_handler(Connection &connection, Server::Entrypoint &ep) + : _connection(connection), + _rpc_ack_avail(ep, *this, &Packet_handler::_packet_handler), + _rpc_ready_submit(ep, *this, &Packet_handler::_ready_handler) + { + /* connect 'ack_avail' to our rpc member */ + _connection.tx_channel()->sigh_ack_avail(_rpc_ack_avail); + _connection.tx_channel()->sigh_ready_to_submit(_rpc_ready_submit); + } + + + /*************************** + ** Packet stream wrapper ** + ***************************/ + + bool packet_avail() const + { + return _connection.source()->ack_avail(); + } + + void wait_for_packet() + { + packet_avail() ? _packet_handler(0) : Server::wait_and_dispatch_one_signal(); + } + + Packet_descriptor alloc(size_t size) + { + /* is size larger than packet stream */ + if (size > _connection.source()->bulk_buffer_size()) { + PERR("Packet allocation of (%zu bytes) too large, buffer has %zu bytes", + size, _connection.source()->bulk_buffer_size()); + throw Usb::Session::Tx::Source::Packet_alloc_failed(); + } + + while (true) { + try { + Packet_descriptor p = _connection.source()->alloc_packet(size); + return p; + } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { + /* block until some packets are freed */ + wait_for_packet(); + } + } + } + + void submit(Packet_descriptor &p) + { + /* check if submit queue is full */ + if (!_connection.source()->ready_to_submit()) { + _ready_submit = false; + + /* wait for ready_to_submit signal */ + while (!_ready_submit) + Server::wait_and_dispatch_one_signal(); + } + + _connection.source()->submit_packet(p); + + /* + * If an acknowledgement available signal occurred in the meantime, + * retrieve packets. + */ + if (packet_avail()) + _packet_handler(0); + } + + void *content(Packet_descriptor &p) + { + return _connection.source()->packet_content(p); + } + + void release(Packet_descriptor &p) + { + _connection.source()->release_packet(p); + } +}; + +#endif /* _INCLUDE__USB__PACKET_HANDLER_ */ diff --git a/repos/os/include/usb/types.h b/repos/os/include/usb/types.h new file mode 100644 index 000000000..28ce656c3 --- /dev/null +++ b/repos/os/include/usb/types.h @@ -0,0 +1,210 @@ +/** + * \brief Basic types for USB + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__USB__TYPES_H_ +#define _INCLUDE__USB__TYPES_H_ + +#include +#include + +namespace Usb { + struct Device_descriptor; + struct Config_descriptor; + struct Interface_descriptor; + struct Endpoint_descriptor; + struct String; +} + +namespace Usb { + + enum Endpoint_type { + ENDPOINT_BULK = 0x2, + ENDPOINT_INTERRUPT = 0x3, + }; + + /** + * The following three enums are ORed together to form a control request + * type + */ + enum Endpoint_direction { + ENDPOINT_OUT = 0, + ENDPOINT_IN = 0x80, + }; + + enum Request_type { + TYPE_STANDARD = 0, + TYPE_CLASS = (1U << 5), + TYPE_VENDOR = (2U << 5), + TYPE_RESERVED = (3U << 5), + }; + + enum Recipient { + RECIPIENT_DEVICE = 0, + RECIPIENT_INTERFACE = 0x1, + RECIPIENT_ENDPOINT = 0x2, + RECIPIENT_OTHER = 0x3, + }; + + /** + * UTF-16 string + */ + typedef Genode::uint16_t utf16_t; +} + + +/** + * String containing UTF-16 plane 0 characters + */ +struct Usb::String +{ + utf16_t *string = nullptr; + unsigned length = 0; + + void copy(unsigned len, void *from, Genode::Allocator *md_alloc) + { + length = len; + string = (utf16_t *)md_alloc->alloc(length * sizeof(utf16_t)); + Genode::memcpy(string, from, sizeof(utf16_t) * length); + } + + void free(Genode::Allocator *md_alloc) + { + if (!string) + return; + + md_alloc->free(string, length * sizeof(utf16_t)); + string = nullptr; + } + + /** + * Create char version + */ + char *to_char(char *buffer, unsigned len) + { + if (!length) + return (char *)""; + + len = Genode::min(length, len); + for (unsigned i = 0; i < len; i++) + buffer[i] = string[i] & 0xff; + + buffer[len] = 0; + return buffer; + } + + /** + * Print for debugging + */ + void print() + { + char buffer[128]; + Genode::printf("%s\n", to_char(buffer, 128)); + } +}; + +/** + * USB hardware device descriptor + */ +struct Usb::Device_descriptor +{ + Genode::uint8_t length; + Genode::uint8_t type = 0x1; + + Genode::uint16_t usb; /* USB version in BCD (binary-coded decimal ) */ + Genode::uint8_t dclass; + Genode::uint8_t dsubclass; + Genode::uint8_t dprotocol; + Genode::uint8_t max_packet_size; /* of endpoint zero */ + + Genode::uint16_t vendor_id; + Genode::uint16_t product_id; + Genode::uint16_t device_release; /* release number in BCD */ + + Genode::uint8_t manufactorer_index; /* index of string describing manufacturer */ + Genode::uint8_t product_index; + Genode::uint8_t serial_number_index; + + Genode::uint8_t num_configs; + + /** + * Genode extensions (POD only) + */ + unsigned bus; + unsigned num; + unsigned speed; +} __attribute__((packed)); + +/** + * USB hardware configuration descriptor + */ +struct Usb::Config_descriptor +{ + Genode::uint8_t length; + Genode::uint8_t type = 0x2; + + /* + * Total length of data returned for this configuration. Includes the + * combined length of all descriptors (configuration, interface, endpoint, + * and class or vendor specific) returned for this configuration. + */ + Genode::uint16_t total_length; + Genode::uint8_t num_interfaces; + + Genode::uint8_t config_value; /* value used to set this configuration */ + Genode::uint8_t config_index; /* index of string descriptor */ + + Genode::uint8_t attributes; + Genode::uint8_t max_power; /* maximum power consumption */ +} __attribute__((packed)); + +/** + * USB hardware interface descriptor + */ +struct Usb::Interface_descriptor +{ + Genode::uint8_t length; + Genode::uint8_t type = 0x4; + + Genode::uint8_t number; /* interface number */ + Genode::uint8_t alt_settings; /* value used for setting alternate setting + using the 'number' field */ + Genode::uint8_t num_endpoints; + + Genode::uint8_t iclass; + Genode::uint8_t isubclass; + Genode::uint8_t iprotocol; + + Genode::uint8_t interface_index; /* index of string descriptor */ + + /** + * Genode extensions (POD only) + */ + bool active = false; +} __attribute__((packed)); + +/** + * USB hardware endpoint descriptor + */ +struct Usb::Endpoint_descriptor +{ + Genode::uint8_t length; + Genode::uint8_t type = 0x5; + + Genode::uint8_t address; + Genode::uint8_t attributes; + + Genode::uint16_t max_packet_size; /* for this endpoint */ + Genode::uint8_t polling_interval; +} __attribute__((packed)); + +#endif /* _INCLUDE__USB__TYPES_H_ */ diff --git a/repos/os/include/usb/usb.h b/repos/os/include/usb/usb.h new file mode 100644 index 000000000..2e82b02d6 --- /dev/null +++ b/repos/os/include/usb/usb.h @@ -0,0 +1,556 @@ +/** + * \brief USB session wrapper + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ +#ifndef _INCLUDE__USB__USB_H_ +#define _INCLUDE__USB__USB_H_ + +#include +#include +#include +#include + +#include + +namespace Usb { + + /* debugging */ + bool constexpr verbose_descr = false; + + class Device; + class Config; + class Alternate_interface; + class Interface; + class Endpoint; + class Meta_data; + class Sync_completion; +} + + +class Usb::Meta_data +{ + protected: + + Genode::Allocator * const _md_alloc; + Connection &_connection; + Packet_handler &_handler; + + public: + + Meta_data(Genode::Allocator * const md_alloc, Connection &device, + Packet_handler &handler) + : _md_alloc(md_alloc), _connection(device), _handler(handler) { } +}; + +/** + * Completion for synchronous calls + */ +class Usb::Sync_completion : Completion +{ + private: + + bool _completed = false; + Packet_descriptor &_p; + + public: + + Sync_completion(Packet_handler &handler, Packet_descriptor &p) + : _p(p) + { + Completion *c = p.completion; + p.completion = this; + + handler.submit(p); + + while (!_completed) + handler.wait_for_packet(); + + if (c) + c->complete(p); + } + + void complete(Packet_descriptor &p) override + { + _p = p; + _completed = true; + } +}; + + +class Usb::Endpoint : public Endpoint_descriptor +{ + public: + + Endpoint(Endpoint_descriptor &endpoint_descr) + : Endpoint_descriptor(endpoint_descr) { } + + bool is_bulk() const { return (attributes & 0x3) == ENDPOINT_BULK; } + bool is_interrupt() const { return (attributes & 0x3) == ENDPOINT_INTERRUPT; } + + void dump() + { + if (verbose_descr) + PLOG("\tEndpoint: len: %x type: %x address: %x attributes: %x", + length, type, address, attributes); + } +}; + + +class Usb::Alternate_interface : public Interface_descriptor, + public Meta_data +{ + private: + + enum { MAX_ENDPOINTS = 16 }; + Endpoint *_endpoints[MAX_ENDPOINTS]; + + public: + + String interface_string; + + Alternate_interface(Interface_descriptor &interface_desc, + Meta_data &md) + : Interface_descriptor(interface_desc), Meta_data(md) + { + dump(); + + for (Genode::uint8_t i = 0; i < num_endpoints; i++) + { + Endpoint_descriptor descr; + _connection.endpoint_descriptor(number, alt_settings, i, &descr); + _endpoints[i] = new (_md_alloc) Endpoint(descr); + _endpoints[i]->dump(); + } + } + + ~Alternate_interface() + { + for (unsigned i = 0; i < num_endpoints; i++) + destroy(_md_alloc, _endpoints[i]); + + interface_string.free(_md_alloc); + } + + Endpoint &endpoint(unsigned index) + { + if (index >= num_endpoints) + throw Session::Invalid_endpoint(); + + return *_endpoints[index]; + } + + void dump() + { + if (!verbose_descr) + return; + + PWRN("Interface: len: %x: type: %x number: %x alt_setting: %x", + length, type, number, alt_settings); + PWRN(" num_endpoints: %x class: %x subclass: %x: protocol: %x", + num_endpoints, iclass, isubclass, iprotocol); + } +}; + + +class Usb::Interface : public Meta_data +{ + private: + + friend class Config; + + enum { MAX_ALT = 10 }; + + Alternate_interface *_interface[MAX_ALT]; + unsigned _count = 0; + Alternate_interface *_current = nullptr; + bool _claimed = false; + + + void _check() + { + if (!_claimed) + throw Session::Interface_not_claimed(); + } + + protected: + + void _add(Alternate_interface *iface) + { + _interface[_count++] = iface; + + if (iface->active) + _current = iface; + } + + public: + + Interface(Meta_data &md) + : Meta_data(md) { } + + + /*************** + ** Accessors ** + ***************/ + + unsigned alternate_count() const { return _count; } + Alternate_interface ¤t() { return *_current; } + + Alternate_interface &alternate_interface(unsigned index) + { + if (index >= _count) + throw Session::Interface_not_found(); + + return *_interface[index]; + } + + Endpoint &endpoint(unsigned index) { return _current->endpoint(index); } + + + /*************************** + ** Packet stream helpers ** + ***************************/ + + Packet_descriptor alloc(size_t size) + { + return _handler.alloc(size); + } + + void submit(Packet_descriptor &p) + { + _handler.submit(p); + } + + void release(Packet_descriptor &p) + { + _handler.release(p); + } + + void *content(Packet_descriptor &p) + { + return _handler.content(p); + } + + /****************************** + ** Interface to USB service ** + ******************************/ + + /** + * Claim interface + */ + void claim() + { + _connection.claim_interface(_interface[0]->number); + _claimed = true; + } + + /** + * Release interface + */ + void release() + { + if (_claimed) + return; + + Packet_descriptor p = alloc(0); + p.type = Packet_descriptor::RELEASE_IF; + p.number = _interface[0]->number; + p.succeded = false; + + Sync_completion sync(_handler, p); + + if (p.succeded) + _claimed = false; + } + + void set_alternate_interface(Alternate_interface &alternate) + { + _check(); + + Packet_descriptor p = alloc(0); + p.type = Packet_descriptor::ALT_SETTING; + p.succeded = false; + p.interface.number = alternate.number; + p.interface.alt_setting = alternate.alt_settings; + + Sync_completion sync(_handler, p); + + if (p.succeded) + _current = _interface[p.interface.alt_setting]; + } + + void control_transfer(Packet_descriptor &p, uint8_t request_type, uint8_t request, + uint16_t value, uint16_t index, int timeout, + bool block = true, Completion *c = nullptr) + { + _check(); + + p.type = Usb::Packet_descriptor::CTRL; + p.succeded = false; + p.control.request = request; + p.control.request_type = request_type; + p.control.value = value; + p.control.index = index; + p.control.timeout = timeout; + p.completion = c; + + if(block) Sync_completion sync(_handler, p); + else _handler.submit(p); + } + + void bulk_transfer(Packet_descriptor &p, Endpoint &ep, int timeout, + bool block = true, Completion *c = nullptr) + { + _check(); + + if (!ep.is_bulk()) + throw Session::Invalid_endpoint(); + + p.type = Usb::Packet_descriptor::BULK; + p.succeded = false; + p.transfer.ep = ep.address; + p.transfer.timeout = timeout; + p.completion = c; + + if(block) Sync_completion sync(_handler, p); + else _handler.submit(p); + } + + void interrupt_transfer(Packet_descriptor &p, Endpoint &ep, int timeout, + bool block = true, Completion *c = nullptr) + { + _check(); + + if (!ep.is_interrupt()) + throw Session::Invalid_endpoint(); + + p.type = Usb::Packet_descriptor::IRQ; + p.succeded = false; + p.transfer.ep = ep.address; + p.transfer.timeout = timeout; + p.completion = c; + + if(block) Sync_completion sync(_handler, p); + else _handler.submit(p); + } +}; + + +class Usb::Config : public Config_descriptor, + public Meta_data +{ + private: + + enum { MAX_INTERFACES = 32 }; + + Interface *_interfaces[MAX_INTERFACES]; + unsigned _total_interfaces = 0; + + public: + + String config_string; + + Config(Config_descriptor &config_desc, Meta_data &md) + : Config_descriptor(config_desc), Meta_data(md) + { + dump(); + + for (Genode::uint8_t i = 0; i < num_interfaces; i++) { + + Interface_descriptor descr; + _connection.interface_descriptor(i, 0, &descr); + _interfaces[descr.number] = new(_md_alloc) Interface(md); + + /* read number of alternative settings */ + unsigned alt_settings = _connection.alt_settings(i); + _total_interfaces += alt_settings; + + /* alt settings */ + for (unsigned j = 0; j < alt_settings; j++) { + _connection.interface_descriptor(i, j, &descr); + if (descr.number != i) + PERR("Interface number != index"); + + _interfaces[descr.number]->_add(new(_md_alloc) Alternate_interface(descr, md)); + } + } + } + + ~Config() + { + for (unsigned i = 0; i < num_interfaces; i++) + destroy(_md_alloc, _interfaces[i]); + + config_string.free(_md_alloc); + } + + Interface &interface(unsigned num) + { + + if (num >= num_interfaces) + throw Session::Interface_not_found(); + + return *_interfaces[num]; + } + + void dump() + { + if (verbose_descr) + PINF("Config: len: %x type %x total_len: %x num_intf: %x config_value: %x", + length, type, total_length, num_interfaces, config_value); + } +}; + + +class Usb::Device : public Meta_data +{ + private: + + Packet_handler _handler; + + void _clear() + { + if (!config) + return; + + manufactorer_string.free(_md_alloc); + product_string.free(_md_alloc); + serial_number_string.free(_md_alloc); + destroy(_md_alloc, config); + } + + public: + + enum Speed { + SPEED_UNKNOWN = 0, /* enumerating */ + SPEED_LOW, + SPEED_FULL, /* usb 1.1 */ + SPEED_HIGH, /* usb 2.0 */ + SPEED_WIRELESS, /* wireless (usb 2.5) */ + SPEED_SUPER, /* usb 3.0 */ + }; + + Device_descriptor device_descr; + Config *config = nullptr; + + String manufactorer_string; + String product_string; + String serial_number_string; + + Device(Genode::Allocator * const md_alloc, Connection &connection, + Server::Entrypoint &ep) + : Meta_data(md_alloc, connection, _handler), _handler(connection, ep) + { } + + Device_descriptor const *descriptor() { return &device_descr; } + Config *config_descriptor() { return config; } + + char const *speed_string(unsigned speed) + { + switch (speed) { + case SPEED_LOW : return "LOW"; + case SPEED_FULL : return "FULL"; + case SPEED_HIGH : return "HIGH"; + case SPEED_WIRELESS: return "WIRELESS"; + case SPEED_SUPER : return "SUPER"; + + default: return ""; + } + } + + void string_descriptor(uint8_t index, String *target) + { + Packet_descriptor p = _handler.alloc(128); + p.type = Packet_descriptor::STRING; + p.string.index = index; + p.string.length = 128; + + Sync_completion sync(_handler, p); + target->copy(p.string.length, _handler.content(p), _md_alloc); + } + + /** + * Re-read all descriptors (device, config, interface, and endpoints) + * Must be called before Usb::Device can be used (asynchronous) + */ + void update_config() + { + /* free info from previous call */ + _clear(); + + Config_descriptor config_descr; + + _connection.config_descriptor(&device_descr, &config_descr); + dump(); + + config = new (_md_alloc) Config(config_descr, *this); + + /* retrieve string descriptors */ + string_descriptor(device_descr.manufactorer_index, &manufactorer_string); + string_descriptor(device_descr.product_index, &product_string); + string_descriptor(device_descr.serial_number_index, &serial_number_string); + string_descriptor(config->config_index, &config->config_string); + + for (unsigned i = 0; i < config->num_interfaces; i++) { + Interface &iface = config->interface(i); + + for (unsigned j = 0; j < iface.alternate_count(); j++) + string_descriptor(iface.alternate_interface(j).interface_index, + &iface.alternate_interface(j).interface_string); + } + } + + /** + * Set configuration, no interfaces can be claimed (asynchronous) + */ + void set_configuration(uint8_t num) + { + if (!config) { + PERR("No current configuration found"); + return; + } + + if (!num || num > device_descr.num_configs) { + PERR("Valid configuration values: 1 ... %u", device_descr.num_configs); + return; + } + + if (config && num == config->config_value) + return; + + Packet_descriptor p = _handler.alloc(0); + p.type = Packet_descriptor::CONFIG; + p.number = num; + + Sync_completion sync(_handler, p); + + if (p.succeded) + update_config(); + } + + Interface &interface(unsigned interface_num) + { + return config->interface(interface_num); + } + + void dump() + { + if (!verbose_descr) + return; + + PINF("Device: len: %x type: %x class: %x sub-class: %x proto: %x max_packet: %x", + device_descr.length, device_descr.type, device_descr.dclass, device_descr.dsubclass, + device_descr.dprotocol, device_descr.max_packet_size); + PINF(" vendor: %x product: %x configs: %x", + device_descr.vendor_id, device_descr.product_id, device_descr.num_configs); + } +}; + +#endif /* _INCLUDE__USB__USB_H_ */ diff --git a/repos/os/include/usb_session/capability.h b/repos/os/include/usb_session/capability.h new file mode 100644 index 000000000..4525dd0a9 --- /dev/null +++ b/repos/os/include/usb_session/capability.h @@ -0,0 +1,23 @@ +/** + * \brief USB session capability + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ +#ifndef _INCLUDE__USB_SESSION__CAPABILITY_H_ +#define _INCLUDE__USB_SESSION__CAPABILITY_H_ + +#include + +namespace Usb { + class Session; + typedef Genode::Capability Session_capability; +} + +#endif /* _INCLUDE__USB_SESSION__CAPABILITY_H_ */ diff --git a/repos/os/include/usb_session/client.h b/repos/os/include/usb_session/client.h new file mode 100644 index 000000000..2f919d56a --- /dev/null +++ b/repos/os/include/usb_session/client.h @@ -0,0 +1,95 @@ +/** + * \brief USB session client implementation + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ +#ifndef _INCLUDE__USB_SESSION__CLIENT_H_ +#define _INCLUDE__USB_SESSION__CLIENT_H_ + +#include + +#include +#include +#include + +namespace Usb { + class Session_client; + class Interface_client; +} + +class Usb::Session_client : public Genode::Rpc_client +{ + private: + + Packet_stream_tx::Client _tx; + + public: + + /** + * Constructor + * + * \param session session capability + * \param tx_buffer_alloc allocator used for managing the + * transmission buffer + */ + Session_client(Session_capability session, + Genode::Range_allocator *tx_buffer_alloc, + Genode::Signal_context_capability state_change) + : + Genode::Rpc_client(session), + _tx(call(), tx_buffer_alloc) + { + if (state_change.valid()) + sigh_state_change(state_change); + } + + /*************************** + ** USB session interface ** + ***************************/ + + bool plugged() override { return call(); } + Tx *tx_channel() override { return &_tx; } + Tx::Source *source() override { return _tx.source(); } + + void sigh_state_change(Genode::Signal_context_capability sigh) override { + call(sigh); } + + void config_descriptor(Device_descriptor *device_descr, + Config_descriptor *config_descr) override + { + call(device_descr, config_descr); + } + + unsigned alt_settings(unsigned index) + { + return call(index); + } + + void interface_descriptor(unsigned index, unsigned alt_setting, + Interface_descriptor *interface_descr) override + { + call(index, alt_setting, interface_descr); + } + + void endpoint_descriptor(unsigned interface_num, + unsigned alt_setting, + unsigned endpoint_num, + Endpoint_descriptor *endpoint_descr) override + { + call(interface_num, alt_setting, endpoint_num, endpoint_descr); + } + + void claim_interface(unsigned interface_num) + { + call(interface_num); + } +}; + +#endif /* _INCLUDE__USB_SESSION__CLIENT_H_ */ diff --git a/repos/os/include/usb_session/connection.h b/repos/os/include/usb_session/connection.h new file mode 100644 index 000000000..fdfbd7117 --- /dev/null +++ b/repos/os/include/usb_session/connection.h @@ -0,0 +1,38 @@ +/** + * \brief Client connection to USB server + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ +#ifndef _INCLUDE__USB_SESSION__CONNECTION_H_ +#define _INCLUDE__USB_SESSION__CONNECTION_H_ + +#include +#include +#include + +namespace Usb { + struct Connection; +} + +struct Usb::Connection : Genode::Connection, Session_client +{ + Connection(Genode::Range_allocator *tx_block_alloc, + unsigned long vendor_id, unsigned long product_id, + Genode::size_t tx_buf_size = 512 * 1024, + Genode::Signal_context_capability sigh_state_changed = + Genode::Signal_context_capability()) + : + Genode::Connection(session("ram_quota=%zd, tx_buf_size=%zd, vendor=%lu, product=%lu", + 3 * 4096 + tx_buf_size, tx_buf_size, vendor_id, product_id)), + Session_client(cap(), tx_block_alloc, sigh_state_changed) + { } +}; + +#endif /* _INCLUDE__USB_SESSION__CONNECTION_H_ */ diff --git a/repos/os/include/usb_session/rpc_object.h b/repos/os/include/usb_session/rpc_object.h new file mode 100644 index 000000000..4ded3151d --- /dev/null +++ b/repos/os/include/usb_session/rpc_object.h @@ -0,0 +1,54 @@ +/** + * \brief Server RPC object with packet stream + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ +#ifndef _INCLUDE__USB_SESSION__RPC_OBJECT_H_ +#define _INCLUDE__USB_SESSION__RPC_OBJECT_H_ + +#include +#include +#include + +namespace Usb { + class Session_rpc_object; +} + +class Usb::Session_rpc_object : public Genode::Rpc_object +{ + protected: + + Packet_stream_tx::Rpc_object _tx; + + public: + + /** + * Constructor + * + * \param tx_ds dataspace used as communication buffer + * for the tx packet stream + * \param ep entry point used for packet-stream channel + */ + Session_rpc_object(Genode::Dataspace_capability tx_ds, + Genode::Rpc_entrypoint &ep) + : _tx(tx_ds, ep) { } + + /** + * Return capability to packet-stream channel + * + * This function is called by the client via an RPC call at session + * construction time. + */ + Genode::Capability _tx_cap() { return _tx.cap(); } + + Tx::Sink *sink() { return _tx.sink(); } +}; + +#endif /* _INCLUDE__USB_SESSION__RPC_OBJECT_H_ */ diff --git a/repos/os/include/usb_session/usb_session.h b/repos/os/include/usb_session/usb_session.h new file mode 100644 index 000000000..5b24efa81 --- /dev/null +++ b/repos/os/include/usb_session/usb_session.h @@ -0,0 +1,193 @@ +/** + * \brief USB session for raw device connection + * \author Sebastian Sumpf + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ +#ifndef _INCLUDE__USB_SESSION__USB_SESSION_H_ +#define _INCLUDE__USB_SESSION__USB_SESSION_H_ + +#include +#include +#include +#include + + +namespace Usb { + using namespace Genode; + class Session; + struct Packet_descriptor; + struct Completion; +} + +/** + * USB packet type + */ +struct Usb::Packet_descriptor : ::Packet_descriptor +{ + enum Type { STRING, CTRL, BULK, IRQ, ALT_SETTING, CONFIG, RELEASE_IF }; + + Type type; + bool succeded = false; + Completion *completion = nullptr; + + union + { + struct + { + uint8_t index; + unsigned length; + } string; + + struct + { + uint8_t request; + uint8_t request_type; + uint16_t value; + uint16_t index; + int timeout; + } control; + + struct + { + uint8_t ep; + int actual_size; /* returned */ + int timeout; + } transfer; + + struct + { + uint8_t number; + uint8_t alt_setting; + } interface; + + struct + { + uint8_t number; + }; + }; + + bool is_read_transfer() { return transfer.ep & ENDPOINT_IN; } + + Packet_descriptor(off_t offset = 0, size_t size = 0) + : ::Packet_descriptor(offset, size) { } + + Packet_descriptor(::Packet_descriptor p, Type type, Completion *completion = nullptr) + : ::Packet_descriptor(p.offset(), p.size()), type(type), completion(completion) { } +}; + +/** + * Completion for asynchronous communication + */ +struct Usb::Completion +{ + virtual void complete(Usb::Packet_descriptor &p) = 0; +}; + +struct Usb::Session : public Genode::Session +{ + /**************** + ** Exceptions ** + ****************/ + + class Device_not_found : public Exception { }; + class Interface_not_found : public Exception { }; + class Interface_already_claimed : public Exception { }; + class Interface_not_claimed : public Exception { }; + class Invalid_endpoint : public Exception { }; + + + /******************* + ** Packet stream ** + *******************/ + + enum { TX_QUEUE_SIZE = 64 }; + + typedef Packet_stream_policy Tx_policy; + + typedef Packet_stream_tx::Channel Tx; + + /** + * Request packet-transmission channel + */ + virtual Tx *tx_channel() { return 0; } + + /** + * Request client-side packet-stream interface of tx channel + */ + virtual Tx::Source *source() { return 0; } + + + /*********************** + ** Session interface ** + ***********************/ + + static const char *service_name() { return "Usb"; } + + /** + * Send from the server to the client upon device state change + */ + virtual void sigh_state_change(Signal_context_capability sigh) = 0; + + /** + * Is the device present + */ + virtual bool plugged() = 0; + + /** + * Retrieve device and current configurations despcriptors + */ + virtual void config_descriptor(Device_descriptor *device_descr, + Config_descriptor *config_descr) = 0; + + /** + * Return number of alt settings for iterface + */ + virtual unsigned alt_settings(unsigned index) = 0; + + /** + * Return interface descriptor for interface index/alternate setting tuple + */ + virtual void interface_descriptor(unsigned index, unsigned alt_setting, + Interface_descriptor *interface_descr) = 0; + + /** + * Return endpoint for interface index/alternate setting tuple + */ + virtual void endpoint_descriptor(unsigned interface_num, + unsigned alt_setting, + unsigned endpoint_num, + Endpoint_descriptor *endpoint_descr) = 0; + + /** + * Claim an interface number + */ + virtual void claim_interface(unsigned interface_num) = 0; + + GENODE_RPC(Rpc_plugged, bool, plugged); + GENODE_RPC(Rpc_sigh_state_change, void, sigh_state_change, Signal_context_capability); + GENODE_RPC(Rpc_tx_cap, Capability, _tx_cap); + GENODE_RPC_THROW(Rpc_config_descr, void, config_descriptor, GENODE_TYPE_LIST(Device_not_found), + Device_descriptor *, Config_descriptor *); + GENODE_RPC(Rpc_alt_settings, unsigned, alt_settings, unsigned); + GENODE_RPC_THROW(Rpc_iface_descr, void, interface_descriptor, GENODE_TYPE_LIST(Device_not_found, + Interface_not_found), unsigned, unsigned, Interface_descriptor *); + GENODE_RPC_THROW(Rpc_ep_descr, void, endpoint_descriptor, GENODE_TYPE_LIST(Device_not_found, + Interface_not_found), unsigned, unsigned, unsigned, Endpoint_descriptor *); + GENODE_RPC_THROW(Rpc_claim_interface, void, claim_interface, GENODE_TYPE_LIST(Interface_not_found, + Interface_already_claimed), unsigned); + GENODE_RPC_THROW(Rpc_release_interface, void, release_interface, GENODE_TYPE_LIST(Interface_not_found), + unsigned); + GENODE_RPC_INTERFACE(Rpc_plugged, Rpc_sigh_state_change, Rpc_tx_cap, Rpc_config_descr, + Rpc_iface_descr, Rpc_ep_descr, Rpc_alt_settings, Rpc_claim_interface); +}; + +#endif /* _INCLUDE__USB_SESSION__USB_SESSION_H_ */