From 4803937dd2bfdf916da6549e6088ca7d35f6fd18 Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Fri, 17 Sep 2021 13:54:28 +0200 Subject: [PATCH] os: introduce C-API to provide USB service Fix #4270 --- repos/os/include/genode_c_api/usb.h | 223 +++++++++ repos/os/src/lib/genode_c_api/usb.cc | 719 +++++++++++++++++++++++++++ 2 files changed, 942 insertions(+) create mode 100644 repos/os/include/genode_c_api/usb.h create mode 100644 repos/os/src/lib/genode_c_api/usb.cc diff --git a/repos/os/include/genode_c_api/usb.h b/repos/os/include/genode_c_api/usb.h new file mode 100644 index 0000000000..304d0d79ed --- /dev/null +++ b/repos/os/include/genode_c_api/usb.h @@ -0,0 +1,223 @@ +/* + * \brief C-API Genode USB backend + * \author Stefan Kalkowski + * \date 2021-09-14 + */ + +/* + * Copyright (C) 2006-2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _GENODE_C_API__USB_H_ +#define _GENODE_C_API__USB_H_ + +#include + + +struct genode_usb_session; /* definition is private to the implementation */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/******************** + ** Initialization ** + ********************/ + +/** + * Callback called during peer session request to allocate dma-capable shared buffer + */ +typedef struct genode_attached_dataspace * (*genode_usb_alloc_peer_buffer_t) + (unsigned long size); + +/** + * Callback called when closing peer session to free shared buffer + */ +typedef void (*genode_usb_free_peer_buffer_t) + (struct genode_attached_dataspace * ds); + +/** + * Callback to copy over config descriptor for given device + */ +typedef unsigned (*genode_usb_rpc_config_desc_t) + (unsigned long bus, unsigned long dev, void * dev_desc, void * conf_desc); + +/** + * Callback that returns number of alt-settings of an interface for given device + */ +typedef int (*genode_usb_rpc_alt_settings_t) + (unsigned long bus, unsigned long dev, unsigned idx); + +/** + * Callback to copy over interface descriptor for given device/interface + */ +typedef int (*genode_usb_rpc_iface_desc_t) + (unsigned long bus, unsigned long dev, unsigned idx, unsigned alt, + void * buf, unsigned long buf_size, int * active); + +/** + * Callback to copy over additional vendor specific data of an interface + */ +typedef int (*genode_usb_rpc_iface_extra_t) + (unsigned long bus, unsigned long dev, unsigned idx, unsigned alt, + void * buf, unsigned long buf_size); + +/** + * Callback to copy over endpoint descriptor for given device/iface/endpoint + */ +typedef int (*genode_usb_rpc_endp_desc_t) + (unsigned long bus, unsigned long dev, unsigned idx, unsigned alt, + unsigned endp, void * buf, unsigned long buf_size); + +struct genode_usb_rpc_callbacks { + genode_usb_alloc_peer_buffer_t alloc_fn; + genode_usb_free_peer_buffer_t free_fn; + genode_usb_rpc_config_desc_t cfg_desc_fn; + genode_usb_rpc_alt_settings_t alt_settings_fn; + genode_usb_rpc_iface_desc_t iface_desc_fn; + genode_usb_rpc_iface_extra_t iface_extra_fn; + genode_usb_rpc_endp_desc_t endp_desc_fn; +}; + +/** + * Initialize USB root component + * + * \param handler signal handler to be installed at each USB session + */ +void genode_usb_init(struct genode_env * env, + struct genode_allocator * alloc, + struct genode_signal_handler * handler, + struct genode_usb_rpc_callbacks * callbacks); + + +/************************************ + ** USB device lifetime management ** + ************************************/ + +void genode_usb_announce_device(unsigned long vendor, + unsigned long product, + unsigned long cla, + unsigned long bus, + unsigned long dev); + +void genode_usb_discontinue_device(unsigned long bus, unsigned long dev); + + +/********************************** + ** USB session request handling ** + **********************************/ + +typedef unsigned short genode_usb_session_handle_t; +typedef unsigned short genode_usb_request_handle_t; + +struct genode_usb_request_string +{ + unsigned char index; + unsigned length; +}; + +struct genode_usb_request_control +{ + unsigned char request; + unsigned char request_type; + unsigned short value; + unsigned short index; + int actual_size; + int timeout; +}; + +enum Iso { MAX_PACKETS = 32 }; +enum Transfer { BULK, IRQ, ISOC }; +typedef enum Transfer genode_usb_transfer_type_t; + +struct genode_usb_request_transfer +{ + unsigned char ep; + int actual_size; + int polling_interval; + int number_of_packets; + unsigned long packet_size[MAX_PACKETS]; + unsigned long actual_packet_size[MAX_PACKETS]; +}; + +enum Request_return_error { + NO_ERROR, + INTERFACE_OR_ENDPOINT_ERROR, + MEMORY_ERROR, + NO_DEVICE_ERROR, + PACKET_INVALID_ERROR, + PROTOCOL_ERROR, + STALL_ERROR, + TIMEOUT_ERROR, + UNKNOWN_ERROR +}; +typedef enum Request_return_error genode_usb_request_ret_t; + +typedef genode_usb_request_ret_t (*genode_usb_req_ctrl_t) + (struct genode_usb_request_control * req, + void * payload, + unsigned long payload_size, + void * opaque_data); + +typedef genode_usb_request_ret_t (*genode_usb_req_transfer_t) + (struct genode_usb_request_transfer * req, + genode_usb_transfer_type_t type, + genode_usb_session_handle_t session_handle, + genode_usb_request_handle_t request_handle, + void * payload, + unsigned long payload_size, + void * opaque_data); + +typedef genode_usb_request_ret_t (*genode_usb_req_string_t) + (struct genode_usb_request_string * req, + void * payload, + unsigned long payload_size, + void * opaque_data); + +typedef genode_usb_request_ret_t (*genode_usb_req_altsetting_t) + (unsigned iface, unsigned alt_setting, void * opaque_data); + +typedef genode_usb_request_ret_t (*genode_usb_req_config_t) + (unsigned config_idx, void * opaque_data); + +typedef genode_usb_request_ret_t (*genode_usb_req_flush_t) + (unsigned char ep, void * opaque_data); + +typedef genode_usb_request_ret_t (*genode_usb_response_t) + (struct genode_usb_request_transfer * req, + void * opaque_data); + +struct genode_usb_request_callbacks { + genode_usb_req_ctrl_t control_fn; + genode_usb_req_transfer_t transfer_fn; + genode_usb_req_string_t string_fn; + genode_usb_req_altsetting_t altsetting_fn; + genode_usb_req_config_t config_fn; + genode_usb_req_flush_t flush_fn; +}; + +genode_usb_session_handle_t genode_usb_session_by_bus_dev(unsigned long bus, + unsigned long dev); + +int genode_usb_request_by_session(genode_usb_session_handle_t session_handle, + struct genode_usb_request_callbacks * const c, + void * callback_data); + +void genode_usb_ack_request(genode_usb_session_handle_t session_handle, + genode_usb_request_handle_t request_handle, + genode_usb_response_t callback, + void * callback_data); + +void genode_usb_notify_peers(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _GENODE_C_API__USB_H_ */ + diff --git a/repos/os/src/lib/genode_c_api/usb.cc b/repos/os/src/lib/genode_c_api/usb.cc new file mode 100644 index 0000000000..0b0e6cebd6 --- /dev/null +++ b/repos/os/src/lib/genode_c_api/usb.cc @@ -0,0 +1,719 @@ +/* + * \brief Genode USB service provider C-API + * \author Stefan Kalkowski + * \date 2021-09-14 + */ + +/* + * Copyright (C) 2006-2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Genode; + +struct Device { + unsigned long vendor; + unsigned long product; + unsigned long cla; + unsigned long bus; + unsigned long dev; + genode_usb_session * usb_session { nullptr }; + + Device(unsigned long vendor, + unsigned long product, + unsigned long cla, + unsigned long bus, + unsigned long dev) + : vendor(vendor), product(product), + cla(cla), bus(bus), dev(dev) {} + + using Label = String<64>; + + Label label() { return Label("usb-", bus, "-", dev); } +}; + + +class Root; + + +class genode_usb_session : public Usb::Session_rpc_object +{ + private: + + friend class ::Root; + + enum { MAX_PACKETS_IN_FLY = 32 }; + + struct Packet { + enum State { FREE, USED } state; + Usb::Packet_descriptor pd; + } packets[MAX_PACKETS_IN_FLY] { Packet::FREE, {} }; + + ::Root & _root; + Attached_dataspace & _ds; + Signal_context_capability _sigh_state_cap {}; + unsigned short _id; + Session_label const _label; + List_element _le { this }; + + void _ack(int err, Usb::Packet_descriptor p); + + genode_usb_session(const genode_usb_session&); + + public: + + genode_usb_session(::Root & root, + Attached_dataspace & ds, + Env & env, + Signal_context_capability cap, + unsigned short id, + Session_label label); + + virtual ~genode_usb_session() {} + + /* + * Handle one outstanding request from the packet-stream + * of this session + * + * \param callbacks C-API callback struct to handle request + * \param data opaque pointer for C-side to identify related data + * \returns true if a request got handled, false if nothing was done + */ + bool request(genode_usb_request_callbacks & callbacks, void * data); + + /* + * Handle response for an asynchronous request + * + * \param handle handle that identifies the original request + * \param callback C-API callback to handle the response + * \param data opaque pointer for C-side to identify related data + */ + void handle_response(genode_usb_request_handle_t handle, + genode_usb_response_t callback, + void * data); + + /* + * Notify session about state changes + */ + void notify(); + + + /*************************** + ** USB session interface ** + ***************************/ + + void sigh_state_change(Signal_context_capability sigh) override; + bool plugged() override; + void config_descriptor(Usb::Device_descriptor *device_descr, + Usb::Config_descriptor *config_descr) override; + unsigned alt_settings(unsigned index) override; + void interface_descriptor(unsigned index, unsigned alt_setting, + Usb::Interface_descriptor *interface_descr) override; + bool interface_extra(unsigned index, unsigned alt_setting, + Usb::Interface_extra *interface_data) override; + void endpoint_descriptor(unsigned interface_num, + unsigned alt_setting, + unsigned endpoint_num, + Usb::Endpoint_descriptor *endpoint_descr) override; + void claim_interface(unsigned interface_num) override; + void release_interface(unsigned interface_num) override; +}; + + +class Root : public Root_component +{ + private: + + enum { MAX_DEVICES = 32 }; + + + Env & _env; + Signal_context_capability _sigh_cap; + Attached_rom_dataspace _config { _env, "config" }; + Reporter _reporter { _env, "devices" }; + Constructible _devices[MAX_DEVICES]; + List> _sessions {}; + Bit_allocator<16> _id_alloc {}; + bool _announced { false }; + + Root(const Root&); + Root & operator=(const Root&); + + genode_usb_session * _create_session(const char * args, + Affinity const &) override; + + void _destroy_session(genode_usb_session * session) override; + + template + void _for_each_device(FUNC const & fn) + { + for (unsigned idx = 0; idx < MAX_DEVICES; idx++) + if (_devices[idx].constructed()) + fn(*_devices[idx]); + } + + template + void _for_each_session(FUNC const & fn) + { + for (List_element * s = _sessions.first(); + s; s = s->next()) + fn(*s->object()); + } + + /* + * Update report about existing USB devices + */ + void _report(); + + /* + * Returns true if given device matches the session's policy + */ + bool _matches(Device & d, genode_usb_session & s); + + public: + + Root(Env & env, Allocator & alloc, Signal_context_capability); + + void announce_device(unsigned long vendor, + unsigned long product, + unsigned long cla, + unsigned long bus, + unsigned long dev); + + void discontinue_device(unsigned long bus, unsigned long dev); + + /* + * Returns the session's handle for a given device, + * or an invalid handle if there is no active session for the device + */ + genode_usb_session_handle_t session(unsigned long bus, + unsigned long dev); + + /* + * Finds the bus/device numbers of the device associated for a + * given session + * + * \returns true if a device is associated, otherwise false + */ + bool device_associated(genode_usb_session * session, + unsigned long & bus, + unsigned long & dev); + + /* + * Apply a functor to the session with the given handle + */ + template + void session(genode_usb_session_handle_t id, FUNC const & fn); +}; + + +static ::Root * _usb_root = nullptr; +static genode_usb_rpc_callbacks * _callbacks = nullptr; + + +void genode_usb_session::sigh_state_change(Signal_context_capability sigh) +{ + _sigh_state_cap = sigh; +} + + +void genode_usb_session::notify() +{ + Signal_transmitter state_changed { _sigh_state_cap }; + state_changed.submit(); +} + + +bool genode_usb_session::plugged() +{ + unsigned long bus, dev; + return _root.device_associated(this, bus, dev); +} + + +void genode_usb_session::config_descriptor(Usb::Device_descriptor * device_descr, + Usb::Config_descriptor * config_descr) +{ + unsigned long bus, dev; + if (!_root.device_associated(this, bus, dev)) + throw Device_not_found(); + + unsigned speed = _callbacks->cfg_desc_fn(bus, dev, (void*) device_descr, + config_descr); + device_descr->num = dev; + device_descr->speed = speed; +} + + +unsigned genode_usb_session::alt_settings(unsigned index) +{ + unsigned long bus, dev; + if (!_root.device_associated(this, bus, dev)) + throw Device_not_found(); + + int err = _callbacks->alt_settings_fn(bus, dev, index); + if (err < 0) + throw Interface_not_found(); + return (unsigned) err; +} + + +void genode_usb_session::interface_descriptor(unsigned index, + unsigned alt_setting, + Usb::Interface_descriptor * desc) +{ + unsigned long bus, dev; + if (!_root.device_associated(this, bus, dev)) + throw Device_not_found(); + + int active; + if (_callbacks->iface_desc_fn(bus, dev, index, alt_setting, (void*)desc, + sizeof(Usb::Interface_descriptor), &active)) + throw Interface_not_found(); + + if (active) + desc->active = true; +} + + +bool genode_usb_session::interface_extra(unsigned index, + unsigned alt_setting, + Usb::Interface_extra * interface_data) +{ + unsigned long bus, dev; + if (!_root.device_associated(this, bus, dev)) + throw Device_not_found(); + + int len = _callbacks->iface_extra_fn(bus, dev, index, alt_setting, + (void*)interface_data->data, + sizeof(interface_data->data)); + if (len < 0) + throw Interface_not_found(); + + interface_data->length = len; + return len; +} + + +void genode_usb_session::endpoint_descriptor(unsigned interface_num, + unsigned alt_setting, + unsigned endpoint_num, + Usb::Endpoint_descriptor * endp) +{ + unsigned long bus, dev; + if (!_root.device_associated(this, bus, dev)) + throw Device_not_found(); + + if (_callbacks->endp_desc_fn(bus, dev, interface_num, alt_setting, + endpoint_num, (void*)endp, + sizeof(Usb::Endpoint_descriptor))) + throw Interface_not_found(); +} + + +void genode_usb_session::claim_interface(unsigned) +{ + warning(__func__, " gets ignored!"); +} + + +void genode_usb_session::release_interface(unsigned) +{ + warning(__func__, " gets ignored!"); +} + + +void genode_usb_session::_ack(int err, Usb::Packet_descriptor p) +{ + if (err == NO_ERROR) + p.succeded = true; + else + p.error = (Usb::Packet_descriptor::Error)err; + sink()->acknowledge_packet(p); +} + + +bool genode_usb_session::request(genode_usb_request_callbacks & req, void * data) +{ + using Packet_descriptor = Usb::Packet_descriptor; + + genode_usb_request_handle_t idx; + + /* find free packet slot */ + for (idx = 0; idx < MAX_PACKETS_IN_FLY; idx++) { + if (packets[idx].state == Packet::FREE) + break; + } + if (idx == MAX_PACKETS_IN_FLY) + return false; + + Packet_descriptor p; + + /* get next packet from request stream */ + while (true) { + if (!sink()->packet_avail() || !sink()->ack_slots_free()) + return false; + + p = sink()->get_packet(); + if (sink()->packet_valid(p)) + break; + + p.error = Packet_descriptor::PACKET_INVALID_ERROR; + sink()->acknowledge_packet(p); + } + + addr_t offset = (addr_t)sink()->packet_content(p) - + (addr_t)sink()->ds_local_base(); + void * addr = (void*)((addr_t)_ds.local_addr() + offset); + + switch (p.type) { + case Packet_descriptor::STRING: + _ack(req.string_fn((genode_usb_request_string*)&p.string, + addr, p.size(), data), p); + break; + case Packet_descriptor::CTRL: + _ack(req.control_fn((genode_usb_request_control*)&p.control, + addr, p.size(), data), p); + break; + case Packet_descriptor::BULK: + packets[idx].state = Packet::USED; + packets[idx].pd = p; + req.transfer_fn((genode_usb_request_transfer*)&p.transfer, BULK, + _id, idx, addr, p.size(), data); + break; + case Packet_descriptor::IRQ: + packets[idx].state = Packet::USED; + packets[idx].pd = p; + req.transfer_fn((genode_usb_request_transfer*)&p.transfer, IRQ, + _id, idx, addr, p.size(), data); + break; + case Packet_descriptor::ISOC: + packets[idx].state = Packet::USED; + packets[idx].pd = p; + req.transfer_fn((genode_usb_request_transfer*)&p.transfer, ISOC, + _id, idx, addr, p.size(), data); + break; + case Packet_descriptor::ALT_SETTING: + _ack(req.altsetting_fn(p.interface.number, + p.interface.alt_setting, data), p); + break; + case Packet_descriptor::CONFIG: + _ack(req.config_fn(p.number, data), p); + break; + case Packet_descriptor::RELEASE_IF: + warning("Release interface gets ignored!"); + break; + case Packet_descriptor::FLUSH_TRANSFERS: + _ack(req.flush_fn(p.number, data), p); + break; + }; + + return true; +} + + +void genode_usb_session::handle_response(genode_usb_request_handle_t id, + genode_usb_response_t callback, + void * callback_data) +{ + Usb::Packet_descriptor p = packets[id].pd; + _ack(callback((genode_usb_request_transfer*)&p.transfer, callback_data), p); + packets[id].state = Packet::FREE; +} + + +genode_usb_session::genode_usb_session(::Root & root, + Attached_dataspace & ds, + Env & env, + Signal_context_capability cap, + unsigned short id, + Session_label const label) +: + Usb::Session_rpc_object(ds.cap(), env.ep().rpc_ep(), env.rm()), + _root(root), + _ds(ds), + _id(id), + _label(label) +{ + _tx.sigh_packet_avail(cap); +} + + +bool ::Root::_matches(Device & d, genode_usb_session & s) +{ + Session_policy const policy(s._label, _config.xml()); + + unsigned long vendor = policy.attribute_value("vendor_id", 0); + unsigned long product = policy.attribute_value("product_id", 0); + unsigned long bus = policy.attribute_value("bus", 0); + unsigned long dev = policy.attribute_value("dev", 0); + unsigned long cla = policy.attribute_value("class", 0); + + if (bus && dev) + return (bus == d.bus) && (dev == d.dev); + if (vendor && product) + return (vendor == d.vendor) && (product == d.product); + if (cla) + return (cla == d.cla) && (d.label() == s._label.last_element()); + + return false; +} + + +genode_usb_session * ::Root::_create_session(const char * args, + Affinity const &) +{ + Session_label const label = label_from_args(args); + + 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); + + if (!tx_buf_size) + throw Service_denied(); + + /* check session quota */ + size_t session_size = max(4096, sizeof(genode_usb_session)); + if (ram_quota < session_size) + throw Insufficient_ram_quota(); + + if (tx_buf_size > ram_quota - session_size) { + error("Insufficient 'ram_quota',got ", ram_quota, " need ", + tx_buf_size + session_size); + throw Insufficient_ram_quota(); + } + + Attached_dataspace & ds = + *static_cast(_callbacks->alloc_fn(tx_buf_size)); + genode_usb_session * ret = new (md_alloc()) + genode_usb_session(*this, ds, _env, _sigh_cap, _id_alloc.alloc(), label); + _sessions.insert(&ret->_le); + + if (!ret) throw Service_denied(); + + bool found_device = false; + _for_each_device([&] (Device & d) { + if (found_device || d.usb_session || !_matches(d, *ret)) + return; + d.usb_session = ret; + found_device = true; + }); + + return ret; +} + + +void ::Root::_destroy_session(genode_usb_session * session) +{ + _for_each_device([&] (Device & d) { + if (d.usb_session == session) + d.usb_session = nullptr; + }); + + unsigned short id = session->_id; + Attached_dataspace & ds = session->_ds; + _sessions.remove(&session->_le); + Genode::destroy(md_alloc(), session); + _id_alloc.free((addr_t)id); + _callbacks->free_fn(genode_attached_dataspace_ptr(ds)); +} + + +void ::Root::_report() +{ + using Value = String<64>; + + _reporter.enabled(true); + Reporter::Xml_generator xml(_reporter, [&] () { + _for_each_device([&] (Device & d) { + xml.node("device", [&] { + xml.attribute("label", d.label()); + xml.attribute("vendor_id", Value(Hex(d.vendor))); + xml.attribute("product_id", Value(Hex(d.product))); + xml.attribute("bus", Value(Hex(d.bus))); + xml.attribute("dev", Value(Hex(d.dev))); + xml.attribute("class", Value(Hex(d.cla))); + }); + }); + }); +} + + +void ::Root::announce_device(unsigned long vendor, + unsigned long product, + unsigned long cla, + unsigned long bus, + unsigned long dev) +{ + for (unsigned idx = 0; idx < MAX_DEVICES; idx++) { + if (_devices[idx].constructed()) + continue; + + _devices[idx].construct(vendor, product, cla, bus, dev); + if (!_announced) { + _env.parent().announce(_env.ep().manage(*this)); + _announced = true; + } + _report(); + + _for_each_session([&] (genode_usb_session & s) { + if (_devices[idx]->usb_session || !_matches(*_devices[idx], s)) + return; + _devices[idx]->usb_session = &s; + s.notify(); + }); + return; + } + + error("Could not announce driver for new USB device, no slot left!"); +} + + +void ::Root::discontinue_device(unsigned long bus, unsigned long dev) +{ + for (unsigned idx = 0; idx < MAX_DEVICES; idx++) { + if (!_devices[idx].constructed() || + _devices[idx]->bus != bus || + _devices[idx]->dev != dev) + continue; + + if (_devices[idx]->usb_session) + _devices[idx]->usb_session->notify(); + + _devices[idx].destruct(); + _report(); + return; + } +} + + +genode_usb_session_handle_t ::Root::session(unsigned long bus, unsigned long dev) +{ + genode_usb_session * session = nullptr; + _for_each_device([&] (Device & d) { + if (d.bus == bus && d.dev == dev) + session = d.usb_session; + }); + + return session ? session->_id : 0; +} + + +template +void ::Root::session(genode_usb_session_handle_t id, FUNC const & fn) +{ + _for_each_session([&] (genode_usb_session & s) { + if (s._id == id) fn(s); + }); +} + + +bool ::Root::device_associated(genode_usb_session * session, + unsigned long & bus, unsigned long & dev) +{ + bool ret = false; + _for_each_device([&] (Device & d) { + if (d.usb_session == session) { + bus = d.bus; + dev = d.dev; + ret = true; + } + }); + return ret; +} + + +::Root::Root(Env & env, Allocator & alloc, Signal_context_capability cap) +: + Root_component(env.ep(), alloc), + _env(env), _sigh_cap(cap) +{ + /* Reserve id zero which is invalid */ + _id_alloc.alloc(); +} + + +extern "C" void genode_usb_init(genode_env * env_ptr, + genode_allocator * alloc_ptr, + genode_signal_handler * sigh_ptr, + genode_usb_rpc_callbacks * callbacks) +{ + static ::Root root(*static_cast(env_ptr), + *static_cast(alloc_ptr), + cap(sigh_ptr)); + _callbacks = callbacks; + _usb_root = &root; +} + + +extern "C" void genode_usb_announce_device(unsigned long vendor, + unsigned long product, + unsigned long cla, + unsigned long bus, + unsigned long dev) +{ + if (!_usb_root) + return; + + _usb_root->announce_device(vendor, product, cla, bus, dev); +} + + +extern "C" void genode_usb_discontinue_device(unsigned long bus, + unsigned long dev) +{ + if (_usb_root) + _usb_root->discontinue_device(bus, dev); +} + + +extern "C" genode_usb_session_handle_t +genode_usb_session_by_bus_dev(unsigned long bus, unsigned long dev) +{ + genode_usb_session_handle_t ret = _usb_root ? _usb_root->session(bus, dev) : 0; + return ret; +} + + +extern "C" int +genode_usb_request_by_session(genode_usb_session_handle_t id, + struct genode_usb_request_callbacks * const req, + void * data) +{ + bool ret = false; + + if (!_usb_root) + return ret; + + _usb_root->session(id, [&] (genode_usb_session & session) { + ret = session.request(*req, data); }); + return ret; +} + + +extern "C" void genode_usb_ack_request(genode_usb_session_handle_t session_id, + genode_usb_request_handle_t request_id, + genode_usb_response_t callback, + void * callback_data) +{ + if (!_usb_root) + return; + + _usb_root->session(session_id, [&] (genode_usb_session & session) { + session.handle_response(request_id, callback, callback_data); }); +} + + +extern "C" void genode_usb_notify_peers() { }