usb_drv: change Usb session and raw driver

- Use 'label' attribute to identify device instead of
  bus/dev and vendor_id/product_id

- Implement release_interface RPC

- Report 'label' as well as 'bus' and 'dev'

- Add policy handling to raw driver (includes reconfiguration
  at runtime)

- Use own memory backing store for large DMA allocations

Issue #1863.
This commit is contained in:
Sebastian Sumpf 2016-01-06 14:43:21 +01:00 committed by Christian Helmuth
parent 3daa8a3d04
commit 716eab21e3
10 changed files with 244 additions and 67 deletions

View File

@ -208,17 +208,46 @@ Configuration snippet:
! </config>
!</start>
The optional 'devices' report lists the connected devices and gets updated when devices are added or removed.
The optional 'devices' report lists the connected devices and gets updated
when devices are added or removed.
Example report:
<devices>
<device vendor_id="0x17ef" product_id="0x4816"/>
<device vendor_id="0x0a5c" product_id="0x217f"/>
<device vendor_id="0x8087" product_id="0x0020"/>
<device vendor_id="0x8087" product_id="0x0020"/>
<device vendor_id="0x1d6b" product_id="0x0002"/>
<device vendor_id="0x1d6b" product_id="0x0002"/>
</devices>
!<devices>
! <device label="usb-1-7" vendor_id="0x1f75" product_id="0x0917" bus="0x0001" dev="0x0007"/>
! <device label="usb-1-6" vendor_id="0x13fe" product_id="0x5200" bus="0x0001" dev="0x0006"/>
! <device label="usb-1-4" vendor_id="0x17ef" product_id="0x4816" bus="0x0001" dev="0x0004"/>
! <device label="usb-1-3" vendor_id="0x0a5c" product_id="0x217f" bus="0x0001" dev="0x0003"/>
! <device label="usb-2-2" vendor_id="0x8087" product_id="0x0020" bus="0x0002" dev="0x0002"/>
! <device label="usb-1-2" vendor_id="0x8087" product_id="0x0020" bus="0x0001" dev="0x0002"/>
! <device label="usb-2-1" vendor_id="0x1d6b" product_id="0x0002" bus="0x0002" dev="0x0001"/>
! <device label="usb-1-1" vendor_id="0x1d6b" product_id="0x0002" bus="0x0001" dev="0x0001"/>
!</devices>
There is no distinction yet for multiple devices of the same type.
For every device a unique identifier is generated that is used to access the
USB device. Only devices that have a valid policy configured at the USB driver
can be accessed by a client. The following configuration allows 'comp1' to
access the device 'usb-1-6':
!<start name="usb_drv">
! <resource name="RAM" quantum="8M"/>
! <provides><service name="Usb"/></provides>
! <config uhci="yes" ehci="yes" xhci="yes">
! <raw>
! <report devices="yes"/>
! <policy label="comp1 -> usb-1-6" vendor_id="0x13fe" product_id="0x5200" bus="0x0001" dev="0x0006"/>
! </raw>
! </config>
!</start>
In addition to the mandatory 'label' attribute the policy node also
contains optional attribute tuples of which at least one has to be present.
The 'vendor_id' and 'product_id' tuple selects a device regardless of its
location on the USB bus and is mostly used in static configurations. The
'bus' and 'dev' tuple selects a specific device via its bus locations and
device address. It is mostly used in dynamic configurations because the device
address is not fixed and may change every time the same device is plugged in.
The configuration of the USB driver can be changed at runtime to satisfy
dynamic configurations or rather policies when using the 'Usb' session
interface.

View File

@ -1,9 +1,14 @@
set usb_raw_device ""
#
# Check if USB_RAW_DEVICE is set for Qemu
#
if {![info exists ::env(USB_RAW_DEVICE)] && [have_include power_on/qemu]} {
puts "\nPlease define USB_RAW_DEVICE environment variable and set it to your USB device <bus.device>\n"
exit 0
if {[have_include power_on/qemu]} {
if {![info exists ::env(USB_RAW_DEVICE)]} {
puts "\nPlease define USB_RAW_DEVICE environment variable and set it to your USB device <bus.device>\n"
exit 0
}
set usb_raw_device $::env(USB_RAW_DEVICE)
}
#
@ -67,7 +72,9 @@ append config {
<resource name="RAM" quantum="7M"/>
<provides><service name="Usb"/></provides>
<config uhci="yes" ehci="yes" xhci="yes">
<raw/>
<raw>
<policy label="usb_terminal -> usb_serial" vendor_id="0x67b" product_id="0x2303"/>
</raw>
</config>
</start>
<start name="usb_terminal">
@ -97,7 +104,7 @@ append_platform_drv_boot_modules
build_boot_image $boot_modules
append qemu_args " -m 256 -nographic -usb -usbdevice host:$::env(USB_RAW_DEVICE) -nographic"
append qemu_args " -m 256 -nographic -usb -usbdevice host:$usb_raw_device -nographic"
run_genode_until forever

View File

@ -930,6 +930,12 @@ void kfree(const void *);
void *kmalloc(size_t size, gfp_t flags);
void *kcalloc(size_t n, size_t size, gfp_t flags);
/**
* Genode specific for large DMA allocations
*/
void *dma_malloc(size_t size);
void dma_free(void *ptr);
struct kmem_cache;
/**

View File

@ -110,6 +110,11 @@ class Genode::Slab_backend_alloc : public Genode::Allocator,
return _range.alloc(size, out_addr);
}
void free(void *addr)
{
_range.free(addr);
}
void free(void *addr, size_t /* size */) override { }
size_t overhead(size_t size) const override { return 0; }
bool need_size_for_free() const override { return false; }
@ -303,6 +308,22 @@ class Malloc
_allocator[nr]->free((void *)(addr - 1));
}
void *alloc_large(size_t size)
{
void *addr;
if (!_back_allocator->alloc(size, &addr)) {
PERR("Large back end allocation failed (%zu bytes)", size);
return nullptr;
}
return addr;
}
void free_large(void *ptr)
{
_back_allocator->free(ptr);
}
Genode::addr_t phys_addr(void *a)
{
return _back_allocator->phys_addr((addr_t)a);
@ -376,6 +397,18 @@ void atomic_set(atomic_t *p, unsigned int v) { (*(volatile int *)p) = v; }
** Memory allocation, linux/slab.h **
*************************************/
void *dma_malloc(size_t size)
{
return Malloc::dma()->alloc_large(size);
}
void dma_free(void *ptr)
{
Malloc::dma()->free_large(ptr);
}
void *kmalloc(size_t size, gfp_t flags)
{
void *addr = flags & GFP_NOIO ? Malloc::dma()->alloc(size) : Malloc::mem()->alloc(size);

View File

@ -14,6 +14,7 @@
#include <base/env.h>
#include <base/printf.h>
#include <os/reporter.h>
#include <os/session_policy.h>
#include <root/component.h>
#include <usb_session/rpc_object.h>
#include <util/list.h>
@ -21,6 +22,7 @@
#include <extern_c_begin.h>
#include <linux/usb.h>
#include "raw.h"
#include "lx_emul.h"
#include <extern_c_end.h>
#include <signal.h>
@ -50,7 +52,7 @@ struct Device : List<Device>::Element
return &_l;
}
static Device * device(uint16_t vendor, uint16_t product)
static Device * device_product(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)
@ -60,6 +62,18 @@ struct Device : List<Device>::Element
return nullptr;
}
static Device * device_bus(long bus, long dev)
{
for (Device *d = list()->first(); d; d = d->next()) {
if (d->udev->bus->busnum == bus && d->udev->devnum == dev)
return d;
}
return nullptr;
}
static Genode::Reporter &device_list_reporter()
{
static Genode::Reporter _r("devices", 512*1024);
@ -74,7 +88,13 @@ struct Device : List<Device>::Element
for (Device *d = list()->first(); d; d = d->next()) {
xml.node("device", [&] ()
{
char buf[7];
char buf[16];
unsigned const bus = d->udev->bus->busnum;
unsigned const dev = d->udev->devnum;
Genode::snprintf(buf, sizeof(buf), "usb-%d-%d", bus, dev);
xml.attribute("label", buf);
Genode::snprintf(buf, sizeof(buf), "0x%4x",
d->udev->descriptor.idVendor);
@ -83,6 +103,12 @@ struct Device : List<Device>::Element
Genode::snprintf(buf, sizeof(buf), "0x%4x",
d->udev->descriptor.idProduct);
xml.attribute("product_id", buf);
Genode::snprintf(buf, sizeof(buf), "0x%4x", bus);
xml.attribute("bus", buf);
Genode::snprintf(buf, sizeof(buf), "0x%4x", dev);
xml.attribute("dev", buf);
});
}
});
@ -176,13 +202,9 @@ class Usb::Worker
kfree(buf);
if (err < 0 && err != -EPIPE) {
p.succeded = false;
return;
}
p.control.actual_size = err;
p.succeded = true;
p.succeded = (err < 0 && err != -EPIPE) ? false : true;
}
/**
@ -224,7 +246,7 @@ class Usb::Worker
if (read)
Genode::memcpy(_sink->packet_content(p), urb->transfer_buffer,
urb->actual_length);
urb->actual_length);
}
_ack_packet(p);
@ -237,7 +259,7 @@ class Usb::Worker
data->worker->_async_finish(data->packet, urb,
!!(data->packet.transfer.ep & USB_DIR_IN));
kfree (data);
kfree (urb->transfer_buffer);
dma_free(urb->transfer_buffer);
usb_free_urb(urb);
}
@ -247,7 +269,7 @@ class Usb::Worker
bool _bulk(Packet_descriptor &p, bool read)
{
unsigned pipe;
void *buf = kmalloc(p.size(), GFP_NOIO);
void *buf = dma_malloc(p.size());
if (read)
pipe = usb_rcvbulkpipe(_device->udev, p.transfer.ep);
@ -282,7 +304,7 @@ class Usb::Worker
bool _irq(Packet_descriptor &p, bool read)
{
unsigned pipe;
void *buf = kmalloc(p.size(), GFP_NOIO);
void *buf = dma_malloc(p.size());
if (read)
pipe = usb_rcvintpipe(_device->udev, p.transfer.ep);
@ -501,11 +523,14 @@ class Usb::Session_component : public Session_rpc_object,
Server::Entrypoint &_ep;
unsigned long _vendor;
unsigned long _product;
long _bus = 0;
long _dev = 0;
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;
Ram_dataspace_capability _tx_ds;
void _signal_state_change()
@ -526,18 +551,22 @@ class Usb::Session_component : public Session_rpc_object,
DEVICE_REMOVE,
};
Session_component(Genode::Dataspace_capability tx_ds, Server::Entrypoint &ep,
unsigned long vendor, unsigned long product)
Session_component(Genode::Ram_dataspace_capability tx_ds, Server::Entrypoint &ep,
unsigned long vendor, unsigned long product,
long bus, long dev)
: Session_rpc_object(tx_ds, ep.rpc_ep()),
_ep(ep),
_vendor(vendor), _product(product),
_vendor(vendor), _product(product), _bus(bus), _dev(dev),
_packet_avail(ep, *this, &Session_component::_receive),
_ready_ack(ep, *this, &Session_component::_receive),
_worker(sink())
_worker(sink()), _tx_ds(tx_ds)
{
Device *device = Device::device(_vendor, _product);
Device *device;
if (bus && dev)
device = Device::device_bus(bus, dev);
else
device = Device::device_product(_vendor, _product);
if (device) {
PDBG("Found device");
state_change(DEVICE_ADD, device);
}
@ -566,6 +595,15 @@ class Usb::Session_component : public Session_rpc_object,
throw Interface_already_claimed();
}
void release_interface(unsigned interface_num) override
{
usb_interface *iface = _device->interface(interface_num);
if (!iface)
throw Interface_not_found();
usb_driver_release_interface(&raw_intf_driver, iface);
}
void config_descriptor(Device_descriptor *device_descr,
Config_descriptor *config_descr) override
{
@ -622,7 +660,9 @@ class Usb::Session_component : public Session_rpc_object,
bool session_device(Device *device)
{
usb_device_descriptor *descr = &device->udev->descriptor;
return (descr->idVendor == _vendor && descr->idProduct == _product) ? true : false;
return (descr->idVendor == _vendor && descr->idProduct == _product)
|| (_bus && _dev && _bus == device->udev->bus->busnum &&
_dev == device->udev->devnum) ? true : false;
}
bool state_change(State state, Device *device)
@ -661,6 +701,8 @@ class Usb::Session_component : public Session_rpc_object,
if (_worker.device_ready())
Signal_transmitter(_sigh_state_change).submit(1);
}
Ram_dataspace_capability tx_ds() { return _tx_ds; }
};
@ -687,40 +729,83 @@ class Usb::Root : public Genode::Root_component<Session_component>
Server::Entrypoint &_ep;
Genode::Signal_rpc_member<Usb::Root> _config_dispatcher = {
_ep, *this, &Usb::Root::_handle_config };
Genode::Reporter _config_reporter { "config" };
void _handle_config(unsigned)
{
Genode::config()->reload();
Genode::Xml_node config = Genode::config()->xml_node();
if (!_config_reporter.is_enabled())
_config_reporter.enabled(true);
bool const uhci = config.attribute_value<bool>("uhci", false);
bool const ehci = config.attribute_value<bool>("ehci", false);
bool const xhci = config.attribute_value<bool>("xhci", false);
Genode::Reporter::Xml_generator xml(_config_reporter, [&] {
if (uhci) xml.attribute("uhci", "yes");
if (ehci) xml.attribute("ehci", "yes");
if (xhci) xml.attribute("xhci", "yes");
xml.append(config.content_base(), config.content_size());
});
}
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);
try {
Xml_node raw = Genode::config()->xml_node().sub_node("raw");
Genode::Session_label label(args);
Genode::Session_policy policy(label, raw);
unsigned long vendor = Arg_string::find_arg(args, "vendor").ulong_value(0);
unsigned long product = Arg_string::find_arg(args, "product").ulong_value(0);
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);
/* check session quota */
size_t session_size = max<size_t>(4096, sizeof(Session_component));
if (ram_quota < session_size)
throw Root::Quota_exceeded();
unsigned long vendor = policy.attribute_value<unsigned long>("vendor", 0);
unsigned long product = policy.attribute_value<unsigned long>("product", 0);
unsigned long bus = policy.attribute_value<unsigned long>("bus", 0);
unsigned long dev = policy.attribute_value<unsigned long>("dev", 0);
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();
/* 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();
}
Ram_dataspace_capability tx_ds = env()->ram_session()->alloc(tx_buf_size);
Session_component *session = new (md_alloc())
Session_component(tx_ds, _ep, vendor, product, bus, dev);
::Session::list()->insert(session);
return session;
} catch (Genode::Session_policy::No_policy_defined) {
PERR("Invalid session request, no matching policy for '%s'",
Genode::Session_label(args).string());
throw Genode::Root::Unavailable();
}
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;
}
void _destroy_session(Session_component *session)
{
Ram_dataspace_capability tx_ds = session->tx_ds();
::Session::list()->remove(session);
Genode::Root_component<Session_component>::_destroy_session(session);
Genode::env()->ram_session()->free(tx_ds);
}
public:
@ -728,7 +813,10 @@ class Usb::Root : public Genode::Root_component<Session_component>
Root(Server::Entrypoint &session_ep,
Genode::Allocator *md_alloc)
: Genode::Root_component<Session_component>(&session_ep.rpc_ep(), md_alloc),
_ep(session_ep) { }
_ep(session_ep)
{
Genode::config()->sigh(_config_dispatcher);
}
};
@ -753,14 +841,14 @@ int raw_notify(struct notifier_block *nb, unsigned long action, void *data)
case USB_DEVICE_ADD:
{
::Session::list()->state_change(Usb::Session_component::DEVICE_ADD,
new (env()->heap()) Device(udev));
new (env()->heap()) Device(udev));
break;
}
case USB_DEVICE_REMOVE:
{
Device *dev = Device::device(udev->descriptor.idVendor,
udev->descriptor.idProduct);
Device *dev = Device::device_bus(udev->bus->busnum,
udev->devnum);
if (dev) {
::Session::list()->state_change(Usb::Session_component::DEVICE_REMOVE, dev);
destroy(env()->heap(), dev);

View File

@ -62,7 +62,7 @@ struct Usb::Pl2303_driver : Completion
Server::Entrypoint &ep;
Server::Signal_rpc_member<Pl2303_driver> dispatcher{ ep, *this, &Pl2303_driver::state_change };
Genode::Allocator_avl alloc;
Usb::Connection connection{ &alloc, VENDOR, PRODUCT, 512 * 1024, dispatcher };
Usb::Connection connection{ &alloc, "usb_serial", 512 * 1024, dispatcher };
Usb::Device device;
Signal_context_capability connected_sigh;
Signal_context_capability read_sigh;

View File

@ -87,10 +87,15 @@ class Usb::Session_client : public Genode::Rpc_client<Session>
call<Rpc_ep_descr>(interface_num, alt_setting, endpoint_num, endpoint_descr);
}
void claim_interface(unsigned interface_num)
{
call<Rpc_claim_interface>(interface_num);
}
void claim_interface(unsigned interface_num)
{
call<Rpc_claim_interface>(interface_num);
}
void release_interface(unsigned interface_num)
{
call<Rpc_release_interface>(interface_num);
}
};
#endif /* _INCLUDE__USB_SESSION__CLIENT_H_ */

View File

@ -22,14 +22,17 @@ namespace Usb { struct Connection; }
struct Usb::Connection : Genode::Connection<Session>, Session_client
{
/**
* Connect to a USB device.
*/
Connection(Genode::Range_allocator *tx_block_alloc,
unsigned long vendor_id, unsigned long product_id,
char const *label = "",
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)),
Genode::Connection<Session>(session("ram_quota=%zd, tx_buf_size=%zd, label=\"%s\"",
3 * 4096 + tx_buf_size, tx_buf_size, label)),
Session_client(cap(), tx_block_alloc, sigh_state_changed)
{ }
};

View File

@ -175,7 +175,12 @@ struct Usb::Session : public Genode::Session
/**
* Claim an interface number
*/
virtual void claim_interface(unsigned interface_num) = 0;
virtual void claim_interface(unsigned interface_num) = 0;
/**
* Release an interface number
*/
virtual void release_interface(unsigned interface_num) = 0;
GENODE_RPC(Rpc_plugged, bool, plugged);
GENODE_RPC(Rpc_sigh_state_change, void, sigh_state_change, Signal_context_capability);
@ -192,7 +197,8 @@ struct Usb::Session : public Genode::Session
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);
Rpc_iface_descr, Rpc_ep_descr, Rpc_alt_settings, Rpc_claim_interface,
Rpc_release_interface);
};
#endif /* _INCLUDE__USB_SESSION__USB_SESSION_H_ */

View File

@ -51,7 +51,7 @@ namespace Usb_proxy_device_genode {
Data(unsigned int vendor_id, unsigned int product_id)
: _alloc(Genode::env()->heap()),
usb_connection(&_alloc, vendor_id, product_id)
usb_connection(&_alloc)
{
/* wait until device and server are ready */