diff --git a/repos/dde_linux/README b/repos/dde_linux/README
index 0d27793ddd..2e8a8d6f9a 100644
--- a/repos/dde_linux/README
+++ b/repos/dde_linux/README
@@ -208,17 +208,46 @@ Configuration snippet:
!
!
-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:
-
-
-
-
-
-
-
-
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
-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':
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+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.
diff --git a/repos/dde_linux/run/usb_terminal.run b/repos/dde_linux/run/usb_terminal.run
index ab0915d2c0..4b989d98fa 100644
--- a/repos/dde_linux/run/usb_terminal.run
+++ b/repos/dde_linux/run/usb_terminal.run
@@ -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 \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 \n"
+ exit 0
+ }
+ set usb_raw_device $::env(USB_RAW_DEVICE)
}
#
@@ -67,7 +72,9 @@ append config {
-
+
+
+
@@ -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
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 1580bdd731..758aa552b5 100644
--- a/repos/dde_linux/src/lib/usb/include/lx_emul.h
+++ b/repos/dde_linux/src/lib/usb/include/lx_emul.h
@@ -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;
/**
diff --git a/repos/dde_linux/src/lib/usb/lx_emul.cc b/repos/dde_linux/src/lib/usb/lx_emul.cc
index 03c60c0b61..b69446b229 100644
--- a/repos/dde_linux/src/lib/usb/lx_emul.cc
+++ b/repos/dde_linux/src/lib/usb/lx_emul.cc
@@ -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);
diff --git a/repos/dde_linux/src/lib/usb/raw/raw.cc b/repos/dde_linux/src/lib/usb/raw/raw.cc
index 581a16c4e2..419c5dee6b 100644
--- a/repos/dde_linux/src/lib/usb/raw/raw.cc
+++ b/repos/dde_linux/src/lib/usb/raw/raw.cc
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -21,6 +22,7 @@
#include
#include
#include "raw.h"
+#include "lx_emul.h"
#include
#include
@@ -50,7 +52,7 @@ struct Device : List::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::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::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::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 _packet_avail;
Signal_rpc_member _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
Server::Entrypoint &_ep;
+ Genode::Signal_rpc_member _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("uhci", false);
+ bool const ehci = config.attribute_value("ehci", false);
+ bool const xhci = config.attribute_value("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(4096, sizeof(Session_component));
- if (ram_quota < session_size)
- throw Root::Quota_exceeded();
+ unsigned long vendor = policy.attribute_value("vendor", 0);
+ unsigned long product = policy.attribute_value("product", 0);
+ unsigned long bus = policy.attribute_value("bus", 0);
+ unsigned long dev = policy.attribute_value("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(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::_destroy_session(session);
+
+ Genode::env()->ram_session()->free(tx_ds);
}
public:
@@ -728,7 +813,10 @@ class Usb::Root : public Genode::Root_component
Root(Server::Entrypoint &session_ep,
Genode::Allocator *md_alloc)
: Genode::Root_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);
diff --git a/repos/dde_linux/src/server/usb_terminal/main.cc b/repos/dde_linux/src/server/usb_terminal/main.cc
index cda520c371..1f4441da53 100644
--- a/repos/dde_linux/src/server/usb_terminal/main.cc
+++ b/repos/dde_linux/src/server/usb_terminal/main.cc
@@ -62,7 +62,7 @@ struct Usb::Pl2303_driver : Completion
Server::Entrypoint &ep;
Server::Signal_rpc_member 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;
diff --git a/repos/os/include/usb_session/client.h b/repos/os/include/usb_session/client.h
index 3126321a6f..d52a13aa02 100644
--- a/repos/os/include/usb_session/client.h
+++ b/repos/os/include/usb_session/client.h
@@ -87,10 +87,15 @@ class Usb::Session_client : public Genode::Rpc_client
call(interface_num, alt_setting, endpoint_num, endpoint_descr);
}
- void claim_interface(unsigned interface_num)
- {
- call(interface_num);
- }
+ void claim_interface(unsigned interface_num)
+ {
+ call(interface_num);
+ }
+
+ void release_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
index 997695bf7b..1e1e7fcfed 100644
--- a/repos/os/include/usb_session/connection.h
+++ b/repos/os/include/usb_session/connection.h
@@ -22,14 +22,17 @@ namespace Usb { struct Connection; }
struct Usb::Connection : Genode::Connection, 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("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("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)
{ }
};
diff --git a/repos/os/include/usb_session/usb_session.h b/repos/os/include/usb_session/usb_session.h
index c197617c7f..d1acd9b03f 100644
--- a/repos/os/include/usb_session/usb_session.h
+++ b/repos/os/include/usb_session/usb_session.h
@@ -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_ */
diff --git a/repos/ports/src/virtualbox/frontend/USBProxyDevice-genode.cpp b/repos/ports/src/virtualbox/frontend/USBProxyDevice-genode.cpp
index 9e54dbc2ec..6afc0e9f96 100644
--- a/repos/ports/src/virtualbox/frontend/USBProxyDevice-genode.cpp
+++ b/repos/ports/src/virtualbox/frontend/USBProxyDevice-genode.cpp
@@ -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 */