From c5a55e5af45af019a669b95b9d3bc6c920ba5666 Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Fri, 16 Jun 2023 21:31:44 +0200 Subject: [PATCH] genode_c_api/usb_client: API USB clients Through this API C-code can connect to an USB service. issue #4958 --- repos/os/include/genode_c_api/usb_client.h | 147 +++++++++ repos/os/src/lib/genode_c_api/usb_client.cc | 334 ++++++++++++++++++++ 2 files changed, 481 insertions(+) create mode 100644 repos/os/include/genode_c_api/usb_client.h create mode 100644 repos/os/src/lib/genode_c_api/usb_client.cc diff --git a/repos/os/include/genode_c_api/usb_client.h b/repos/os/include/genode_c_api/usb_client.h new file mode 100644 index 0000000000..e2f0811fab --- /dev/null +++ b/repos/os/include/genode_c_api/usb_client.h @@ -0,0 +1,147 @@ +/* + * \brief C-API Genode USB-client backend + * \author Sebastian Sumpf + * \date 2023-06-29 + */ + +/* + * Copyright (C) 2023 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_CLIENT_H_ +#define __GENODE_C_API__USB_CLIENT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long genode_usb_client_handle_t; + +struct genode_range_allocator; + +struct genode_usb_device_descriptor +{ + genode_uint8_t length; + genode_uint8_t type; + genode_uint16_t usb; + genode_uint8_t dclass; + genode_uint8_t dsubclass; + genode_uint8_t dprotocol; + genode_uint8_t max_packet_size; + genode_uint16_t vendor_id; + genode_uint16_t product_id; + genode_uint16_t device_release; + genode_uint8_t manufactorer_index; + genode_uint8_t product_index; + genode_uint8_t serial_number_index; + genode_uint8_t num_configs; + + /* + * Genode extensions (POD only) + */ + unsigned bus; + unsigned num ; + unsigned speed; +} __attribute__((packed)); + + +struct genode_usb_config_descriptor +{ + genode_uint8_t length; + genode_uint8_t type; + genode_uint16_t total_length; + genode_uint8_t num_interfaces; + genode_uint8_t config_value; + genode_uint8_t config_index; + genode_uint8_t attributes; + genode_uint8_t max_power; +} __attribute__((packed)); + + +genode_usb_client_handle_t +genode_usb_client_create(struct genode_env *env, + struct genode_allocator *md_alloc, + struct genode_range_allocator *alloc, + char const *label, + struct genode_signal_handler *handler); + +void genode_usb_client_destroy(genode_usb_client_handle_t handle, + struct genode_allocator *md_alloc); + +void genode_usb_client_sigh_ack_avail(genode_usb_client_handle_t handle, + struct genode_signal_handler *handler); + +int genode_usb_client_config_descriptor(genode_usb_client_handle_t handle, + struct genode_usb_device_descriptor *device_descr, + struct genode_usb_config_descriptor *config_descr); + +bool genode_usb_client_plugged(genode_usb_client_handle_t handle); + +void genode_usb_client_claim_interface(genode_usb_client_handle_t handle, + unsigned interface_num); + +void genode_usb_client_release_interface(genode_usb_client_handle_t handle, + unsigned interface_num); + +struct genode_usb_altsetting +{ + unsigned char interface_number; + unsigned char alt_setting; +}; + +struct genode_usb_config +{ + unsigned char value; +}; + +struct genode_usb_request_packet +{ + unsigned type; + void * req; +}; + +typedef struct genode_usb_request_packet genode_request_packet_t; + +struct genode_usb_client_request_packet +{ + genode_request_packet_t request; + struct genode_usb_buffer buffer; + int actual_length; + int error; + void (*complete_callback)(struct genode_usb_client_request_packet *); + void (*free_callback) (struct genode_usb_client_request_packet *); + void *completion; + void *opaque_data; +}; + +bool genode_usb_client_request(genode_usb_client_handle_t handle, + struct genode_usb_client_request_packet *request); + +void genode_usb_client_request_submit(genode_usb_client_handle_t handle, + struct genode_usb_client_request_packet *request); + +void genode_usb_client_request_finish(genode_usb_client_handle_t handle, + struct genode_usb_client_request_packet *request); + +void genode_usb_client_execute_completions(genode_usb_client_handle_t handle); + +#ifdef __cplusplus +} /* extern "C" */ + +#include + +struct genode_range_allocator : Genode::Range_allocator { }; + +static inline auto genode_range_allocator_ptr(Genode::Range_allocator &alloc) +{ + return static_cast(&alloc); +} +#endif /* __cplusplus */ + +#endif /* __GENODE_C_API__USB_CLIENT_H_ */ diff --git a/repos/os/src/lib/genode_c_api/usb_client.cc b/repos/os/src/lib/genode_c_api/usb_client.cc new file mode 100644 index 0000000000..18015a4649 --- /dev/null +++ b/repos/os/src/lib/genode_c_api/usb_client.cc @@ -0,0 +1,334 @@ +/* + * \brief Genode USB client provider C-API + * \author Sebastian Sumpf + * \date 2023-06-29 + */ + +/* + * Copyright (C) 2023 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 + +using namespace Genode; + +struct Usb_client; +struct Usb_completion; + +using Usb_id_space = Id_space; + + +struct Usb_completion : Usb::Completion +{ + Usb::Packet_descriptor packet { }; + + genode_usb_client_request_packet *request = nullptr; + + void complete(Usb::Packet_descriptor &p) override + { + packet = p; + request->actual_length = (packet.type == Usb::Packet_descriptor::CTRL) ? + packet.control.actual_size : packet.transfer.actual_size; + + request->error = NO_ERROR; + + if (!packet.succeded) { + switch (packet.error) { + case Usb::Packet_descriptor::NO_ERROR: + request->error = NO_ERROR; break; + case Usb::Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR: + request->error = INTERFACE_OR_ENDPOINT_ERROR; break; + case Usb::Packet_descriptor::MEMORY_ERROR: + request->error = MEMORY_ERROR; break; + case Usb::Packet_descriptor::NO_DEVICE_ERROR: + request->error = NO_DEVICE_ERROR; break; + case Usb::Packet_descriptor::PACKET_INVALID_ERROR: + request->error = PACKET_INVALID_ERROR; break; + case Usb::Packet_descriptor::PROTOCOL_ERROR: + request->error = PROTOCOL_ERROR; break; + case Usb::Packet_descriptor::STALL_ERROR: + request->error = STALL_ERROR; break; + case Usb::Packet_descriptor::TIMEOUT_ERROR: + request->error = TIMEOUT_ERROR; break; + case Usb::Packet_descriptor::UNKNOWN_ERROR: + request->error = UNKNOWN_ERROR; break; + } + } + + if (request->complete_callback) + request->complete_callback(request); + } + + void free() + { + request->error = NO_DEVICE_ERROR; + if (request->free_callback) request->free_callback(request); + } +}; + + +struct Usb_client : Usb::Connection +{ + enum { PACKET_SLOTS = Usb::Session::TX_QUEUE_SIZE }; + + Usb_completion completions[PACKET_SLOTS]; + Bit_allocator slots { }; + Usb_id_space::Element const elem; + + Usb_client(Env &env, Usb_id_space &space, char const *label, + Range_allocator *alloc, + Signal_context_capability state_changed) + : Usb::Connection(env, alloc, label, 512*1024, state_changed), + elem(*this, space) + { } + + genode_usb_client_handle_t handle() const { return elem.id().value; } + + void free_completion(Usb_completion *completion) + { + unsigned long slot_idx = (unsigned long)(completion - completions); + slots.free(slot_idx); + } + + Usb_completion *alloc(size_t size) + { + /* + * We don't need to check for 'ready_to_submit' because we have as many + * compltions as packet slots + */ + Usb_completion *completion = nullptr; + try { + completion = &completions[slots.alloc()]; + Usb::Packet_descriptor packet = Usb::Session_client::alloc_packet(size); + packet.completion = completion; + completion->packet = packet; + } catch (Tx::Source::Packet_alloc_failed) { + free_completion(completion); + return nullptr; + } catch (Bit_allocator::Out_of_indices) { + return nullptr; + } + + return completion; + } + + void release(Usb_completion *completion) + { + source()->release_packet(completion->packet); + free_completion(completion); + } +}; + + +static Usb_id_space _usb_space { }; + +template +int usb_client_apply(genode_usb_client_handle_t handle, FUNC const &fn) +{ + Usb_id_space::Id id { .value = handle }; + try { + _usb_space.apply(id, fn); + } catch (Usb_id_space::Id_space::Unknown_id) { + error("Invalid handle: ", handle); + return -1; + } + return 0; +} + + +genode_usb_client_handle_t +genode_usb_client_create(struct genode_env *env, + struct genode_allocator *md_alloc, + struct genode_range_allocator *alloc, + char const *label, + struct genode_signal_handler *handler) +{ + Env &_env = *static_cast(env); + Range_allocator *_alloc = static_cast(alloc); + Allocator *_md_alloc = static_cast(md_alloc); + + Usb_client *client = new (_md_alloc) Usb_client(_env, _usb_space, label, + _alloc, cap(handler)); + return client->handle(); +} + + +void genode_usb_client_destroy(genode_usb_client_handle_t handle, + struct genode_allocator *md_alloc) +{ + usb_client_apply(handle, [&] (Usb_client &usb) { + while(usb.source()->ack_avail()) { + Usb::Packet_descriptor p = usb.source()->get_acked_packet(); + if (p.completion) static_cast(p.completion)->free(); + usb.source()->release_packet(p); + } + destroy(md_alloc, &usb); + }); +}; + + +void genode_usb_client_sigh_ack_avail(genode_usb_client_handle_t handle, + struct genode_signal_handler *handler) +{ + usb_client_apply(handle, [&] (Usb_client &usb) { + usb.tx_channel()->sigh_ack_avail(cap(handler)); }); +} + + +int genode_usb_client_config_descriptor(genode_usb_client_handle_t handle, + genode_usb_device_descriptor *device_descr, + genode_usb_config_descriptor *config_descr) +{ + auto lambda = [&] (Usb_client &usb) { + Usb::Device_descriptor dev_descr; + Usb::Config_descriptor conf_descr; + usb.config_descriptor(&dev_descr, &conf_descr); + + memcpy(device_descr, &dev_descr, sizeof(*device_descr)); + memcpy(config_descr, &config_descr, sizeof(*config_descr)); + }; + + return usb_client_apply(handle, lambda); +} + + +bool genode_usb_client_plugged(genode_usb_client_handle_t handle) +{ + bool plugged = false; + usb_client_apply(handle, [&] (Usb_client &usb) { + plugged = usb.plugged(); }); + + return plugged; +} + +void genode_usb_client_claim_interface(genode_usb_client_handle_t handle, + unsigned interface_num) +{ + usb_client_apply(handle, [&] (Usb_client &usb) { + usb.claim_interface(interface_num); + }); +} + + +void genode_usb_client_release_interface(genode_usb_client_handle_t handle, + unsigned interface_num) +{ + usb_client_apply(handle, [&] (Usb_client &usb) { + usb.release_interface(interface_num); + }); +} + + +bool genode_usb_client_request(genode_usb_client_handle_t handle, + genode_usb_client_request_packet *request) +{ + Usb_completion *completion = nullptr; + + usb_client_apply(handle, [&] (Usb_client &usb) { + completion = usb.alloc(request->buffer.size); + + if (completion) + request->buffer.addr = usb.source()->packet_content(completion->packet); + }); + + if (!completion) return false; + + completion->request = request; + request->completion = completion; + + /* setup packet */ + genode_request_packet_t *req = &request->request; + Usb::Packet_descriptor *packet = &completion->packet; + + switch(req->type) { + case IRQ: + { + packet->type = Usb::Packet_descriptor::IRQ; + + genode_usb_request_transfer *transfer = (genode_usb_request_transfer *)req->req; + packet->transfer.polling_interval = transfer->polling_interval; + packet->transfer.ep = transfer->ep; + break; + } + case CTRL: + { + packet->type = Usb::Packet_descriptor::CTRL; + + genode_usb_request_control *ctrl = (genode_usb_request_control *)req->req; + packet->control.request = ctrl->request; + packet->control.request_type = ctrl->request_type; + packet->control.value = ctrl->value; + packet->control.index = ctrl->index; + packet->control.timeout = ctrl->timeout; + break; + } + case BULK: + { + packet->type = Usb::Packet_descriptor::BULK; + + genode_usb_request_transfer *transfer = (genode_usb_request_transfer *)req->req; + packet->transfer.ep = transfer->ep; + break; + } + case ALT_SETTING: + { + genode_usb_altsetting *alt_setting = (genode_usb_altsetting*)req->req; + + packet->type = Usb::Packet_descriptor::ALT_SETTING; + + packet->interface.number = alt_setting->interface_number; + packet->interface.alt_setting = alt_setting->alt_setting; + break; + } + + case CONFIG: + { + genode_usb_config *config = (genode_usb_config *)req->req; + + packet->type = Usb::Packet_descriptor::CONFIG; + packet->number = config->value; + break; + } + + default: + error("unknown USB client requested"); + }; + + return true; +} + + +void genode_usb_client_request_submit(genode_usb_client_handle_t handle, + genode_usb_client_request_packet *request) +{ + Usb_completion *completion = static_cast(request->completion); + usb_client_apply(handle, [&] (Usb_client &usb) { + usb.source()->submit_packet(completion->packet); }); +} + + +void genode_usb_client_request_finish(genode_usb_client_handle_t handle, + struct genode_usb_client_request_packet *request) +{ + Usb_completion *completion = static_cast(request->completion); + usb_client_apply(handle, [&] (Usb_client &usb) { + usb.release(completion); }); +} + + +void genode_usb_client_execute_completions(genode_usb_client_handle_t handle) +{ + usb_client_apply(handle, [&] (Usb_client &usb) { + while(usb.source()->ack_avail()) { + Usb::Packet_descriptor p = usb.source()->get_acked_packet(); + if (p.completion) p.completion->complete(p); + } + }); +}