usb_drv: Remote USB driver interface

Issue #1329
This commit is contained in:
Sebastian Sumpf 2014-11-13 15:44:15 +01:00 committed by Christian Helmuth
parent e479b9e8e8
commit 0c2bdf9edd
33 changed files with 2264 additions and 71 deletions

View File

@ -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 :

View File

@ -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 <os/server.h>
extern void start_usb_driver(Server::Entrypoint &e);

View File

@ -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();

View File

@ -22,7 +22,9 @@
/* Emulation */
#include <platform/platform.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
#include <platform.h>
/* Linux */

View File

@ -19,7 +19,10 @@
#include <io_mem_session/connection.h>
#include <util/mmio.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
#include <linux/platform_data/usb-omap.h>
using namespace Genode;

View File

@ -19,7 +19,10 @@
/* emulation */
#include <platform/platform.h>
#include <platform.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
/* dwc-otg */
#define new new_

View File

@ -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; }

View File

@ -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_

View File

@ -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" */

View File

@ -18,10 +18,6 @@
#ifndef _LX_EMUL_H_
#define _LX_EMUL_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* DDE Kit includes */
#include <dde_kit/types.h>
#include <dde_kit/printf.h>
@ -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 <linux/list.h>
#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_ */

View File

@ -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 <nic> 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 <raw> config node found - not starting external USB service");
}
try {
if (!config()->xml_node().attribute("uhci").has_value("yes"))
throw -1;

View File

@ -170,24 +170,25 @@ class Routine : public Genode::List<Routine>::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()

View File

@ -98,4 +98,9 @@ namespace Nic
void init(Server::Entrypoint &ep);
}
namespace Raw
{
void init(Server::Entrypoint &ep);
}
#endif /* _SIGNAL_H_ */

View File

@ -18,7 +18,9 @@
#include <input/root.h>
#include <os/ring_buffer.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
#undef RELEASE

View File

@ -22,9 +22,12 @@
/* Local includes */
#include "routine.h"
#include "signal.h"
#include "lx_emul.h"
#include "platform/lx_mem.h"
#include <extern_c_begin.h>
#include "lx_emul.h"
#include <extern_c_end.h>
/* DDE kit includes */
extern "C" {
#include <dde_kit/semaphore.h>
@ -770,8 +773,8 @@ class Driver : public Genode::List<Driver>::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<Driver>::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;
}

View File

@ -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);

View File

@ -19,15 +19,15 @@
#include <nic/xml_node.h>
#include <util/xml_node.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <linux/usb.h>
#include <linux/usb/usbnet.h>
#include <extern_c_end.h>
#include <nic/component.h>
#include "signal.h"
extern "C" {
#include <linux/usb.h>
#include <linux/usb/usbnet.h>
}
static Signal_helper *_signal = 0;

View File

@ -18,7 +18,9 @@
#include <pci_device/client.h>
/* Linux includes */
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
#include <platform/lx_mem.h>
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);
}

View File

@ -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 <base/env.h>
#include <base/printf.h>
#include <root/component.h>
#include <usb_session/rpc_object.h>
#include <util/list.h>
#include <extern_c_begin.h>
#include <linux/usb.h>
#include "raw.h"
#include <extern_c_end.h>
#include <signal.h>
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<Device>::Element
{
usb_device *udev;
Device(usb_device *udev) : udev(udev)
{
list()->insert(this);
}
~Device() { list()->remove(this); }
static List<Device> *list()
{
static List<Device> _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 *>(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<Session_component>::Element
{
private:
Server::Entrypoint &_ep;
unsigned long _vendor;
unsigned long _product;
Device *_device = nullptr;
Signal_context_capability _sigh_state_change;
Signal_rpc_member<Session_component> _packet_avail;
Signal_rpc_member<Session_component> _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<Usb::Session_component>
{
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<Session_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<size_t>(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_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);
}

View File

@ -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_ */

View File

@ -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 <linux/usb.h>
#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);

View File

@ -12,7 +12,9 @@
*/
#include <signal.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
static Signal_helper *_signal = 0;

View File

@ -12,7 +12,10 @@
*/
#include <signal.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
extern "C" {
#include <dde_kit/interrupt.h>

View File

@ -14,7 +14,9 @@
#include <base/env.h>
#include <base/printf.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <extern_c_end.h>
#include "signal.h"
static void handler(void *timer);

View File

@ -17,10 +17,12 @@
#include <util/endian.h>
#include <util/list.h>
#include <extern_c_begin.h>
#include <lx_emul.h>
#include <storage/scsi.h>
#include <extern_c_end.h>
#include <platform/lx_mem.h>
#include <storage/scsi.h>
#include "signal.h"
static Signal_helper *_signal = 0;

View File

@ -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 <base/lock.h>
#include <usb_session/connection.h>
namespace Usb {
class Packet_handler;
}
class Usb::Packet_handler
{
private:
Usb::Connection &_connection;
Signal_rpc_member<Packet_handler> _rpc_ack_avail;
Signal_rpc_member<Packet_handler> _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_ */

View File

@ -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 <base/stdint.h>
#include <base/printf.h>
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 *)"<unknown>";
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_ */

556
repos/os/include/usb/usb.h Normal file
View File

@ -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 <base/allocator.h>
#include <usb/types.h>
#include <usb/packet_handler.h>
#include <usb_session/connection.h>
#include <base/printf.h>
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 &current() { 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 "<unknown>";
}
}
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_ */

View File

@ -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 <base/capability.h>
namespace Usb {
class Session;
typedef Genode::Capability<Session> Session_capability;
}
#endif /* _INCLUDE__USB_SESSION__CAPABILITY_H_ */

View File

@ -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 <base/rpc_client.h>
#include <packet_stream_tx/client.h>
#include <usb_session/capability.h>
#include <usb_session/usb_session.h>
namespace Usb {
class Session_client;
class Interface_client;
}
class Usb::Session_client : public Genode::Rpc_client<Session>
{
private:
Packet_stream_tx::Client<Tx> _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>(session),
_tx(call<Rpc_tx_cap>(), tx_buffer_alloc)
{
if (state_change.valid())
sigh_state_change(state_change);
}
/***************************
** USB session interface **
***************************/
bool plugged() override { return call<Rpc_plugged>(); }
Tx *tx_channel() override { return &_tx; }
Tx::Source *source() override { return _tx.source(); }
void sigh_state_change(Genode::Signal_context_capability sigh) override {
call<Rpc_sigh_state_change>(sigh); }
void config_descriptor(Device_descriptor *device_descr,
Config_descriptor *config_descr) override
{
call<Rpc_config_descr>(device_descr, config_descr);
}
unsigned alt_settings(unsigned index)
{
return call<Rpc_alt_settings>(index);
}
void interface_descriptor(unsigned index, unsigned alt_setting,
Interface_descriptor *interface_descr) override
{
call<Rpc_iface_descr>(index, alt_setting, interface_descr);
}
void endpoint_descriptor(unsigned interface_num,
unsigned alt_setting,
unsigned endpoint_num,
Endpoint_descriptor *endpoint_descr) override
{
call<Rpc_ep_descr>(interface_num, alt_setting, endpoint_num, endpoint_descr);
}
void claim_interface(unsigned interface_num)
{
call<Rpc_claim_interface>(interface_num);
}
};
#endif /* _INCLUDE__USB_SESSION__CLIENT_H_ */

View File

@ -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 <usb_session/client.h>
#include <base/connection.h>
#include <base/allocator.h>
namespace Usb {
struct Connection;
}
struct Usb::Connection : Genode::Connection<Session>, 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>(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_ */

View File

@ -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 <base/rpc_server.h>
#include <packet_stream_tx/rpc_object.h>
#include <usb_session/usb_session.h>
namespace Usb {
class Session_rpc_object;
}
class Usb::Session_rpc_object : public Genode::Rpc_object<Session, Session_rpc_object>
{
protected:
Packet_stream_tx::Rpc_object<Tx> _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> _tx_cap() { return _tx.cap(); }
Tx::Sink *sink() { return _tx.sink(); }
};
#endif /* _INCLUDE__USB_SESSION__RPC_OBJECT_H_ */

View File

@ -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 <os/packet_stream.h>
#include <packet_stream_tx/packet_stream_tx.h>
#include <session/session.h>
#include <usb/types.h>
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<Usb::Packet_descriptor,
TX_QUEUE_SIZE, TX_QUEUE_SIZE,
char> Tx_policy;
typedef Packet_stream_tx::Channel<Tx_policy> 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>, _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_ */