From 716eab21e36610b50de7250391bb0c50270e8a5e Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Wed, 6 Jan 2016 14:43:21 +0100 Subject: [PATCH] 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. --- repos/dde_linux/README | 49 +++-- repos/dde_linux/run/usb_terminal.run | 17 +- repos/dde_linux/src/lib/usb/include/lx_emul.h | 6 + repos/dde_linux/src/lib/usb/lx_emul.cc | 33 ++++ repos/dde_linux/src/lib/usb/raw/raw.cc | 170 +++++++++++++----- .../dde_linux/src/server/usb_terminal/main.cc | 2 +- repos/os/include/usb_session/client.h | 13 +- repos/os/include/usb_session/connection.h | 9 +- repos/os/include/usb_session/usb_session.h | 10 +- .../frontend/USBProxyDevice-genode.cpp | 2 +- 10 files changed, 244 insertions(+), 67 deletions(-) diff --git a/repos/dde_linux/README b/repos/dde_linux/README index 0d27793dd..2e8a8d6f9 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 ab0915d2c..4b989d98f 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 1580bdd73..758aa552b 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 03c60c0b6..b69446b22 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 581a16c4e..419c5dee6 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 cda520c37..1f4441da5 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 3126321a6..d52a13aa0 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 997695bf7..1e1e7fcfe 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 c197617c7..d1acd9b03 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 9e54dbc2e..6afc0e9f9 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 */