From 954316182798ac2420de3ec8aa331bb27282c2c2 Mon Sep 17 00:00:00 2001
From: Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
Date: Sun, 9 May 2021 05:04:04 +0200
Subject: [PATCH] usb_host: Move task to device in USB session

A Lx::Task is now associated to a USB device not the session any more.
This implies that a task lives as long as the device making it possible
to gracefully handle outstanding requests (i.e., synchronous) in case
the session has been closed.

issue #4149
---
 repos/dde_linux/src/drivers/usb_host/raw.cc | 611 ++++++++++----------
 1 file changed, 300 insertions(+), 311 deletions(-)

diff --git a/repos/dde_linux/src/drivers/usb_host/raw.cc b/repos/dde_linux/src/drivers/usb_host/raw.cc
index b804e2bde0..9476809077 100644
--- a/repos/dde_linux/src/drivers/usb_host/raw.cc
+++ b/repos/dde_linux/src/drivers/usb_host/raw.cc
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) 2014-2017 Genode Labs GmbH
+ * Copyright (C) 2014-2021 Genode Labs GmbH
  *
  * This file is distributed under the terms of the GNU General Public License
  * version 2.
@@ -38,200 +38,69 @@ constexpr bool verbose_raw = false;
 namespace Usb {
 	class  Session_component;
 	class  Root;
-	class  Worker;
 	class  Cleaner;
 }
 
 /**
  * Keep track of all registered USB devices (via raw driver)
  */
-struct Device : List<Device>::Element
+class Device : public List<Device>::Element
 {
-	usb_device *udev;
+	using Tx = Usb::Session_rpc_object::Tx;
+	using Packet_descriptor = Usb::Packet_descriptor;
 
-	static List<Device> *list()
-	{
-		static List<Device> _l;
-		return &_l;
-	}
-
-	static Device * device_by_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)
-				return d;
-		}
-
-		return nullptr;
-	}
-
-
-	static Device * device_by_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 Device * device_by_class(long class_value, Session_label label)
-	{
-		for (Device *d = list()->first(); d; d = d->next()) {
-			if (class_value ==  d->device_class_value() && label == d->label())
-				return d;
-		}
-
-		return nullptr;
-	}
-
-	static void report_device_list();
-
-	Device(usb_device *udev) : udev(udev)
-	{
-		list()->insert(this);
-		report_device_list();
-	}
-
-	~Device()
-	{
-		list()->remove(this);
-		report_device_list();
-	}
-
-	usb_interface *interface(unsigned index)
-	{
-		if (!udev || !udev->actconfig)
-			return nullptr;
-
-		if (index >= udev->actconfig->desc.bNumInterfaces)
-			return nullptr;
-
-		usb_interface *iface = udev->actconfig->interface[index];
-		return iface;
-	}
-
-	template <typename FN>
-	void _with_interface(unsigned index, FN const &fn)
-	{
-		if (!udev || !udev->actconfig)
-			return;
-
-		if (index >= udev->actconfig->desc.bNumInterfaces)
-			return;
-
-		usb_interface * const interface_ptr = udev->actconfig->interface[index];
-
-		if (!interface_ptr)
-			return;
-
-		fn(*interface_ptr);
-	}
-
-	template <typename FN>
-	void _for_each_interface(FN const &fn)
-	{
-		if (!udev || !udev->actconfig)
-			return;
-
-		for (unsigned i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
-			_with_interface(i, fn);
-	}
-
-	/**
-	 * Return pseudo device class of USB device
-	 *
-	 * The returned value expresses the type of USB device. If the device
-	 * has at least one HID interface, the value is USB_CLASS_HID. Otherwise,
-	 * the class of the first interface is interpreted at type the device.
-	 *
-	 * Note this classification of USB devices is meant as an interim solution
-	 * only to assist the implementation of access-control policies.
-	 */
-	unsigned device_class_value()
-	{
-		unsigned result = 0;
-
-		_with_interface(0, [&] (usb_interface &interface) {
-			if (interface.cur_altsetting)
-				result = interface.cur_altsetting->desc.bInterfaceClass; });
-
-		_for_each_interface([&] (usb_interface &interface) {
-			if (interface.cur_altsetting)
-				if (interface.cur_altsetting->desc.bInterfaceClass == USB_CLASS_HID)
-					result = USB_CLASS_HID; });
-
-		return result;
-	}
-
-	void report(Xml_generator &xml)
-	{
-		if (!udev || !udev->actconfig)
-			return;
-
-		using namespace Genode;
-
-		using Value = String<64>;
-
-		xml.attribute("label",      label());
-		xml.attribute("vendor_id",  Value(Hex(udev->descriptor.idVendor)));
-		xml.attribute("product_id", Value(Hex(udev->descriptor.idProduct)));
-		xml.attribute("bus",        Value(Hex(udev->bus->busnum)));
-		xml.attribute("dev",        Value(Hex(udev->devnum)));
-		xml.attribute("class",      Value(Hex(device_class_value())));
-
-		_for_each_interface([&] (usb_interface &interface) {
-
-			if (!interface.cur_altsetting)
-				return;
-
-			xml.node("interface", [&] () {
-
-				uint8_t const class_value =
-					interface.cur_altsetting->desc.bInterfaceClass;
-
-				xml.attribute("class", Value(Hex(class_value)));
-			});
-		});
-	}
-
-	usb_host_endpoint *endpoint(usb_interface *iface, unsigned alt_setting,
-	                            unsigned endpoint_num)
-	{
-		return &iface->altsetting[alt_setting].endpoint[endpoint_num];
-	}
-
-	Session_label label()
-	{
-		if (!udev || !udev->bus)
-			return Session_label("usb-unknown");
-		return Session_label("usb-", udev->bus->busnum, "-", udev->devnum);
-	}
-};
-
-
-/**
- * Handle packet stream request, this way the entrypoint always returns to it's
- * server loop
- */
-class Usb::Worker : public Genode::Weak_object<Usb::Worker>
-{
 	private:
 
-		completion _packet_avail;
+		usb_device &_udev;
+		Lx::Task    _task { _run, this, "device_worker",
+		                    Lx::Task::PRIORITY_2,
+		                    Lx::scheduler() };
+		completion  _packet_avail;
+		Tx::Sink   *_sink { nullptr };
 
-		Session::Tx::Sink        *_sink;
-		Device                   *_device      = nullptr;
 		Signal_context_capability _sigh_ready;
-		Lx::Task                 *_task        = nullptr;
-		unsigned                  _p_in_flight = 0;
-		bool                      _device_ready = false;
+		unsigned                  _p_in_flight  { 0 };
 
-		void _ack_packet(Packet_descriptor &p)
+		template<typename FN>
+		void _with_packet_stream(FN const &fn)
 		{
-			_sink->acknowledge_packet(p);
-			_p_in_flight--;
+			if (_sink == nullptr) return;
+			fn(*_sink);
+		}
+
+		template <typename FN>
+		void _with_interface(unsigned index, FN const &fn)
+		{
+			if (!_udev.actconfig)
+				return;
+
+			if (index >= _udev.actconfig->desc.bNumInterfaces)
+				return;
+
+			usb_interface * const interface_ptr = _udev.actconfig->interface[index];
+
+			if (!interface_ptr)
+				return;
+
+			fn(*interface_ptr);
+		}
+
+		template <typename FN>
+		void _for_each_interface(FN const &fn)
+		{
+			if (!_udev.actconfig)
+				return;
+
+			for (unsigned i = 0; i < _udev.actconfig->desc.bNumInterfaces; i++)
+				_with_interface(i, fn);
+		}
+
+		void _ack_packet(Packet_descriptor p)
+		{
+			_with_packet_stream([&](Tx::Sink &sink) {
+				sink.acknowledge_packet(p);
+				_p_in_flight--;
+			});
 		}
 
 		/**
@@ -242,7 +111,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			char *buffer = _sink->packet_content(p);
 			int   length;
 
-			if ((length = usb_string(_device->udev, p.string.index, buffer, p.size())) < 0) {
+			if ((length = usb_string(&_udev, p.string.index, buffer, p.size())) < 0) {
 				warning("Could not read string descriptor index: ", (unsigned)p.string.index);
 				p.string.length = 0;
 			} else {
@@ -259,13 +128,16 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		{
 			void *buf = kmalloc(4096, GFP_NOIO);
 
-			int err = usb_control_msg(_device->udev, usb_rcvctrlpipe(_device->udev, 0),
+			int err = usb_control_msg(&_udev, usb_rcvctrlpipe(&_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);
+			if (err > 0 && p.size()) {
+				_with_packet_stream([&](Tx::Sink &sink) {
+					Genode::memcpy(sink.packet_content(p), buf, err);
+				});
+			}
 
 			kfree(buf);
 
@@ -302,7 +174,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			if (p.size())
 				Genode::memcpy(buf, _sink->packet_content(p), p.size());
 
-			int err = usb_control_msg(_device->udev, usb_sndctrlpipe(_device->udev, 0),
+			int err = usb_control_msg(&_udev, usb_sndctrlpipe(&_udev, 0),
 			                          p.control.request, p.control.request_type,
 			                          p.control.value, p.control.index, buf, p.size(),
 			                          p.control.timeout);
@@ -313,7 +185,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 
 				if (p.control.request == USB_REQ_CLEAR_FEATURE &&
 				    p.control.value == USB_ENDPOINT_HALT) {
-					usb_reset_endpoint(_device->udev, p.control.index);
+					usb_reset_endpoint(&_udev, p.control.index);
 				}
 			} else {
 				p.control.actual_size = 0;
@@ -342,24 +214,23 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		 */
 		struct Complete_data
 		{
-			Weak_ptr<Worker>   worker;
+			Device            *device;
 			Packet_descriptor  packet;
 
-			Complete_data(Weak_ptr<Worker> &w, Packet_descriptor &p)
-			: worker(w), packet(p) { }
+			Complete_data(Device *device, Packet_descriptor &p)
+			: device(device), packet(p) { }
 		};
 
 		Complete_data * alloc_complete_data(Packet_descriptor &p)
 		{
 			void * data = kmalloc(sizeof(Complete_data), GFP_KERNEL);
-			construct_at<Complete_data>(data, this->weak_ptr(), p);
+			construct_at<Complete_data>(data, this, p);
 			return reinterpret_cast<Complete_data *>(data);
 		}
 
 		static void free_complete_data(Complete_data *data)
 		{
 			data->packet.~Packet_descriptor();
-			data->worker.~Weak_ptr<Worker>();
 			kfree (data);
 		}
 
@@ -380,8 +251,10 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 					 * controller used the offsets into the original buffer to
 					 * store the data.
 					 */
-					Genode::memcpy(_sink->packet_content(p), urb->transfer_buffer,
-					               urb->transfer_buffer_length);
+					_with_packet_stream([&](Tx::Sink &sink) {
+						Genode::memcpy(sink.packet_content(p), urb->transfer_buffer,
+						               urb->transfer_buffer_length);
+					});
 				}
 			} else if (urb->status == -ESHUTDOWN) {
 				p.error = Packet_descriptor::NO_DEVICE_ERROR;
@@ -401,13 +274,8 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		{
 			Complete_data *data = (Complete_data *)urb->context;
 
-			{
-				Locked_ptr<Worker> worker(data->worker);
-
-				if (worker.valid())
-					worker->_async_finish(data->packet, urb,
-					                      !!(data->packet.transfer.ep & USB_DIR_IN));
-			}
+			data->device->_async_finish(data->packet, urb,
+			                            !!(data->packet.transfer.ep & USB_DIR_IN));
 
 			free_complete_data(data);
 			dma_free(urb->transfer_buffer);
@@ -423,9 +291,9 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			void    *buf = dma_malloc(p.size());
 
 			if (read)
-				pipe = usb_rcvbulkpipe(_device->udev, p.transfer.ep);
+				pipe = usb_rcvbulkpipe(&_udev, p.transfer.ep);
 			else {
-				pipe = usb_sndbulkpipe(_device->udev, p.transfer.ep);
+				pipe = usb_sndbulkpipe(&_udev, p.transfer.ep);
 				Genode::memcpy(buf, _sink->packet_content(p), p.size());
 			}
 
@@ -433,13 +301,13 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			if (!bulk_urb) {
 				error("Failed to allocate bulk URB");
 				dma_free(buf);
-				p.error = Usb::Packet_descriptor::MEMORY_ERROR;
+				p.error = Packet_descriptor::MEMORY_ERROR;
 				return false;
 			}
 
 			Complete_data *data = alloc_complete_data(p);
 
-			usb_fill_bulk_urb(bulk_urb, _device->udev, pipe, buf, p.size(),
+			usb_fill_bulk_urb(bulk_urb, &_udev, pipe, buf, p.size(),
 			                 _async_complete, data);
 
 			int ret = usb_submit_urb(bulk_urb, GFP_KERNEL);
@@ -471,9 +339,9 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			void    *buf = dma_malloc(p.size());
 
 			if (read)
-				pipe = usb_rcvintpipe(_device->udev, p.transfer.ep);
+				pipe = usb_rcvintpipe(&_udev, p.transfer.ep);
 			else {
-				pipe = usb_sndintpipe(_device->udev, p.transfer.ep);
+				pipe = usb_sndintpipe(&_udev, p.transfer.ep);
 				Genode::memcpy(buf, _sink->packet_content(p), p.size());
 			}
 
@@ -491,8 +359,8 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 
 			if (p.transfer.polling_interval == Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL) {
 
-				usb_host_endpoint *ep = read ? _device->udev->ep_in[p.transfer.ep & 0x0f]
-				                             : _device->udev->ep_out[p.transfer.ep & 0x0f];
+				usb_host_endpoint *ep = read ? _udev.ep_in[p.transfer.ep & 0x0f]
+				                             : _udev.ep_out[p.transfer.ep & 0x0f];
 
 				if (!ep) {
 					error("could not get ep: ", p.transfer.ep);
@@ -506,7 +374,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			} else
 				polling_interval = p.transfer.polling_interval;
 
-			usb_fill_int_urb(irq_urb, _device->udev, pipe, buf, p.size(),
+			usb_fill_int_urb(irq_urb, &_udev, pipe, buf, p.size(),
 			                 _async_complete, data, polling_interval);
 
 			int ret = usb_submit_urb(irq_urb, GFP_KERNEL);
@@ -551,12 +419,12 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			void              *buf = dma_malloc(p.size());
 
 			if (read) {
-				pipe = usb_rcvisocpipe(_device->udev, p.transfer.ep);
-				ep   = _device->udev->ep_in[p.transfer.ep & 0x0f];
+				pipe = usb_rcvisocpipe(&_udev, p.transfer.ep);
+				ep   = _udev.ep_in[p.transfer.ep & 0x0f];
 			}
 			else {
-				pipe = usb_sndisocpipe(_device->udev, p.transfer.ep);
-				ep   = _device->udev->ep_out[p.transfer.ep & 0x0f];
+				pipe = usb_sndisocpipe(&_udev, p.transfer.ep);
+				ep   = _udev.ep_out[p.transfer.ep & 0x0f];
 				Genode::memcpy(buf, _sink->packet_content(p), p.size());
 			}
 
@@ -576,7 +444,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			}
 
 			Complete_data *data         = alloc_complete_data(p);
-			urb->dev                    = _device->udev;
+			urb->dev                    = &_udev;
 			urb->pipe                   = pipe;
 			urb->start_frame            = -1;
 			urb->stream_id              = 0;
@@ -619,8 +487,10 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		 */
 		void _alt_setting(Packet_descriptor &p)
 		{
-			int err = usb_set_interface(_device->udev, p.interface.number,
+
+			int err = usb_set_interface(&_udev, p.interface.number,
 			                            p.interface.alt_setting);
+
 			if (!err)
 				p.succeded = true;
 			else
@@ -632,7 +502,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		 */
 		void _config(Packet_descriptor &p)
 		{
-			usb_host_config *config = _device->udev->actconfig;
+			usb_host_config *config = _udev.actconfig;
 
 			if (config) {
 				for (unsigned i = 0; i < config->desc.bNumInterfaces; i++) {
@@ -643,7 +513,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 				}
 			}
 
-			int err = usb_set_configuration(_device->udev, p.number);
+			int err = usb_set_configuration(&_udev, p.number);
 
 			if (!err)
 				p.succeded = true;
@@ -656,7 +526,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		 */
 		void _release_interface(Packet_descriptor &p)
 		{
-			usb_interface *iface = _device->interface(p.number);
+			usb_interface *iface = interface(p.number);
 
 			if (!iface)
 				return;
@@ -670,12 +540,11 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		 */
 		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())
-			{
+				/*
+				 * Get packets until there are no more free ack slots or avaiable
+				 * packets
+				 */
+			while (_sink && _p_in_flight < _sink->ack_slots_free() && _sink->packet_avail()) {
 				Packet_descriptor p = _sink->get_packet();
 
 				if (verbose_raw)
@@ -683,13 +552,6 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 
 				_p_in_flight++;
 
-				if (!_device || !_device->udev ||
-				    _device->udev->state == USB_STATE_NOTATTACHED) {
-				    p.error = Packet_descriptor::NO_DEVICE_ERROR;
-				    _ack_packet(p);
-				    continue;
-				}
-
 				if (!_sink->packet_valid(p)) {
 					p.error = Packet_descriptor::PACKET_INVALID_ERROR;
 					_ack_packet(p);
@@ -741,18 +603,6 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			}
 		}
 
-		void _wait_for_device()
-		{
-			wait_queue_head_t wait;
-			_wait_event(wait, _device);
-			_wait_event(wait, _device->udev->actconfig);
-
-			if (_sigh_ready.valid())
-				Signal_transmitter(_sigh_ready).submit(1);
-
-			_device_ready = true;
-		}
-
 		/**
 		 * Wait for packets
 		 */
@@ -760,7 +610,11 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 		{
 			/* wait for device to become ready */
 			init_completion(&_packet_avail);
-			_wait_for_device();
+			wait_queue_head_t wait;
+			_wait_event(wait, ready());
+
+			if (_sigh_ready.valid())
+				Signal_transmitter(_sigh_ready).submit();
 
 			while (true) {
 				wait_for_completion(&_packet_avail);
@@ -768,53 +622,183 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
 			}
 		}
 
+		static void _run(void *data)
+		{
+			Device *device = static_cast<Device *>(data);
+			device->_wait();
+		}
+
 	public:
 
-		static void run(void *worker)
+		Device(usb_device &udev) : _udev(udev)
 		{
-			Worker *w = static_cast<Worker *>(worker);
-			w->_wait();
+			list()->insert(this);
+
+			if (!Lx::scheduler().active()) {
+				Lx::scheduler().schedule(); }
+
+			report_device_list();
 		}
 
-		Worker(Session::Tx::Sink *sink)
-		: _sink(sink)
-		{ }
-
-		~Worker()
+		~Device()
 		{
-			Weak_object<Worker>::lock_for_destruction();
-		}
-
-		void start()
-		{
-			if (!_task) {
-				_task = new (Lx::Malloc::mem()) Lx::Task(run, this, "raw_worker",
-				                                              Lx::Task::PRIORITY_2,
-				                                              Lx::scheduler());
-				if (!Lx::scheduler().active()) {
-					Lx::scheduler().schedule();
-				}
-			}
-		}
-
-		void stop()
-		{
-			if (_task) {
-				Lx::scheduler().remove(_task);
-				destroy(Lx::Malloc::mem(), _task);
-				_task = nullptr;
-			}
+			Lx::scheduler().remove(&_task);
+			list()->remove(this);
+			report_device_list();
 		}
 
 		void packet_avail() { ::complete(&_packet_avail); }
 
-		void device(Device *device, Signal_context_capability sigh_ready = Signal_context_capability())
+		unsigned num_interfaces() const
 		{
-			_device       = device;
-			_sigh_ready   = sigh_ready;
+			if (ready())
+				return _udev.actconfig->desc.bNumInterfaces;
+
+			return 0;
 		}
 
-		bool device_ready() { return _device_ready; }
+		void packet_stream(Tx::Sink *sink)
+		{
+			_sink        = sink;
+			_p_in_flight = 0;
+
+			if (!_sink) {
+				_sigh_ready = Signal_context_capability { };
+			}
+		}
+
+		bool ready() const { return _udev.actconfig; }
+
+		void sigh_ready(Signal_context_capability sigh_ready)
+		{
+			_sigh_ready = sigh_ready;
+		}
+
+		static List<Device> *list()
+		{
+			static List<Device> _l;
+			return &_l;
+		}
+
+		static Device * device_by_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)
+					return d;
+			}
+
+			return nullptr;
+		}
+
+		static Device * device_by_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 Device * device_by_class(long class_value, Session_label label)
+		{
+			for (Device *d = list()->first(); d; d = d->next()) {
+				if (class_value ==  d->device_class_value() && label == d->label())
+					return d;
+			}
+
+			return nullptr;
+		}
+
+		static void report_device_list();
+
+
+		usb_interface *interface(unsigned index)
+		{
+			if (!_udev.actconfig)
+				return nullptr;
+
+			if (index >= _udev.actconfig->desc.bNumInterfaces)
+				return nullptr;
+
+			usb_interface *iface = _udev.actconfig->interface[index];
+			return iface;
+		}
+
+
+		/**
+		 * Return pseudo device class of USB device
+		 *
+		 * The returned value expresses the type of USB device. If the device
+		 * has at least one HID interface, the value is USB_CLASS_HID. Otherwise,
+		 * the class of the first interface is interpreted at type the device.
+		 *
+		 * Note this classification of USB devices is meant as an interim solution
+		 * only to assist the implementation of access-control policies.
+		 */
+		unsigned device_class_value()
+		{
+			unsigned result = 0;
+
+			_with_interface(0, [&] (usb_interface &interface) {
+				if (interface.cur_altsetting)
+					result = interface.cur_altsetting->desc.bInterfaceClass; });
+
+			_for_each_interface([&] (usb_interface &interface) {
+				if (interface.cur_altsetting)
+					if (interface.cur_altsetting->desc.bInterfaceClass == USB_CLASS_HID)
+						result = USB_CLASS_HID; });
+
+			return result;
+		}
+
+		long bus()  const { return _udev.bus->busnum; }
+		long dev()  const { return _udev.devnum; }
+
+		usb_device &udev() const { return _udev; }
+
+		void report(Xml_generator &xml)
+		{
+			if (!_udev.actconfig)
+				return;
+
+			using namespace Genode;
+			using Value = String<64>;
+
+			xml.attribute("label",      label());
+			xml.attribute("vendor_id",  Value(Hex(_udev.descriptor.idVendor)));
+			xml.attribute("product_id", Value(Hex(_udev.descriptor.idProduct)));
+			xml.attribute("bus",        Value(Hex(_udev.bus->busnum)));
+			xml.attribute("dev",        Value(Hex(_udev.devnum)));
+			xml.attribute("class",      Value(Hex(device_class_value())));
+
+			_for_each_interface([&] (usb_interface &interface) {
+
+				if (!interface.cur_altsetting)
+					return;
+
+				xml.node("interface", [&] () {
+
+					uint8_t const class_value =
+						interface.cur_altsetting->desc.bInterfaceClass;
+
+					xml.attribute("class", Value(Hex(class_value)));
+				});
+			});
+		}
+
+		usb_host_endpoint *endpoint(usb_interface *iface, unsigned alt_setting,
+		                            unsigned endpoint_num)
+		{
+			return &iface->altsetting[alt_setting].endpoint[endpoint_num];
+		}
+
+		Session_label label()
+		{
+			if (!_udev.bus)
+				return Session_label("usb-unknown");
+			return Session_label("usb-", _udev.bus->busnum, "-", _udev.devnum);
+		}
 };
 
 
@@ -858,7 +842,9 @@ class Usb::Cleaner : List<::Interface>
 			::Interface *interface = new(Lx::Malloc::mem()) ::Interface(iface);
 			insert(interface);
 			_task.unblock();
-			Lx::scheduler().schedule();
+
+			if (!Lx::scheduler().active())
+				Lx::scheduler().schedule();
 		}
 };
 
@@ -883,7 +869,6 @@ class Usb::Session_component : public Session_rpc_object,
 		Signal_context_capability          _sigh_state_change;
 		Io_signal_handler<Session_component> _packet_avail;
 		Io_signal_handler<Session_component> _ready_ack;
-		Worker                             _worker;
 		Ram_dataspace_capability           _tx_ds;
 		Usb::Cleaner                      &_cleaner;
 
@@ -896,8 +881,12 @@ class Usb::Session_component : public Session_rpc_object,
 
 		void _receive()
 		{
-			_worker.packet_avail();
-			Lx::scheduler().schedule();
+			if (!_device) return;
+
+			if (!Lx::scheduler().active()) {
+				_device->packet_avail();
+				Lx::scheduler().schedule();
+			}
 		}
 
 	public:
@@ -919,7 +908,7 @@ class Usb::Session_component : public Session_rpc_object,
 		  _bus(bus), _dev(dev), _class(class_),
 		  _packet_avail(ep, *this, &Session_component::_receive),
 		  _ready_ack(ep, *this, &Session_component::_receive),
-		  _worker(sink()), _tx_ds(tx_ds), _cleaner(cleaner)
+		  _tx_ds(tx_ds), _cleaner(cleaner)
 		{
 			Device *device_ptr;
 			if (bus && dev) {
@@ -930,8 +919,10 @@ class Usb::Session_component : public Session_rpc_object,
 				device_ptr = Device::device_by_class(_class, _label);
 			}
 
-			if (device_ptr)
+			if (device_ptr) {
+				device_ptr->packet_stream(sink());
 				state_change(DEVICE_ADD, device_ptr);
+			}
 
 			/* register signal handlers */
 			_tx.sigh_packet_avail(_packet_avail);
@@ -940,13 +931,12 @@ class Usb::Session_component : public Session_rpc_object,
 		~Session_component()
 		{
 			/* release claimed interfaces */
-			if (_device && _device->udev && _device->udev->actconfig) {
-				unsigned const num = _device->udev->actconfig->desc.bNumInterfaces;
-				for (unsigned i = 0; i < num; i++)
+			if (_device && _device->ready())
+				for (unsigned i = 0; i < _device->num_interfaces() ; i++)
 					release_interface(i);
-			}
 
-			_worker.stop();
+			if (_device)
+				_device->packet_stream(nullptr);
 		}
 
 		/***********************
@@ -960,7 +950,7 @@ class Usb::Session_component : public Session_rpc_object,
 			if (!_device)
 				throw Device_not_found();
 
-			usb_interface *iface   = _device->interface(interface_num);
+			usb_interface *iface = _device->interface(interface_num);
 			if (!iface)
 				throw Interface_not_found();
 
@@ -986,15 +976,15 @@ class Usb::Session_component : public Session_rpc_object,
 			if (!_device)
 				throw Device_not_found();
 
-			Genode::memcpy(device_descr, &_device->udev->descriptor, sizeof(usb_device_descriptor));
+			Genode::memcpy(device_descr, &_device->udev().descriptor, sizeof(usb_device_descriptor));
 
-			if (_device->udev->actconfig)
-				Genode::memcpy(config_descr, &_device->udev->actconfig->desc, sizeof(usb_config_descriptor));
+			if (_device->udev().actconfig)
+				Genode::memcpy(config_descr, &_device->udev().actconfig->desc, sizeof(usb_config_descriptor));
 			else
 				Genode::memset(config_descr, 0, sizeof(usb_config_descriptor));
 
-			device_descr->num   = _device->udev->devnum;
-			device_descr->speed = _device->udev->speed;
+			device_descr->num   = _device->udev().devnum;
+			device_descr->speed = _device->udev().speed;
 		}
 
 		unsigned alt_settings(unsigned index) override
@@ -1054,10 +1044,10 @@ class Usb::Session_component : public Session_rpc_object,
 		                         unsigned              endpoint_num,
 		                         Endpoint_descriptor  *endpoint_descr) override
 		{
-			if (!_device || !_device->udev)
+			if (!_device)
 				throw Device_not_found();
 
-			usb_interface *iface = usb_ifnum_to_if(_device->udev, interface_num);
+			usb_interface *iface = usb_ifnum_to_if(&_device->udev(), interface_num);
 			if (!iface)
 				throw Interface_not_found();
 
@@ -1071,12 +1061,12 @@ class Usb::Session_component : public Session_rpc_object,
 
 		bool session_device(Device *device)
 		{
-			usb_device_descriptor *descr = &device->udev->descriptor;
+			usb_device_descriptor *descr = &device->udev().descriptor;
 			usb_interface *iface = device->interface(0);
 
 			return (descr->idVendor == _vendor && descr->idProduct == _product)
-			       || (_bus && _dev && _bus == device->udev->bus->busnum &&
-			           _dev == device->udev->devnum)
+			       || (_bus && _dev && _bus == device->bus() &&
+			           _dev == device->dev())
 			       || (iface && iface->cur_altsetting &&
 			           _class == device->device_class_value() &&
 			           _label == device->label())? true : false;
@@ -1091,20 +1081,20 @@ class Usb::Session_component : public Session_rpc_object,
 
 					if (_device)
 						warning("Device type already present (vendor: ",
-						         Hex(device->udev->descriptor.idVendor),
-						         " product: ", Hex(device->udev->descriptor.idProduct),
+						         Hex(device->udev().descriptor.idVendor),
+						         " product: ", Hex(device->udev().descriptor.idProduct),
 						         ") Overwrite!");
 
 					_device = device;
-					_worker.device(_device, _sigh_state_change);
-					_worker.start();
+					_device->packet_stream(sink());
+					_device->sigh_ready(_sigh_state_change);
 					return true;
 
 				case DEVICE_REMOVE:
 					if (!session_device(device))
 						return false;
+
 					_device = nullptr;
-					_worker.stop();
 					_signal_state_change();
 					return true;
 			}
@@ -1116,8 +1106,8 @@ class Usb::Session_component : public Session_rpc_object,
 		{
 			_sigh_state_change = sigh;
 
-			if (_worker.device_ready())
-				Signal_transmitter(_sigh_state_change).submit(1);
+			if (_device && _device->ready())
+				Signal_transmitter(_sigh_state_change).submit();
 		}
 
 		Ram_dataspace_capability tx_ds() { return _tx_ds; }
@@ -1278,7 +1268,7 @@ void Device::report_device_list()
 
 		for (Device *d = list()->first(); d; d = d->next()) {
 
-			if (!d->udev || !d->udev->bus) {
+			if (!d->udev().bus) {
 				Genode::warning("device ", d->label().string(), " state incomplete");
 				continue;
 			}
@@ -1301,13 +1291,12 @@ int raw_notify(struct notifier_block *nb, unsigned long action, void *data)
 		   " vendor: ",  Hex(udev->descriptor.idVendor),
 		   " product: ", Hex(udev->descriptor.idProduct));
 
-
 	switch (action) {
 
 		case USB_DEVICE_ADD:
 		{
 			::Session::list()->state_change(Usb::Session_component::DEVICE_ADD,
-			                                new (Lx::Malloc::mem()) Device(udev));
+			                                new (Lx::Malloc::mem()) Device(*udev));
 			break;
 		}