diff --git a/repos/dde_linux/run/usb_hid_raw.run b/repos/dde_linux/run/usb_hid_raw.run index e39e05f328..dea604b01c 100644 --- a/repos/dde_linux/run/usb_hid_raw.run +++ b/repos/dde_linux/run/usb_hid_raw.run @@ -115,7 +115,7 @@ install_config { - + @@ -192,7 +192,7 @@ set fd [open [run_dir]/genode/usb_host_drv.config w] puts $fd { - + } close $fd @@ -222,8 +222,7 @@ unify_output {(?n).*0557:2213.*} "" unify_output {(?n) on usb-usbbus.*$} "" unify_output {(?n) at usb-usbbus.*\)} ")" unify_output {(?n)hid-generic.*input:} "hid-generic: input:" -unify_output {(?n)usb-[0-9]-[0-9]} "usb-X-X" -unify_output {(?n)device number [0-9]+} "device number X" +unify_output {(?n)usb-[0-9]-[0-9]: USB disconnect, device number [0-9]} "usb-X-X: USB disconnect, device number X" unify_output {(?n)input[0-9]} "inputX" unify_output {(?n) as /devices/.*} "" unify_output {(?n)^\[init -\> usb_drv.*} "" @@ -238,12 +237,12 @@ compare_output_to { [init -> usb_hid_drv] usb usb-X-X: USB disconnect, device number X [init -> usb_hid_drv] Disconnected device: inputX [init -> usb_hid_drv] Disconnected device: inputX -[init -> usb_hid_drv] input: HID 03eb:204d -[init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) -[init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Keyboard [HID 03eb:204d] -[init -> usb_hid_drv] input: HID 03eb:204d -[init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) MOUSE -[init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Mouse [HID 03eb:204d] +[init -> usb_hid_drv] input: Dean Camera LUFA Mouse and Keyboard Demo +[init -> usb_hid_drv] Connected device: inputX (Dean Camera LUFA Mouse and Keyboard Demo) +[init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Keyboard [Dean Camera LUFA Mouse and Keyboard Demo] +[init -> usb_hid_drv] input: Dean Camera LUFA Mouse and Keyboard Demo +[init -> usb_hid_drv] Connected device: inputX (Dean Camera LUFA Mouse and Keyboard Demo) MOUSE +[init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Mouse [Dean Camera LUFA Mouse and Keyboard Demo] [init -> event_dump] Input event #5 PRESS KEY_X 65534 key count: 1 [init -> event_dump] Input event #6 RELEASE KEY_X key count: 0 [init -> event_dump] Input event #7 REL_MOTION -1+1 key count: 0 diff --git a/repos/dde_linux/run/usb_hid_reconnect.run b/repos/dde_linux/run/usb_hid_reconnect.run index a2350570d4..d22a88daf6 100644 --- a/repos/dde_linux/run/usb_hid_reconnect.run +++ b/repos/dde_linux/run/usb_hid_reconnect.run @@ -70,7 +70,7 @@ install_config { - + @@ -127,7 +127,7 @@ set fd [open [run_dir]/genode/usb_host_drv.config w] puts $fd { - + } close $fd @@ -136,7 +136,7 @@ build_boot_image [build_artifacts] append qemu_args " -device nec-usb-xhci,id=xhci -device usb-kbd,bus=xhci.0 -nographic" # wait for keyboard -run_genode_until {.*USB HID.*Keyboard.*} 60 +run_genode_until {.*USB HID v1.11 Keyboard.*} 60 set spawn_id $qemu_spawn_id diff --git a/repos/dde_linux/run/usb_net.run b/repos/dde_linux/run/usb_net.run index 7595e566a2..0b3b9270cf 100644 --- a/repos/dde_linux/run/usb_net.run +++ b/repos/dde_linux/run/usb_net.run @@ -16,6 +16,7 @@ import_from_depot [depot_user]/src/[base_src] \ [depot_user]/src/vfs \ [depot_user]/src/vfs_lwip \ [depot_user]/src/init + build { test/lwip/http_srv } install_config { @@ -111,9 +112,11 @@ install_config { # set config {} append_if [have_board rpi] config { - } + + } append_if [have_spec x86] config { - } + + } append config {} set fd [open [run_dir]/genode/usb_host_drv.config w] puts $fd $config diff --git a/repos/dde_linux/src/drivers/usb_hid/README b/repos/dde_linux/src/drivers/usb_hid/README index d2d304d001..e8f1b3b966 100644 --- a/repos/dde_linux/src/drivers/usb_hid/README +++ b/repos/dde_linux/src/drivers/usb_hid/README @@ -1,21 +1,9 @@ USB HID driver ############## -Supports keyboard and mouse connected via USB. It connects to one or multiple -USB sessions and reports input events to an event session. A run script can be -found under 'run/usb_hid_raw.run'. - -Configuration snippet: - -! -! -! -! - -When the use_report attribute is set, the HID driver will request a ROM called -"report" that is used to iterate over all devices provided by the USB host -controller driver (resp. a rom filter). In that mode the driver tries to -claim all HID devices (class 0x3) via dedicated USB sessions. +Supports keyboard and mouse connected via USB. It drives one or multiple devices +depending on the provided ones in its USB sessions and reports input events to an +event session. A run script can be found under 'run/usb_hid_raw.run'. Keyboard LED handling diff --git a/repos/dde_linux/src/drivers/usb_hid/dummies.c b/repos/dde_linux/src/drivers/usb_hid/dummies.c index 8ce268f1dc..d594322f3a 100644 --- a/repos/dde_linux/src/drivers/usb_hid/dummies.c +++ b/repos/dde_linux/src/drivers/usb_hid/dummies.c @@ -166,29 +166,6 @@ int __init usb_devio_init(void) } -#include - -int usb_string(struct usb_device * dev,int index,char * buf,size_t size) -{ - lx_emul_trace(__func__); - return 0; -} - - -extern char * usb_cache_string(struct usb_device * udev,int index); -char * usb_cache_string(struct usb_device * udev,int index) -{ - lx_emul_trace(__func__); - return NULL; -} - - -void usb_kill_urb(struct urb * urb) -{ - lx_emul_trace(__func__); -} - - #include struct usb_hcd * usb_get_hcd(struct usb_hcd * hcd) @@ -336,8 +313,53 @@ void usb_remove_sysfs_intf_files(struct usb_interface * intf) } -extern void usb_disable_interface(struct usb_device * dev,struct usb_interface * intf,bool reset_hardware); -void usb_disable_interface(struct usb_device * dev,struct usb_interface * intf,bool reset_hardware) +const struct attribute_group *usb_interface_groups[] = { NULL }; + + +#include + +void add_device_randomness(const void * buf,size_t len) { lx_emul_trace(__func__); } + + +#include + +int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) +{ + lx_emul_trace(__func__); + return 0; +} + + +void usb_hcd_flush_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + lx_emul_trace(__func__); +} + + +void usb_hcd_disable_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + lx_emul_trace(__func__); +} + + +void usb_hcd_reset_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + lx_emul_trace(__func__); +} + + +#if defined(CONFIG_OF) +#include + +bool usb_of_has_combined_node(struct usb_device * udev) +{ + lx_emul_trace(__func__); + return true; +} +#endif diff --git a/repos/dde_linux/src/drivers/usb_hid/lx_emul.c b/repos/dde_linux/src/drivers/usb_hid/lx_emul.c index 7b649a2b61..0ab5a182de 100644 --- a/repos/dde_linux/src/drivers/usb_hid/lx_emul.c +++ b/repos/dde_linux/src/drivers/usb_hid/lx_emul.c @@ -23,36 +23,7 @@ const struct attribute_group input_poller_attribute_group; pteval_t __default_kernel_pte_mask __read_mostly = ~0; -struct device_type usb_if_device_type = { - .name = "usb_interface" -}; - struct usb_driver usbfs_driver = { .name = "usbfs" }; const struct attribute_group *usb_device_groups[] = { }; - - -/* - * Taken and ajdusted from linux/drivers/usb/core/message.c If this is required - * by more drivers in the future, we will move it to - * dde_linux/src/lib/virt/shadow/drivers/usb/core/message.c - */ -int usb_clear_halt(struct usb_device *dev, int pipe) -{ - int result; - int endp = usb_pipeendpoint(pipe); - - if (usb_pipein(pipe)) - endp |= USB_DIR_IN; - - /* we don't care if it wasn't halted first. in fact some devices - * (like some ibmcam model 1 units) seem to expect hosts to make - * this request for iso endpoints, which can't halt! - */ - result = usb_control_msg(dev, usb_sndctrlpipe(dev, endp), - USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, - USB_ENDPOINT_HALT, endp, NULL, 0, - USB_CTRL_SET_TIMEOUT); - return result; -} diff --git a/repos/dde_linux/src/drivers/usb_hid/lx_user.c b/repos/dde_linux/src/drivers/usb_hid/lx_user.c index f65d78749b..3b880dc440 100644 --- a/repos/dde_linux/src/drivers/usb_hid/lx_user.c +++ b/repos/dde_linux/src/drivers/usb_hid/lx_user.c @@ -1,6 +1,7 @@ /* * \brief Post kernel activity * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2023-06-29 */ @@ -11,36 +12,20 @@ * version 2. */ -#include -#include - -static struct task_struct *main_task = NULL; - - -struct task_struct *lx_user_new_usb_task(int (*func)(void*), void *args) -{ - int pid = kernel_thread(func, args, CLONE_FS | CLONE_FILES); - return find_task_by_pid_ns(pid, NULL); -} - - -void lx_user_destroy_usb_task(struct task_struct *task) -{ - if (task != current) { - printk("%s: task: %px is not current: %px\n", __func__, - task, current); - return; - } - - /* unblock main task which initiated destruction */ - lx_emul_task_unblock(main_task); - - do_exit(0); -} - +#include +#include +#include +#include +#include void lx_user_init(void) { - int pid = kernel_thread(lx_user_main_task, &main_task, CLONE_FS | CLONE_FILES); - main_task = find_task_by_pid_ns(pid, NULL); + lx_emul_usb_client_init(); + lx_emul_input_leds_init(); +} + + +void lx_user_handle_io(void) +{ + lx_emul_usb_client_ticker(); } diff --git a/repos/dde_linux/src/drivers/usb_hid/main.cc b/repos/dde_linux/src/drivers/usb_hid/main.cc index 475283b421..c6065ba104 100644 --- a/repos/dde_linux/src/drivers/usb_hid/main.cc +++ b/repos/dde_linux/src/drivers/usb_hid/main.cc @@ -1,6 +1,7 @@ /* * \brief C++ initialization, session, and client handling * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2023-06-29 */ @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -30,343 +32,65 @@ using namespace Genode; -struct Task_handler -{ - task_struct *task; - Signal_handler handler; - bool handling_signal { false }; - bool running { true }; - - /* - * If the task is currently executing and the signal handler - * is called again via 'block_and_schedule()', we need to - * keep this information, so the task does not block at the - * end when a new signal already occurred. - * - * Initialized as true for the initial run of the task. - */ - bool _signal_pending { true }; - - void handle_signal() - { - _signal_pending = true; - lx_emul_task_unblock(task); - handling_signal = true; - Lx_kit::env().scheduler.execute(); - handling_signal = false; - } - - bool signal_pending() - { - bool ret = _signal_pending; - _signal_pending = false; - return ret; - } - - void block_and_schedule() - { - lx_emul_task_schedule(true); - } - - void destroy_task() - { - running = false; - lx_emul_task_unblock(task); - /* will be unblocked by lx_user_destroy_usb_task */ - lx_emul_task_schedule(true); - } - - Task_handler(Entrypoint & ep, task_struct *task) - : task(task), handler(ep, *this, &Task_handler::handle_signal) { } - - /* non-copyable */ - Task_handler(const Task_handler&) = delete; - Task_handler & operator=(const Task_handler&) = delete; -}; - - -struct Leds +struct Main { Env &env; - Attached_rom_dataspace &config_rom; - - task_struct *led_task { lx_user_new_usb_task(led_task_entry, this) }; - - Task_handler led_task_handler { env.ep(), led_task }; + Attached_rom_dataspace config_rom { env, "config" }; Usb::Led_state capslock { env, "capslock" }, numlock { env, "numlock" }, scrlock { env, "scrlock" }; - Leds(Env &env, Attached_rom_dataspace &config_rom) - : env(env), config_rom(config_rom) { }; + Signal_handler
signal_handler { env.ep(), *this, + &Main::handle_signal }; + Signal_handler
usb_rom_handler { env.ep(), *this, + &Main::handle_usb_rom }; + Signal_handler
config_handler { env.ep(), *this, + &Main::handle_config }; - /* non-copyable */ - Leds(const Leds&) = delete; - Leds & operator=(const Leds&) = delete; + Main(Env &env) + : + env(env) + { + Lx_kit::initialize(env, signal_handler); + + Genode_c_api::initialize_usb_client(env, Lx_kit::env().heap, + signal_handler, usb_rom_handler); + + genode_event_init(genode_env_ptr(env), + genode_allocator_ptr(Lx_kit::env().heap)); + + config_rom.sigh(config_handler); + handle_config(); + + lx_emul_start_kernel(nullptr); + } + + void handle_signal() + { + lx_user_handle_io(); + Lx_kit::env().scheduler.execute(); + } + + void handle_usb_rom() + { + lx_emul_usb_client_rom_update(); + Lx_kit::env().scheduler.execute(); + } void handle_config() { config_rom.update(); - Genode::Xml_node config =config_rom.xml(); - - capslock.update(config, led_task_handler.handler); - numlock .update(config, led_task_handler.handler); - scrlock .update(config, led_task_handler.handler); - } - - - /********** - ** Task ** - **********/ - - static int led_task_entry(void *arg) - { - Leds &led = *reinterpret_cast(arg); - - while (true) { - led.handle_config(); - - lx_emul_input_leds_update(led.capslock.enabled(), - led.numlock.enabled(), - led.scrlock.enabled()); - - led.led_task_handler.block_and_schedule(); - } - } -}; - - -struct Device : Registry::Element -{ - using Label = String<64>; - - Env &env; - Label label; - - /* - * Dedicated allocator per device to notice dangling - * allocations on device destruction. - */ - Allocator_avl alloc { &Lx_kit::env().heap }; - - task_struct *state_task { lx_user_new_usb_task(state_task_entry, this) }; - task_struct *urb_task { lx_user_new_usb_task(urb_task_entry, this) }; - - Task_handler state_task_handler { env.ep(), state_task }; - Task_handler urb_task_handler { env.ep(), urb_task }; - - genode_usb_client_handle_t usb_handle { - genode_usb_client_create(genode_env_ptr(env), - genode_allocator_ptr(Lx_kit::env().heap), - genode_range_allocator_ptr(alloc), - label.string(), - genode_signal_handler_ptr(state_task_handler.handler)) }; - - bool updated { true }; - bool registered { false }; - - void *lx_device_handle { nullptr }; - - Device(Env &env, Registry ®istry, Label label) - : - Registry::Element(registry, *this), - env(env), label(label) - { - genode_usb_client_sigh_ack_avail(usb_handle, - genode_signal_handler_ptr(urb_task_handler.handler)); - } - - ~Device() - { - genode_usb_client_destroy(usb_handle, - genode_allocator_ptr(Lx_kit::env().heap)); - - state_task_handler.destroy_task(); - urb_task_handler.destroy_task(); - } - - /* non-copyable */ - Device(const Device&) = delete; - Device & operator=(const Device&) = delete; - - void register_device() - { - registered = true; - lx_device_handle = lx_emul_usb_client_register_device(usb_handle, label.string()); - if (!lx_device_handle) registered = false; - } - - void unregister_device() - { - lx_emul_usb_client_unregister_device(usb_handle, lx_device_handle); - registered = false; - } - - bool deinit() { return !registered && - !state_task_handler.handling_signal && - !urb_task_handler.handling_signal; } - - /********** - ** Task ** - **********/ - - static int state_task_entry(void *arg) - { - Device &device = *reinterpret_cast(arg); - - while (device.state_task_handler.running) { - while (device.state_task_handler.signal_pending()) { - if (genode_usb_client_plugged(device.usb_handle) && !device.registered) - device.register_device(); - - if (!genode_usb_client_plugged(device.usb_handle) && device.registered) - device.unregister_device(); - } - device.state_task_handler.block_and_schedule(); - } - lx_user_destroy_usb_task(device.state_task_handler.task); - return 0; - } - - static int urb_task_entry(void *arg) - { - Device &device = *reinterpret_cast(arg); - - while (device.urb_task_handler.running) { - if (device.registered) - genode_usb_client_execute_completions(device.usb_handle); - - device.urb_task_handler.block_and_schedule(); - } - lx_user_destroy_usb_task(device.urb_task_handler.task); - return 0; - } -}; - - -struct Driver -{ - Env &env; - - Task_handler task_handler; - - Heap &heap { Lx_kit::env().heap }; - - bool use_report { false }; - - Constructible report_rom { }; - - Attached_rom_dataspace config_rom { env, "config" }; - - Leds leds { env, config_rom }; - - Registry devices { }; - - Driver(Env &env, task_struct *task) - : env(env), task_handler(env.ep(), task) - { - try { - Xml_node config = config_rom.xml(); - use_report = config.attribute_value("use_report", false); - } catch(...) { } - - if (use_report) - warning("use compatibility mode: ", - "will claim all HID devices from USB report"); - } - - void scan_report() - { - if (!report_rom.constructed()) { - report_rom.construct(env, "report"); - report_rom->sigh(task_handler.handler); - } - - report_rom->update(); - - devices.for_each([&] (Device & d) { d.updated = false; }); - - try { - Xml_node report_node = report_rom->xml(); - report_node.for_each_sub_node([&] (Xml_node & dev_node) - { - unsigned long c = 0; - dev_node.attribute("class").value(c); - if (c != 0x3 /* USB_CLASS_HID */) return; - - Device::Label label; - dev_node.attribute("label").value(label); - - bool found = false; - - devices.for_each([&] (Device & d) { - if (d.label == label) d.updated = found = true; }); - - if (!found) new (heap) Device(env, devices, label); - }); - } catch(...) { - error("Error parsing USB devices report"); - throw; - }; - - devices.for_each([&] (Device & d) { - if (!d.updated && d.deinit()) { - destroy(heap, &d); - } - }); - } -}; - - -struct Main -{ - Env &env; - - Signal_handler
signal_handler { env.ep(), *this, &Main::handle_signal }; - - Main(Env &env) : env(env) { } - - void handle_signal() - { + Genode::Xml_node config = config_rom.xml(); + capslock.update(config, config_handler); + numlock .update(config, config_handler); + scrlock .update(config, config_handler); + lx_emul_input_leds_update(capslock.enabled(), numlock.enabled(), + scrlock.enabled()); Lx_kit::env().scheduler.execute(); } }; -void Component::construct(Env & env) -{ - static Main main { env }; - Lx_kit::initialize(env, main.signal_handler); - - env.exec_static_constructors(); - - genode_event_init(genode_env_ptr(env), - genode_allocator_ptr(Lx_kit::env().heap)); - - lx_emul_start_kernel(nullptr); -} - - -/********** - ** Task ** - **********/ - -int lx_user_main_task(void *data) -{ - task_struct *task = *static_cast(data); - - static Driver driver { Lx_kit::env().env, task }; - - for (;;) { - while (driver.task_handler.signal_pending()) { - if (!driver.use_report) - static Device dev(driver.env, driver.devices, Device::Label("")); - else - driver.scan_report(); - } - driver.task_handler.block_and_schedule(); - } - return 0; -} +void Component::construct(Env & env) { static Main main(env); } diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/generated_dummies.c b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/generated_dummies.c index a13d48ff85..ef6cba4fa7 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/generated_dummies.c @@ -1,7 +1,7 @@ /* * \brief Dummy definitions of Linux Kernel functions * \author Automatically generated file - do no edit - * \date 2024-02-05 + * \date 2024-02-22 */ #include @@ -89,14 +89,6 @@ int _printk_deferred(const char * fmt,...) } -#include - -void add_device_randomness(const void * buf,size_t len) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int add_uevent_var(struct kobj_uevent_env * env,const char * format,...) @@ -426,6 +418,14 @@ void led_trigger_event(struct led_trigger * trig,enum led_brightness brightness) unsigned long lpj_fine; +#include + +bool of_device_is_available(const struct device_node * device) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void of_device_uevent(struct device * dev,struct kobj_uevent_env * env) @@ -487,6 +487,14 @@ int power_supply_powers(struct power_supply * psy,struct device * dev) } +#include + +struct scatterlist * sg_next(struct scatterlist * sg) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void smp_call_function_many(const struct cpumask * mask,smp_call_func_t func,void * info,bool wait) @@ -532,14 +540,6 @@ struct timerqueue_node * timerqueue_iterate_next(struct timerqueue_node * node) } -#include - -void usb_block_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -547,13 +547,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -562,29 +555,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -607,14 +577,6 @@ void usb_hub_remove_port_device(struct usb_hub * hub,int port1) } -#include - -int usb_interrupt_msg(struct usb_device * usb_dev,unsigned int pipe,void * data,int len,int * actual_length,int timeout) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_major_cleanup(void); void usb_major_cleanup(void) { @@ -630,8 +592,9 @@ struct device_node * usb_of_get_device_node(struct usb_device * hub,int port1) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) +#include + +struct device_node * usb_of_get_interface_node(struct usb_device * udev,u8 config,u8 ifnum) { lx_emul_trace_and_stop(__func__); } @@ -645,22 +608,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -int usb_unlink_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/source.list b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/source.list index 56d90ec552..9425845977 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/source.list +++ b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v6/source.list @@ -32,8 +32,11 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c +fs/nls/nls_base.c kernel/kthread.c kernel/locking/mutex.c kernel/locking/osq_lock.c diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/generated_dummies.c b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/generated_dummies.c index c78d9df508..02c52260aa 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/generated_dummies.c @@ -1,7 +1,7 @@ /* * \brief Dummy definitions of Linux Kernel functions * \author Automatically generated file - do no edit - * \date 2024-02-05 + * \date 2024-02-22 */ #include @@ -79,14 +79,6 @@ int _printk_deferred(const char * fmt,...) } -#include - -void add_device_randomness(const void * buf,size_t len) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int add_uevent_var(struct kobj_uevent_env * env,const char * format,...) @@ -328,6 +320,14 @@ void led_trigger_event(struct led_trigger * trig,enum led_brightness brightness) unsigned long lpj_fine; +#include + +bool of_device_is_available(const struct device_node * device) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void of_device_uevent(struct device * dev,struct kobj_uevent_env * env) @@ -384,6 +384,14 @@ int power_supply_powers(struct power_supply * psy,struct device * dev) } +#include + +struct scatterlist * sg_next(struct scatterlist * sg) +{ + lx_emul_trace_and_stop(__func__); +} + + #include bool static_key_initialized; @@ -413,14 +421,6 @@ struct timerqueue_node * timerqueue_iterate_next(struct timerqueue_node * node) } -#include - -void usb_block_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -428,13 +428,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -443,29 +436,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -488,14 +458,6 @@ void usb_hub_remove_port_device(struct usb_hub * hub,int port1) } -#include - -int usb_interrupt_msg(struct usb_device * usb_dev,unsigned int pipe,void * data,int len,int * actual_length,int timeout) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_major_cleanup(void); void usb_major_cleanup(void) { @@ -511,8 +473,9 @@ struct device_node * usb_of_get_device_node(struct usb_device * hub,int port1) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) +#include + +struct device_node * usb_of_get_interface_node(struct usb_device * udev,u8 config,u8 ifnum) { lx_emul_trace_and_stop(__func__); } @@ -526,22 +489,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -int usb_unlink_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/source.list b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/source.list index 1ca8382d6a..befcb3fe3d 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/source.list +++ b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v7/source.list @@ -32,8 +32,11 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c +fs/nls/nls_base.c kernel/kthread.c kernel/locking/mutex.c kernel/locking/osq_lock.c diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/generated_dummies.c b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/generated_dummies.c index cf2c08907b..d581d1766c 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/generated_dummies.c @@ -1,7 +1,7 @@ /* * \brief Dummy definitions of Linux Kernel functions * \author Automatically generated file - do no edit - * \date 2024-02-05 + * \date 2024-02-22 */ #include @@ -89,14 +89,6 @@ int _printk_deferred(const char * fmt,...) } -#include - -void add_device_randomness(const void * buf,size_t len) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int add_uevent_var(struct kobj_uevent_env * env,const char * format,...) @@ -391,6 +383,14 @@ void led_trigger_event(struct led_trigger * trig,enum led_brightness brightness) unsigned long lpj_fine; +#include + +bool of_device_is_available(const struct device_node * device) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void of_device_uevent(struct device * dev,struct kobj_uevent_env * env) @@ -452,6 +452,14 @@ int power_supply_powers(struct power_supply * psy,struct device * dev) } +#include + +struct scatterlist * sg_next(struct scatterlist * sg) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void smp_call_function_many(const struct cpumask * mask,smp_call_func_t func,void * info,bool wait) @@ -497,14 +505,6 @@ struct timerqueue_node * timerqueue_iterate_next(struct timerqueue_node * node) } -#include - -void usb_block_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -512,13 +512,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -527,29 +520,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -572,14 +542,6 @@ void usb_hub_remove_port_device(struct usb_hub * hub,int port1) } -#include - -int usb_interrupt_msg(struct usb_device * usb_dev,unsigned int pipe,void * data,int len,int * actual_length,int timeout) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_major_cleanup(void); void usb_major_cleanup(void) { @@ -595,8 +557,9 @@ struct device_node * usb_of_get_device_node(struct usb_device * hub,int port1) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) +#include + +struct device_node * usb_of_get_interface_node(struct usb_device * udev,u8 config,u8 ifnum) { lx_emul_trace_and_stop(__func__); } @@ -610,22 +573,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -int usb_unlink_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/source.list b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/source.list index f271be1d07..372de08452 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/source.list +++ b/repos/dde_linux/src/drivers/usb_hid/spec/arm_v8/source.list @@ -30,8 +30,11 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c +fs/nls/nls_base.c kernel/kthread.c kernel/locking/mutex.c kernel/locking/osq_lock.c diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/generated_dummies.c b/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/generated_dummies.c index 8873823f37..3f0883dfbc 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/generated_dummies.c @@ -75,14 +75,6 @@ void ack_bad_irq(unsigned int irq) } -#include - -void add_device_randomness(const void * buf,size_t len) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int add_uevent_var(struct kobj_uevent_env * env,const char * format,...) @@ -367,6 +359,14 @@ int power_supply_powers(struct power_supply * psy,struct device * dev) } +#include + +struct scatterlist * sg_next(struct scatterlist * sg) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void smp_call_function_many(const struct cpumask * mask,smp_call_func_t func,void * info,bool wait) @@ -412,14 +412,6 @@ struct timerqueue_node * timerqueue_iterate_next(struct timerqueue_node * node) } -#include - -void usb_block_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -427,13 +419,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -442,29 +427,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -487,14 +449,6 @@ void usb_hub_remove_port_device(struct usb_hub * hub,int port1) } -#include - -int usb_interrupt_msg(struct usb_device * usb_dev,unsigned int pipe,void * data,int len,int * actual_length,int timeout) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_major_cleanup(void); void usb_major_cleanup(void) { @@ -502,13 +456,6 @@ void usb_major_cleanup(void) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const char * usb_speed_string(enum usb_device_speed speed) @@ -517,22 +464,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -int usb_unlink_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/source.list b/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/source.list index d00d444b2d..fd56b47202 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/source.list +++ b/repos/dde_linux/src/drivers/usb_hid/spec/x86_32/source.list @@ -25,8 +25,11 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c +fs/nls/nls_base.c kernel/kthread.c kernel/locking/mutex.c kernel/locking/osq_lock.c diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/generated_dummies.c b/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/generated_dummies.c index bdd521e390..f7dcdd65de 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/generated_dummies.c @@ -75,14 +75,6 @@ void ack_bad_irq(unsigned int irq) } -#include - -void add_device_randomness(const void * buf,size_t len) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int add_uevent_var(struct kobj_uevent_env * env,const char * format,...) @@ -335,6 +327,14 @@ int power_supply_powers(struct power_supply * psy,struct device * dev) } +#include + +struct scatterlist * sg_next(struct scatterlist * sg) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void smp_call_function_many(const struct cpumask * mask,smp_call_func_t func,void * info,bool wait) @@ -380,14 +380,6 @@ struct timerqueue_node * timerqueue_iterate_next(struct timerqueue_node * node) } -#include - -void usb_block_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -395,13 +387,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -410,29 +395,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -455,14 +417,6 @@ void usb_hub_remove_port_device(struct usb_hub * hub,int port1) } -#include - -int usb_interrupt_msg(struct usb_device * usb_dev,unsigned int pipe,void * data,int len,int * actual_length,int timeout) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_major_cleanup(void); void usb_major_cleanup(void) { @@ -470,13 +424,6 @@ void usb_major_cleanup(void) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const char * usb_speed_string(enum usb_device_speed speed) @@ -485,22 +432,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -int usb_unlink_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/source.list b/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/source.list index d00d444b2d..fd56b47202 100644 --- a/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/source.list +++ b/repos/dde_linux/src/drivers/usb_hid/spec/x86_64/source.list @@ -25,8 +25,11 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c +fs/nls/nls_base.c kernel/kthread.c kernel/locking/mutex.c kernel/locking/osq_lock.c diff --git a/repos/dde_linux/src/drivers/usb_hid/target.inc b/repos/dde_linux/src/drivers/usb_hid/target.inc index f6624d91fa..ca9bc69719 100644 --- a/repos/dde_linux/src/drivers/usb_hid/target.inc +++ b/repos/dde_linux/src/drivers/usb_hid/target.inc @@ -8,11 +8,11 @@ SRC_C += dummies.c \ lx_emul.c \ lx_user.c -SRC_C += lx_emul/shadow/drivers/input/evdev.c \ - lx_emul/shadow/drivers/input/input-leds.c \ - lx_emul/virt/shadow/drivers/usb/core/message.c \ - lx_emul/virt/shadow/drivers/usb/core/urb.c \ - lx_emul/virt/usb_client.c +SRC_C += lx_emul/shadow/drivers/input/evdev.c +SRC_C += lx_emul/shadow/drivers/input/input-leds.c +SRC_C += lx_emul/virt/shadow/drivers/usb/core/buffer.c +SRC_C += lx_emul/virt/shadow/drivers/usb/core/hcd.c +SRC_C += lx_emul/virt/usb_client.c SRC_CC = main.cc diff --git a/repos/dde_linux/src/drivers/usb_hid/usb_hid.h b/repos/dde_linux/src/drivers/usb_hid/usb_hid.h index 4f26ecd346..33f1085bf8 100644 --- a/repos/dde_linux/src/drivers/usb_hid/usb_hid.h +++ b/repos/dde_linux/src/drivers/usb_hid/usb_hid.h @@ -18,14 +18,7 @@ extern "C" { #endif -struct task_struct; - -int lx_user_main_task(void *); -struct task_struct *lx_user_new_usb_task(int (*func)(void*), void *args); -void lx_user_destroy_usb_task(struct task_struct*); - -void lx_led_state_update(bool capslock, bool numlock, bool scrlock); - +void lx_emul_led_state_update(void); #ifdef __cplusplus } /* extern "C" */ diff --git a/repos/dde_linux/src/drivers/usb_net/dummies.c b/repos/dde_linux/src/drivers/usb_net/dummies.c index ad8bb79767..d750c18400 100644 --- a/repos/dde_linux/src/drivers/usb_net/dummies.c +++ b/repos/dde_linux/src/drivers/usb_net/dummies.c @@ -157,42 +157,6 @@ void usb_hcd_synchronize_unlinks(struct usb_device * udev) } -#include - -struct urb * usb_get_from_anchor(struct usb_anchor * anchor) -{ - lx_emul_trace(__func__); - return NULL; -} - - -extern char * usb_cache_string(struct usb_device * udev,int index); -char * usb_cache_string(struct usb_device * udev,int index) -{ - lx_emul_trace(__func__); - return NULL; -} - - -void usb_kill_urb(struct urb * urb) -{ - lx_emul_trace(__func__); -} - - -int usb_unlink_urb(struct urb * urb) -{ - lx_emul_trace(__func__); - return 0; -} - - -void usb_poison_urb(struct urb * urb) -{ - lx_emul_trace(__func__); -} - - #include struct usb_hcd * usb_get_hcd(struct usb_hcd * hcd) @@ -484,15 +448,51 @@ void usb_remove_ep_devs(struct usb_host_endpoint * endpoint) } -extern void usb_disable_interface(struct usb_device * dev,struct usb_interface * intf,bool reset_hardware); -void usb_disable_interface(struct usb_device * dev,struct usb_interface * intf,bool reset_hardware) -{ - lx_emul_trace(__func__); -} - - extern void netdev_unregister_kobject(struct net_device * ndev); void netdev_unregister_kobject(struct net_device * ndev) { lx_emul_trace(__func__); } + + +void usb_hcd_flush_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + lx_emul_trace(__func__); +} + + +void usb_hcd_disable_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + lx_emul_trace(__func__); +} + + +void usb_hcd_reset_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + lx_emul_trace(__func__); +} + +const struct attribute_group *usb_interface_groups[] = { NULL }; + + +#include + +int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) +{ + lx_emul_trace(__func__); + return 0; +} + + +#if defined(CONFIG_OF) +#include + +bool usb_of_has_combined_node(struct usb_device * udev) +{ + lx_emul_trace(__func__); + return true; +} +#endif diff --git a/repos/dde_linux/src/drivers/usb_net/lx_emul.c b/repos/dde_linux/src/drivers/usb_net/lx_emul.c index 4573db6ee6..9b69696272 100644 --- a/repos/dde_linux/src/drivers/usb_net/lx_emul.c +++ b/repos/dde_linux/src/drivers/usb_net/lx_emul.c @@ -17,11 +17,6 @@ pteval_t __default_kernel_pte_mask __read_mostly = ~0; -struct device_type usb_if_device_type = { - .name = "usb_interface" -}; - - struct usb_driver usbfs_driver = { .name = "usbfs" }; @@ -147,349 +142,3 @@ int netdev_register_kobject(struct net_device * ndev) if (use_mac_address) eth_hw_addr_set(ndev, mac_address); return 0; } - - -/* - * core/message.c - */ -#include -#include -#include -#include - -int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, - struct usb_interface *intf, - u8 *buffer, - int buflen) -{ - /* duplicates are ignored */ - struct usb_cdc_union_desc *union_header = NULL; - - /* duplicates are not tolerated */ - struct usb_cdc_header_desc *header = NULL; - struct usb_cdc_ether_desc *ether = NULL; - struct usb_cdc_mdlm_detail_desc *detail = NULL; - struct usb_cdc_mdlm_desc *desc = NULL; - - unsigned int elength; - int cnt = 0; - - memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header)); - hdr->phonet_magic_present = false; - while (buflen > 0) { - elength = buffer[0]; - if (!elength) { - dev_err(&intf->dev, "skipping garbage byte\n"); - elength = 1; - goto next_desc; - } - if ((buflen < elength) || (elength < 3)) { - dev_err(&intf->dev, "invalid descriptor buffer length\n"); - break; - } - if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); - goto next_desc; - } - - switch (buffer[2]) { - case USB_CDC_UNION_TYPE: /* we've found it */ - if (elength < sizeof(struct usb_cdc_union_desc)) - goto next_desc; - if (union_header) { - dev_err(&intf->dev, "More than one union descriptor, skipping ...\n"); - goto next_desc; - } - union_header = (struct usb_cdc_union_desc *)buffer; - break; - case USB_CDC_COUNTRY_TYPE: - if (elength < sizeof(struct usb_cdc_country_functional_desc)) - goto next_desc; - hdr->usb_cdc_country_functional_desc = - (struct usb_cdc_country_functional_desc *)buffer; - break; - case USB_CDC_HEADER_TYPE: - if (elength != sizeof(struct usb_cdc_header_desc)) - goto next_desc; - if (header) - return -EINVAL; - header = (struct usb_cdc_header_desc *)buffer; - break; - case USB_CDC_ACM_TYPE: - if (elength < sizeof(struct usb_cdc_acm_descriptor)) - goto next_desc; - hdr->usb_cdc_acm_descriptor = - (struct usb_cdc_acm_descriptor *)buffer; - break; - case USB_CDC_ETHERNET_TYPE: - if (elength != sizeof(struct usb_cdc_ether_desc)) - goto next_desc; - if (ether) - return -EINVAL; - ether = (struct usb_cdc_ether_desc *)buffer; - break; - case USB_CDC_CALL_MANAGEMENT_TYPE: - if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor)) - goto next_desc; - hdr->usb_cdc_call_mgmt_descriptor = - (struct usb_cdc_call_mgmt_descriptor *)buffer; - break; - case USB_CDC_DMM_TYPE: - if (elength < sizeof(struct usb_cdc_dmm_desc)) - goto next_desc; - hdr->usb_cdc_dmm_desc = - (struct usb_cdc_dmm_desc *)buffer; - break; - case USB_CDC_MDLM_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_desc)) - goto next_desc; - if (desc) - return -EINVAL; - desc = (struct usb_cdc_mdlm_desc *)buffer; - break; - case USB_CDC_MDLM_DETAIL_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_detail_desc)) - goto next_desc; - if (detail) - return -EINVAL; - detail = (struct usb_cdc_mdlm_detail_desc *)buffer; - break; - case USB_CDC_NCM_TYPE: - if (elength < sizeof(struct usb_cdc_ncm_desc)) - goto next_desc; - hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer; - break; - case USB_CDC_MBIM_TYPE: - if (elength < sizeof(struct usb_cdc_mbim_desc)) - goto next_desc; - - hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer; - break; - case USB_CDC_MBIM_EXTENDED_TYPE: - if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) - break; - hdr->usb_cdc_mbim_extended_desc = - (struct usb_cdc_mbim_extended_desc *)buffer; - break; - case CDC_PHONET_MAGIC_NUMBER: - hdr->phonet_magic_present = true; - break; - default: - /* - * there are LOTS more CDC descriptors that - * could legitimately be found here. - */ - dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n", - buffer[2], elength); - goto next_desc; - } - cnt++; -next_desc: - buflen -= elength; - buffer += elength; - } - hdr->usb_cdc_union_desc = union_header; - hdr->usb_cdc_header_desc = header; - hdr->usb_cdc_mdlm_detail_desc = detail; - hdr->usb_cdc_mdlm_desc = desc; - hdr->usb_cdc_ether_desc = ether; - return cnt; -} - - -/** - * usb_get_string - gets a string descriptor - * @dev: the device whose string descriptor is being retrieved - * @langid: code for language chosen (from string descriptor zero) - * @index: the number of the descriptor - * @buf: where to put the string - * @size: how big is "buf"? - * - * Context: task context, might sleep. - * - * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character, - * in little-endian byte order). - * The usb_string() function will often be a convenient way to turn - * these strings into kernel-printable form. - * - * Strings may be referenced in device, configuration, interface, or other - * descriptors, and could also be used in vendor-specific ways. - * - * This call is synchronous, and may not be used in an interrupt context. - * - * Return: The number of bytes received on success, or else the status code - * returned by the underlying usb_control_msg() call. - */ -static int usb_get_string(struct usb_device *dev, unsigned short langid, - unsigned char index, void *buf, int size) -{ - int i; - int result; - - if (size <= 0) /* No point in asking for no data */ - return -EINVAL; - - for (i = 0; i < 3; ++i) { - /* retry on length 0 or stall; some devices are flakey */ - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (USB_DT_STRING << 8) + index, langid, buf, size, - USB_CTRL_GET_TIMEOUT); - if (result == 0 || result == -EPIPE) - continue; - if (result > 1 && ((u8 *) buf)[1] != USB_DT_STRING) { - result = -ENODATA; - continue; - } - break; - } - return result; -} - -static void usb_try_string_workarounds(unsigned char *buf, int *length) -{ - int newlength, oldlength = *length; - - for (newlength = 2; newlength + 1 < oldlength; newlength += 2) - if (!isprint(buf[newlength]) || buf[newlength + 1]) - break; - - if (newlength > 2) { - buf[0] = newlength; - *length = newlength; - } -} - -static int usb_string_sub(struct usb_device *dev, unsigned int langid, - unsigned int index, unsigned char *buf) -{ - int rc; - - /* Try to read the string descriptor by asking for the maximum - * possible number of bytes */ - if (dev->quirks & USB_QUIRK_STRING_FETCH_255) - rc = -EIO; - else - rc = usb_get_string(dev, langid, index, buf, 255); - - /* If that failed try to read the descriptor length, then - * ask for just that many bytes */ - if (rc < 2) { - rc = usb_get_string(dev, langid, index, buf, 2); - if (rc == 2) - rc = usb_get_string(dev, langid, index, buf, buf[0]); - } - - if (rc >= 2) { - if (!buf[0] && !buf[1]) - usb_try_string_workarounds(buf, &rc); - - /* There might be extra junk at the end of the descriptor */ - if (buf[0] < rc) - rc = buf[0]; - - rc = rc - (rc & 1); /* force a multiple of two */ - } - - if (rc < 2) - rc = (rc < 0 ? rc : -EINVAL); - - return rc; -} - -static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) -{ - int err; - - if (dev->have_langid) - return 0; - - if (dev->string_langid < 0) - return -EPIPE; - - err = usb_string_sub(dev, 0, 0, tbuf); - - /* If the string was reported but is malformed, default to english - * (0x0409) */ - if (err == -ENODATA || (err > 0 && err < 4)) { - dev->string_langid = 0x0409; - dev->have_langid = 1; - dev_err(&dev->dev, - "language id specifier not provided by device, defaulting to English\n"); - return 0; - } - - /* In case of all other errors, we assume the device is not able to - * deal with strings at all. Set string_langid to -1 in order to - * prevent any string to be retrieved from the device */ - if (err < 0) { - dev_info(&dev->dev, "string descriptor 0 read error: %d\n", - err); - dev->string_langid = -1; - return -EPIPE; - } - - /* always use the first langid listed */ - dev->string_langid = tbuf[2] | (tbuf[3] << 8); - dev->have_langid = 1; - dev_dbg(&dev->dev, "default language 0x%04x\n", - dev->string_langid); - return 0; -} - -/** - * usb_string - returns UTF-8 version of a string descriptor - * @dev: the device whose string descriptor is being retrieved - * @index: the number of the descriptor - * @buf: where to put the string - * @size: how big is "buf"? - * - * Context: task context, might sleep. - * - * This converts the UTF-16LE encoded strings returned by devices, from - * usb_get_string_descriptor(), to null-terminated UTF-8 encoded ones - * that are more usable in most kernel contexts. Note that this function - * chooses strings in the first language supported by the device. - * - * This call is synchronous, and may not be used in an interrupt context. - * - * Return: length of the string (>= 0) or usb_control_msg status (< 0). - */ -int usb_string(struct usb_device *dev, int index, char *buf, size_t size) -{ - unsigned char *tbuf; - int err; - - if (dev->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; - if (size <= 0 || !buf) - return -EINVAL; - buf[0] = 0; - if (index <= 0 || index >= 256) - return -EINVAL; - tbuf = kmalloc(256, GFP_NOIO); - if (!tbuf) - return -ENOMEM; - - err = usb_get_langid(dev, tbuf); - if (err < 0) - goto errout; - - err = usb_string_sub(dev, dev->string_langid, index, tbuf); - if (err < 0) - goto errout; - - size--; /* leave room for trailing NULL char in output buffer */ - err = utf16s_to_utf8s((wchar_t *) &tbuf[2], (err - 2) / 2, - UTF16_LITTLE_ENDIAN, buf, size); - buf[err] = 0; - - if (tbuf[1] != USB_DT_STRING) - dev_dbg(&dev->dev, - "wrong descriptor type %02x for string %d (\"%s\")\n", - tbuf[1], index, buf); - - errout: - kfree(tbuf); - return err; -} diff --git a/repos/dde_linux/src/drivers/usb_net/lx_user.c b/repos/dde_linux/src/drivers/usb_net/lx_user.c index c95ed3c1c5..59e6359079 100644 --- a/repos/dde_linux/src/drivers/usb_net/lx_user.c +++ b/repos/dde_linux/src/drivers/usb_net/lx_user.c @@ -165,7 +165,7 @@ static genode_uplink_rx_result_t uplink_rx_one_packet(struct genode_uplink_rx_co } -static int user_task_function(void *arg) +static int network_loop(void *arg) { for (;;) { @@ -206,18 +206,23 @@ static int user_task_function(void *arg) } -struct task_struct *user_task_struct_ptr; /* used by 'Device' for lx_emul_task_unblock */ +static struct task_struct *net_task; void lx_user_init(void) { pid_t pid; - lx_user_main_task(NULL); + lx_emul_usb_client_init(); - pid = kernel_thread(user_task_function, NULL, CLONE_FS | CLONE_FILES); + pid = kernel_thread(network_loop, NULL, CLONE_FS | CLONE_FILES); + net_task = find_task_by_pid_ns(pid, NULL); +} - user_task_struct_ptr = find_task_by_pid_ns(pid, NULL); +void lx_user_handle_io(void) +{ + lx_emul_usb_client_ticker(); + if (net_task) lx_emul_task_unblock(net_task); } @@ -232,8 +237,7 @@ bool force_uplink_destroy = false; void rtmsg_ifinfo(int type, struct net_device * dev, unsigned int change, gfp_t flags) { /* trigger handle_create_uplink / handle_destroy_uplink */ - if (user_task_struct_ptr) - lx_emul_task_unblock(user_task_struct_ptr); + if (net_task) lx_emul_task_unblock(net_task); if (force_uplink_destroy) { struct genode_uplink *uplink = dev_genode_uplink(dev); diff --git a/repos/dde_linux/src/drivers/usb_net/main.cc b/repos/dde_linux/src/drivers/usb_net/main.cc index 9f23bab484..35bcc34ddd 100644 --- a/repos/dde_linux/src/drivers/usb_net/main.cc +++ b/repos/dde_linux/src/drivers/usb_net/main.cc @@ -21,7 +21,7 @@ #include #include -#include +#include #include /* C-interface */ @@ -29,106 +29,55 @@ using namespace Genode; -extern task_struct *user_task_struct_ptr; -extern bool force_uplink_destroy; extern bool use_mac_address; extern unsigned char mac_address[6]; -struct Device +struct Main { - Env &env; + Env &env; Attached_rom_dataspace config_rom { env, "config" }; + unsigned long usb_config { 0 }; - /* - * Dedicated allocator per device to notice dangling - * allocations on device destruction. - */ - Allocator_avl alloc { &Lx_kit::env().heap }; + Signal_handler
signal_handler { env.ep(), *this, + &Main::handle_signal }; + Signal_handler
usb_rom_handler { env.ep(), *this, + &Main::handle_usb_rom }; + Signal_handler
config_handler { env.ep(), *this, + &Main::handle_config }; - task_struct *state_task { lx_user_new_usb_task(state_task_entry, this) }; - task_struct *urb_task { lx_user_new_usb_task(urb_task_entry, this) }; - - Signal_handler task_state_handler { env.ep(), *this, &Device::handle_task_state }; - Signal_handler urb_handler { env.ep(), *this, &Device::handle_urb }; - - genode_usb_client_handle_t usb_handle { - genode_usb_client_create(genode_env_ptr(env), - genode_allocator_ptr(Lx_kit::env().heap), - genode_range_allocator_ptr(alloc), - "", - genode_signal_handler_ptr(task_state_handler)) }; - - Signal_handler nic_handler { env.ep(), *this, &Device::handle_nic }; - Signal_handler config_handler { env.ep(), *this, &Device::handle_config }; - - bool registered { false }; - - void *lx_device_handle { nullptr }; - - Device(Env &env) + Main(Env &env) : env(env) { - genode_usb_client_sigh_ack_avail(usb_handle, - genode_signal_handler_ptr(urb_handler)); + Lx_kit::initialize(env, signal_handler); + + Genode_c_api::initialize_usb_client(env, Lx_kit::env().heap, + signal_handler, usb_rom_handler); genode_mac_address_reporter_init(env, Lx_kit::env().heap); genode_uplink_init(genode_env_ptr(env), genode_allocator_ptr(Lx_kit::env().heap), - genode_signal_handler_ptr(nic_handler)); + genode_signal_handler_ptr(signal_handler)); config_rom.sigh(config_handler); handle_config(); + + lx_emul_start_kernel(nullptr); } - /* non-copyable */ - Device(const Device&) = delete; - Device & operator=(const Device&) = delete; - - void register_device() + void handle_signal() { - registered = true; - lx_device_handle = lx_emul_usb_client_register_device(usb_handle, "usb_nic"); - if (!lx_device_handle) { - registered = false; - return; - } - - if (usb_config != 0) - lx_emul_usb_client_set_configuration(usb_handle, lx_device_handle, usb_config); - - } - - void unregister_device() - { - force_uplink_destroy = true; - lx_emul_usb_client_unregister_device(usb_handle, lx_device_handle); - registered = false; - force_uplink_destroy = false; - } - - void handle_task_state() - { - lx_emul_task_unblock(state_task); - Lx_kit::env().scheduler.execute(); - } - - void handle_urb() - { - lx_emul_task_unblock(urb_task); + lx_user_handle_io(); Lx_kit::env().scheduler.execute(); genode_uplink_notify_peers(); } - void handle_nic() + void handle_usb_rom() { - if (!user_task_struct_ptr) - return; - - lx_emul_task_unblock(user_task_struct_ptr); + lx_emul_usb_client_rom_update(); Lx_kit::env().scheduler.execute(); } @@ -138,15 +87,11 @@ struct Device genode_mac_address_reporter_config(config_rom.xml()); /* read USB configuration setting */ - unsigned long config = config_rom.xml().attribute_value("configuration", 0ul); - if (registered && config != 0 && config != usb_config) - lx_emul_usb_client_set_configuration(usb_handle, lx_device_handle, config); - - usb_config = config; + usb_config = config_rom.xml().attribute_value("configuration", 0ul); /* retrieve possible MAC */ - Nic::Mac_address mac; try { + Nic::Mac_address mac; Xml_node::Attribute mac_node = config_rom.xml().attribute("mac"); mac_node.value(mac); mac.copy(mac_address); @@ -156,79 +101,7 @@ struct Device use_mac_address = false; } } - - - /********** - ** Task ** - **********/ - - static int state_task_entry(void *arg) - { - Device &device = *reinterpret_cast(arg); - - while (true) { - if (genode_usb_client_plugged(device.usb_handle) && !device.registered) - device.register_device(); - - if (!genode_usb_client_plugged(device.usb_handle) && device.registered) - device.unregister_device(); - lx_emul_task_schedule(true); - } - - return 0; - } - - static int urb_task_entry(void *arg) - { - Device &device = *reinterpret_cast(arg); - - while (true) { - if (device.registered) { - genode_usb_client_execute_completions(device.usb_handle); - } - - lx_emul_task_schedule(true); - } - - return 0; - } }; -struct Main -{ - Env &env; - - Signal_handler
signal_handler { env.ep(), *this, &Main::handle_signal }; - - Main(Env &env) : env(env) { } - - void handle_signal() - { - Lx_kit::env().scheduler.execute(); - } -}; - - -void Component::construct(Env & env) -{ - static Main main { env }; - Lx_kit::initialize(env, main.signal_handler); - - env.exec_static_constructors(); - - lx_emul_start_kernel(nullptr); -} - - -/********** - ** Task ** - **********/ - -int lx_user_main_task(void *) -{ - /* one device only */ - static Device dev(Lx_kit::env().env); - - return 0; -} +void Component::construct(Env & env) { static Main main { env }; } diff --git a/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/generated_dummies.c b/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/generated_dummies.c index bcb976b396..5a2eb7b11e 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/generated_dummies.c @@ -1,7 +1,7 @@ /* * \brief Dummy definitions of Linux Kernel functions * \author Automatically generated file - do no edit - * \date 2024-01-23 + * \date 2024-02-22 */ #include @@ -651,6 +651,14 @@ loff_t noop_llseek(struct file * file,loff_t offset,int whence) } +#include + +bool of_device_is_available(const struct device_node * device) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void of_device_uevent(struct device * dev,struct kobj_uevent_env * env) @@ -888,14 +896,6 @@ void rtmsg_ifinfo_send(struct sk_buff * skb,struct net_device * dev,gfp_t flags) } -#include - -void sg_init_table(struct scatterlist * sgl,unsigned int nents) -{ - lx_emul_trace_and_stop(__func__); -} - - #include void sk_error_report(struct sock * sk) @@ -954,14 +954,6 @@ void tick_broadcast(const struct cpumask * mask) } -#include - -int usb_clear_halt(struct usb_device * dev,int pipe) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -969,13 +961,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -984,29 +969,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -1044,8 +1006,9 @@ struct device_node * usb_of_get_device_node(struct usb_device * hub,int port1) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) +#include + +struct device_node * usb_of_get_interface_node(struct usb_device * udev,u8 config,u8 ifnum) { lx_emul_trace_and_stop(__func__); } @@ -1059,14 +1022,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/source.list b/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/source.list index fe09ea89de..1f955b23b7 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/source.list +++ b/repos/dde_linux/src/drivers/usb_net/spec/arm_v6/source.list @@ -28,7 +28,9 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c fs/nls/nls_base.c kernel/kthread.c @@ -71,6 +73,7 @@ lib/kstrtox.c lib/radix-tree.c lib/rbtree.c lib/rhashtable.c +lib/scatterlist.c lib/siphash.c lib/string.c lib/string_helpers.c diff --git a/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/generated_dummies.c b/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/generated_dummies.c index 84318e1e37..cece007585 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/generated_dummies.c @@ -1,7 +1,7 @@ /* * \brief Dummy definitions of Linux Kernel functions * \author Automatically generated file - do no edit - * \date 2024-02-01 + * \date 2024-02-22 */ #include @@ -474,6 +474,14 @@ loff_t noop_llseek(struct file * file,loff_t offset,int whence) } +#include + +bool of_device_is_available(const struct device_node * device) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void of_device_uevent(struct device * dev,struct kobj_uevent_env * env) @@ -682,14 +690,6 @@ void rtmsg_ifinfo_send(struct sk_buff * skb,struct net_device * dev,gfp_t flags) } -#include - -void sg_init_table(struct scatterlist * sgl,unsigned int nents) -{ - lx_emul_trace_and_stop(__func__); -} - - #include void sk_error_report(struct sock * sk) @@ -732,14 +732,6 @@ struct sk_buff * tcp_get_timestamping_opt_stats(const struct sock * sk,const str } -#include - -int usb_clear_halt(struct usb_device * dev,int pipe) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -747,13 +739,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -762,29 +747,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -822,8 +784,9 @@ struct device_node * usb_of_get_device_node(struct usb_device * hub,int port1) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) +#include + +struct device_node * usb_of_get_interface_node(struct usb_device * udev,u8 config,u8 ifnum) { lx_emul_trace_and_stop(__func__); } @@ -837,14 +800,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/source.list b/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/source.list index d70e23841f..6ac86e1bd4 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/source.list +++ b/repos/dde_linux/src/drivers/usb_net/spec/arm_v7/source.list @@ -27,7 +27,9 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c fs/nls/nls_base.c kernel/kthread.c @@ -69,6 +71,7 @@ lib/radix-tree.c lib/rhashtable.c lib/rbtree.c lib/xarray.c +lib/scatterlist.c lib/siphash.c lib/string.c lib/string_helpers.c diff --git a/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/generated_dummies.c b/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/generated_dummies.c index 13e003694a..50c43d7d7d 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/generated_dummies.c @@ -1,7 +1,7 @@ /* * \brief Dummy definitions of Linux Kernel functions * \author Automatically generated file - do no edit - * \date 2024-01-23 + * \date 2024-02-22 */ #include @@ -569,6 +569,14 @@ loff_t noop_llseek(struct file * file,loff_t offset,int whence) } +#include + +bool of_device_is_available(const struct device_node * device) +{ + lx_emul_trace_and_stop(__func__); +} + + #include void of_device_uevent(struct device * dev,struct kobj_uevent_env * env) @@ -782,14 +790,6 @@ void rtmsg_ifinfo_send(struct sk_buff * skb,struct net_device * dev,gfp_t flags) } -#include - -void sg_init_table(struct scatterlist * sgl,unsigned int nents) -{ - lx_emul_trace_and_stop(__func__); -} - - #include void sk_error_report(struct sock * sk) @@ -848,14 +848,6 @@ void tick_broadcast(const struct cpumask * mask) } -#include - -int usb_clear_halt(struct usb_device * dev,int pipe) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -863,13 +855,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -878,29 +863,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -938,8 +900,9 @@ struct device_node * usb_of_get_device_node(struct usb_device * hub,int port1) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) +#include + +struct device_node * usb_of_get_interface_node(struct usb_device * udev,u8 config,u8 ifnum) { lx_emul_trace_and_stop(__func__); } @@ -953,14 +916,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/source.list b/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/source.list index b59eedf3d7..767d461279 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/source.list +++ b/repos/dde_linux/src/drivers/usb_net/spec/arm_v8/source.list @@ -26,7 +26,9 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c fs/nls/nls_base.c kernel/kthread.c @@ -70,6 +72,7 @@ lib/radix-tree.c lib/rhashtable.c lib/rbtree.c lib/xarray.c +lib/scatterlist.c lib/siphash.c lib/string.c lib/string_helpers.c diff --git a/repos/dde_linux/src/drivers/usb_net/spec/x86_32/generated_dummies.c b/repos/dde_linux/src/drivers/usb_net/spec/x86_32/generated_dummies.c index 9747154e38..4d2ba44d1a 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/x86_32/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_net/spec/x86_32/generated_dummies.c @@ -707,14 +707,6 @@ void rtmsg_ifinfo_send(struct sk_buff * skb,struct net_device * dev,gfp_t flags) } -#include - -void sg_init_table(struct scatterlist * sgl,unsigned int nents) -{ - lx_emul_trace_and_stop(__func__); -} - - #include void sk_error_report(struct sock * sk) @@ -773,14 +765,6 @@ struct sk_buff * tcp_get_timestamping_opt_stats(const struct sock * sk,const str } -#include - -int usb_clear_halt(struct usb_device * dev,int pipe) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -788,13 +772,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -803,29 +780,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -855,13 +809,6 @@ void usb_major_cleanup(void) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const char * usb_speed_string(enum usb_device_speed speed) @@ -870,14 +817,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_net/spec/x86_32/source.list b/repos/dde_linux/src/drivers/usb_net/spec/x86_32/source.list index 017cb0ebc6..3a3867b077 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/x86_32/source.list +++ b/repos/dde_linux/src/drivers/usb_net/spec/x86_32/source.list @@ -21,7 +21,9 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c fs/nls/nls_base.c kernel/kthread.c @@ -63,6 +65,7 @@ lib/radix-tree.c lib/rhashtable.c lib/rbtree.c lib/xarray.c +lib/scatterlist.c lib/siphash.c lib/string.c lib/string_helpers.c diff --git a/repos/dde_linux/src/drivers/usb_net/spec/x86_64/generated_dummies.c b/repos/dde_linux/src/drivers/usb_net/spec/x86_64/generated_dummies.c index 69c42aa87a..9f018f4977 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/x86_64/generated_dummies.c +++ b/repos/dde_linux/src/drivers/usb_net/spec/x86_64/generated_dummies.c @@ -680,14 +680,6 @@ void rtmsg_ifinfo_send(struct sk_buff * skb,struct net_device * dev,gfp_t flags) } -#include - -void sg_init_table(struct scatterlist * sgl,unsigned int nents) -{ - lx_emul_trace_and_stop(__func__); -} - - #include void sk_error_report(struct sock * sk) @@ -746,14 +738,6 @@ struct sk_buff * tcp_get_timestamping_opt_stats(const struct sock * sk,const str } -#include - -int usb_clear_halt(struct usb_device * dev,int pipe) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void usb_devio_cleanup(void); void usb_devio_cleanup(void) { @@ -761,13 +745,6 @@ void usb_devio_cleanup(void) } -extern void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware); -void usb_disable_endpoint(struct usb_device * dev,unsigned int epaddr,bool reset_hardware) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint ** eps,unsigned int num_eps,gfp_t mem_flags) @@ -776,29 +753,6 @@ int usb_free_streams(struct usb_interface * interface,struct usb_host_endpoint * } -extern int usb_get_device_descriptor(struct usb_device * dev,unsigned int size); -int usb_get_device_descriptor(struct usb_device * dev,unsigned int size) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_get_status(struct usb_device * dev,int recip,int type,int target,void * data) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -int usb_hcd_alloc_bandwidth(struct usb_device * udev,struct usb_host_config * new_config,struct usb_host_interface * cur_alt,struct usb_host_interface * new_alt) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int usb_hcd_find_raw_port_number(struct usb_hcd * hcd,int port1) @@ -828,13 +782,6 @@ void usb_major_cleanup(void) } -extern int usb_set_isoch_delay(struct usb_device * dev); -int usb_set_isoch_delay(struct usb_device * dev) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const char * usb_speed_string(enum usb_device_speed speed) @@ -843,14 +790,6 @@ const char * usb_speed_string(enum usb_device_speed speed) } -#include - -void usb_unpoison_urb(struct urb * urb) -{ - lx_emul_trace_and_stop(__func__); -} - - #include const u8 uuid_index[16] = {}; diff --git a/repos/dde_linux/src/drivers/usb_net/spec/x86_64/source.list b/repos/dde_linux/src/drivers/usb_net/spec/x86_64/source.list index 017cb0ebc6..3a3867b077 100644 --- a/repos/dde_linux/src/drivers/usb_net/spec/x86_64/source.list +++ b/repos/dde_linux/src/drivers/usb_net/spec/x86_64/source.list @@ -21,7 +21,9 @@ drivers/usb/core/config.c drivers/usb/core/driver.c drivers/usb/core/generic.c drivers/usb/core/hub.c +drivers/usb/core/message.c drivers/usb/core/quirks.c +drivers/usb/core/urb.c drivers/usb/core/usb.c fs/nls/nls_base.c kernel/kthread.c @@ -63,6 +65,7 @@ lib/radix-tree.c lib/rhashtable.c lib/rbtree.c lib/xarray.c +lib/scatterlist.c lib/siphash.c lib/string.c lib/string_helpers.c diff --git a/repos/dde_linux/src/drivers/usb_net/target.inc b/repos/dde_linux/src/drivers/usb_net/target.inc index f90b95df23..6058c4e822 100644 --- a/repos/dde_linux/src/drivers/usb_net/target.inc +++ b/repos/dde_linux/src/drivers/usb_net/target.inc @@ -8,9 +8,9 @@ SRC_C += dummies.c \ lx_emul.c \ lx_user.c -SRC_C += lx_emul/virt/shadow/drivers/usb/core/message.c \ - lx_emul/virt/shadow/drivers/usb/core/urb.c \ - lx_emul/virt/usb_client.c +SRC_C += lx_emul/virt/shadow/drivers/usb/core/buffer.c +SRC_C += lx_emul/virt/shadow/drivers/usb/core/hcd.c +SRC_C += lx_emul/virt/usb_client.c SRC_CC = main.cc \ wdm_terminal.cc diff --git a/repos/dde_linux/src/include/lx_emul/input_leds.h b/repos/dde_linux/src/include/lx_emul/input_leds.h index 864d5ac11a..7e8549c61f 100644 --- a/repos/dde_linux/src/include/lx_emul/input_leds.h +++ b/repos/dde_linux/src/include/lx_emul/input_leds.h @@ -18,6 +18,7 @@ extern "C" { #endif +extern void lx_emul_input_leds_init(void); extern void lx_emul_input_leds_update(bool capslock, bool numlock, bool scrolllock); #ifdef __cplusplus diff --git a/repos/dde_linux/src/include/lx_emul/usb.h b/repos/dde_linux/src/include/lx_emul/usb.h index 38d9245ed6..445e78a826 100644 --- a/repos/dde_linux/src/include/lx_emul/usb.h +++ b/repos/dde_linux/src/include/lx_emul/usb.h @@ -14,11 +14,14 @@ #ifndef _LX_EMUL__USB_H_ #define _LX_EMUL__USB_H_ +#include + #ifdef __cplusplus extern "C" { #endif -extern struct genode_usb_rpc_callbacks lx_emul_usb_rpc_callbacks; +extern void lx_emul_usb_release_device(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev); #ifdef __cplusplus } diff --git a/repos/dde_linux/src/include/lx_emul/usb_client.h b/repos/dde_linux/src/include/lx_emul/usb_client.h index 66c60eb5c6..04966625ab 100644 --- a/repos/dde_linux/src/include/lx_emul/usb_client.h +++ b/repos/dde_linux/src/include/lx_emul/usb_client.h @@ -4,9 +4,10 @@ extern "C" { #endif -void *lx_emul_usb_client_register_device(genode_usb_client_handle_t handle, char const *label); -void lx_emul_usb_client_unregister_device(genode_usb_client_handle_t handle, void *data); -int lx_emul_usb_client_set_configuration(genode_usb_client_handle_t, void *data, unsigned long config); +int lx_emul_usb_client_set_configuration(genode_usb_client_dev_handle_t, void *data, unsigned long config); +void lx_emul_usb_client_init(void); +void lx_emul_usb_client_rom_update(void); +void lx_emul_usb_client_ticker(void); #ifdef __cplusplus } /* extern "C" */ diff --git a/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/input-leds.c b/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/input-leds.c index be2a00f820..a42a9d28ab 100644 --- a/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/input-leds.c +++ b/repos/dde_linux/src/lib/lx_emul/shadow/drivers/input/input-leds.c @@ -52,25 +52,49 @@ static void update_leds(struct led_handler *handler) } +static int led_task_loop(void *arg) +{ + for (;;) { + + struct led_handler *handler; + + list_for_each_entry(handler, &led_handlers, list) { + update_leds(handler); + } + + if (led_update.state == BLOCKED) + complete(&led_update.update); + + led_update.state = NONE; + + lx_emul_task_schedule(true); + } + + return 0; +} + + +static struct task_struct *led_task = NULL; + + +void lx_emul_input_leds_init(void) +{ + int pid = kernel_thread(led_task_loop, &led_task, CLONE_FS | CLONE_FILES); + led_task = find_task_by_pid_ns(pid, NULL); +} + + void lx_emul_input_leds_update(bool capslock, bool numlock, bool scrolllock) { - - struct led_handler *handler; - - led_update.state = UPDATE; - + led_update.state = UPDATE; led_update.capsl = capslock; led_update.numl = numlock; led_update.scrolll = scrolllock; - list_for_each_entry(handler, &led_handlers, list) { - update_leds(handler); - } + if (!led_task) + return; - if (led_update.state == BLOCKED) - complete(&led_update.update); - - led_update.state = NONE; + lx_emul_task_unblock(led_task); } diff --git a/repos/dde_linux/src/lib/lx_emul/usb.c b/repos/dde_linux/src/lib/lx_emul/usb.c index 45421dafa2..0c913cbc3b 100644 --- a/repos/dde_linux/src/lib/lx_emul/usb.c +++ b/repos/dde_linux/src/lib/lx_emul/usb.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -32,13 +33,6 @@ struct usb_interface; -struct usb_find_request { - genode_usb_bus_num_t bus; - genode_usb_dev_num_t dev; - struct usb_device * ret; -}; - - static int usb_drv_probe(struct usb_interface *interface, const struct usb_device_id *id) { return -ENODEV; } @@ -55,219 +49,54 @@ static struct usb_driver usb_drv = { }; -static int check_usb_device(struct usb_device *usb_dev, void * data) +static genode_usb_request_ret_t handle_return_code(int err) { - struct usb_find_request * req = (struct usb_find_request *) data; - if (usb_dev->devnum == req->dev && usb_dev->bus->busnum == req->bus) - req->ret = usb_dev; - return 0; -} - -static struct usb_device * find_usb_device(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev) -{ - struct usb_find_request req = { bus, dev, NULL }; - usb_for_each_dev(&req, check_usb_device); - return req.ret; -} - - -static struct usb_interface * interface(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev, - unsigned index) -{ - struct usb_device * udev = find_usb_device(bus, dev); - - if (!udev) - return NULL; - - if (!udev->actconfig) - return NULL; - - if (index >= udev->actconfig->desc.bNumInterfaces) - return NULL; - - return udev->actconfig->interface[index]; -} - - -static unsigned config_descriptor(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev, - void * dev_desc, void *conf_desc) -{ - struct usb_device * udev = find_usb_device(bus, dev); - if (!udev) - return 0; - - memcpy(dev_desc, &udev->descriptor, sizeof(struct usb_device_descriptor)); - if (udev->actconfig) - memcpy(conf_desc, &udev->actconfig->desc, - sizeof(struct usb_config_descriptor)); - else - memset(conf_desc, 0, sizeof(struct usb_config_descriptor)); - - return udev->speed; -} - - -static int alt_settings(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev, - unsigned index) -{ - struct usb_interface * iface = interface(bus, dev, index); - return (iface) ? iface->num_altsetting : -1; -} - - -static int interface_descriptor(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev, - unsigned index, unsigned setting, - void * buf, unsigned long size, int * active) -{ - struct usb_interface * iface = interface(bus, dev, index); - - if (!iface || setting >= iface->num_altsetting) - return -1; - - memcpy(buf, &iface->altsetting[setting].desc, - min(sizeof(struct usb_interface_descriptor), size)); - - *active = &iface->altsetting[setting] == iface->cur_altsetting; - return 0; -} - - -static int interface_extra(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev, - unsigned index, unsigned setting, - void * buf, unsigned long size) -{ - struct usb_interface * iface = interface(bus, dev, index); - unsigned long len; - - if (!iface || setting >= iface->num_altsetting) - return -1; - - len = min((unsigned long)iface->altsetting[setting].extralen, size); - memcpy(buf, iface->altsetting[setting].extra, len); - return len; -} - - -static int endpoint_descriptor(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev, - unsigned iface_num, unsigned setting, - unsigned endp, void * buf, unsigned long size) -{ - struct usb_device * udev = find_usb_device(bus, dev); - struct usb_interface * iface; - struct usb_host_endpoint * ep; - - if (!udev) - return -1; - iface = usb_ifnum_to_if(udev, iface_num); - - if (!iface) - return -2; - - if (setting >= iface->num_altsetting || - endp >= iface->altsetting[setting].desc.bNumEndpoints) - return -3; - - ep = &iface->altsetting[setting].endpoint[endp]; - if (!ep) - return -4; - - memcpy(buf, &ep->desc, - min(sizeof(struct usb_endpoint_descriptor), size)); - - return 0; -} - - -static inline void * -usercontext_from_handle(genode_usb_session_handle_t session_handle, - genode_usb_request_handle_t request_handle) -{ - return (void*)(((unsigned long)session_handle << 16) | request_handle); -} - - -static inline void -handle_from_usercontext(void * usercontext, - genode_usb_session_handle_t * session_handle, - genode_usb_request_handle_t * request_handle) -{ - *session_handle = (genode_usb_session_handle_t)((unsigned long)usercontext >> 16); - *request_handle = (genode_usb_request_handle_t)((unsigned long)usercontext & 0xffff); -} - - -static genode_usb_request_ret_t -handle_return_code(struct genode_usb_request_urb req, - struct genode_usb_buffer payload, - void *data) -{ - switch (*(int*)data) { - case 0: return NO_ERROR; - case -ENOENT: return INTERFACE_OR_ENDPOINT_ERROR; - case -ENOMEM: return MEMORY_ERROR; - case -ENODEV: return NO_DEVICE_ERROR; - case -ESHUTDOWN: return NO_DEVICE_ERROR; - case -ENOSPC: return STALL_ERROR; - case -EPROTO: return PROTOCOL_ERROR; - case -EILSEQ: return PROTOCOL_ERROR; - case -EPIPE: return STALL_ERROR; - case -ETIMEDOUT: return TIMEOUT_ERROR; - case -EINVAL: return PACKET_INVALID_ERROR; - default: return UNKNOWN_ERROR; + /* + * USB error codes are documented in kernel sources + * + * Documentation/driver-api/usb/error-codes.rst + */ + switch (err) { + case 0: return OK; + case -ENOENT: return NO_DEVICE; + case -ENODEV: return NO_DEVICE; + case -ESHUTDOWN: return NO_DEVICE; + case -EILSEQ: return NO_DEVICE; /* xHCI ret val when HID vanishs */ + case -EPROTO: return NO_DEVICE; + case -ETIMEDOUT: return TIMEOUT; + case -ENOSPC: return HALT; + case -EPIPE: return HALT; + case -ENOMEM: return INVALID; + case -EINVAL: return INVALID; + default: return INVALID; }; }; -typedef enum usb_rpc_call_type { - CLAIM, - RELEASE_IF, - RELEASE_ALL -} usb_rpc_call_type_t; - - -struct usb_rpc_call_args { - unsigned iface_num; - usb_rpc_call_type_t call; - int ret; -}; - - struct usb_per_dev_data { - struct inode inode; - struct file file; - struct usb_rpc_call_args *rpc; - struct usb_device *dev; - struct task_struct *task; - struct usb_anchor submitted; + struct usb_device * dev; + struct task_struct * task; + struct usb_anchor submitted; + bool kill_task; }; static int poll_usb_device(void * args); -static struct file * open_usb_dev(struct usb_device * udev) +static void open_usb_dev(struct usb_device * udev) { struct usb_per_dev_data * data = dev_get_drvdata(&udev->dev); if (!data) { int pid; data = kmalloc(sizeof(struct usb_per_dev_data), GFP_KERNEL); - data->inode.i_rdev = udev->dev.devt; - data->file.f_inode = &data->inode; - data->file.f_mode = FMODE_WRITE; data->dev = udev; + data->kill_task = false; pid = kernel_thread(poll_usb_device, data, CLONE_FS | CLONE_FILES); data->task = find_task_by_pid_ns(pid, NULL); init_usb_anchor(&data->submitted); dev_set_drvdata(&udev->dev, data); } - - return &data->file; } @@ -282,456 +111,262 @@ static void release_device(struct usb_per_dev_data * data) /** * If the device gets released although it is still available, - * reset the device to be sane for new sessions aquiring it + * reset the device to be sane for new sessions aquiring it. + * Moreover, release each interface as part of reset, therefore + * they have to be claimed before. */ for (ifnum = 0; ifnum < 8; ifnum++) { struct usb_interface * iface = usb_ifnum_to_if(data->dev, ifnum); - if (iface) usb_driver_release_interface(&usb_drv, iface); + if (iface) { + usb_driver_claim_interface(&usb_drv, iface, NULL); + usb_driver_release_interface(&usb_drv, iface); + } } usb_reset_device(data->dev); } -static int claim_iface(struct usb_device *udev, unsigned int ifnum) +static void +handle_control_request(genode_usb_request_handle_t handle, + unsigned char ctrl_request, + unsigned char ctrl_request_type, + unsigned short ctrl_value, + unsigned short ctrl_index, + unsigned long ctrl_timeout, + genode_buffer_t payload, + void *opaque_callback_data) { - struct usb_interface * iface; - int ret = -ENODEV; + struct usb_device *udev = (struct usb_device *) opaque_callback_data; + int ret = 0; + u32 size; + bool send_msg = true; - iface = usb_ifnum_to_if(udev, ifnum); - if (iface) ret = usb_driver_claim_interface(&usb_drv, iface, NULL); - return ret; + /* check for set alternate interface request */ + if (ctrl_request == USB_REQ_SET_INTERFACE && + (ctrl_request_type & 0x7f) == (USB_TYPE_STANDARD|USB_RECIP_INTERFACE)) { + struct usb_interface *iface = usb_ifnum_to_if(udev, ctrl_index); + struct usb_host_interface *alt = + iface ? usb_altnum_to_altsetting(iface, ctrl_value) : NULL; + + if (iface && iface->cur_altsetting != alt) + ret = usb_set_interface(udev, ctrl_index, ctrl_value); + + send_msg = false; + } + + /* check for set device configuration request */ + if (ctrl_request == USB_REQ_SET_CONFIGURATION && + ctrl_request_type == USB_RECIP_DEVICE) { + if (!(udev->actconfig && + udev->actconfig->desc.bConfigurationValue == ctrl_value)) + ret = usb_set_configuration(udev, ctrl_value); + send_msg = false; + } + + /* otherwise send control message */ + if (send_msg) { + int pipe = (ctrl_request_type & 0x80) + ? usb_rcvctrlpipe(udev, 0) : usb_sndctrlpipe(udev, 0); + + usb_unlock_device(udev); + ret = usb_control_msg(udev, pipe, ctrl_request, ctrl_request_type, + ctrl_value, ctrl_index, payload.addr, + payload.size, ctrl_timeout); + usb_lock_device(udev); + } + + size = ret < 0 ? 0 : ret; + genode_usb_ack_request(handle, handle_return_code(ret < 0 ? ret : 0), + &size); } -static int release_iface(struct usb_device *udev, unsigned int ifnum) +static void anchor_and_submit_urb(genode_usb_request_handle_t handle, + struct urb *urb, + struct usb_anchor *anchor) { - struct usb_interface * iface = usb_ifnum_to_if(udev, ifnum); - if (iface) usb_driver_release_interface(&usb_drv, iface); - return 0; -} + int ret; + usb_anchor_urb(urb, anchor); + ret = usb_submit_urb(urb, GFP_KERNEL); - -static int usb_rpc_finished(void *d) -{ - struct usb_rpc_call_args * rpc = (struct usb_rpc_call_args*)d; - return (rpc->ret <= 0); -} - - -static int claim(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev, - unsigned iface_num) -{ - struct usb_device * udev = find_usb_device(bus, dev); - struct usb_per_dev_data * data = udev ? dev_get_drvdata(&udev->dev) : NULL; - - struct usb_rpc_call_args rpc = { - .ret = 1, - .call = CLAIM, - .iface_num = iface_num - }; - - /* - * As long as 'claim' is a rpc-call, and the usb device wasn't opened yet, - * we cannot open the device here, this has to be done from a Linux task. - * So just ignore it here. - */ - if (!data) - return 0; - - data->rpc = &rpc; - lx_emul_task_unblock(data->task); - lx_emul_execute_kernel_until(&usb_rpc_finished, &rpc); - return rpc.ret; -} - - -static int release(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev, - unsigned iface_num) -{ - struct usb_device * udev = find_usb_device(bus, dev); - struct usb_per_dev_data * data = udev ? dev_get_drvdata(&udev->dev) : NULL; - - struct usb_rpc_call_args rpc = { - .ret = 1, - .call = RELEASE_IF, - .iface_num = iface_num - }; - - if (!data) - return -1; - - data->rpc = &rpc; - lx_emul_task_unblock(data->task); - lx_emul_execute_kernel_until(&usb_rpc_finished, &rpc); - return rpc.ret; -} - - -static void release_all(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev) -{ - struct usb_device * udev = find_usb_device(bus, dev); - struct usb_per_dev_data * data = udev ? dev_get_drvdata(&udev->dev) : NULL; - - struct usb_rpc_call_args rpc = { - .ret = 1, - .call = RELEASE_ALL, - }; - - if (!data) + if (!ret) return; - data->rpc = &rpc; - lx_emul_task_unblock(data->task); - lx_emul_execute_kernel_until(&usb_rpc_finished, &rpc); -} - - -struct genode_usb_rpc_callbacks lx_emul_usb_rpc_callbacks = { - .alloc_fn = lx_emul_shared_dma_buffer_allocate, - .free_fn = lx_emul_shared_dma_buffer_free, - .cfg_desc_fn = config_descriptor, - .alt_settings_fn = alt_settings, - .iface_desc_fn = interface_descriptor, - .iface_extra_fn = interface_extra, - .endp_desc_fn = endpoint_descriptor, - .claim_fn = claim, - .release_fn = release, - .release_all_fn = release_all, -}; - - -static genode_usb_request_ret_t -handle_transfer_response(struct genode_usb_request_urb req, - struct genode_usb_buffer payload, - void * data) -{ - struct urb * urb = (struct urb *) data; - struct genode_usb_request_transfer * transfer = - genode_usb_get_request_transfer(&req); - - if (urb->status < 0) - return handle_return_code(req, payload, &urb->status); - - if (transfer) transfer->actual_size = urb->actual_length; - - if (req.type == ISOC) { - int i; - struct genode_usb_isoc_transfer *isoc = - (struct genode_usb_isoc_transfer *)payload.addr; - if (!isoc) - return PACKET_INVALID_ERROR; - - for (i = 0; i < urb->number_of_packets; i++) { - isoc->actual_packet_size[i] = - urb->iso_frame_desc[i].actual_length; - } - } - - return NO_ERROR; -} - - -static void -handle_string_request(struct genode_usb_request_string * req, - genode_usb_session_handle_t session, - genode_usb_request_handle_t request, - struct genode_usb_buffer payload, - void * data) -{ - struct usb_device * udev = (struct usb_device *) data; - int i, ret = -ENODEV; - - if (udev) { - for (i = 0; i < 3; ++i) { - /* retry on length 0 or error; some devices are flakey */ - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (USB_DT_STRING << 8) + req->index, udev->string_langid, - payload.addr, payload.size, - USB_CTRL_GET_TIMEOUT); - if (ret <= 0 && ret != -ETIMEDOUT) - continue; - break; - } - } - - if (ret > 0) ret = 0; - genode_usb_ack_request(session, request, handle_return_code, &ret); -} - - -static void -handle_altsetting_request(unsigned iface, - unsigned alt_setting, - genode_usb_session_handle_t session, - genode_usb_request_handle_t request, - void * data) -{ - int ret = -ENODEV; - struct usb_device * udev = (struct usb_device *) data; - - if (udev) - ret = usb_set_interface(udev, iface, alt_setting); - - if (ret) - printk("Alt setting request (iface=%u alt_setting=%u) failed\n", - iface, alt_setting); - - genode_usb_ack_request(session, request, handle_return_code, &ret); -} - - -static void -handle_config_request(unsigned cfg_idx, - genode_usb_session_handle_t session, - genode_usb_request_handle_t request, - void * data) -{ - struct usb_device * udev = (struct usb_device *) data; - int ret = udev ? 0 : -ENODEV; - - /* - * Skip SET_CONFIGURATION requests if the device already has the - * selected config as active config. This workaround prevents issues - * with Linux guests in vbox and SDC-reader passthrough. - */ - if (udev && !(udev->actconfig && - udev->actconfig->desc.bConfigurationValue == cfg_idx)) { - ret = usb_set_configuration(udev, cfg_idx); - } - - genode_usb_ack_request(session, request, handle_return_code, &ret); -} - - -static void -handle_flush_request(unsigned char ep, - genode_usb_session_handle_t session, - genode_usb_request_handle_t request, - void * data) -{ - struct usb_device * udev = (struct usb_device *) data; - int ret = udev ? 0 : -ENODEV; - struct usb_host_endpoint * endpoint; - - if (udev) { - endpoint = ep & USB_DIR_IN ? udev->ep_in[ep & 0xf] - : udev->ep_out[ep & 0xf]; - if (endpoint) - usb_hcd_flush_endpoint(udev, endpoint); - } - - genode_usb_ack_request(session, request, handle_return_code, &ret); -} - - -static genode_usb_request_ret_t -handle_ctrl_response(struct genode_usb_request_urb req, - struct genode_usb_buffer payload, - void * data) -{ - int status = *(int*)data; - struct genode_usb_request_control * ctrl = - genode_usb_get_request_control(&req); - - if (status < 0) - return handle_return_code(req, payload, &status); - - ctrl->actual_size = status; - return NO_ERROR; + usb_unanchor_urb(urb); + usb_free_urb(urb); + genode_usb_ack_request(handle, handle_return_code(ret), NULL); } static void async_complete(struct urb *urb) { - genode_usb_session_handle_t session_handle; - genode_usb_request_handle_t request_handle; - handle_from_usercontext(urb->context, &session_handle, &request_handle); - genode_usb_ack_request(session_handle, request_handle, - handle_transfer_response, (void*)urb); - usb_free_urb(urb); - lx_user_handle_io(); -} + /* Linux kernel's devio layer limits ISOC packets to 128 */ + u32 sizes[128 + 1]; - -static int fill_bulk_urb(struct usb_device * udev, - struct genode_usb_request_transfer * req, - void * handle, - struct genode_usb_buffer buf, - int read, - struct urb ** urb) -{ - int pipe = (read) - ? usb_rcvbulkpipe(udev, req->ep) : usb_sndbulkpipe(udev, req->ep); - - if (!buf.addr) - return -EINVAL; - - *urb = usb_alloc_urb(0, GFP_KERNEL); - if (!*urb) - return -ENOMEM; - - usb_fill_bulk_urb(*urb, udev, pipe, buf.addr, buf.size, async_complete, handle); - return 0; -} - - -static int fill_irq_urb(struct usb_device * udev, - struct genode_usb_request_transfer * req, - void * handle, - struct genode_usb_buffer buf, - int read, - struct urb ** urb) -{ - int polling_interval; - int pipe = (read) - ? usb_rcvintpipe(udev, req->ep) : usb_sndintpipe(udev, req->ep); - - if (buf.size && !buf.addr) - return -EINVAL; - - *urb = usb_alloc_urb(0, GFP_KERNEL); - if (!*urb) - return -ENOMEM; - - if (req->polling_interval == -1) { - - struct usb_host_endpoint *ep = (req->ep & USB_DIR_IN) ? - udev->ep_in[req->ep & 0xf] : udev->ep_out[req->ep & 0xf]; - - if (!ep) - return -ENOENT; - - polling_interval = ep->desc.bInterval; - } else - polling_interval = req->polling_interval; - - usb_fill_int_urb(*urb, udev, pipe, buf.addr, buf.size, - async_complete, handle, polling_interval); - return 0; -} - - -static int fill_isoc_urb(struct usb_device * udev, - struct genode_usb_request_transfer * req, - void * handle, - struct genode_usb_buffer buf, - int read, - struct urb ** urb) -{ - int i; - unsigned offset = 0; - int pipe = (read) - ? usb_rcvisocpipe(udev, req->ep) : usb_sndisocpipe(udev, req->ep); - struct usb_host_endpoint * ep = - req->ep & USB_DIR_IN ? udev->ep_in[req->ep & 0xf] - : udev->ep_out[req->ep & 0xf]; - - struct genode_usb_isoc_transfer *isoc = (struct genode_usb_isoc_transfer *)buf.addr; - - if (!buf.addr || isoc->number_of_packets > MAX_PACKETS) - return -EINVAL; - if (!ep) - return -ENOENT; - - *urb = usb_alloc_urb(isoc->number_of_packets, GFP_KERNEL); - if (!*urb) - return -ENOMEM; - - (*urb)->dev = udev; - (*urb)->pipe = pipe; - (*urb)->start_frame = -1; - (*urb)->stream_id = 0; - (*urb)->transfer_buffer = isoc->data; - (*urb)->transfer_buffer_length = buf.size - sizeof(*isoc); - (*urb)->number_of_packets = isoc->number_of_packets; - (*urb)->interval = 1 << min(15, ep->desc.bInterval - 1); - (*urb)->context = handle; - (*urb)->transfer_flags = URB_ISO_ASAP | (read ? URB_DIR_IN : URB_DIR_OUT); - (*urb)->complete = async_complete; - - for (i = 0; i < isoc->number_of_packets; i++) { - (*urb)->iso_frame_desc[i].offset = offset; - (*urb)->iso_frame_desc[i].length = isoc->packet_size[i]; - offset += isoc->packet_size[i]; + genode_usb_request_handle_t handle = + (genode_usb_request_handle_t) urb->context; + sizes[0] = urb->actual_length; + if (urb->status >= 0 && urb->number_of_packets > 0) { + unsigned int i; + for (i = 0; i < urb->number_of_packets; i++) + sizes[i+1] = urb->iso_frame_desc[i].actual_length; } - return 0; + genode_usb_ack_request(handle, handle_return_code(urb->status), sizes); + + /* unblock device's task, it may process further URBs now */ + if (urb->dev) { + struct usb_per_dev_data *data = dev_get_drvdata(&urb->dev->dev); + lx_emul_task_unblock(data->task); + } + + usb_free_urb(urb); +} + + + + +static void +handle_irq_request(genode_usb_request_handle_t handle, + unsigned char ep_addr, + genode_buffer_t payload, + void *opaque_callback_data) +{ + struct usb_device *udev = (struct usb_device *) opaque_callback_data; + struct usb_per_dev_data *data = dev_get_drvdata(&udev->dev); + int pipe = (ep_addr & USB_DIR_IN) ? usb_rcvintpipe(udev, ep_addr & 0x7f) + : usb_sndintpipe(udev, ep_addr & 0x7f); + struct usb_host_endpoint *ep = usb_pipe_endpoint(udev, pipe); + struct urb *urb; + + if ((payload.size && !payload.addr) || + !ep || !usb_endpoint_maxp(&ep->desc)) { + int ret = ep ? -EINVAL : -ENODEV; + genode_usb_ack_request(handle, handle_return_code(ret), NULL); + return; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + genode_usb_ack_request(handle, handle_return_code(-ENOMEM), NULL); + return; + } + + usb_fill_int_urb(urb, udev, pipe, payload.addr, payload.size, + async_complete, handle, ep->desc.bInterval); + anchor_and_submit_urb(handle, urb, &data->submitted); } static void -handle_urb_request(struct genode_usb_request_urb req, - genode_usb_session_handle_t session_handle, - genode_usb_request_handle_t request_handle, - struct genode_usb_buffer payload, void * data) +handle_bulk_request(genode_usb_request_handle_t handle, + unsigned char ep_addr, + genode_buffer_t payload, + void *opaque_callback_data) +{ + struct usb_device * udev = (struct usb_device *) opaque_callback_data; + struct usb_per_dev_data * data = dev_get_drvdata(&udev->dev); + struct urb *urb; + + int pipe = (ep_addr & USB_DIR_IN) ? usb_rcvbulkpipe(udev, ep_addr & 0x7f) + : usb_sndbulkpipe(udev, ep_addr & 0x7f); + + if (!payload.addr || payload.size >= (INT_MAX - sizeof(struct urb))) { + genode_usb_ack_request(handle, handle_return_code(-EINVAL), NULL); + return; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + genode_usb_ack_request(handle, handle_return_code(-ENOMEM), NULL); + return; + } + + usb_fill_bulk_urb(urb, udev, pipe, payload.addr, payload.size, + async_complete, handle); + anchor_and_submit_urb(handle, urb, &data->submitted); +} + + +static void +handle_isoc_request(genode_usb_request_handle_t handle, + unsigned char ep_addr, + u32 number_of_packets, + struct genode_usb_isoc_descriptor *packets, + genode_buffer_t payload, + void *opaque_callback_data) +{ + struct usb_device *udev = (struct usb_device *) opaque_callback_data; + struct usb_per_dev_data *data = dev_get_drvdata(&udev->dev); + int pipe = (ep_addr & USB_DIR_IN) ? usb_rcvisocpipe(udev, ep_addr & 0x7f) + : usb_sndisocpipe(udev, ep_addr & 0x7f); + struct usb_host_endpoint *ep = usb_pipe_endpoint(udev, pipe); + struct urb *urb; + unsigned int i; + unsigned offset = 0; + + if (!payload.addr || + number_of_packets > 128 || + number_of_packets < 1 || + !ep) { + int ret = ep ? -EINVAL : -EINVAL; + genode_usb_ack_request(handle, handle_return_code(ret), NULL); + return; + } + + urb = usb_alloc_urb(number_of_packets, GFP_KERNEL); + if (!urb) { + genode_usb_ack_request(handle, handle_return_code(-ENOMEM), NULL); + return; + } + + urb->dev = udev; + urb->pipe = pipe; + urb->start_frame = -1; + urb->stream_id = 0; + urb->transfer_buffer = payload.addr; + urb->transfer_buffer_length = payload.size; + urb->number_of_packets = number_of_packets; + urb->interval = 1 << min(15, ep->desc.bInterval - 1); + urb->context = handle; + urb->transfer_flags = URB_ISO_ASAP | (ep_addr & USB_ENDPOINT_DIR_MASK); + urb->complete = async_complete; + + for (i = 0; i < number_of_packets; i++) { + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = packets[i].size; + offset += packets[i].size; + } + anchor_and_submit_urb(handle, urb, &data->submitted); +} + + +static void +handle_flush_request(unsigned char ep_addr, + genode_usb_request_handle_t handle, + void *data) { struct usb_device * udev = (struct usb_device *) data; - struct usb_per_dev_data * urbs = dev_get_drvdata(&udev->dev); - struct genode_usb_request_control * ctrl = - genode_usb_get_request_control(&req); - struct genode_usb_request_transfer * transfer = - genode_usb_get_request_transfer(&req); - int ret = 0; - int read = transfer ? (transfer->ep & 0x80) : 0; - struct urb * urb = NULL; - void *handle = usercontext_from_handle(session_handle, request_handle); + u32 size = 0; + int ret = udev ? 0 : -ENODEV; - switch (req.type) { - case CTRL: - { - int pipe = (ctrl->request_type & 0x80) - ? usb_rcvctrlpipe(udev, 0) : usb_sndctrlpipe(udev, 0); - usb_unlock_device(udev); - ret = usb_control_msg(udev, pipe, ctrl->request, ctrl->request_type, - ctrl->value, ctrl->index, payload.addr, - payload.size, ctrl->timeout); - usb_lock_device(udev); - genode_usb_ack_request(session_handle, request_handle, - handle_ctrl_response, &ret); - return; - } - case BULK: - ret = fill_bulk_urb(udev, transfer, handle, payload, read, &urb); - break; - case IRQ: - ret = fill_irq_urb(udev, transfer, handle, payload, read, &urb); - break; - case ISOC: - ret = fill_isoc_urb(udev, transfer, handle, payload, read, &urb); - break; - default: - printk("Unknown USB transfer request!\n"); - ret = -EINVAL; - }; - - if (!ret) { - usb_anchor_urb(urb, &urbs->submitted); - ret = usb_submit_urb(urb, GFP_KERNEL); + if (udev) { + struct usb_host_endpoint * endpoint = + ep_addr & USB_DIR_IN ? udev->ep_in[ep_addr & 0xf] + : udev->ep_out[ep_addr & 0xf]; + if (endpoint) usb_hcd_flush_endpoint(udev, endpoint); } - if (!ret) - return; - - if (urb) { - usb_unanchor_urb(urb); - usb_free_urb(urb); - } - - genode_usb_ack_request(session_handle, request_handle, - handle_return_code, &ret); + genode_usb_ack_request(handle, handle_return_code(ret), &size); } static struct genode_usb_request_callbacks request_callbacks = { - .urb_fn = handle_urb_request, - .string_fn = handle_string_request, - .altsetting_fn = handle_altsetting_request, - .config_fn = handle_config_request, - .flush_fn = handle_flush_request, + .ctrl_fn = handle_control_request, + .irq_fn = handle_irq_request, + .bulk_fn = handle_bulk_request, + .isoc_fn = handle_isoc_request, + .flush_fn = handle_flush_request, }; @@ -744,46 +379,10 @@ static inline void exit_usb_task(struct usb_per_dev_data * data) } -static inline void handle_rpc(struct usb_per_dev_data * data) +static inline bool check_for_urbs(struct usb_device * udev) { - struct usb_rpc_call_args * rpc = data->rpc; - if (!rpc) - return; - - data->rpc = NULL; - switch(rpc->call) { - case CLAIM: - if (data->dev) claim_iface(data->dev, rpc->iface_num); - rpc->ret = 0; - return; - case RELEASE_IF: - if (data->dev) release_iface(data->dev, rpc->iface_num); - rpc->ret = 0; - return; - case RELEASE_ALL: - usb_unlock_device(data->dev); - exit_usb_task(data); - rpc->ret = 0; - do_exit(0); - }; -} - - -static inline int check_for_urb(struct usb_device * udev) -{ - genode_usb_session_handle_t session; - - if (!udev) - return 0; - - session = genode_usb_session_by_bus_dev(udev->bus->busnum, - udev->devnum); - - if (!session) - return 0; - - return genode_usb_request_by_session(session, &request_callbacks, - (void*)udev); + return genode_usb_request_by_bus_dev(udev->bus->busnum, udev->devnum, + &request_callbacks, (void*)udev); } @@ -795,20 +394,15 @@ static int poll_usb_device(void * args) for (;;) { if (data->dev) usb_lock_device(data->dev); - while (check_for_urb(data->dev)) ; - - /* - * we have to check for RPC handling here, during - * asynchronous URB handling above the Linux task might - * have been blocked and a new RPC entered the entrypoint - */ - handle_rpc(data); + while (data->dev && check_for_urbs(data->dev)) ; if (data->dev) usb_unlock_device(data->dev); /* check if device got removed */ - if (!find_usb_device(bus, dev)) { - exit_usb_task(data); + if (!data->dev) genode_usb_discontinue_device(bus, dev); + + if (data->kill_task) { + exit_usb_task(data); do_exit(0); } lx_emul_task_schedule(true); @@ -822,7 +416,9 @@ static int wake_up_udev_task(struct usb_device *udev, void * args) { struct usb_per_dev_data * data = dev_get_drvdata(&udev->dev); - if (!genode_usb_session_by_bus_dev(udev->bus->busnum, udev->devnum)) + bool acquired = genode_usb_device_acquired(udev->bus->busnum, + udev->devnum); + if (!acquired && !data) return 0; if (!data) { @@ -830,6 +426,9 @@ static int wake_up_udev_task(struct usb_device *udev, void * args) data = dev_get_drvdata(&udev->dev); } + if (!acquired && data) + data->kill_task = true; + lx_emul_task_unblock(data->task); return 0; } @@ -840,7 +439,7 @@ static int usb_poll_empty_sessions(void * data) for (;;) { usb_for_each_dev(NULL, wake_up_udev_task); lx_emul_task_schedule(false); - genode_usb_handle_empty_sessions(); + genode_usb_handle_disconnected_sessions(); lx_emul_task_schedule(true); } @@ -865,7 +464,129 @@ void lx_user_init(void) } -static int raw_notify(struct notifier_block *nb, unsigned long action, void *data) +struct usb_find_request { + genode_usb_bus_num_t bus; + genode_usb_dev_num_t dev; + struct usb_device * ret; +}; + + +static int check_usb_device(struct usb_device *usb_dev, void * data) +{ + struct usb_find_request * req = (struct usb_find_request *) data; + if (usb_dev->devnum == req->dev && usb_dev->bus->busnum == req->bus) + req->ret = usb_dev; + return 0; +} + +static struct usb_device * find_usb_device(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev) +{ + struct usb_find_request req = { bus, dev, NULL }; + usb_for_each_dev(&req, check_usb_device); + return req.ret; +} + + +static int device_released(void *d) +{ + struct usb_device *udev = (struct usb_device*)d; + struct usb_per_dev_data * data = udev ? dev_get_drvdata(&udev->dev) : NULL; + return !data; +} + + +void lx_emul_usb_release_device(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev) +{ + struct usb_device * udev = find_usb_device(bus, dev); + struct usb_per_dev_data * data = udev ? dev_get_drvdata(&udev->dev) : NULL; + bool acquired = genode_usb_device_acquired(bus, dev); + + if (acquired || !data) + return; + + data->kill_task = true; + lx_emul_task_unblock(data->task); + lx_emul_execute_kernel_until(&device_released, udev); +} + + +static void add_endpoint_callback(struct genode_usb_interface * iface, + unsigned idx, void * data) +{ + struct usb_host_interface *uiface = (struct usb_host_interface*) data; + struct genode_usb_endpoint_descriptor *desc = + (struct genode_usb_endpoint_descriptor*) &uiface->endpoint[idx].desc; + genode_usb_device_add_endpoint(iface, *desc); +} + + +static void interface_string(genode_buffer_t string, void * data) +{ + struct usb_host_interface *uiface = (struct usb_host_interface*) data; + if (uiface->string) + strlcpy(string.addr, uiface->string, string.size); + else + *(char *)string.addr = 0; +} + + +static void add_interface_callback(struct genode_usb_configuration * cfg, + unsigned idx, void * data) +{ + struct usb_host_config *ucfg = (struct usb_host_config*) data; + struct usb_interface_cache *iface_cache = ucfg->intf_cache[idx]; + struct usb_interface *iface = ucfg->interface[idx]; + unsigned i; + + for (i = 0; i < iface_cache->num_altsetting; i++) { + struct genode_usb_interface_descriptor *desc = + (struct genode_usb_interface_descriptor*) + &iface_cache->altsetting[i].desc; + bool set = iface ? &iface->altsetting[i] == iface->cur_altsetting + : false; + genode_usb_device_add_interface(cfg, interface_string, *desc, + add_endpoint_callback, + &iface_cache->altsetting[i], set); + } +} + + +static void add_configuration_callback(struct genode_usb_device * dev, + unsigned idx, void * data) +{ + struct usb_device *udev = (struct usb_device*) data; + struct genode_usb_config_descriptor *desc = + (struct genode_usb_config_descriptor*) &udev->config[idx].desc; + genode_usb_device_add_configuration(dev, *desc, add_interface_callback, + &udev->config[idx], + &udev->config[idx] == udev->actconfig); +} + + +static void manufacturer_string(genode_buffer_t string, void * data) +{ + struct usb_device *udev = (struct usb_device*) data; + if (udev->manufacturer) + strlcpy(string.addr, udev->manufacturer, string.size); + else + *(char *)string.addr = 0; +} + + +static void product_string(genode_buffer_t string, void * data) +{ + struct usb_device *udev = (struct usb_device*) data; + if (udev->product) + strlcpy(string.addr, udev->product, string.size); + else + *(char *)string.addr = 0; +} + + +static int raw_notify(struct notifier_block *nb, unsigned long action, + void *data) { struct usb_device *udev = (struct usb_device*) data; @@ -873,36 +594,29 @@ static int raw_notify(struct notifier_block *nb, unsigned long action, void *dat case USB_DEVICE_ADD: { - /** - * Register pseudo device class of USB device - * - * The registered 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 as device type. - * - * Note this classification of USB devices is meant as an interim - * solution only to assist the implementation of access-control - * policies. - */ - unsigned long class = 0; - unsigned i, num = (udev->actconfig) ? - udev->actconfig->desc.bNumInterfaces : 0; - for (i = 0; i < num; i++) { - struct usb_interface * iface = - (udev->actconfig) ? udev->actconfig->interface[i] : NULL; - if (!iface || !iface->cur_altsetting) continue; - if (i == 0 || - iface->cur_altsetting->desc.bInterfaceClass == - USB_CLASS_HID) - class = iface->cur_altsetting->desc.bInterfaceClass; + struct genode_usb_device_descriptor *desc = + (struct genode_usb_device_descriptor*) &udev->descriptor; + genode_usb_speed_t speed; + switch (udev->speed) { + case USB_SPEED_LOW: speed = GENODE_USB_SPEED_LOW; break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: speed = GENODE_USB_SPEED_FULL; break; + case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: speed = GENODE_USB_SPEED_HIGH; break; + case USB_SPEED_SUPER: speed = GENODE_USB_SPEED_SUPER; break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,13,0) + case USB_SPEED_SUPER_PLUS: + if (udev->ssp_rate == USB_SSP_GEN_2x2) + speed = GENODE_USB_SPEED_SUPER_PLUS_2X2; + else + speed = GENODE_USB_SPEED_SUPER_PLUS; + break; +#endif + default: speed = GENODE_USB_SPEED_FULL; } - - genode_usb_announce_device(udev->descriptor.idVendor, - udev->descriptor.idProduct, - class, - udev->bus->busnum, - udev->devnum); + genode_usb_announce_device(udev->bus->busnum, udev->devnum, speed, + manufacturer_string, product_string, + *desc, add_configuration_callback, udev); break; } @@ -910,7 +624,8 @@ static int raw_notify(struct notifier_block *nb, unsigned long action, void *dat { struct usb_per_dev_data * data = dev_get_drvdata(&udev->dev); if (data) { - data->dev = NULL; + data->dev = NULL; + data->kill_task = true; lx_emul_task_unblock(data->task); } else { /* discontinue unclaimed device */ diff --git a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/buffer.c b/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/buffer.c new file mode 100644 index 0000000000..aed51f50a5 --- /dev/null +++ b/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/buffer.c @@ -0,0 +1,15 @@ + +#include +#include + + +void * hcd_buffer_alloc(struct usb_bus * bus, size_t size, gfp_t mem_flags, dma_addr_t * dma) +{ + return kmalloc(size, GFP_KERNEL); +} + + +void hcd_buffer_free(struct usb_bus * bus, size_t size, void * addr, dma_addr_t dma) +{ + kfree(addr); +} diff --git a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/hcd.c b/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/hcd.c new file mode 100644 index 0000000000..990164017a --- /dev/null +++ b/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/hcd.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +/* wait queue for synchronous unlinks */ +DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); + +extern void lx_user_handle_io(void); + +int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) +{ + genode_usb_client_dev_handle_t handle; + genode_usb_client_ret_val_t r = INVALID; + + /* increment urb's reference count as part of giving it to the HCD + * (which will control it). HCD guarantees that it either returns + * an error or calls giveback(), but not both. + */ + usb_get_urb(urb); + atomic_inc(&urb->use_count); + atomic_inc(&urb->dev->urbnum); + + if (!urb->dev->bus) + return -ENODEV; + + handle = (genode_usb_client_dev_handle_t)urb->dev->filelist.prev; + + switch(usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + { + struct usb_ctrlrequest * ctrl = (struct usb_ctrlrequest *) + urb->setup_packet; + r = genode_usb_client_device_control(handle, + ctrl->bRequest, + ctrl->bRequestType, + ctrl->wValue, + ctrl->wIndex, + urb->transfer_buffer_length, + urb); + break; + } + case PIPE_INTERRUPT: + { + r = genode_usb_client_iface_transfer(handle, IRQ, + urb->ep->desc.bEndpointAddress, + urb->transfer_buffer_length, + urb); + break; + } + case PIPE_BULK: + r = genode_usb_client_iface_transfer(handle, BULK, + urb->ep->desc.bEndpointAddress, + urb->transfer_buffer_length, + urb); + break; + default: + printk("unknown URB requested: %d\n", usb_pipetype(urb->pipe)); + } + + switch (r) { + case NO_DEVICE: return -ENODEV; + case NO_MEMORY: return -ENOMEM; + case HALT: return -EPIPE; + case INVALID: return -EINVAL; + case TIMEOUT: return -ETIMEDOUT; + case OK: break; + }; + + lx_emul_usb_client_ticker(); + return 0; +} + + +int usb_hcd_unlink_urb (struct urb *urb, int status) +{ + struct usb_device *udev = urb->dev; + int ret = -EIDRM; + + if (atomic_read(&urb->use_count) > 0 && + udev->state == USB_STATE_NOTATTACHED) { + ret = 0; + atomic_set(&urb->use_count, 0); + } + + return ret; +} diff --git a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/message.c b/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/message.c deleted file mode 100644 index db190d0819..0000000000 --- a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/message.c +++ /dev/null @@ -1,451 +0,0 @@ -/* - * \brief message.c functions using genode_c_api/usb_client.h - * \author Sebastian Sumpf - * \date 2023-06-20 - */ - -/* - * Copyright (C) 2023 Genode Labs GmbH - * - * This file is distributed under the terms of the GNU General Public License - * version 2. - */ - -#include -#include -#include - -#include "urb_helper.h" - -extern struct bus_type usb_bus_type; -extern struct device_type usb_if_device_type; - - -static void sync_complete(struct genode_usb_client_request_packet *packet) -{ - complete((struct completion *)packet->opaque_data); -}; - - -int usb_control_msg(struct usb_device *dev, unsigned int pipe, - __u8 request, __u8 requesttype, __u16 value, - __u16 index, void *data, __u16 size, int timeout) -{ - unsigned timeout_jiffies; - int ret; - struct urb *urb; - struct completion comp; - - struct genode_usb_client_request_packet packet; - struct genode_usb_request_control control; - struct genode_usb_config config; - - genode_usb_client_handle_t handle; - - if (!dev->bus) return -ENODEV; - - handle = (genode_usb_client_handle_t)dev->bus->controller; - - /* - * If this function is called with a timeout of 0 to wait forever, - * we wait in pieces of 10s each as 'schedule_timeout' might trigger - * immediately otherwise. The intend to wait forever is reflected - * back nonetheless when sending the urb. - */ - timeout_jiffies = timeout ? msecs_to_jiffies(timeout) - : msecs_to_jiffies(10000u); - - /* dummy alloc urb for wait_for_free_urb below */ - urb = (struct urb *)usb_alloc_urb(0, GFP_KERNEL); - if (!urb) return -ENOMEM; - - - /* - * Set configuration also calls this function, but maps to different packet - * Note: Some calls using set configuration do not change the profile but send - * data to the device (e.g., keyboard led handling) where size != 0 - */ - if (request == USB_REQ_SET_CONFIGURATION && size == 0) { - packet.request.type = CONFIG; - config.value = value; - packet.request.req = &config; - packet.buffer.size = 0; - } else { - packet.request.type = CTRL; - control.request = request; - control.request_type = requesttype; - control.value = value; - control.index = index; - control.timeout = timeout ? jiffies_to_msecs(timeout_jiffies) : 0; - packet.request.req = &control; - packet.buffer.size = size; - } - - for (;;) { - - if (genode_usb_client_request(handle, &packet)) break; - - timeout_jiffies = wait_for_free_urb(timeout_jiffies); - if (!timeout_jiffies && timeout) { - ret = -ETIMEDOUT; - goto err_request; - } - } - - if (!(requesttype & USB_DIR_IN)) - memcpy(packet.buffer.addr, data, size); - - init_completion(&comp); - packet.complete_callback = sync_complete; - packet.free_callback = sync_complete; - packet.opaque_data = ∁ - - genode_usb_client_request_submit(handle, &packet); - wait_for_completion(&comp); - - if (packet.actual_length && data && (size >= packet.actual_length)) - memcpy(data, packet.buffer.addr, packet.actual_length); - - ret = packet.error ? packet_errno(packet.error) : packet.actual_length; - genode_usb_client_request_finish(handle, &packet); - -err_request: - kfree(urb); - - return ret; -} - - -int usb_get_descriptor(struct usb_device *dev, unsigned char type, - unsigned char index, void *buf, int size) -{ - int i; - int result; - - if (size <= 0) /* No point in asking for no data */ - return -EINVAL; - - memset(buf, 0, size); /* Make sure we parse really received data */ - - for (i = 0; i < 3; ++i) { - /* retry on length 0 or error; some devices are flakey */ - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (type << 8) + index, 0, buf, size, - USB_CTRL_GET_TIMEOUT); - if (result <= 0 && result != -ETIMEDOUT) - continue; - if (result > 1 && ((u8 *)buf)[1] != type) { - result = -ENODATA; - continue; - } - break; - } - return result; -} - - -void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, bool reset_ep) -{ - int epnum = usb_endpoint_num(&ep->desc); - int is_out = usb_endpoint_dir_out(&ep->desc); - int is_control = usb_endpoint_xfer_control(&ep->desc); - - if (is_out || is_control) - dev->ep_out[epnum] = ep; - if (!is_out || is_control) - dev->ep_in[epnum] = ep; - ep->enabled = 1; -} - - -void usb_enable_interface(struct usb_device *dev, - struct usb_interface *intf, bool reset_eps) -{ - struct usb_host_interface *alt = intf->cur_altsetting; - int i; - - for (i = 0; i < alt->desc.bNumEndpoints; ++i) { - usb_enable_endpoint(dev, &alt->endpoint[i], reset_eps); - } -} - - -int usb_set_interface(struct usb_device *udev, int ifnum, int alternate) -{ - int ret; - struct urb *urb; - struct completion comp; - unsigned timeout_jiffies = msecs_to_jiffies(10000u); - - struct genode_usb_client_request_packet packet; - struct genode_usb_altsetting alt_setting; - - genode_usb_client_handle_t handle; - - struct usb_interface *iface; - - if (!udev->bus) return -ENODEV; - - if (!udev->config) - return -ENODEV; - - if (ifnum >= USB_MAXINTERFACES || ifnum < 0) - return -EINVAL; - - iface = udev->actconfig->interface[ifnum]; - - handle = (genode_usb_client_handle_t)udev->bus->controller; - - /* dummy alloc urb for wait_for_free_urb below */ - urb = (struct urb *)usb_alloc_urb(0, GFP_KERNEL); - if (!urb) return -ENOMEM; - - packet.request.type = ALT_SETTING; - alt_setting.interface_number = ifnum; - alt_setting.alt_setting = alternate; - packet.request.req = &alt_setting; - packet.buffer.size = 0; - - for (;;) { - - if (genode_usb_client_request(handle, &packet)) break; - - timeout_jiffies = wait_for_free_urb(timeout_jiffies); - if (!timeout_jiffies) { - ret = -ETIMEDOUT; - goto err_request; - } - } - - init_completion(&comp); - packet.complete_callback = sync_complete; - packet.free_callback = sync_complete; - packet.opaque_data = ∁ - - genode_usb_client_request_submit(handle, &packet); - wait_for_completion(&comp); - - ret = packet.error ? packet_errno(packet.error) : 0; - genode_usb_client_request_finish(handle, &packet); - - /* reset via alt setting 0 */ - if (!iface) { - printk("%s:%d: Error: interface is null: infum: %d alt setting: %d\n", - __func__, __LINE__, ifnum, alternate); - return 0; - } - - if (!ret) { - iface->cur_altsetting = &iface->altsetting[alternate]; - } - - usb_enable_interface(udev, iface, true); - -err_request: - kfree(urb); - - return ret; -} - - -static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, - struct usb_host_config *config, - u8 inum) -{ - struct usb_interface_assoc_descriptor *retval = NULL; - struct usb_interface_assoc_descriptor *intf_assoc; - int first_intf; - int last_intf; - int i; - - for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) { - intf_assoc = config->intf_assoc[i]; - if (intf_assoc->bInterfaceCount == 0) - continue; - - first_intf = intf_assoc->bFirstInterface; - last_intf = first_intf + (intf_assoc->bInterfaceCount - 1); - if (inum >= first_intf && inum <= last_intf) { - if (!retval) - retval = intf_assoc; - else - dev_err(&dev->dev, "Interface #%d referenced" - " by multiple IADs\n", inum); - } - } - - return retval; -} - - -int usb_set_configuration(struct usb_device *dev, int configuration) -{ - int i, ret; - struct usb_host_config *cp = NULL; - struct usb_interface **new_interfaces = NULL; - int n, nintf; - - if (dev->authorized == 0 || configuration == -1) - configuration = 0; - else { - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - if (dev->config[i].desc.bConfigurationValue == - configuration) { - cp = &dev->config[i]; - break; - } - } - } - if ((!cp && configuration != 0)) - return -EINVAL; - - /* The USB spec says configuration 0 means unconfigured. - * But if a device includes a configuration numbered 0, - * we will accept it as a correctly configured state. - * Use -1 if you really want to unconfigure the device. - */ - if (cp && configuration == 0) - dev_warn(&dev->dev, "config 0 descriptor??\n"); - - /* Allocate memory for new interfaces before doing anything else, - * so that if we run out then nothing will have changed. */ - n = nintf = 0; - if (cp) { - nintf = cp->desc.bNumInterfaces; - new_interfaces = (struct usb_interface **) - kmalloc(nintf * sizeof(*new_interfaces), GFP_KERNEL); - if (!new_interfaces) - return -ENOMEM; - - for (; n < nintf; ++n) { - new_interfaces[n] = (struct usb_interface*) - kzalloc( sizeof(struct usb_interface), GFP_KERNEL); - if (!new_interfaces[n]) { - ret = -ENOMEM; - while (--n >= 0) - kfree(new_interfaces[n]); - kfree(new_interfaces); - return ret; - } - } - } - - /* - * Initialize the new interface structures and the - * hc/hcd/usbcore interface/endpoint state. - */ - for (i = 0; i < nintf; ++i) { - struct usb_interface_cache *intfc; - struct usb_interface *intf; - struct usb_host_interface *alt; - u8 ifnum; - - cp->interface[i] = intf = new_interfaces[i]; - intfc = cp->intf_cache[i]; - intf->altsetting = intfc->altsetting; - intf->num_altsetting = intfc->num_altsetting; - intf->authorized = 1; //FIXME - - alt = usb_altnum_to_altsetting(intf, 0); - - /* No altsetting 0? We'll assume the first altsetting. - * We could use a GetInterface call, but if a device is - * so non-compliant that it doesn't have altsetting 0 - * then I wouldn't trust its reply anyway. - */ - if (!alt) - alt = &intf->altsetting[0]; - - ifnum = alt->desc.bInterfaceNumber; - intf->intf_assoc = find_iad(dev, cp, ifnum); - intf->cur_altsetting = alt; - intf->dev.parent = &dev->dev; - intf->dev.driver = NULL; - intf->dev.bus = &usb_bus_type; - intf->dev.type = &usb_if_device_type; - intf->minor = -1; - device_initialize(&intf->dev); - dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, - dev->devpath, configuration, ifnum); - } - kfree(new_interfaces); - - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_SET_CONFIGURATION, 0, configuration, 0, - NULL, 0, USB_CTRL_SET_TIMEOUT); - if (ret < 0 && cp) { - for (i = 0; i < nintf; ++i) { - put_device(&cp->interface[i]->dev); - cp->interface[i] = NULL; - } - cp = NULL; - } - dev->actconfig = cp; - - if (!cp) { - dev->state = USB_STATE_ADDRESS; - return ret; - } - dev->state = USB_STATE_CONFIGURED; - - for (i = 0; i < nintf; ++i) { - struct usb_interface *intf = cp->interface[i]; - usb_enable_interface(dev, intf, true); - - ret = device_add(&intf->dev); - if (ret != 0) { - printk("error: device_add(%s) --> %d\n", dev_name(&intf->dev), ret); - continue; - } - } - - return 0; -} - - -void usb_disable_device(struct usb_device *dev, int skip_ep0) -{ - int i; - - /* getting rid of interfaces will disconnect - * any drivers bound to them (a key side effect) - */ - if (dev->actconfig) { - /* - * FIXME: In order to avoid self-deadlock involving the - * bandwidth_mutex, we have to mark all the interfaces - * before unregistering any of them. - */ - for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) - dev->actconfig->interface[i]->unregistering = 1; - - for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { - struct usb_interface *interface; - - /* remove this interface if it has been registered */ - interface = dev->actconfig->interface[i]; - if (!device_is_registered(&interface->dev)) - continue; - - dev_dbg(&dev->dev, "unregistering interface %s\n", - dev_name(&interface->dev)); - device_del(&interface->dev); - } - - /* Now that the interfaces are unbound, nobody should - * try to access them. - */ - for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { - put_device(&dev->actconfig->interface[i]->dev); - dev->actconfig->interface[i] = NULL; - } - - dev->actconfig = NULL; - if (dev->state == USB_STATE_CONFIGURED) - usb_set_device_state(dev, USB_STATE_ADDRESS); - } - - dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, - skip_ep0 ? "non-ep0" : "all"); -} diff --git a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb.c b/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb.c deleted file mode 100644 index 15e4adaf60..0000000000 --- a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * \brief urb.c functions using genode_c_api/usb_client.h - * \author Sebastian Sumpf - * \date 2023-06-20 - */ - -/* - * Copyright (C) 2023 Genode Labs GmbH - * - * This file is distributed under the terms of the GNU General Public License - * version 2. - */ - -#include -#include -#include - -#include "urb_helper.h" - -#define to_urb(d) container_of(d, struct urb, kref) - - -static DECLARE_WAIT_QUEUE_HEAD(lx_emul_urb_wait); - -int wait_for_free_urb(unsigned int timeout_jiffies) -{ - int ret = 0; - - DECLARE_WAITQUEUE(wait, current); - add_wait_queue(&lx_emul_urb_wait, &wait); - - ret = schedule_timeout(timeout_jiffies); - - remove_wait_queue(&lx_emul_urb_wait, &wait); - - return ret; -} - - -struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) -{ - struct urb *urb = (struct urb*) - kmalloc(sizeof(struct urb) + - iso_packets * sizeof(struct usb_iso_packet_descriptor), - GFP_KERNEL); - - if (!urb) return NULL; - memset(urb, 0, sizeof(*urb)); - kref_init(&urb->kref); - INIT_LIST_HEAD(&urb->urb_list); - INIT_LIST_HEAD(&urb->anchor_list); - - return urb; -} - - -static void free_packet(struct genode_usb_client_request_packet *packet) -{ - kfree(packet->request.req); - kfree(packet); -} - - -static void urb_submit_complete(struct genode_usb_client_request_packet *packet) -{ - struct urb *urb = (struct urb *)packet->opaque_data; - genode_usb_client_handle_t handle = (genode_usb_client_handle_t)urb->hcpriv; - - urb->status = packet->error ? packet_errno(packet->error) : 0; - - if (packet->error == 0 && - packet->actual_length && urb->transfer_buffer && - urb->transfer_buffer_length >= packet->actual_length) - memcpy(urb->transfer_buffer, packet->buffer.addr, packet->actual_length); - - urb->actual_length = packet->actual_length; - - genode_usb_client_request_finish(handle, packet); - - free_packet(packet); - - if (urb->complete) urb->complete(urb); -}; - - -int usb_submit_urb(struct urb *urb, gfp_t mem_flags) -{ - genode_usb_client_handle_t handle; - struct genode_usb_client_request_packet *packet; - struct genode_usb_request_transfer *transfer; - struct genode_usb_request_control *control; - int ret = 0; - unsigned timeout_jiffies = msecs_to_jiffies(10000u); - - if (!urb || !urb->complete) - return -EINVAL; - - if (!urb->dev->bus) - return -ENODEV; - - handle = (genode_usb_client_handle_t)urb->dev->bus->controller; - - packet = (struct genode_usb_client_request_packet *) - kzalloc(sizeof(*packet), GFP_KERNEL); - - if (!packet) return -ENOMEM; - - if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - control = (struct genode_usb_request_control *) - kzalloc(sizeof(*control), GFP_KERNEL); - - if (!control) { - ret = -ENOMEM; - goto transfer; - } - } - else { - transfer = (struct genode_usb_request_transfer *) - kzalloc(sizeof(*transfer), GFP_KERNEL); - - if (!transfer) { - ret = -ENOMEM; - goto transfer; - } - } - - switch(usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - { - struct usb_ctrlrequest * ctrl = (struct usb_ctrlrequest *) - urb->setup_packet; - packet->request.type = CTRL; - control->request = ctrl->bRequest; - control->request_type = ctrl->bRequestType; - control->value = ctrl->wValue; - control->index = ctrl->wIndex; - packet->request.req = control; - break; - } - case PIPE_INTERRUPT: - { - packet->request.type = IRQ; - transfer->polling_interval = urb->interval; - transfer->ep = usb_pipeendpoint(urb->pipe) - | (usb_pipein(urb->pipe) ? USB_DIR_IN : 0); - packet->request.req = transfer; - break; - } - case PIPE_BULK: - { - packet->request.type = BULK; - transfer->ep = usb_pipeendpoint(urb->pipe) - | (usb_pipein(urb->pipe) ? USB_DIR_IN : 0); - packet->request.req = transfer; - break; - } - default: - printk("unknown URB requested: %d\n", usb_pipetype(urb->pipe)); - } - - packet->buffer.size = urb->transfer_buffer_length; - packet->complete_callback = urb_submit_complete; - packet->opaque_data = urb; - packet->free_callback = free_packet; - - for (;;) { - - if (genode_usb_client_request(handle, packet)) break; - - timeout_jiffies = wait_for_free_urb(timeout_jiffies); - if (!timeout_jiffies) { - ret = -ETIMEDOUT; - goto err_request; - } - } - - if (usb_pipeout(urb->pipe)) - memcpy(packet->buffer.addr, urb->transfer_buffer, urb->transfer_buffer_length); - - urb->hcpriv = (void *)handle; - - genode_usb_client_request_submit(handle, packet); - - return ret; - -err_request: - if (transfer) kfree(transfer); - if (control) kfree(control); -transfer: - kfree(packet); - - return ret; -} - - -struct urb *usb_get_urb(struct urb *urb) -{ - if (urb) - kref_get(&urb->kref); - return urb; -} - - -static void urb_destroy(struct kref *kref) -{ - struct urb *urb = to_urb(kref); - - kfree(urb); - wake_up(&lx_emul_urb_wait); -} - - -/* usb_put_urb is defined as usb_free_urb, therefore we need reference counting */ -void usb_free_urb(struct urb *urb) -{ - if (urb) - kref_put(&urb->kref, urb_destroy); -} diff --git a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb_helper.h b/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb_helper.h deleted file mode 100644 index 0a41d3cd9a..0000000000 --- a/repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb_helper.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * \brief URB handling helpers - * \author Sebastian Sumpf - * \date 2023-06-20 - */ - -/* - * Copyright (C) 2023 Genode Labs GmbH - * - * This file is distributed under the terms of the GNU General Public License - * version 2. - */ - -#include -#include - - -/* - * Translate USB-client packet errors to errno - */ -static inline int packet_errno(int error) -{ - switch (error) { - case INTERFACE_OR_ENDPOINT_ERROR: return -ENOENT; - case MEMORY_ERROR: return -ENOMEM; - case NO_DEVICE_ERROR: return -ESHUTDOWN; - case PACKET_INVALID_ERROR: return -EINVAL; - case PROTOCOL_ERROR: return -EPROTO; - case STALL_ERROR: return -EPIPE; - case TIMEOUT_ERROR: return -ETIMEDOUT; - case UNKNOWN_ERROR: - printk("%s: got UNKNOWN_ERROR code\n", __func__); - return -EIO; - } - - return -EIO; -} - - -/* - * Wait for a call to 'urb_destroy' - */ -int wait_for_free_urb(unsigned int timeout_jiffies); diff --git a/repos/dde_linux/src/lib/lx_emul/virt/usb_client.c b/repos/dde_linux/src/lib/lx_emul/virt/usb_client.c index bb6efb3741..18f73afb1f 100644 --- a/repos/dde_linux/src/lib/lx_emul/virt/usb_client.c +++ b/repos/dde_linux/src/lib/lx_emul/virt/usb_client.c @@ -1,9 +1,10 @@ #include #include +#include <../drivers/usb/core/usb.h> #include +#include -static struct hc_driver _hc_driver = { }; struct meta_data { @@ -12,128 +13,208 @@ struct meta_data struct usb_device *udev; }; - -void *lx_emul_usb_client_register_device(genode_usb_client_handle_t handle, char const *label) +static struct usb_hcd * dummy_hc_device(void) +{ + static int initialized = 0; + static struct hc_driver _hc_driver = { }; + static struct usb_hcd hcd; + static struct device sysdev; + static struct mutex address0_mutex; + static struct mutex bandwidth_mutex; + + if (!initialized) { + device_initialize(&sysdev); + mutex_init(&address0_mutex); + mutex_init(&bandwidth_mutex); + kref_init(&hcd.kref); + hcd.driver = &_hc_driver; + hcd.self.bus_name = "usbbus"; + hcd.self.sysdev = &sysdev; + hcd.dev_policy = USB_DEVICE_AUTHORIZE_ALL; + hcd.address0_mutex = &address0_mutex; + hcd.bandwidth_mutex = &bandwidth_mutex; + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd.flags); + set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd.flags); + initialized = 1; + } + return &hcd; +} + +static void * register_device(genode_usb_client_dev_handle_t handle, + char const *label, genode_usb_speed_t speed) { - struct meta_data *meta; int err; - struct genode_usb_device_descriptor dev_descr; - struct genode_usb_config_descriptor conf_descr; + static int num = 0; + struct usb_device * udev; - err = genode_usb_client_config_descriptor(handle, &dev_descr, &conf_descr); - if (err) { - printk("error: failed to read config descriptor\n"); - return NULL;; - } - - meta = (struct meta_data *)kmalloc(sizeof(struct meta_data), GFP_KERNEL); - if (!meta) return NULL; - - meta->sysdev = (struct device*)kzalloc(sizeof(struct device), GFP_KERNEL); - if (!meta->sysdev) goto sysdev; - - device_initialize(meta->sysdev); - - meta->hcd = (struct usb_hcd *)kzalloc(sizeof(struct usb_hcd), GFP_KERNEL); - if (!meta->hcd) goto hcd; - - /* hcd->self is usb_bus */ - meta->hcd->driver = &_hc_driver; - meta->hcd->self.bus_name = "usbbus"; - meta->hcd->self.sysdev = meta->sysdev; - - meta->udev = usb_alloc_dev(NULL, &meta->hcd->self, 0); - if (!meta->udev) { + udev = usb_alloc_dev(NULL, &dummy_hc_device()->self, 0); + if (!udev) { printk("error: could not allocate udev for %s\n", label); - goto udev; - } - - /* usb_alloc_dev sets parent to bus->controller if first argument is NULL */ - meta->hcd->self.controller = (struct device *)handle; - - memcpy(&meta->udev->descriptor, &dev_descr, sizeof(struct usb_device_descriptor)); - meta->udev->devnum = dev_descr.num; - meta->udev->speed = (enum usb_device_speed)dev_descr.speed; - meta->udev->authorized = 1; - meta->udev->bus_mA = 900; /* set to maximum USB3.0 */ - meta->udev->state = USB_STATE_NOTATTACHED; - - dev_set_name(&meta->udev->dev, "%s", label); - - err = usb_new_device(meta->udev); - - if (err) { - printk("error: usb_new_device failed %d\n", err); - goto new_device; - } - return meta; - -new_device: - usb_put_dev(meta->udev); -udev: - kfree(meta->hcd); -hcd: - kfree(meta->sysdev); -sysdev: - kfree(meta); - - return NULL; -} - - -void lx_emul_usb_client_unregister_device(genode_usb_client_handle_t handle, void *data) -{ - struct meta_data *meta = (struct meta_data *)data; - - usb_disconnect(&meta->udev); - usb_put_dev(meta->udev); - kfree(meta->hcd); - kobject_put(&meta->sysdev->kobj); - kfree(meta->sysdev); - kfree(meta); -} - - -#define for_each_claimed_interface(udev, num, intf) \ - for (num = 0, intf = udev->actconfig->interface[num]; \ - num < udev->actconfig->desc.bNumInterfaces; \ - intf = udev->actconfig->interface[++num]) \ - if (!usb_interface_claimed(intf)) continue; else - - -int lx_emul_usb_client_set_configuration(genode_usb_client_handle_t handle, void *data, unsigned long config) -{ - struct meta_data *meta = (struct meta_data *)data; - struct usb_device *udev = meta->udev; - unsigned i = 0; - struct usb_interface *intf; - - /* release claimed interfaces at session */ - for_each_claimed_interface(udev, i, intf) { - genode_usb_client_release_interface(handle, i); + return NULL; } /* - * Release claimed interfaces at driver, 'usb_driver_release_interface' may - * release more than one interface + * We store handle in filelist list head to be used in hcd urb submission + * before sending any URB. The filelist member is referenced in devio.c + * only which is not used here. */ - for_each_claimed_interface(udev, i, intf) { - usb_driver_release_interface(to_usb_driver(intf->dev.driver), intf); + udev->filelist.prev = (struct list_head *) handle; + + udev->devnum = num++; + + switch (speed) { + case GENODE_USB_SPEED_LOW: udev->speed = USB_SPEED_LOW; break; + case GENODE_USB_SPEED_FULL: udev->speed = USB_SPEED_FULL; break; + case GENODE_USB_SPEED_HIGH: udev->speed = USB_SPEED_HIGH; break; + case GENODE_USB_SPEED_SUPER: udev->speed = USB_SPEED_SUPER; break; + case GENODE_USB_SPEED_SUPER_PLUS: + udev->speed = USB_SPEED_SUPER_PLUS; + udev->ssp_rate = USB_SSP_GEN_2x1; + break; + case GENODE_USB_SPEED_SUPER_PLUS_2X2: + udev->speed = USB_SPEED_SUPER_PLUS; + udev->ssp_rate = USB_SSP_GEN_2x2; + break; + default: + udev->speed = USB_SPEED_FULL; + break; + }; + + udev->authorized = 1; + udev->bus_mA = 900; /* set to maximum USB3.0 */ + usb_set_device_state(udev, USB_STATE_ADDRESS); + + dev_set_name(&udev->dev, "%s", label); + device_set_wakeup_capable(&udev->dev, 1); + udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64); + err = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); + if (err < 0) { + dev_err(&udev->dev, "can't read device descriptor: %d\n", err); + usb_put_dev(udev); + return NULL; } - /* set unconfigured (internal reset in host driver) first */ - usb_set_configuration(meta->udev, 0); - return usb_set_configuration(meta->udev, config); + err = usb_new_device(udev); + if (err) { + printk("error: usb_new_device failed %d\n", err); + usb_put_dev(udev); + return NULL; + } + + return udev; } -void * hcd_buffer_alloc(struct usb_bus * bus, size_t size, gfp_t mem_flags, dma_addr_t * dma) +static void urb_out(void * data, genode_buffer_t buf) { - return kmalloc(size, GFP_KERNEL); + struct urb *urb = (struct urb *) data; + memcpy(buf.addr, urb->transfer_buffer, + urb->transfer_buffer_length); } -void hcd_buffer_free(struct usb_bus * bus, size_t size, void * addr, dma_addr_t dma) +static void urb_in(void * data, genode_buffer_t buf) { - kfree(addr); + struct urb *urb = (struct urb *) data; + memcpy(urb->transfer_buffer, buf.addr, buf.size); + urb->actual_length = buf.size; +} + + +static genode_uint32_t isoc_urb_out(void * data, genode_uint32_t idx, + genode_buffer_t buf) +{ + printk("%s: not implemented yet, we had no isochronous Linux driver yet", + __func__); + return 0; +} + + +static void isoc_urb_in(void * data, genode_uint32_t idx, genode_buffer_t buf) +{ + printk("%s: not implemented yet, we had no isochronous Linux driver yet", + __func__); +} + + +static void urb_complete(void * data, genode_usb_client_ret_val_t result) +{ + struct urb *urb = (struct urb *) data; + switch (result) { + case OK: urb->status = 0; break; + case NO_DEVICE: urb->status = -ENOENT; break; + case NO_MEMORY: urb->status = -ENOMEM; break; + case HALT: urb->status = -EPIPE; break; + case INVALID: urb->status = -EINVAL; break; + case TIMEOUT: urb->status = -ETIMEDOUT; + }; + if (urb->complete) urb->complete(urb); + + atomic_dec(&urb->use_count); + usb_put_urb(urb); +} + + +static void unregister_device(genode_usb_client_dev_handle_t handle, void *data) +{ + struct usb_device *udev = (struct usb_device *)data; + genode_usb_client_device_update(urb_out, urb_in, isoc_urb_out, + isoc_urb_in, urb_complete); + udev->filelist.prev = NULL; + usb_disconnect(&udev); + usb_put_dev(udev); +} + + +static int usb_loop(void *arg) +{ + for (;;) { + genode_usb_client_device_update(urb_out, urb_in, isoc_urb_out, + isoc_urb_in, urb_complete); + lx_emul_task_schedule(true); + } + return -1; +} + + +static struct task_struct * usb_task = NULL; + + +static int usb_rom_loop(void *arg) +{ + for (;;) { + + genode_usb_client_update(register_device, unregister_device); + + /* block until lx_emul_task_unblock */ + lx_emul_task_schedule(true); + } + return 0; +} + + +static struct task_struct *usb_rom_task = NULL; + + +void lx_emul_usb_client_init(void) +{ + pid_t pid; + + pid = kernel_thread(usb_rom_loop, NULL, CLONE_FS | CLONE_FILES); + usb_rom_task = find_task_by_pid_ns(pid, NULL); + + pid = kernel_thread(usb_loop, NULL, CLONE_FS | CLONE_FILES); + usb_task = find_task_by_pid_ns(pid, NULL); +} + + +void lx_emul_usb_client_rom_update(void) +{ + if (usb_rom_task) lx_emul_task_unblock(usb_rom_task); +} + + +void lx_emul_usb_client_ticker(void) +{ + if (usb_task) lx_emul_task_unblock(usb_task); } diff --git a/repos/gems/sculpt/default-pc.sculpt b/repos/gems/sculpt/default-pc.sculpt index bc48659d3b..825c875071 100644 --- a/repos/gems/sculpt/default-pc.sculpt +++ b/repos/gems/sculpt/default-pc.sculpt @@ -7,7 +7,7 @@ gpu_drv: intel import: pkg/drivers_managed-pc pkg/pc_wifi src/pc_nic_drv # selection of launcher-menu entries -launcher: vm_fs shared_fs usb_devices_rom download_debug_info +launcher: vm_fs shared_fs download_debug_info # selection of accepted depot-package providers depot: genodelabs cnuke alex-ab mstein nfeske cproc chelmuth jschlatow diff --git a/repos/gems/sculpt/drivers/pc b/repos/gems/sculpt/drivers/pc index 27a1a06e88..41047a8642 100644 --- a/repos/gems/sculpt/drivers/pc +++ b/repos/gems/sculpt/drivers/pc @@ -173,7 +173,7 @@ - + diff --git a/repos/gems/sculpt/launcher/usb_devices_rom b/repos/gems/sculpt/launcher/usb_devices_rom deleted file mode 100644 index b432c3c03e..0000000000 --- a/repos/gems/sculpt/launcher/usb_devices_rom +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/repos/gems/src/app/driver_manager/main.cc b/repos/gems/src/app/driver_manager/main.cc index 27dd1b8267..f326405ffc 100644 --- a/repos/gems/src/app/driver_manager/main.cc +++ b/repos/gems/src/app/driver_manager/main.cc @@ -735,39 +735,41 @@ void Driver_manager::Main::_generate_usb_drv_config(Reporter &usb_drv_config, /* usb hid drv gets all hid devices */ xml.node("policy", [&] () { xml.attribute("label_prefix", "usb_hid_drv"); - xml.attribute("class", "0x3"); + xml.node("device", [&] () { + xml.attribute("class", "0x3"); + }); }); + /* produce policy nodes for all storage devices */ devices.for_each_sub_node("device", [&] (Xml_node device) { - typedef String<64> Label; - typedef String<32> Id; + bool usb_storage = false; + device.for_each_sub_node("config", [&] (Xml_node cfg) { - Label const label = device.attribute_value("label", Label()); - Id const vendor_id = device.attribute_value("vendor_id", Id()); - Id const product_id = device.attribute_value("product_id", Id()); + cfg.for_each_sub_node("interface", [&] (Xml_node iface) { - /* - * Limit USB sessions to storage and vendor specific in order to avoid - * conflicts with the USB driver's built-in HID drivers. - */ - unsigned long const class_code = device.attribute_value("class", 0UL); + enum { USB_CLASS_MASS_STORAGE = 8 }; - enum { USB_CLASS_MASS_STORAGE = 8, USB_CLASS_VENDOR_SPECIFIC = 0xff }; + if (iface.attribute_value("class", 0UL) == + USB_CLASS_MASS_STORAGE) + usb_storage = true; + }); + }); - bool const expose_as_usb_raw = (class_code == USB_CLASS_MASS_STORAGE) || - (class_code == USB_CLASS_VENDOR_SPECIFIC); - if (!expose_as_usb_raw) + if (!usb_storage) return; - xml.node("policy", [&] () { - xml.attribute("label_suffix", label); - xml.attribute("vendor_id", vendor_id); - xml.attribute("product_id", product_id); + using Name = String<64>; - /* annotate policy to make storage devices easy to spot */ - if (class_code == USB_CLASS_MASS_STORAGE) - xml.attribute("class", "storage"); + Name const name = device.attribute_value("name", Name()); + + xml.node("policy", [&] () { + + xml.attribute("label_suffix", name); + xml.attribute("class", "storage"); + xml.node("device", [&] () { + xml.attribute("name", name); + }); }); }); }); diff --git a/repos/libports/recipes/raw/usb_webcam/usb_webcam.config b/repos/libports/recipes/raw/usb_webcam/usb_webcam.config index 7becd5177a..a1cba8a896 100644 --- a/repos/libports/recipes/raw/usb_webcam/usb_webcam.config +++ b/repos/libports/recipes/raw/usb_webcam/usb_webcam.config @@ -72,7 +72,7 @@ - + @@ -101,7 +101,7 @@ - + diff --git a/repos/libports/run/acpica.run b/repos/libports/run/acpica.run index 0d49ce7e5c..d30326bb28 100644 --- a/repos/libports/run/acpica.run +++ b/repos/libports/run/acpica.run @@ -113,7 +113,7 @@ set config { - + @@ -123,7 +123,6 @@ set config { - diff --git a/repos/libports/run/smartcard.run b/repos/libports/run/smartcard.run index 5bf6380109..2ae2661d1f 100644 --- a/repos/libports/run/smartcard.run +++ b/repos/libports/run/smartcard.run @@ -79,7 +79,7 @@ install_config { - + @@ -106,7 +106,7 @@ install_config { append usb_config { - + } set fd [open [run_dir]/genode/usb_host_drv.config w] puts $fd $usb_config diff --git a/repos/libports/run/webcam.inc b/repos/libports/run/webcam.inc index c29f13033c..7f067e53f9 100644 --- a/repos/libports/run/webcam.inc +++ b/repos/libports/run/webcam.inc @@ -87,7 +87,7 @@ append config { - + @@ -108,8 +108,9 @@ append config { - + + + @@ -161,7 +162,7 @@ append config { - + } append config $test_vfs_config append config { @@ -200,7 +201,7 @@ append config { - + diff --git a/repos/libports/src/lib/libusb/genode_usb_raw.cc b/repos/libports/src/lib/libusb/genode_usb_raw.cc index e56e7aa034..7b39411842 100644 --- a/repos/libports/src/lib/libusb/genode_usb_raw.cc +++ b/repos/libports/src/lib/libusb/genode_usb_raw.cc @@ -1,6 +1,7 @@ /* * \brief Genode backend for libusb * \author Christian Prochaska + * \author Stefan Kalkowski * \date 2016-09-19 */ @@ -12,10 +13,11 @@ */ #include +#include #include #include -#include -#include +#include +#include #include #include @@ -25,275 +27,244 @@ #include "libusbi.h" +using namespace Genode; -static Libc::Allocator libc_alloc { }; - - -struct Completion : Usb::Completion -{ - struct usbi_transfer *itransfer; - - Completion(struct usbi_transfer *itransfer) - : itransfer(itransfer) { } - - void complete(Usb::Packet_descriptor &p) override { } -}; +bool libusb_genode_backend_signaling = false; +static int vfs_libusb_fd { -1 }; struct Usb_device { - private: + template + struct Urb_tpl : URB + { + void * const buf; + size_t const size; + usbi_transfer * const itransfer { nullptr }; - unsigned _open { 0 }; + template + Urb_tpl(void *buf, size_t size, usbi_transfer *itransfer, + ARGS &&... args) + : URB(args...), buf(buf), size(size), itransfer(itransfer) {} - public: + template + Urb_tpl(void *buf, size_t size, ARGS &&... args) + : URB(args...), buf(buf), size(size) {} + }; - struct Device_has_vanished {}; + struct Interface : Usb::Interface, Registry::Element + { + using Urb = Urb_tpl; - Usb::Connection *usb_connection; - int vfs_libusb_fd; + Usb_device &_device; - Usb::Device_descriptor device_descriptor; - Usb::Config_descriptor config_descriptor; - char *raw_config_descriptor = nullptr; + Interface(Usb_device &device, uint8_t idx); - bool _retrieve_raw_config_descriptor() - { - Genode::log("libusb: retrieve configuration descriptor"); + void handle_events(); + }; - Usb::Packet_descriptor p = - usb_connection->alloc_packet(config_descriptor.total_length); - p.type = Usb::Packet_descriptor::CTRL; - p.control.request = LIBUSB_REQUEST_GET_DESCRIPTOR; - p.control.request_type = LIBUSB_ENDPOINT_IN; - p.control.value = (LIBUSB_DT_CONFIG << 8) | 0; - p.control.index = 0; + using Urb = Urb_tpl; - usb_connection->source()->submit_packet(p); + Env &_env; + Allocator &_alloc; + Tslab _iface_slab { _alloc }; + Signal_context_capability _handler_cap; + Usb::Connection _session { _env }; + Usb::Device _device { _session, _alloc, _env.rm() }; + libusb_speed _speed { LIBUSB_SPEED_UNKNOWN }; + unsigned _open { 0 }; + Registry _interfaces {}; - /* wait for ack */ - struct pollfd pollfds { - .fd = vfs_libusb_fd, - .events = POLLIN, - .revents = 0 - }; + void _wait_for_urb(Urb &urb); - int poll_result = poll(&pollfds, 1, -1); + Usb_device(Env &env, Allocator &alloc, Signal_context_capability cap) + : + _env(env), _alloc(alloc), _handler_cap(cap) + { + String<32> speed; + _session.with_xml([&] (Xml_node xml) { + xml.with_optional_sub_node("device", [&] (Xml_node node) { + speed = node.attribute_value("speed", String<32>()); }); + }); - if ((poll_result != 1) || !(pollfds.revents & POLLIN)) { - Genode::error(__PRETTY_FUNCTION__, - ": could not read raw configuration descriptor"); - return false; - } + if (speed == "low") _speed = LIBUSB_SPEED_LOW; + else if (speed == "full") _speed = LIBUSB_SPEED_FULL; + else if (speed == "high") _speed = LIBUSB_SPEED_HIGH; + else if (speed == "super" || + speed == "super_plus" || + speed == "super_plus_2x2") _speed = LIBUSB_SPEED_SUPER; - p = usb_connection->source()->get_acked_packet(); + _device.sigh(_handler_cap); + } - bool ret = false; - if (!p.succeded) { - Genode::error(__PRETTY_FUNCTION__, - ": could not read raw configuration descriptor"); - } else if (p.control.actual_size != config_descriptor.total_length) { - Genode::error(__PRETTY_FUNCTION__, - ": received configuration descriptor of unexpected size"); - } else { - char *packet_content = usb_connection->source()->packet_content(p); - Genode::memcpy(raw_config_descriptor, packet_content, - config_descriptor.total_length); - ret = true; - } + void close() { _open--; } + void open() { _open++; } - usb_connection->source()->release_packet(p); - return ret; - } + void handle_events(); - Usb_device(Usb::Connection *usb, int vfs_libusb_fd) - : usb_connection(usb), vfs_libusb_fd(vfs_libusb_fd) - { - usb_connection->config_descriptor(&device_descriptor, &config_descriptor); - - raw_config_descriptor = (char*)malloc(config_descriptor.total_length); - - for (unsigned attempt = 0; attempt < 10; ++attempt) - if (_retrieve_raw_config_descriptor()) - break; - } - - ~Usb_device() - { - free(raw_config_descriptor); - } - - bool altsetting(int number, int alt_setting) - { - if (!usb_connection->source()->ready_to_submit()) - return false; - - Usb::Packet_descriptor p = - usb_connection->alloc_packet(0); - - p.type = Usb::Packet_descriptor::ALT_SETTING; - p.interface.number = number; - p.interface.alt_setting = alt_setting; - - usb_connection->source()->submit_packet(p); - - return true; - } - - void close() { _open--; } - void open() { _open++; } - - void handle_events() - { - struct libusb_context *ctx = nullptr; - - while (usb_connection->source()->ack_avail()) { - - Usb::Packet_descriptor p = - usb_connection->source()->get_acked_packet(); - - if (p.type == Usb::Packet_descriptor::ALT_SETTING) { - usb_connection->source()->release_packet(p); - continue; - } - - Completion *completion = static_cast(p.completion); - struct usbi_transfer *itransfer = completion->itransfer; - - if (_open) - ctx = ITRANSFER_CTX(itransfer); - - destroy(libc_alloc, completion); - - if (_open == 0) { - usb_connection->source()->release_packet(p); - continue; - } - - if (!p.succeded || itransfer->flags & USBI_TRANSFER_CANCELLING) { - if (!p.succeded) { - if (p.error == Usb::Packet_descriptor::NO_DEVICE_ERROR) - throw Device_has_vanished(); - } - itransfer->transferred = 0; - usb_connection->source()->release_packet(p); - usbi_signal_transfer_completion(itransfer); - continue; - } - - char *packet_content = usb_connection->source()->packet_content(p); - - struct libusb_transfer *transfer = - USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - - switch (transfer->type) { - - case LIBUSB_TRANSFER_TYPE_CONTROL: { - - itransfer->transferred = p.control.actual_size; - - struct libusb_control_setup *setup = - (struct libusb_control_setup*)transfer->buffer; - - if ((setup->bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == - LIBUSB_ENDPOINT_IN) { - Genode::memcpy(transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, - packet_content, p.control.actual_size); - } - - break; - } - - case LIBUSB_TRANSFER_TYPE_BULK: - case LIBUSB_TRANSFER_TYPE_BULK_STREAM: - case LIBUSB_TRANSFER_TYPE_INTERRUPT: { - - itransfer->transferred = p.transfer.actual_size; - - if (IS_XFERIN(transfer)) - Genode::memcpy(transfer->buffer, packet_content, - p.transfer.actual_size); - - break; - } - - case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: { - - itransfer->transferred = p.transfer.actual_size; - - if (IS_XFERIN(transfer)) { - - Usb::Isoc_transfer &isoc = *(Usb::Isoc_transfer *)packet_content; - - unsigned out_offset = 0; - for (unsigned i = 0; i < isoc.number_of_packets; i++) { - size_t const actual_length = isoc.actual_packet_size[i]; - - /* - * Copy the data from the proper offsets within the buffer as - * a short read is still stored at this location. - */ - unsigned char * dst = transfer->buffer + out_offset; - char const * src = isoc.data() + out_offset; - - Genode::memcpy(dst, src, actual_length); - out_offset += transfer->iso_packet_desc[i].length; - - transfer->iso_packet_desc[i].actual_length = actual_length; - transfer->iso_packet_desc[i].status = LIBUSB_TRANSFER_COMPLETED; - } - transfer->num_iso_packets = isoc.number_of_packets; - - } - - break; - } - - default: - Genode::error(__PRETTY_FUNCTION__, - ": unsupported transfer type"); - usb_connection->source()->release_packet(p); - continue; - } - - usb_connection->source()->release_packet(p); - - usbi_signal_transfer_completion(itransfer); - } - - if (ctx != nullptr) - usbi_signal_event(ctx); - } + static Constructible& singleton(); }; -static int vfs_libusb_fd { -1 }; -static Usb::Connection *usb_connection { nullptr }; -static Usb_device *device_instance { nullptr }; - - -/* - * This function is called by the VFS plugin on 'open()' - * to pass a pointer to the USB connection. - */ -void libusb_genode_usb_connection(Usb::Connection *usb) +Usb_device::Interface::Interface(Usb_device &device, uint8_t idx) +: + Usb::Interface(device._device, Usb::Interface::Index{idx, 0}, (1UL << 20)), + Registry::Element(device._interfaces, *this), + _device(device) { - usb_connection = usb; + sigh(device._handler_cap); +} + + +static inline addr_t isoc_packet_offset(uint32_t idx, + struct libusb_transfer *transfer) +{ + addr_t offset = 0; + for (uint32_t i = 0; i < idx; i++) + offset += transfer->iso_packet_desc[i].length; + return offset; +} + + +void Usb_device::Interface::handle_events() +{ + update_urbs( + + /* produce out content */ + [] (Urb &urb, Byte_range_ptr &dst) { + memcpy(dst.start, urb.buf, min(dst.num_bytes, urb.size)); }, + + /* consume in results */ + [] (Urb &urb, Const_byte_range_ptr const &src) { + memcpy(urb.buf, src.start, min(src.num_bytes, urb.size)); + if (urb.itransfer) urb.itransfer->transferred = src.num_bytes; + }, + + /* produce out content of isochronous packet i */ + [] (Urb &urb, uint32_t i, Byte_range_ptr &dst) { + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(urb.itransfer); + if (IS_XFEROUT(transfer)) { + addr_t src = (addr_t)urb.buf+isoc_packet_offset(i, transfer); + memcpy(dst.start, (void*)src, min(transfer->iso_packet_desc[i].length, + dst.num_bytes)); + } + return transfer->iso_packet_desc[i].length; + }, + + /* consume in results of isochronous packet i */ + [] (Urb &urb, uint32_t i, Const_byte_range_ptr const &src) { + struct libusb_transfer *transfer = + USBI_TRANSFER_TO_LIBUSB_TRANSFER(urb.itransfer); + addr_t dst = (addr_t)urb.buf+isoc_packet_offset(i, transfer); + memcpy((void*)dst, src.start, src.num_bytes); + transfer->iso_packet_desc[i].actual_length = src.num_bytes; + transfer->iso_packet_desc[i].status = LIBUSB_TRANSFER_COMPLETED; + }, + + /* complete USB request */ + [this] (Urb &urb, Usb::Tagged_packet::Return_value v) + { + if (v != Usb::Tagged_packet::OK) + error("transfer failed, return value ", (int)v); + + if (!urb.itransfer) + return; + + libusb_context *ctx = _device._open ? ITRANSFER_CTX(urb.itransfer) + : nullptr; + usbi_signal_transfer_completion(urb.itransfer); + if (ctx) usbi_signal_event(ctx); + destroy(_device._iface_slab, &urb); + }); +} + + +void Usb_device::_wait_for_urb(Urb &urb) +{ + while (!urb.completed()) { + handle_events(); + + struct pollfd pollfds { + .fd = vfs_libusb_fd, + .events = POLLIN, + .revents = 0 + }; + + int poll_result = poll(&pollfds, 1, -1); + if ((poll_result != 1) || !(pollfds.revents & POLLIN)) + error("could not complete request"); + } +} + + +void Usb_device::handle_events() +{ + _device.update_urbs( + + /* produce out content */ + [] (Urb &urb, Byte_range_ptr &dst) { + memcpy(dst.start, urb.buf, min(dst.num_bytes, urb.size)); }, + + /* consume in results */ + [] (Urb &urb, Const_byte_range_ptr const &src) { + memcpy(urb.buf, src.start, min(src.num_bytes, urb.size)); + if (urb.itransfer) urb.itransfer->transferred = src.num_bytes; + }, + + /* complete USB request */ + [this] (Urb &urb, Usb::Tagged_packet::Return_value v) + { + if (v != Usb::Tagged_packet::OK) + error("control transfer failed, return value ", (int)v); + + if (!urb.itransfer) + return; + + libusb_context *ctx = _open ? ITRANSFER_CTX(urb.itransfer) + : nullptr; + usbi_signal_transfer_completion(urb.itransfer); + if (ctx) usbi_signal_event(ctx); + destroy(_alloc, &urb); + }); +} + + +Constructible& Usb_device::singleton() +{ + static Constructible dev {}; + return dev; +} + + +static Usb_device& device() +{ + struct Libusb_not_initialized {}; + + if (!Usb_device::singleton().constructed()) + throw Libusb_not_initialized(); + + return *Usb_device::singleton(); +} + + +void libusb_genode_backend_init(Env &env, Allocator &alloc, + Signal_context_capability handler) +{ + Usb_device::singleton().construct(env, alloc, handler); } static int genode_init(struct libusb_context* ctx) { - if (!device_instance) { + if (vfs_libusb_fd == -1) { vfs_libusb_fd = open("/dev/libusb", O_RDONLY); if (vfs_libusb_fd == -1) { Genode::error("could not open /dev/libusb"); return LIBUSB_ERROR_OTHER; } - device_instance = new (libc_alloc) Usb_device(usb_connection, - vfs_libusb_fd); } else { Genode::error("tried to init genode usb context twice"); return LIBUSB_ERROR_OTHER; @@ -305,11 +276,11 @@ static int genode_init(struct libusb_context* ctx) static void genode_exit(void) { - if (device_instance) { - destroy(libc_alloc, device_instance); - device_instance = nullptr; + Usb_device::singleton().constructed(); + Usb_device::singleton().destruct(); + + if (vfs_libusb_fd != -1) { close(vfs_libusb_fd); - usb_connection = nullptr; vfs_libusb_fd = -1; } } @@ -341,27 +312,7 @@ int genode_get_device_list(struct libusb_context *ctx, /* initialize device structure */ dev->bus_number = busnum; dev->device_address = devaddr; - - Usb_device *usb_device = device_instance; - *(Usb_device**)dev->os_priv = usb_device; - - switch (usb_device->device_descriptor.speed) { - case Usb::Device::SPEED_LOW: - dev->speed = LIBUSB_SPEED_LOW; - break; - case Usb::Device::SPEED_FULL: - dev->speed = LIBUSB_SPEED_FULL; - break; - case Usb::Device::SPEED_HIGH: - dev->speed = LIBUSB_SPEED_HIGH; - break; - case Usb::Device::SPEED_SUPER: - dev->speed = LIBUSB_SPEED_SUPER; - break; - default: - Genode::warning(__PRETTY_FUNCTION__, ": unknown device speed"); - dev->speed = LIBUSB_SPEED_UNKNOWN; - } + dev->speed = device()._speed; int result = usbi_sanitize_device(dev); if (result < 0) { @@ -386,9 +337,7 @@ int genode_get_device_list(struct libusb_context *ctx, static int genode_open(struct libusb_device_handle *dev_handle) { - if (device_instance) - device_instance->open(); - + device().open(); return usbi_add_pollfd(HANDLE_CTX(dev_handle), vfs_libusb_fd, POLLIN); } @@ -396,47 +345,47 @@ static int genode_open(struct libusb_device_handle *dev_handle) static void genode_close(struct libusb_device_handle *dev_handle) { - if (device_instance) - device_instance->close(); - + device().close(); usbi_remove_pollfd(HANDLE_CTX(dev_handle), vfs_libusb_fd); } -static int genode_get_device_descriptor(struct libusb_device *device, +static int genode_get_device_descriptor(struct libusb_device *, unsigned char* buffer, int *host_endian) { - Usb_device *usb_device = *(Usb_device**)device->os_priv; - - Genode::memcpy(buffer, &usb_device->device_descriptor, - sizeof(libusb_device_descriptor)); - + Usb_device::Urb urb(buffer, sizeof(libusb_device_descriptor), + device()._device, LIBUSB_REQUEST_GET_DESCRIPTOR, + LIBUSB_ENDPOINT_IN, (LIBUSB_DT_DEVICE << 8) | 0, 0, + LIBUSB_DT_DEVICE_SIZE); + device()._wait_for_urb(urb); *host_endian = 0; - return LIBUSB_SUCCESS; } -static int genode_get_config_descriptor(struct libusb_device *device, - uint8_t config_index, +static int genode_get_config_descriptor(struct libusb_device *, + uint8_t idx, unsigned char *buffer, size_t len, int *host_endian) { - if (config_index != 0) { - Genode::error(__PRETTY_FUNCTION__, - ": only the first configuration is supported"); - return LIBUSB_ERROR_NOT_SUPPORTED; - } + /* read minimal config descriptor */ + genode_usb_config_descriptor desc; + Usb_device::Urb cfg(&desc, sizeof(desc), device()._device, + LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_ENDPOINT_IN, + (LIBUSB_DT_CONFIG << 8) | idx, 0, sizeof(desc)); + device()._wait_for_urb(cfg); - Usb_device *usb_device = *(Usb_device**)device->os_priv; - - Genode::memcpy(buffer, usb_device->raw_config_descriptor, len); + /* read again whole configuration */ + Usb_device::Urb all(buffer, len, device()._device, + LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_ENDPOINT_IN, + (LIBUSB_DT_CONFIG << 8) | idx, 0, + (size_t)desc.total_length); + device()._wait_for_urb(all); *host_endian = 0; - - return len; + return desc.total_length; } @@ -445,7 +394,6 @@ static int genode_get_active_config_descriptor(struct libusb_device *device, size_t len, int *host_endian) { - /* only configuration 0 is currently supported */ return genode_get_config_descriptor(device, 0, buffer, len, host_endian); } @@ -464,21 +412,24 @@ static int genode_set_configuration(struct libusb_device_handle *dev_handle, static int genode_claim_interface(struct libusb_device_handle *dev_handle, int interface_number) { - Usb_device *usb_device = *(Usb_device**)dev_handle->dev->os_priv; + bool found = false; - try { - usb_device->usb_connection->claim_interface(interface_number); - } catch (Usb::Session::Interface_not_found) { - Genode::error(__PRETTY_FUNCTION__, ": interface not found"); - return LIBUSB_ERROR_NOT_FOUND; - } catch (Usb::Session::Interface_already_claimed) { - Genode::error(__PRETTY_FUNCTION__, ": interface already claimed"); + device()._interfaces.for_each([&] (Usb_device::Interface const &iface) { + if (iface.index().number == interface_number) found = true; }); + + if (found) { + error(__PRETTY_FUNCTION__, ": interface already claimed"); return LIBUSB_ERROR_BUSY; - } catch (...) { - Genode::error(__PRETTY_FUNCTION__, ": unknown exception"); + } + + if (interface_number > 0xff || interface_number < 0) { + error(__PRETTY_FUNCTION__, ": invalid interface number ", + interface_number); return LIBUSB_ERROR_OTHER; } + new (device()._alloc) + Usb_device::Interface(device(), (uint8_t) interface_number); return LIBUSB_SUCCESS; } @@ -486,19 +437,16 @@ static int genode_claim_interface(struct libusb_device_handle *dev_handle, static int genode_release_interface(struct libusb_device_handle *dev_handle, int interface_number) { - Usb_device *usb_device = *(Usb_device**)dev_handle->dev->os_priv; + int ret = LIBUSB_ERROR_NOT_FOUND; - try { - usb_device->usb_connection->release_interface(interface_number); - } catch (Usb::Session::Interface_not_found) { - Genode::error(__PRETTY_FUNCTION__, ": interface not found"); - return LIBUSB_ERROR_NOT_FOUND; - } catch (...) { - Genode::error(__PRETTY_FUNCTION__, ": unknown exception"); - return LIBUSB_ERROR_OTHER; - } + device()._interfaces.for_each([&] (Usb_device::Interface &iface) { + if (iface.index().number != interface_number) + return; + destroy(device()._alloc, &iface); + ret = LIBUSB_SUCCESS; + }); - return LIBUSB_SUCCESS; + return ret; } @@ -506,10 +454,19 @@ static int genode_set_interface_altsetting(struct libusb_device_handle* dev_hand int interface_number, int altsetting) { - Usb_device *usb_device = *(Usb_device**)dev_handle->dev->os_priv; + using P = Usb::Device::Packet_descriptor; + using Rt = P::Request_type; - return usb_device->altsetting(interface_number, altsetting) ? LIBUSB_SUCCESS - : LIBUSB_ERROR_BUSY; + if (interface_number < 0 || interface_number > 0xff || + altsetting < 0 || altsetting > 0xff) + return LIBUSB_ERROR_INVALID_PARAM; + + Usb_device::Urb urb(nullptr, 0, device()._device, P::Request::SET_INTERFACE, + Rt::value(P::Recipient::IFACE, P::Type::STANDARD, + P::Direction::OUT), + (uint8_t)altsetting, (uint8_t)interface_number, 0); + device()._wait_for_urb(urb); + return LIBUSB_SUCCESS; } @@ -517,11 +474,8 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - - Usb_device *usb_device = *(Usb_device**)transfer->dev_handle->dev->os_priv; - - if (!usb_device->usb_connection->source()->ready_to_submit()) - return LIBUSB_ERROR_BUSY; + Usb::Interface::Packet_descriptor::Type type = + Usb::Interface::Packet_descriptor::FLUSH; switch (transfer->type) { @@ -530,123 +484,49 @@ static int genode_submit_transfer(struct usbi_transfer * itransfer) struct libusb_control_setup *setup = (struct libusb_control_setup*)transfer->buffer; - Usb::Packet_descriptor p; - - try { - p = usb_device->usb_connection->alloc_packet(setup->wLength); - } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { - return LIBUSB_ERROR_BUSY; - } - - p.completion = new (libc_alloc) Completion(itransfer); - - p.type = Usb::Packet_descriptor::CTRL; - p.control.request = setup->bRequest; - p.control.request_type = setup->bmRequestType; - p.control.value = setup->wValue; - p.control.index = setup->wIndex; - p.control.timeout = transfer->timeout; - - if ((setup->bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == - LIBUSB_ENDPOINT_OUT) { - - char *packet_content = - usb_device->usb_connection->source()->packet_content(p); - - Genode::memcpy(packet_content, - transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, - setup->wLength); - } - - usb_device->usb_connection->source()->submit_packet(p); - + void * addr = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; + new (device()._alloc) + Usb_device::Urb(addr, setup->wLength, itransfer, + device()._device, setup->bRequest, + setup->bmRequestType, setup->wValue, + setup->wIndex, setup->wLength); + device().handle_events(); return LIBUSB_SUCCESS; } case LIBUSB_TRANSFER_TYPE_BULK: case LIBUSB_TRANSFER_TYPE_BULK_STREAM: - case LIBUSB_TRANSFER_TYPE_INTERRUPT: { - - if (IS_XFEROUT(transfer) && - transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) { - Genode::error(__PRETTY_FUNCTION__, - ": zero packet not supported"); - return LIBUSB_ERROR_NOT_SUPPORTED; - } - - Usb::Packet_descriptor p; - - try { - p = usb_device->usb_connection->alloc_packet(transfer->length); - } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { - return LIBUSB_ERROR_BUSY; - } - - if (transfer->type == LIBUSB_TRANSFER_TYPE_INTERRUPT) { - p.type = Usb::Packet_descriptor::IRQ; - p.transfer.polling_interval = - Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL; - } else - p.type = Usb::Packet_descriptor::BULK; - - p.completion = new (libc_alloc) Completion(itransfer); - p.transfer.ep = transfer->endpoint; - - if (IS_XFEROUT(transfer)) { - char *packet_content = - usb_device->usb_connection->source()->packet_content(p); - Genode::memcpy(packet_content, transfer->buffer, - transfer->length); - } - - usb_device->usb_connection->source()->submit_packet(p); - - return LIBUSB_SUCCESS; - } - - case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: { - - size_t total_length = 0; - for (int i = 0; i < transfer->num_iso_packets; i++) { - total_length += transfer->iso_packet_desc[i].length; - } - - Usb::Packet_descriptor p; - size_t p_size = Usb::Isoc_transfer::size(total_length); - try { - p = usb_device->usb_connection->alloc_packet(p_size); - } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { - return LIBUSB_ERROR_BUSY; - } - - p.type = Usb::Packet_descriptor::ISOC; - p.transfer.polling_interval = - Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL; - - p.completion = new (libc_alloc) Completion(itransfer); - p.transfer.ep = transfer->endpoint; - - Usb::Isoc_transfer &isoc = - *(Usb::Isoc_transfer *) usb_device->usb_connection->source()->packet_content(p); - isoc.number_of_packets = transfer->num_iso_packets; - for (int i = 0; i < transfer->num_iso_packets; i++) { - isoc.packet_size[i] = transfer->iso_packet_desc[i].length; - isoc.actual_packet_size[i] = 0; - } - - if (IS_XFEROUT(transfer)) - Genode::memcpy(isoc.data(), transfer->buffer, transfer->length); - - usb_device->usb_connection->source()->submit_packet(p); - - return LIBUSB_SUCCESS; - } + type = Usb::Interface::Packet_descriptor::BULK; + break; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + type = Usb::Interface::Packet_descriptor::IRQ; + break; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + type = Usb::Interface::Packet_descriptor::ISOC; + break; default: usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); return LIBUSB_ERROR_INVALID_PARAM; } + + bool found = false; + device()._interfaces.for_each([&] (Usb_device::Interface &iface) { + iface.for_each_endpoint([&] (Usb::Endpoint &ep) { + if (found || transfer->endpoint != ep.address()) + return; + found = true; + new (device()._iface_slab) + Usb_device::Interface::Urb(transfer->buffer, transfer->length, + itransfer, iface, ep, type, + transfer->length, + transfer->num_iso_packets); + iface.handle_events(); + }); + }); + + return found ? LIBUSB_SUCCESS : LIBUSB_ERROR_NOT_FOUND; } @@ -659,16 +539,14 @@ static int genode_cancel_transfer(struct usbi_transfer * itransfer) static void genode_clear_transfer_priv(struct usbi_transfer * itransfer) { } -static int genode_handle_events(struct libusb_context *, - struct pollfd *, - POLL_NFDS_TYPE, int) +static int genode_handle_events(struct libusb_context *, struct pollfd *, + POLL_NFDS_TYPE, int) { - if (device_instance) { - device_instance->handle_events(); - return LIBUSB_SUCCESS; - } - - return LIBUSB_ERROR_NO_DEVICE; + libusb_genode_backend_signaling = false; + device().handle_events(); + device()._interfaces.for_each([&] (Usb_device::Interface &iface) { + iface.handle_events(); }); + return LIBUSB_SUCCESS; } @@ -679,8 +557,7 @@ static int genode_handle_transfer_completion(struct usbi_transfer * itransfer) if (itransfer->flags & USBI_TRANSFER_CANCELLING) status = LIBUSB_TRANSFER_CANCELLED; - return usbi_handle_transfer_completion(itransfer, - status); + return usbi_handle_transfer_completion(itransfer, status); } @@ -743,7 +620,7 @@ const struct usbi_os_backend genode_usb_raw_backend = { /*.get_timerfd_clockid =*/ NULL, #endif - /*.device_priv_size =*/ sizeof(Usb_device*), + /*.device_priv_size =*/ 0, /*.device_handle_priv_size =*/ 0, /*.transfer_priv_size =*/ 0, }; diff --git a/repos/libports/src/lib/qemu-usb/host.cc b/repos/libports/src/lib/qemu-usb/host.cc index 4d8a8fed02..a4efab0113 100644 --- a/repos/libports/src/lib/qemu-usb/host.cc +++ b/repos/libports/src/lib/qemu-usb/host.cc @@ -2,867 +2,877 @@ * \brief USB session back end * \author Josef Soentgen * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2015-12-18 */ #include #include #include -#include -#include +#include +#include +#include #include -#include #include #include #include +#include #include - using namespace Genode; -static bool const verbose_devices = false; -static bool const verbose_host = false; -static bool const verbose_warnings = false; Mutex _mutex; -static void update_ep(USBDevice *, uint8_t, uint8_t); -static bool claim_interfaces(USBDevice *); -static void reset_alt_settings(USBDevice *); +using handle_t = unsigned long; -using Packet_alloc_failed = Usb::Session::Tx::Source::Packet_alloc_failed; -using Packet_type = Usb::Packet_descriptor::Type; -using Packet_error = Usb::Packet_descriptor::Error; +class Device; +class Interface; +class Endpoint; -static unsigned endpoint_number(USBEndpoint const *usb_ep) + +class Urb : Usb::Endpoint, public Usb::Interface::Urb, + Registry::Element { - bool in = usb_ep->pid == USB_TOKEN_IN; - return usb_ep->nr | (in ? USB_DIR_IN : 0); - -} - -class Isoc_packet : Fifo::Element -{ - friend class Fifo; - friend class Usb_host_device; - private: - Usb::Packet_descriptor _packet; - int _offset { 0 }; - char *_content; - int _size; + friend class ::Interface; + using Pdesc = Usb::Interface::Packet_descriptor; - int _packet_index { 0 }; - unsigned _packet_in_offset { 0 }; - Usb::Isoc_transfer *_isoc { (Usb::Isoc_transfer *)_content }; + static Pdesc::Type _type(uint8_t t) + { + switch(t) { + case USB_ENDPOINT_XFER_BULK: return Pdesc::BULK; + case USB_ENDPOINT_XFER_INT: return Pdesc::IRQ; + case USB_ENDPOINT_XFER_ISOC: return Pdesc::ISOC; + default: return Pdesc::FLUSH; + }; + return Pdesc::FLUSH; + } + + ::Endpoint &_endpoint; + USBPacket * const _packet { nullptr }; + bool _canceled { false }; public: - Isoc_packet(Usb::Packet_descriptor packet, char *content) + Urb(Registry ®istry, + ::Interface &iface, + ::Endpoint &endp, + uint8_t type, + size_t size, + USBPacket *packet); + + Urb(Registry ®istry, + ::Interface &iface, + ::Endpoint &endp, + uint8_t type, + size_t size, + uint32_t isoc_packets); + + bool isoc() const { return Usb::Interface::Urb::_type == Pdesc::ISOC; } + void cancel() { _canceled = true; } + bool canceled() const { return _canceled; } + USBPacket *packet() const { return _packet; } + + size_t read_cache(Byte_range_ptr &dst); + void write_cache(Const_byte_range_ptr const &src); + void destroy(); +}; + + +class Isoc_cache +{ + public: + + enum { + PACKETS_PER_URB = 32, + URBS = 4, + MAX_PACKETS = 256, + }; + + private: + + ::Interface &_iface; + Endpoint &_ep; + Allocator &_alloc; + uint8_t _read { 0 }; + uint8_t _wrote { 0 }; + unsigned char *_buffer; + + Constructible _urbs[URBS]; + + void _new_urb(); + + uint8_t _level() const; + + void _copy_to_host(USBPacket *p); + void _copy_to_guest(USBPacket *p); + + public: + + Isoc_cache(::Interface &iface, Endpoint &ep, Allocator &alloc); + + void handle(USBPacket *p); + size_t read(Byte_range_ptr &dst); + void write(Const_byte_range_ptr const &src); + void destroy(Urb * urb); + void flush(); +}; + + +class Endpoint : public List_model::Element +{ + private: + + uint8_t const _address; + uint8_t const _attributes; + uint8_t const _max_packet_size; + Constructible _isoc_cache { }; + + public: + + Endpoint(Xml_node const &n, Allocator &alloc, ::Interface &iface) : - _packet(packet), _content(content), - _size (_packet.read_transfer() ? _packet.transfer.actual_size : _packet.size()) - { } - - Isoc_packet() - : _packet { Usb::Packet_descriptor() }, _content { nullptr }, _size { 0 } - { } - - bool copy(USBPacket *usb_packet) + _address(n.attribute_value("address", 0xff)), + _attributes(n.attribute_value("attributes", 0xff)), + _max_packet_size(n.attribute_value("max_packet_size", 0)) { - if (!valid()) return false; - - unsigned remaining = _size - _offset; - int copy_size = min(usb_packet->iov.size, remaining); - - usb_packet_copy(usb_packet, _isoc->data() + _offset, copy_size); - - _offset += copy_size; - - if (!_packet.read_transfer()) { - _isoc->packet_size[_isoc->number_of_packets] = copy_size; - _isoc->number_of_packets++; - } - - return remaining <= usb_packet->iov.size; + if ((_attributes&0x3) == Usb::Endpoint::ISOC) + _isoc_cache.construct(iface, *this, alloc); } - bool copy_read(USBPacket *usb_packet) - { - if (!valid()) return false; + uint8_t address() const { return _address; } + uint8_t attributes() const { return _attributes; } + uint8_t max_packet_size() const { return _max_packet_size; } - unsigned remaining = _isoc->actual_packet_size[_packet_index]; - /* this should not happen as there asserts in the qemu code */ - if (remaining > usb_packet->iov.size) { - Genode::error("iov too small, ignoring packet content"); - } - int copy_size = min(usb_packet->iov.size, remaining); + bool matches(Xml_node const &node) const { + return address() == node.attribute_value("address", 0xff); } - char *src = _isoc->data() + _packet_in_offset; - usb_packet_copy(usb_packet, src, copy_size); + static bool type_matches(Xml_node const &node) { + return node.has_type("endpoint"); } - _packet_in_offset += _isoc->packet_size[_packet_index]; - ++_packet_index; + bool in() const { return address() & (1<<7); } - return _packet_index == _isoc->number_of_packets; - } + void handle_isoc_packet(USBPacket *p) { + if (_isoc_cache.constructed()) _isoc_cache->handle(p); } - bool valid() const { return _content != nullptr; } + size_t read_cache(Byte_range_ptr &dst) { + return (_isoc_cache.constructed()) ? _isoc_cache->read(dst) : 0; } - unsigned packet_count() const - { - if (!valid()) return 0; + void write_cache(Const_byte_range_ptr const &src) { + if (_isoc_cache.constructed()) _isoc_cache->write(src); } - return _isoc->number_of_packets; - } + void destroy_urb(Urb * urb) { + if (_isoc_cache.constructed()) _isoc_cache->destroy(urb); } - Usb::Packet_descriptor& packet() { return _packet; } + void flush() { + if (_isoc_cache.constructed()) _isoc_cache->flush(); } }; -struct Completion : Usb::Completion +class Interface : public List_model<::Interface>::Element { - USBPacket *p = nullptr; - USBDevice *dev = nullptr; - uint8_t *data = nullptr; - unsigned endpoint = 0; + private: - enum State { VALID, FREE, CANCELED }; - State state = FREE; + friend class Urb; - void complete(Usb::Packet_descriptor &p) override { } + Device &_device; + Constructible _iface {}; + List_model _endpoints {}; + uint8_t const _number; + uint8_t const _alt_setting; + bool _active; + size_t _buf_size { 2 * 1024 * 1024 }; - void complete(Usb::Packet_descriptor &packet, char *content) - { - if (state != VALID) { - return; + Usb::Interface &_session(); + + public: + + Interface(Device &device, Xml_node const &n) + : + _device(device), + _number(n.attribute_value("number", 0xff)), + _alt_setting(n.attribute_value("alt_setting", 0xff)), + _active(n.attribute_value("active", false)) {} + + void destroy_all_urbs(); + + uint8_t number() const { return _number; }; + uint8_t alt_setting() const { return _alt_setting; }; + bool active() const { return _active; } + + bool matches(Xml_node const &n) const + { + uint8_t nr = n.attribute_value("number", 0xff); + uint8_t alt = n.attribute_value("alt_setting", 0xff); + return _number == nr && _alt_setting == alt; } - int actual_size = 0; + static bool type_matches(Xml_node const &node) { + return node.has_type("interface"); } - switch (packet.type) { - case Packet_type::CTRL: - actual_size = packet.control.actual_size; + void update(Allocator &alloc, Xml_node const &node) + { + _active = node.attribute_value("active", false); + _endpoints.update_from_xml(node, - /* - * Disable remote wakeup (bit 5) in 'bmAttributes' (content[7]) in reported - * configuration descriptor. On some systems (e.g., Windows) this will - * cause devices to stop working. - */ - if (packet.control.request == USB_REQ_GET_DESCRIPTOR - && actual_size >= 8 && content[1] == USB_DT_CONFIG) - content[7] &= ~USB_CFG_ATT_WAKEUP; + /* create */ + [&] (Xml_node const &node) -> Endpoint & { + return *new (alloc) Endpoint(node, alloc, *this); }, - break; - case Packet_type::BULK: - case Packet_type::IRQ: - actual_size = packet.transfer.actual_size; - default: - break; + /* destroy */ + [&] (Endpoint &endp) { + destroy(alloc, &endp); }, + + /* update */ + [&] (Endpoint &, Xml_node const &) { } + ); } - if (actual_size < 0) actual_size = 0; + void update_urbs(); - if (verbose_host) - log(__func__, ": packet: ", p, " packet.type: ", (int)packet.type, " " - "actual_size: ", Hex(actual_size)); - - p->actual_length = 0; - - if (packet.succeded) { - - if (p->pid == USB_TOKEN_IN && actual_size > 0) { - if (data) Genode::memcpy(data, content, actual_size); - else usb_packet_copy(p, content, actual_size); - } - - p->actual_length = actual_size; - p->status = USB_RET_SUCCESS; - } - else { - if (packet.error == Packet_error::STALL_ERROR) - p->status = USB_RET_STALL; - else - p->status = USB_RET_IOERROR; + template + void with_endpoint(uint8_t index, FN const &fn) + { + _endpoints.for_each([&] (Endpoint &endp) { + if (endp.address() == index) fn(endp); + }); } - switch (packet.type) { - case Packet_type::CONFIG: - if (!claim_interfaces(dev)) - p->status = USB_RET_IOERROR; - else - reset_alt_settings(dev); - - usb_generic_async_ctrl_complete(dev, p); - break; - case Packet_type::ALT_SETTING: - update_ep(dev, packet.interface.number, packet.interface.alt_setting); - usb_generic_async_ctrl_complete(dev, p); - break; - case Packet_type::CTRL: - usb_generic_async_ctrl_complete(dev, p); - break; - case Packet_type::BULK: - case Packet_type::IRQ: - usb_packet_complete(dev, p); - break; - default: - break; - } - } - - bool valid() const { return state == VALID; } - void cancel() { state = CANCELED; } - void free() { state = FREE; } - unsigned ep() const { return p ? endpoint_number(p->ep) : endpoint; } + template + void for_each_endpoint(FN const &fn) { + _endpoints.for_each([&] (Endpoint &endp) { fn(endp); }); } }; -/** - * Helper for the formatted output and device info - */ -struct Dev_info +class Device : public List_model::Element { - Session_label const label; + public: - Dev_info(Session_label &label) : label(label) { } + using Name = String<64>; + using Speed = String<32>; - void print(Output &out) const - { - Genode::print(out, label); - } + private: - bool operator != (Dev_info const &other) const - { - if (label.length() && other.label.length()) - return label != other.label; + Name const _name; + Speed const _speed; + Id_space::Element const _elem; + Usb::Device _device; + Signal_context_capability _sigh_cap; + USBHostDevice *_qemu_device { nullptr }; + List_model<::Interface> _ifaces {}; - return true; - } + /** + * Noncopyable + */ + Device(Device const &); + Device &operator = (Device const &); + + public: + + struct Urb : Usb::Device::Urb + { + using Request_type = + Usb::Device::Packet_descriptor::Request_type::access_t; + + USBPacket * const _packet; + + Urb(Device &device, + uint8_t request, + uint8_t request_type, + uint16_t value, + uint16_t index, + size_t size, + USBPacket *packet) + : + Usb::Device::Urb(device._device, request, + (Request_type)request_type, + value, index, size), + _packet(packet) { } + }; + + Device(Name &name, + Speed &speed, + Usb::Connection &usb, + Allocator &alloc, + Region_map &rm, + Id_space &space, + Signal_context_capability cap) + : + _name(name), + _speed(speed), + _elem(*this, space), + _device(usb, alloc, rm, name), + _sigh_cap(cap) + { + _device.sigh(_sigh_cap); + } + + ~Device(); + + Usb::Device &session() { return _device; } + + Name name() { return _name; } + + int speed() + { + if (_speed == "low") return USB_SPEED_LOW; + if (_speed == "full") return USB_SPEED_FULL; + if (_speed == "high") return USB_SPEED_HIGH; + if (_speed == "super") return USB_SPEED_SUPER; + if (_speed == "super_plus") return USB_SPEED_SUPER; + if (_speed == "super_plus_2x2") return USB_SPEED_SUPER; + return USB_SPEED_FULL; + } + + Signal_context_capability sigh_cap() { return _sigh_cap; } + + handle_t handle() const { return _elem.id().value; } + + void qemu_device(USBHostDevice *dev) { _qemu_device = dev; } + USBHostDevice* qemu_device() { return _qemu_device; } + + bool matches(Xml_node const &node) const { + return _name == node.attribute_value("name", Name()); } + + static bool type_matches(Xml_node const &node) { + return node.has_type("device"); } + + void update(Allocator &alloc, Xml_node const &node) + { + Xml_node active_config = node; + + node.for_each_sub_node("config", [&] (Xml_node const &node) { + if (node.attribute_value("active", false)) + active_config = node; + }); + + _ifaces.update_from_xml(active_config, + + /* create */ + [&] (Xml_node const &node) -> ::Interface & { + return *new (alloc) ::Interface(*this, node); }, + + /* destroy */ + [&] (::Interface &iface) { + /* first clean up urbs before isoc-caches get destroyed */ + iface.destroy_all_urbs(); + iface.update(alloc, Xml_node("")); + destroy(alloc, &iface); }, + + /* update */ + [&] (::Interface &iface, Xml_node const &node) { + iface.update(alloc, node); } + ); + } + + void update_urbs(); + + template + void with_active_interfaces(FN const &fn) + { + _ifaces.for_each([&] (::Interface &iface) { + if (iface.active()) fn(iface); }); + } }; -struct Usb_host_device : List::Element +struct Session { - Usb_host_device(const Usb_host_device&); - const Usb_host_device& operator=(const Usb_host_device&); + Env &_env; + Allocator &_alloc; + Signal_context_capability _handler_cap; + Usb::Connection _usb { _env }; + List_model _model {}; + Id_space _space {}; + Registry _urb_registry {}; - struct Could_not_create_device : Exception { }; - - bool deleted = false; - char const *label = nullptr; - Dev_info const info; - - USBHostDevice *qemu_dev { nullptr }; - - /* submit queue + ack queue + 1 -> max nr of packets in flight */ - enum { NUM_COMPLETIONS = Usb::Session::TX_QUEUE_SIZE * 2 + 1 }; - Completion completion[NUM_COMPLETIONS]; - - Fifo isoc_read_queue { }; - Reconstructible isoc_write_packet { }; - - Genode::Ring_buffer _isoch_out_queue { }; - unsigned _isoch_out_pending { 0 }; - - Entrypoint &_ep; - Signal_handler state_dispatcher { _ep, *this, &Usb_host_device::state_change }; - - Allocator &_alloc; - Allocator_avl _usb_alloc { &_alloc }; - Usb::Connection usb_raw; //{ &_usb_alloc, label, 1024*1024, state_dispatcher }; - - Signal_handler ack_avail_dispatcher { _ep, *this, &Usb_host_device::ack_avail }; - - void _release_interfaces() - { - Usb::Config_descriptor cdescr; - Usb::Device_descriptor ddescr; - - try { usb_raw.config_descriptor(&ddescr, &cdescr); } - catch (Usb::Session::Device_not_found) { return; } - - for (unsigned i = 0; i < cdescr.num_interfaces; i++) { - try { usb_raw.release_interface(i); } - catch (Usb::Session::Device_not_found) { return; } - catch (Usb::Session::Interface_not_found) { return; } - } - } - - bool _claim_interfaces() - { - Usb::Config_descriptor cdescr; - Usb::Device_descriptor ddescr; - - try { usb_raw.config_descriptor(&ddescr, &cdescr); } - catch (Usb::Session::Device_not_found) { return false; } - - bool result = true; - for (unsigned i = 0; i < cdescr.num_interfaces; i++) { - try { - usb_raw.claim_interface(i); - } catch (Usb::Session::Interface_already_claimed) { - result = false; - } catch (Usb::Session::Interface_not_found) { } - } - - if (!result) error("device already claimed"); - - return result; - } - - Usb_host_device(Entrypoint &ep, Allocator &alloc, Env &env, Dev_info info) + Session(Env &env, Allocator &alloc, + Signal_context_capability io_handler_cap, + Signal_context_capability rom_handler_cap) : - _alloc(alloc), - usb_raw(env, &_usb_alloc, info.label.string(), 6*1024*1024, state_dispatcher), - info(info), _ep(ep) + _env(env), _alloc(alloc), _handler_cap(io_handler_cap) { - usb_raw.tx_channel()->sigh_ack_avail(ack_avail_dispatcher); - - if (!_claim_interfaces()) - throw Could_not_create_device(); - - qemu_dev = create_usbdevice(this); - - if (!qemu_dev) - throw Could_not_create_device(); + _usb.sigh(rom_handler_cap); } - ~Usb_host_device() + ~Session() { + _model.for_each([&] (Device &dev) { destroy(_alloc, &dev); }); } + + void update() { - isoc_in_flush(0, true); - } - - static int to_qemu_speed(unsigned speed) - { - switch (speed) { - case Usb::Device::SPEED_LOW: return USB_SPEED_LOW; - case Usb::Device::SPEED_FULL: return USB_SPEED_FULL; - case Usb::Device::SPEED_HIGH: return USB_SPEED_HIGH; - case Usb::Device::SPEED_SUPER: return USB_SPEED_SUPER; - default: return 0; - } - } - - void ack_avail() - { - Mutex::Guard guard(_mutex); - - /* we are already dead, do nothing */ - if (deleted == true) return; - - while (usb_raw.source()->ack_avail()) { - Usb::Packet_descriptor packet = usb_raw.source()->get_acked_packet(); - Completion *c = dynamic_cast(packet.completion); - - if ((packet.type == Packet_type::ISOC && !packet.read_transfer()) || - (c && c->state == Completion::CANCELED)) { - free_packet(packet); - continue; - } - - char *content = usb_raw.source()->packet_content(packet); - - if (packet.type != Packet_type::ISOC) { - if (c) c->complete(packet, content); - free_packet(packet); - } else { - /* isochronous in */ - free_completion(packet); - Isoc_packet *new_packet = new (_alloc) - Isoc_packet(packet, content); - isoc_read_queue.enqueue(*new_packet); - } - } - } - - - /********************************** - ** Isochronous packet handling ** - **********************************/ - - bool isoc_read(USBPacket *packet) - { - if (isoc_read_queue.empty()) - return false; - - isoc_read_queue.head([&] (Isoc_packet &head) { - if (head.copy_read(packet)) { - isoc_read_queue.remove(head); - free_packet(&head); - } - }); - - return true; - } - - unsigned _isoc_in_pending = 0; - - bool isoc_new_packet() - { - return _isoc_in_pending < 32 ? true : false; - } - - void isoc_in_packet(USBPacket *usb_packet) - { - enum { NUMBER_OF_PACKETS = 1 }; - isoc_read(usb_packet); - - if (!isoc_new_packet()) { - return; - } - - size_t const payload_size = usb_packet->ep->max_packet_size * NUMBER_OF_PACKETS; - size_t const packet_size = Usb::Isoc_transfer::size(payload_size); - try { - Usb::Packet_descriptor packet = alloc_packet(packet_size, true); - packet.type = Packet_type::ISOC; - packet.transfer.ep = usb_packet->ep->nr | USB_DIR_IN; - packet.transfer.polling_interval = Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL; - - Usb::Isoc_transfer &isoc = - *(Usb::Isoc_transfer *)usb_raw.source()->packet_content(packet); - isoc.number_of_packets = NUMBER_OF_PACKETS; - for (unsigned i = 0; i < NUMBER_OF_PACKETS; i++) - isoc.packet_size[i] = usb_packet->ep->max_packet_size; - - Completion *c = dynamic_cast(packet.completion); - c->p = nullptr; - c->dev = usb_packet->ep->dev; - c->data = nullptr; - c->endpoint = endpoint_number(usb_packet->ep); - - _isoc_in_pending++; - submit(packet); - } catch (Packet_alloc_failed) { - if (verbose_warnings) - warning("xHCI: packet allocation failed (size ", Hex(packet_size), "in ", __func__, ")"); - } - } - - void isoc_in_flush(unsigned ep, bool all = false) - { - /* flush finished and stored data */ - isoc_read_queue.for_each([&] (Isoc_packet &packet) { - if (!all && (!packet.valid() || packet.packet().transfer.ep != ep)) { - return; - } - - isoc_read_queue.remove(packet); - free_packet(&packet); - }); - - /* flush in flight packets */ - flush_completion(ep); - } - - void isoc_out_packet(USBPacket *usb_packet) - { - enum { NUMBER_OF_PACKETS = 32 }; - - bool const valid = isoc_write_packet->valid(); - if (valid) { - isoc_write_packet->copy(usb_packet); - - if (isoc_write_packet->packet_count() < NUMBER_OF_PACKETS) - return; - - _isoch_out_queue.add(*isoc_write_packet); - } - - size_t const payload_size = usb_packet->ep->max_packet_size * NUMBER_OF_PACKETS; - size_t const packet_size = Usb::Isoc_transfer::size(payload_size); - try { - Usb::Packet_descriptor packet = alloc_packet(packet_size, false); - packet.type = Packet_type::ISOC; - packet.transfer.ep = usb_packet->ep->nr; - packet.transfer.polling_interval = Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL; - - char *packet_content = usb_raw.source()->packet_content(packet); - - Usb::Isoc_transfer &isoc = *(Usb::Isoc_transfer *)packet_content; - isoc.number_of_packets = 0; - - isoc_write_packet.construct(packet, packet_content); - if (!valid) isoc_write_packet->copy(usb_packet); - - } catch (Packet_alloc_failed) { - if (verbose_warnings) - warning("xHCI: packet allocation failed (size ", Hex(packet_size), "in ", __func__, ")"); - isoc_write_packet.construct(); - return; - } - - if (_isoch_out_pending == 0 && _isoch_out_queue.avail_capacity() > 1) - return; - - while (!_isoch_out_queue.empty()) { - Isoc_packet i = _isoch_out_queue.get(); - submit(i.packet()); - _isoch_out_pending++; - } - } - - - /*********************** - ** Device disconnect ** - ***********************/ - - void _destroy() - { - /* mark delete before removing */ - deleted = true; - - /* remove from USB bus */ - remove_usbdevice(qemu_dev); - qemu_dev = nullptr; - } - - void state_change() - { - Mutex::Guard guard(_mutex); - if (usb_raw.plugged()) - return; - - _destroy(); - } - - void destroy() - { - Mutex::Guard guard(_mutex); - - _release_interfaces(); - - _destroy(); - } - - - /******************* - ** Packet stream ** - *******************/ - - Usb::Packet_descriptor alloc_packet(int length, bool completion) - { - if (!usb_raw.source()->ready_to_submit()) { - throw Packet_alloc_failed(); - } - - Usb::Packet_descriptor packet = usb_raw.alloc_packet(length); - - if (!completion) { - packet.completion = nullptr; - return packet; - } - - packet.completion = alloc_completion(); - if (!packet.completion) { - usb_raw.source()->release_packet(packet); - throw Packet_alloc_failed(); - } - - return packet; - } - - void free_packet(Usb::Packet_descriptor &packet) - { - if (packet.type == Packet_type::ISOC) { - if (packet.read_transfer()) _isoc_in_pending--; - else _isoch_out_pending--; - } - - free_completion(packet); - usb_raw.source()->release_packet(packet); - } - - void free_packet(Isoc_packet *packet) - { - free_packet(packet->packet()); - Genode::destroy(_alloc, packet); - } - - Completion *alloc_completion() - { - for (unsigned i = 0; i < NUM_COMPLETIONS; i++) - if (completion[i].state == Completion::FREE) { - completion[i].state = Completion::VALID; - return &completion[i]; - } - - return nullptr; - } - - void free_completion(Usb::Packet_descriptor &packet) - { - if (packet.completion) { - dynamic_cast(packet.completion)->free(); - } - /* make sure we free the completion only once! */ - packet.completion = nullptr; - } - - Completion *find_valid_completion(USBPacket *p) - { - for (unsigned i = 0; i < NUM_COMPLETIONS; i++) - if (completion[i].p == p && completion[i].valid()) - return &completion[i]; - - return nullptr; - } - - void flush_completion(unsigned ep) - { - for (unsigned i = 0; i < NUM_COMPLETIONS; i++) - { - if (!completion[i].valid()) - continue; - - if (completion[i].ep() == ep) { - completion[i].cancel(); - } - } - } - - void submit(Usb::Packet_descriptor p) - { - usb_raw.source()->submit_packet(p); - } - - bool claim_interfaces() { return _claim_interfaces(); } - - void set_configuration(uint8_t value, USBPacket *p) - { - _release_interfaces(); - - try { - Usb::Packet_descriptor packet = alloc_packet(0, true); - packet.type = Usb::Packet_descriptor::CONFIG; - packet.number = value; - - Completion *c = dynamic_cast(packet.completion); - c->p = p; - c->dev = cast_USBDevice(qemu_dev); - submit(packet); - p->status = USB_RET_ASYNC; - } catch (...) { - if (verbose_warnings) - warning(__func__, " packet allocation failed"); - p->status = USB_RET_NAK; - } - - } - - void set_interface(int index, uint8_t value, USBPacket *p) - { - try { - Usb::Packet_descriptor packet = alloc_packet(0, true); - packet.type = Usb::Packet_descriptor::ALT_SETTING; - packet.interface.number = index; - packet.interface.alt_setting = value; - - Completion *c = dynamic_cast(packet.completion); - c->p = p; - c->dev = cast_USBDevice(qemu_dev); - submit(packet); - p->status = USB_RET_ASYNC; - } catch (...) { - if (verbose_warnings) - warning(__func__, " packet allocation failed"); - p->status = USB_RET_NAK; - } - } - - - void flush_transfers(uint8_t ep) - { - try { - Usb::Packet_descriptor packet = alloc_packet(0, false); - packet.type = Usb::Packet_descriptor::FLUSH_TRANSFERS; - packet.number = ep; - submit(packet); - } catch (...) { - if (verbose_warnings) - warning(__func__, " packet allocation failed"); - } - } - - - void reset_alt_settings(USBDevice *udev) - { - /* retrieve device speed */ - Usb::Config_descriptor cdescr; - Usb::Device_descriptor ddescr; - - try { usb_raw.config_descriptor(&ddescr, &cdescr); } - catch (Usb::Session::Device_not_found) { return; } - - for (unsigned i = 0; i < cdescr.num_interfaces; i++) { - udev->altsetting[i] = usb_raw.alt_settings(i); - } - } - - - void update_ep(USBDevice *udev) - { - usb_ep_reset(udev); - - /* retrieve device speed */ - Usb::Config_descriptor cdescr; - Usb::Device_descriptor ddescr; - - try { usb_raw.config_descriptor(&ddescr, &cdescr); } - catch (Usb::Session::Device_not_found) { return; } - - for (unsigned i = 0; i < cdescr.num_interfaces; i++) { - try { - Usb::Interface_descriptor iface; - uint8_t const altsetting = udev->altsetting[i]; - usb_raw.interface_descriptor(i, altsetting, &iface); - for (unsigned k = 0; k < iface.num_endpoints; k++) { - Usb::Endpoint_descriptor endp; - usb_raw.endpoint_descriptor(i, altsetting, k, &endp); - - int const pid = (endp.address & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; - int const ep = (endp.address & 0xf); - uint8_t const type = (endp.attributes & 0x3); - - usb_ep_set_max_packet_size(udev, pid, ep, endp.max_packet_size); - usb_ep_set_type(udev, pid, ep, type); - usb_ep_set_ifnum(udev, pid, ep, i); - usb_ep_set_halted(udev, pid, ep, 0); + _usb.with_xml([&] (Xml_node node) { + _model.update_from_xml(node, + + /* create */ + [&] (Xml_node const &node) -> Device & + { + Device::Name name = + node.attribute_value("name", Device::Name()); + Device::Speed speed = + node.attribute_value("speed", Device::Speed()); + Device &dev = *new (_alloc) + Device(name, speed, _usb, _alloc, _env.rm(), + _space, _handler_cap); + return dev; + }, + + /* destroy */ + [&] (Device &dev) + { + if (dev.qemu_device()) + remove_usbdevice(dev.qemu_device()); + dev.update(_alloc, Xml_node("")); + destroy(_alloc, &dev); + }, + + /* update */ + [&] (Device &dev, Xml_node const &node) + { + dev.update(_alloc, node); } - } catch (Usb::Session::Interface_not_found) { } - } + ); + }); + /* add new devices for C-API client after it got successfully added */ + _model.for_each([&] (Device &dev) { + if (!dev.qemu_device()) + dev.qemu_device(create_usbdevice((void*)dev.handle(), + dev.speed())); + }); } }; -/******************** - ** Qemu interface ** - ********************/ +static Constructible<::Session>& _usb_session() +{ + static Constructible<::Session> session {}; + return session; +} + + +Urb::Urb(Registry ®istry, + ::Interface &iface, + ::Endpoint &endp, + uint8_t type, + size_t size, + USBPacket *packet) +: + Registry::Element(registry, *this), + Usb::Endpoint(endp.address(), endp.attributes()), + Usb::Interface::Urb(iface._session(), *this, + _type(type), size), + _endpoint(endp), _packet(packet) { } + + +Urb::Urb(Registry ®istry, + ::Interface &iface, + ::Endpoint &endp, + uint8_t type, + size_t size, + uint32_t isoc_packets) +: + Registry::Element(registry, *this), + Usb::Endpoint(endp.address(), endp.attributes()), + Usb::Interface::Urb(iface._session(), *this, + _type(type), size, isoc_packets), + _endpoint(endp) {} + + +size_t Urb::read_cache(Byte_range_ptr &dst) +{ + return _canceled ? 0 : _endpoint.read_cache(dst); +} + + +void Urb::write_cache(Const_byte_range_ptr const &src) +{ + if (!_canceled) _endpoint.write_cache(src); +} + + +void Urb::destroy() +{ + _endpoint.destroy_urb(this); +} + + + +uint8_t Isoc_cache::_level() const { + return (_ep.in()) ? _read-_wrote : _wrote-_read; } + + +void Isoc_cache::_new_urb() +{ + uint8_t urbs = (_ep.in()) ? URBS : _level() / PACKETS_PER_URB; + + bool sent = false; + + for (unsigned i = 0; urbs && i < URBS; i++) { + if (_urbs[i].constructed()) + continue; + _urbs[i].construct(_usb_session()->_urb_registry, + _iface, _ep, + USB_ENDPOINT_XFER_ISOC, + _ep.max_packet_size()*PACKETS_PER_URB, + PACKETS_PER_URB); + --urbs; + sent = true; + } + + if (sent) _iface.update_urbs(); +} + + +void Isoc_cache::_copy_to_host(USBPacket *p) +{ + size_t size = p->iov.size; + + if (!size || _level() >= MAX_PACKETS-1) + return; + + size_t offset = _wrote++ * _ep.max_packet_size(); + + if (size != _ep.max_packet_size()) { + error("Assumption about QEmu Isochronous out packets wrong!"); + size = _ep.max_packet_size(); + } + + usb_packet_copy(p, _buffer+offset, size); +} + + +void Isoc_cache::_copy_to_guest(USBPacket *p) +{ + size_t count = p->iov.size / _ep.max_packet_size(); + + if (!count || !_level()) + return; + + size_t offset = _read * _ep.max_packet_size(); + _read += count; + + usb_packet_copy(p, _buffer+offset, count * _ep.max_packet_size()); +} + + +void Isoc_cache::handle(USBPacket *p) +{ + if (_ep.in()) _copy_to_guest(p); + else _copy_to_host(p); + _new_urb(); +} + + +size_t Isoc_cache::read(Byte_range_ptr &dst) +{ + if (_ep.in()) + return _ep.max_packet_size(); + + size_t offset = _read++ * _ep.max_packet_size(); + Genode::memcpy(dst.start, (void*)(_buffer+offset), _ep.max_packet_size()); + return _ep.max_packet_size(); +} + + +void Isoc_cache::write(Const_byte_range_ptr const &src) +{ + size_t offset = _wrote * _ep.max_packet_size(); + _wrote += src.num_bytes / _ep.max_packet_size(); + Genode::memcpy((void*)(_buffer+offset), src.start, src.num_bytes); +} + + +void Isoc_cache::destroy(Urb * urb) +{ + for (unsigned i = 0; i < URBS; i++) + if (_urbs[i].constructed() && &*_urbs[i] == urb) { + _urbs[i].destruct(); + return; + } + _new_urb(); +} + + +void Isoc_cache::flush() +{ + _read = 0; + _wrote = 0; + for (unsigned i = 0; i < URBS; i++) + if (_urbs[i].constructed()) + _urbs[i]->cancel(); +} + + +Isoc_cache::Isoc_cache(::Interface &iface, Endpoint &ep, Allocator &alloc) +: + _iface(iface), _ep(ep), _alloc(alloc), + _buffer((unsigned char*)_alloc.alloc(MAX_PACKETS*ep.max_packet_size())) {} + + +Usb::Interface &::Interface::_session() +{ + if (!_iface.constructed()) { + _iface.construct(_device.session(), + Usb::Interface::Index{_number, _alt_setting}, + _buf_size); + _iface->sigh(_device.sigh_cap()); + } + return *_iface; +}; -#define TRACE_AND_STOP do { warning(__func__, " not implemented"); } while(false) #define USB_HOST_DEVICE(obj) \ OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE) -static void reset_alt_settings(USBDevice *udev) +static void usb_host_update_ep(USBDevice *udev) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; + USBHostDevice *d = USB_HOST_DEVICE(udev); + handle_t handle = (handle_t)d->data; - dev->reset_alt_settings(udev); + usb_ep_reset(udev); + _usb_session()->_space.apply({ handle }, + [&] (Device & device) { + device.with_active_interfaces([&] (::Interface &iface) { + iface.for_each_endpoint([&] (Endpoint &endp) { + int const pid = (endp.address() & USB_DIR_IN) + ? USB_TOKEN_IN : USB_TOKEN_OUT; + int const ep = (endp.address() & 0xf); + uint8_t const type = (endp.attributes() & 0x3); + usb_ep_set_max_packet_size(udev, pid, ep, endp.max_packet_size()); + usb_ep_set_type(udev, pid, ep, type); + usb_ep_set_ifnum(udev, pid, ep, iface.number()); + usb_ep_set_halted(udev, pid, ep, 0); + }); + }); + }); } -static void update_ep(USBDevice *udev, uint8_t interface, uint8_t altsetting) +void produce_out_data(USBPacket * const p, Byte_range_ptr &dst) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; + USBEndpoint *ep = p ? p->ep : nullptr; + USBDevice *udev = ep ? ep->dev : nullptr; - udev->altsetting[interface] = altsetting; - dev->update_ep(udev); + if (!udev) + return; + + switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { + case USB_ENDPOINT_XFER_CONTROL: + Genode::memcpy(dst.start, udev->data_buf, dst.num_bytes); + return; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + usb_packet_copy(p, dst.start, dst.num_bytes); + break; + default: + error("cannot produce data for unknown packet"); + } } -static bool claim_interfaces(USBDevice *udev) +void consume_in_data(USBPacket * const p, Const_byte_range_ptr const &src) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; + USBEndpoint *ep = p ? p->ep : nullptr; + USBDevice *udev = ep ? ep->dev : nullptr; - return dev->claim_interfaces(); + if (!udev) + return; + + switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { + case USB_ENDPOINT_XFER_CONTROL: + p->actual_length = src.num_bytes; + Genode::memcpy(udev->data_buf, src.start, src.num_bytes); + + /* + * Disable remote wakeup (bit 5) in 'bmAttributes' (byte 7) in reported + * configuration descriptor. On some systems (e.g., Windows) this will + * cause devices to stop working. + */ + if ((udev->setup_buf[1] == USB_REQ_GET_DESCRIPTOR) && + (udev->setup_buf[3] == USB_DT_CONFIG)) + udev->data_buf[7] &= ~USB_CFG_ATT_WAKEUP; + return; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + /* + * unfortunately usb_packet_copy does not provide a signature + * for const-access of the source + */ + usb_packet_copy(p, const_cast(src.start), src.num_bytes); + break; + default: + error("cannot consume data of unknown packet"); + } +} + + +void complete_packet(USBPacket * const p, Usb::Tagged_packet::Return_value v) +{ + USBEndpoint *ep = p ? p->ep : nullptr; + USBDevice *udev = ep ? ep->dev : nullptr; + + if (!udev) + return; + + bool ok = v == Usb::Tagged_packet::OK; + + switch (v) { + case Usb::Tagged_packet::OK: p->status = USB_RET_SUCCESS; break; + case Usb::Tagged_packet::HALT: p->status = USB_RET_STALL; break; + default: p->status = USB_RET_IOERROR; + }; + + switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { + case USB_ENDPOINT_XFER_CONTROL: + if (ok && udev->setup_buf[1] == USB_REQ_SET_INTERFACE) { + usb_host_update_devices(); + usb_host_update_ep(udev); + } + usb_generic_async_ctrl_complete(udev, p); + return; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + usb_packet_complete(udev, p); + break; + default: + error("cannot produce data for unknown packet"); + } +} + + +void ::Interface::update_urbs() +{ + if (!_iface.constructed()) + return; + + _iface->update_urbs( + [&] (Urb &urb, Byte_range_ptr &dst) { + if (!urb.canceled()) produce_out_data(urb._packet, dst); }, + [&] (Urb &urb, Const_byte_range_ptr const &src) { + if (!urb.canceled()) consume_in_data(urb._packet, src); }, + [&] (Urb &urb, uint32_t, Byte_range_ptr &dst) { + return urb.read_cache(dst); }, + [&] (Urb &urb, uint32_t, Const_byte_range_ptr const &src) { + urb.write_cache(src); }, + [&] (Urb &urb, Usb::Tagged_packet::Return_value v) + { + if (!urb.isoc() && !urb.canceled()) { + complete_packet(urb._packet, v); + if (_usb_session().constructed()) + destroy(_usb_session()->_alloc, &urb); + } else + urb.destroy(); + } + ); +} + + +void ::Interface::destroy_all_urbs() +{ + if (_iface.constructed()) + _iface->dissolve_all_urbs([] (Urb &urb) + { + if (!urb.isoc() && !urb.canceled()) { + complete_packet(urb._packet, Usb::Tagged_packet::NO_DEVICE); + if (_usb_session().constructed()) + destroy(_usb_session()->_alloc, &urb); + } else + urb.destroy(); + }); +} + + +void Device::update_urbs() +{ + _device.update_urbs( + [&] (Urb &urb, Byte_range_ptr &dst) { + produce_out_data(urb._packet, dst); }, + [&] (Urb &urb, Const_byte_range_ptr const &src) { + consume_in_data(urb._packet, src); }, + [&] (Urb &urb, Usb::Tagged_packet::Return_value v) { + complete_packet(urb._packet, v); + if (_usb_session().constructed()) + destroy(_usb_session()->_alloc, &urb); + } + ); + + _ifaces.for_each([&] (::Interface &iface) { + iface.update_urbs(); }); +} + + +Device::~Device() +{ + _device.dissolve_all_urbs([] (Urb &urb) + { + complete_packet(urb._packet, Usb::Tagged_packet::NO_DEVICE); + if (_usb_session().constructed()) + destroy(_usb_session()->_alloc, &urb); + }); +} + + +extern "C" void usb_host_update_device_transfers() +{ + if (!_usb_session().constructed()) + return; + + _usb_session()->_model.for_each([&] (Device & device) { + device.update_urbs(); }); } static void usb_host_realize(USBDevice *udev, Error **errp) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; - - /* retrieve device speed */ - Usb::Config_descriptor cdescr; - Usb::Device_descriptor ddescr; - - try { dev->usb_raw.config_descriptor(&ddescr, &cdescr); } - catch (Usb::Session::Device_not_found) { return; } - - if (verbose_host) - log("set udev->speed to %d", Usb_host_device::to_qemu_speed(ddescr.speed)); - - udev->speed = Usb_host_device::to_qemu_speed(ddescr.speed); - udev->speedmask = (1 << udev->speed); + USBHostDevice *d = USB_HOST_DEVICE(udev); + handle_t handle = (handle_t)d->data; udev->flags |= (1 << USB_DEV_FLAG_IS_HOST); - - dev->update_ep(udev); + usb_host_update_ep(udev); } static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; - Completion *c = dev->find_valid_completion(p); - - if (c) - c->cancel(); + _usb_session()->_urb_registry.for_each([&] (::Urb &urb) { + if (urb.packet() == p) urb.cancel(); }); } static void usb_host_handle_data(USBDevice *udev, USBPacket *p) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; - Genode::size_t size = 0; - Usb::Packet_descriptor::Type type = Usb::Packet_descriptor::BULK; - bool const in = p->pid == USB_TOKEN_IN; + USBHostDevice *d = USB_HOST_DEVICE(udev); + handle_t handle = (handle_t)d->data; + uint8_t type = usb_ep_get_type(udev, p->pid, p->ep->nr); + uint8_t ep = p->ep->nr | ((p->pid == USB_TOKEN_IN) ? USB_DIR_IN : 0); - switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { - case USB_ENDPOINT_XFER_BULK: - type = Usb::Packet_descriptor::BULK; - size = usb_packet_size(p); - p->status = USB_RET_ASYNC; - break; - case USB_ENDPOINT_XFER_INT: - type = Usb::Packet_descriptor::IRQ; - size = p->iov.size; - p->status = USB_RET_ASYNC; - break; - case USB_ENDPOINT_XFER_ISOC: - if (in) - dev->isoc_in_packet(p); - else - dev->isoc_out_packet(p); - return; - default: - error("not supported data request"); - p->status = USB_RET_NAK; - return; - } + _usb_session()->_space.apply({ handle }, [&] (Device & device) { + device.with_active_interfaces([&] (::Interface &iface) { + iface.with_endpoint(ep, [&] (Endpoint &endp) { - try { - Usb::Packet_descriptor packet = dev->alloc_packet(size, true); - packet.type = type; - packet.transfer.ep = p->ep->nr | (in ? USB_DIR_IN : 0); - packet.transfer.polling_interval = Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL; - - if (!in) { - char * const content = dev->usb_raw.source()->packet_content(packet); - usb_packet_copy(p, content, size); - } - - Completion *c = dynamic_cast(packet.completion); - c->p = p; - c->dev = udev; - c->data = nullptr; - - dev->submit(packet); - } catch (Packet_alloc_failed) { - if (verbose_warnings) - warning("xHCI: packet allocation failed (size ", Hex(size), "in ", __func__, ")"); - p->status = USB_RET_NAK; - } + switch (type) { + case USB_ENDPOINT_XFER_BULK: [[fallthrough]]; + case USB_ENDPOINT_XFER_INT: + p->status = USB_RET_ASYNC; + new (_usb_session()->_alloc) + ::Urb(_usb_session()->_urb_registry, + iface, endp, type, usb_packet_size(p), p); + iface.update_urbs(); + return; + case USB_ENDPOINT_XFER_ISOC: + p->status = USB_RET_SUCCESS; + endp.handle_isoc_packet(p); + return; + default: + error("not supported data request ", (int)type); + p->status = USB_RET_NAK; + return; + } + }); + }); + }); } @@ -870,22 +880,13 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p, int request, int value, int index, int length, uint8_t *data) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; - - if (verbose_host) - log("r: ", Hex(request), " v: ", Hex(value), " " - "i: ", Hex(index), " length: ", length); + USBHostDevice *d = USB_HOST_DEVICE(udev); + handle_t handle = (handle_t)d->data; switch (request) { case DeviceOutRequest | USB_REQ_SET_ADDRESS: udev->addr = value; - return; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - dev->set_configuration(value & 0xff, p); - return; - case InterfaceOutRequest | USB_REQ_SET_INTERFACE: - dev->set_interface(index, value, p); + p->status = USB_RET_SUCCESS; return; } @@ -895,61 +896,34 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p, error("r->usb3ep0quirk = true"); } - Usb::Packet_descriptor packet; - try { - packet = dev->alloc_packet(length, true); - } catch (...) { - if (verbose_warnings) - warning("Packet allocation failed"); - return; - } + _usb_session()->_space.apply({ handle }, + [&] (Device & device) { + new (_usb_session()->_alloc) + Device::Urb(device, request & 0xff, (request >> 8) & 0xff, + value, index, length, p); + device.update_urbs(); + }); - packet.type = Usb::Packet_descriptor::CTRL; - packet.control.request_type = request >> 8; - packet.control.request = request & 0xff; - packet.control.index = index; - packet.control.value = value; - - /* - * Send usb ctrl transfers with one second timeout as some devices (e.g., - * smartcard readers) do not response to certain control transfers. - */ - packet.control.timeout = 1000; /* ms */ - - Completion *c = dynamic_cast(packet.completion); - c->p = p; - c->dev = udev; - c->data = data; - - if (!(packet.control.request_type & USB_DIR_IN) && length) { - char *packet_content = dev->usb_raw.source()->packet_content(packet); - Genode::memcpy(packet_content, data, length); - } - - dev->submit(packet); p->status = USB_RET_ASYNC; } static void usb_host_ep_stopped(USBDevice *udev, USBEndpoint *usb_ep) { - USBHostDevice *d = USB_HOST_DEVICE(udev); - Usb_host_device *dev = (Usb_host_device *)d->data; + USBHostDevice *d = USB_HOST_DEVICE(udev); + handle_t handle = (handle_t)d->data; + uint8_t ep = usb_ep->nr | ((usb_ep->pid == USB_TOKEN_IN) ? USB_DIR_IN : 0); - bool in = usb_ep->pid == USB_TOKEN_IN; - unsigned ep = endpoint_number(usb_ep); - - /* flush pending transfers */ - dev->flush_transfers(ep); - - switch (usb_ep->type) { - case USB_ENDPOINT_XFER_ISOC: - if (in) { - dev->isoc_in_flush(ep, true); - } - default: - return; - } + _usb_session()->_space.apply({ handle }, [&] (Device & device) { + device.with_active_interfaces([&] (::Interface &iface) { + iface.with_endpoint(ep, [&] (Endpoint &endp) { + endp.flush(); + new (_usb_session()->_alloc) + ::Urb(_usb_session()->_urb_registry, iface, endp, + USB_ENDPOINT_XFER_INVALID, 0, nullptr); + }); + }); + }); } @@ -987,135 +961,15 @@ static void usb_host_register_types(void) } -struct Usb_devices : List -{ - Entrypoint &_ep; - Env &_env; - Allocator &_alloc; - Signal_handler _device_dispatcher { _ep, *this, &Usb_devices::_devices_update }; - Attached_rom_dataspace _devices_rom = { _env, "usb_devices" }; - - void _garbage_collect() - { - for (Usb_host_device *next = first(); next;) { - Usb_host_device *current = next; - next = current->next(); - - if (current->deleted == false) - continue; - - remove(current); - Genode::destroy(_alloc, current); - } - } - - template - void for_each(FUNC const &fn) - { - for (Usb_host_device *d = first(); d; d = d->next()) - fn(*d); - } - - void _devices_update() - { - Mutex::Guard guard(_mutex); - - _garbage_collect(); - - _devices_rom.update(); - if (!_devices_rom.valid()) - return; - - for_each([] (Usb_host_device &device) { - device.deleted = true; - }); - - if (verbose_devices) - log(_devices_rom.local_addr()); - - Xml_node devices_node(_devices_rom.local_addr(), _devices_rom.size()); - devices_node.for_each_sub_node("device", [&] (Xml_node const &node) { - - Session_label label = node.attribute_value("label", String<160>()); - - Dev_info const dev_info(label); - - if (!node.has_attribute("label")) { - error("no label found for device ", dev_info); - return; - } - - /* ignore if already created */ - bool exists = false; - for_each([&] (Usb_host_device &device) { - if (device.info != dev_info) - return; - - exists = true; - device.deleted = false; - }); - - if (exists) - return; - - try { - Usb_host_device *new_device = new (_alloc) - Usb_host_device(_ep, _alloc, _env, dev_info); - - insert(new_device); - - log("Attach USB device ", dev_info); - } catch (...) { - error("could not attach USB device ", dev_info); - } - }); - - /* remove devices deleted by update */ - for_each([] (Usb_host_device &device) { - if (device.deleted == false) return; - - device._release_interfaces(); - device._destroy(); - }); - _garbage_collect(); - } - - Usb_devices(Entrypoint &ep, Allocator &alloc, Env &env) - : _ep(ep), _env(env), _alloc(alloc) - { - _devices_rom.sigh(_device_dispatcher); - _devices_update(); - } - - void destroy() - { - for (Usb_host_device *d = first(); d; d = d->next()) - d->destroy(); - - _garbage_collect(); - } -}; - - -static Usb_devices *_devices; - - -extern "C" void usb_host_destroy() -{ - if (_devices == nullptr) return; - - _devices->destroy(); -} - - extern "C" void usb_host_update_devices() { - if (_devices == nullptr) return; - - _devices->_devices_update(); + if (_usb_session().constructed()) _usb_session()->update(); } +extern "C" void usb_host_destroy() { } + + /* * Do not use type_init macro because of name mangling */ @@ -1123,8 +977,33 @@ extern "C" void _type_init_usb_host_register_types(Entrypoint *ep, Allocator *alloc, Env *env) { - usb_host_register_types(); + struct Helper + { + Signal_handler io_handler; + Signal_handler rom_handler; - static Usb_devices devices(*ep, *alloc, *env); - _devices = &devices; + Helper(Entrypoint &ep) + : + io_handler(ep, *this, &Helper::io), + rom_handler(ep, *this, &Helper::rom_update) {} + + void io() + { + Mutex::Guard guard(::_mutex); + usb_host_update_device_transfers(); + } + + void rom_update() + { + Mutex::Guard guard(::_mutex); + usb_host_update_devices(); + } + }; + static Helper helper(*ep); + + Mutex::Guard guard(::_mutex); + usb_host_register_types(); + _usb_session().construct(*env, *alloc, helper.io_handler, + helper.rom_handler); + usb_host_update_devices(); } diff --git a/repos/libports/src/lib/qemu-usb/include/qemu_emul.h b/repos/libports/src/lib/qemu-usb/include/qemu_emul.h index 82e29694b8..0a89e151c8 100644 --- a/repos/libports/src/lib/qemu-usb/include/qemu_emul.h +++ b/repos/libports/src/lib/qemu-usb/include/qemu_emul.h @@ -886,7 +886,7 @@ typedef struct USBHostDevice } USBHostDevice; -USBHostDevice *create_usbdevice(void *data); +USBHostDevice *create_usbdevice(void *data, int speed); void remove_usbdevice(USBHostDevice *device); void usb_host_destroy(); diff --git a/repos/libports/src/lib/qemu-usb/qemu_emul.cc b/repos/libports/src/lib/qemu-usb/qemu_emul.cc index 2a8b218437..0447901cd6 100644 --- a/repos/libports/src/lib/qemu-usb/qemu_emul.cc +++ b/repos/libports/src/lib/qemu-usb/qemu_emul.cc @@ -448,12 +448,14 @@ static USBHostDevice *_create_usbdevice(int const type, FUNC const &fn_init) } -USBHostDevice *create_usbdevice(void *data) +USBHostDevice *create_usbdevice(void *data, int speed) { return _create_usbdevice(Object_pool::USB_HOST_DEVICE, [&](Wrapper &obj) { /* set data pointer */ obj._usb_host_device.data = data; + obj._usb_device.speed = speed; + obj._usb_device.speedmask = (1 << speed); }); } diff --git a/repos/libports/src/lib/vfs/libusb/vfs_libusb.cc b/repos/libports/src/lib/vfs/libusb/vfs_libusb.cc index 77d7050ff4..bafd83825f 100644 --- a/repos/libports/src/lib/vfs/libusb/vfs_libusb.cc +++ b/repos/libports/src/lib/vfs/libusb/vfs_libusb.cc @@ -14,17 +14,18 @@ /* Genode includes */ #include -#include #include #include #include +using namespace Genode; + /* - * This function is implemented in the Genode backend - * of libusb. + * These functions are implemented in the Genode backend of libusb. */ -extern void libusb_genode_usb_connection(Usb::Connection *); +extern void libusb_genode_backend_init(Env&, Allocator&, Signal_context_capability); +extern bool libusb_genode_backend_signaling; class Libusb_file_system : public Vfs::Single_file_system { @@ -36,30 +37,15 @@ class Libusb_file_system : public Vfs::Single_file_system { private: - Genode::Env &_env; - Vfs::Env::User &_vfs_user; - Genode::Allocator_avl _alloc_avl; + Env &_env; + Vfs::Env::User &_vfs_user; - Genode::Io_signal_handler _state_changed_handler { - _env.ep(), *this, &Libusb_vfs_handle::_handle_state_changed }; + Io_signal_handler _handler { + _env.ep(), *this, &Libusb_vfs_handle::_handle }; - void _handle_state_changed() - { - /* - * The handler is installed only to receive state-change - * signals from the USB connection using the 'Usb_device' - * constructor. - */ - } - - Usb::Connection _usb_connection { - _env, &_alloc_avl, "usb_device", 1024*1024, _state_changed_handler }; - - Genode::Io_signal_handler _ack_avail_handler { - _env.ep(), *this, &Libusb_vfs_handle::_handle_ack_avail }; - - void _handle_ack_avail() + void _handle() { + libusb_genode_backend_signaling = true; _vfs_user.wakeup_vfs_user(); } @@ -67,49 +53,35 @@ class Libusb_file_system : public Vfs::Single_file_system Libusb_vfs_handle(Directory_service &ds, File_io_service &fs, - Genode::Allocator &alloc, - Genode::Env &env, + Allocator &alloc, + Env &env, Vfs::Env::User &vfs_user) : Single_vfs_handle(ds, fs, alloc, 0), - _env(env), _vfs_user(vfs_user), _alloc_avl(&alloc) + _env(env), _vfs_user(vfs_user) { - _usb_connection.tx_channel()->sigh_ack_avail(_ack_avail_handler); - - Genode::log("libusb: waiting until device is plugged..."); - while (!_usb_connection.plugged()) - _env.ep().wait_and_dispatch_one_io_signal(); - Genode::log("libusb: device is plugged"); - - libusb_genode_usb_connection(&_usb_connection); + log("libusb: waiting until device is plugged..."); + libusb_genode_backend_init(env, alloc, _handler); + log("libusb: device is plugged"); } - bool read_ready() const override - { - auto &nonconst_this = const_cast(*this); - return nonconst_this._usb_connection.source()->ack_avail(); - } + bool read_ready() const override { + return libusb_genode_backend_signaling; } - Read_result read(Genode::Byte_range_ptr const &, Genode::size_t &) override - { - return READ_ERR_IO; - } + Read_result read(Byte_range_ptr const &, size_t &) override { + return READ_ERR_IO; } - bool write_ready() const override - { - return true; - } + bool write_ready() const override { + return true; } - Write_result write(Genode::Const_byte_range_ptr const &, Genode::size_t &) override - { - return WRITE_ERR_IO; - } + Write_result write(Const_byte_range_ptr const &, + size_t &) override { + return WRITE_ERR_IO; } }; public: - Libusb_file_system(Vfs::Env &env, - Genode::Xml_node config) + Libusb_file_system(Vfs::Env &env, Xml_node config) : Single_file_system(Vfs::Node_type::CONTINUOUS_FILE, name(), Vfs::Node_rwx::ro(), config), @@ -124,9 +96,9 @@ class Libusb_file_system : public Vfs::Single_file_system ** Directory service interface ** *********************************/ - Open_result open(char const *path, unsigned, - Vfs::Vfs_handle **out_handle, - Genode::Allocator &alloc) override + Open_result open(char const *path, unsigned, + Vfs::Vfs_handle **out_handle, + Allocator &alloc) override { if (!_single_file(path)) return OPEN_ERR_UNACCESSIBLE; @@ -141,7 +113,7 @@ class Libusb_file_system : public Vfs::Single_file_system struct Libusb_factory : Vfs::File_system_factory { - Vfs::File_system *create(Vfs::Env &env, Genode::Xml_node node) override + Vfs::File_system *create(Vfs::Env &env, Xml_node node) override { return new (env.alloc()) Libusb_file_system(env, node); } diff --git a/repos/os/include/genode_c_api/base.h b/repos/os/include/genode_c_api/base.h index bf7ab2ad61..6157c21062 100644 --- a/repos/os/include/genode_c_api/base.h +++ b/repos/os/include/genode_c_api/base.h @@ -94,4 +94,11 @@ typedef struct genode_shared_dataspace * typedef void (*genode_shared_dataspace_free_t) (struct genode_shared_dataspace * ds); +struct genode_buffer +{ + void * addr; + unsigned long size; +}; +typedef struct genode_buffer genode_buffer_t; + #endif /* _INCLUDE__GENODE_C_API__BASE_H_ */ diff --git a/repos/os/include/genode_c_api/usb.h b/repos/os/include/genode_c_api/usb.h index aee3f3119d..d65d2d7cce 100644 --- a/repos/os/include/genode_c_api/usb.h +++ b/repos/os/include/genode_c_api/usb.h @@ -15,110 +15,77 @@ #define _GENODE_C_API__USB_H_ #include +#include -struct genode_usb_session; /* definition is private to the implementation */ +struct genode_usb_device; +struct genode_usb_configuration; +struct genode_usb_interface; -typedef unsigned short genode_usb_vendor_id_t; -typedef unsigned short genode_usb_product_id_t; -typedef unsigned int genode_usb_bus_num_t; -typedef unsigned int genode_usb_dev_num_t; -typedef unsigned char genode_usb_class_num_t; +typedef unsigned int genode_usb_bus_num_t; +typedef unsigned int genode_usb_dev_num_t; #ifdef __cplusplus extern "C" { #endif -/******************** - ** Initialization ** - ********************/ - -/** - * Callback to copy over config descriptor for given device - */ -typedef unsigned (*genode_usb_rpc_config_desc_t) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t 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) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t dev, unsigned idx); - -/** - * Callback to copy over interface descriptor for given device/interface - */ -typedef int (*genode_usb_rpc_iface_desc_t) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t 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) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t 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) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t dev, unsigned idx, - unsigned alt, unsigned endp, void * buf, unsigned long buf_size); - -/** - * Callback to claim a given interface - */ -typedef int (*genode_usb_rpc_claim_t) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t dev, unsigned iface); - -/** - * Callback to release a given interface - */ -typedef int (*genode_usb_rpc_release_t) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t dev, unsigned iface); - -/** - * Callback to release all interfaces - */ -typedef void (*genode_usb_rpc_release_all_t) - (genode_usb_bus_num_t bus, genode_usb_dev_num_t dev); - -struct genode_usb_rpc_callbacks { - genode_shared_dataspace_alloc_attach_t alloc_fn; - genode_shared_dataspace_free_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; - genode_usb_rpc_claim_t claim_fn; - genode_usb_rpc_release_t release_fn; - genode_usb_rpc_release_all_t release_all_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(genode_usb_vendor_id_t vendor, - genode_usb_product_id_t product, - genode_usb_class_num_t cla, - genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev); + +/** + * Callback to announce configuration of a device + */ +typedef void (*genode_usb_dev_add_config_t) + (struct genode_usb_device *dev, unsigned idx, void *opaque_data); + +/** + * Callback to announce interface of a device configuration + */ +typedef void (*genode_usb_dev_add_iface_t) + (struct genode_usb_configuration *cfg, unsigned idx, void *opaque_data); + +/** + * Callback to announce endpoint of an interface + */ +typedef void (*genode_usb_dev_add_endp_t) + (struct genode_usb_interface *cfg, unsigned idx, void *opaque_data); + +/** + * Callback to request string item of a device + */ +typedef void (*genode_usb_dev_string_item_t) + (genode_buffer_t string, void *opaque_data); + +void +genode_usb_device_add_endpoint(struct genode_usb_interface *iface, + struct genode_usb_endpoint_descriptor desc); + +void +genode_usb_device_add_interface(struct genode_usb_configuration *cfg, + genode_usb_dev_string_item_t info_string, + struct genode_usb_interface_descriptor desc, + genode_usb_dev_add_endp_t callback, + void *opaque_data, + bool active); + +void +genode_usb_device_add_configuration(struct genode_usb_device *dev, + struct genode_usb_config_descriptor desc, + genode_usb_dev_add_iface_t callback, + void *opaque_data, + bool active); + +void genode_usb_announce_device(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_speed_t speed, + genode_usb_dev_string_item_t manufacturer_string, + genode_usb_dev_string_item_t product_string, + struct genode_usb_device_descriptor desc, + genode_usb_dev_add_config_t callback, + void *opaque_data); void genode_usb_discontinue_device(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev); @@ -128,145 +95,104 @@ void genode_usb_discontinue_device(genode_usb_bus_num_t bus, ** USB session request handling ** **********************************/ -typedef unsigned short genode_usb_session_handle_t; -typedef unsigned short genode_usb_request_handle_t; +typedef void* genode_usb_request_handle_t; -struct genode_usb_request_string -{ - unsigned char index; - unsigned length; -}; +enum Request_return { OK, NO_DEVICE, INVALID, TIMEOUT, HALT, }; +typedef enum Request_return genode_usb_request_ret_t; -struct genode_usb_request_control -{ - unsigned char request; - unsigned char request_type; - unsigned short value; - unsigned short index; - int actual_size; - int timeout; -}; -struct genode_usb_request_transfer -{ - unsigned char ep; - int actual_size; - int polling_interval; -}; +typedef void (*genode_usb_req_control_t) + (genode_usb_request_handle_t handle, + unsigned char ctrl_request, + unsigned char ctrl_request_type, + unsigned short ctrl_value, + unsigned short ctrl_index, + unsigned long ctrl_timeout, + genode_buffer_t payload, + void *opaque_data); -enum Isoc { MAX_PACKETS = 32 }; +typedef void (*genode_usb_req_irq_t) + (genode_usb_request_handle_t handle, + unsigned char ep, + genode_buffer_t payload, + void *opaque_data); -struct genode_usb_isoc_transfer -{ - unsigned number_of_packets; - unsigned packet_size[MAX_PACKETS]; - unsigned actual_packet_size[MAX_PACKETS]; - char data[]; -}; +typedef void (*genode_usb_req_bulk_t) + (genode_usb_request_handle_t handle, + unsigned char ep, + genode_buffer_t payload, + void *opaque_data); -enum Urb_type { CTRL, BULK, IRQ, ISOC, ALT_SETTING, CONFIG, NONE }; -typedef enum Urb_type genode_usb_urb_t; - -struct genode_usb_request_urb -{ - genode_usb_urb_t type; - void * req; -}; - -struct genode_usb_buffer -{ - void * addr; - unsigned long size; -}; - -static inline struct genode_usb_request_control * -genode_usb_get_request_control(struct genode_usb_request_urb * urb) -{ - return (urb->type == CTRL) ? (struct genode_usb_request_control*)urb->req : 0; -} - -static inline struct genode_usb_request_transfer * -genode_usb_get_request_transfer(struct genode_usb_request_urb * urb) -{ - return (urb->type != CTRL) ? (struct genode_usb_request_transfer*)urb->req : 0; -} - -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 void (*genode_usb_req_urb_t) - (struct genode_usb_request_urb req, - genode_usb_session_handle_t session_handle, - genode_usb_request_handle_t request_handle, - struct genode_usb_buffer payload, - void * opaque_data); - -typedef void (*genode_usb_req_string_t) - (struct genode_usb_request_string * req, - genode_usb_session_handle_t session_handle, - genode_usb_request_handle_t request_handle, - struct genode_usb_buffer payload, - void * opaque_data); - -typedef void (*genode_usb_req_altsetting_t) - (unsigned iface, unsigned alt_setting, - genode_usb_session_handle_t session_handle, - genode_usb_request_handle_t request_handle, - void * opaque_data); - -typedef void (*genode_usb_req_config_t) - (unsigned config_idx, - genode_usb_session_handle_t session_handle, - genode_usb_request_handle_t request_handle, - void * opaque_data); +typedef void (*genode_usb_req_isoc_t) + (genode_usb_request_handle_t handle, + unsigned char ep, + genode_uint32_t number_of_packets, + struct genode_usb_isoc_descriptor *packets, + genode_buffer_t payload, + void *opaque_data); typedef void (*genode_usb_req_flush_t) - (unsigned char ep, - genode_usb_session_handle_t session_handle, - genode_usb_request_handle_t request_handle, - void * opaque_data); - -typedef genode_usb_request_ret_t (*genode_usb_response_t) - (struct genode_usb_request_urb req, - struct genode_usb_buffer payload, - void * opaque_data); + (unsigned char ep, + genode_usb_request_handle_t handle, + void *opaque_data); struct genode_usb_request_callbacks { - genode_usb_req_urb_t urb_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_req_control_t ctrl_fn; + genode_usb_req_irq_t irq_fn; + genode_usb_req_bulk_t bulk_fn; + genode_usb_req_isoc_t isoc_fn; + genode_usb_req_flush_t flush_fn; }; -genode_usb_session_handle_t genode_usb_session_by_bus_dev(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev); +typedef struct genode_usb_request_callbacks * genode_usb_req_callback_t; -int genode_usb_request_by_session(genode_usb_session_handle_t session_handle, - struct genode_usb_request_callbacks * const c, - void * callback_data); +bool genode_usb_device_acquired(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev); -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); +bool +genode_usb_request_by_bus_dev(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_req_callback_t const callback, + void *opaque_data); + +void genode_usb_ack_request(genode_usb_request_handle_t request_handle, + genode_usb_request_ret_t ret, + genode_uint32_t *actual_sizes); void genode_usb_notify_peers(void); -void genode_usb_handle_empty_sessions(void); +void genode_usb_handle_disconnected_sessions(void); #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +#include +#include + +/** + * Callback to signal release of device(s) + */ +typedef void (*genode_usb_dev_release_t) (genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev); + +namespace Genode_c_api { + + using namespace Genode; + + /** + * Initialize USB root component + */ + void initialize_usb_service(Env &env, + Signal_context_capability sigh_cap, + genode_shared_dataspace_alloc_attach_t alloc_fn, + genode_shared_dataspace_free_t free_fn, + genode_usb_dev_release_t release_fn); +} + +#endif /* __cplusplus */ + #endif /* _GENODE_C_API__USB_H_ */ diff --git a/repos/os/include/genode_c_api/usb_client.h b/repos/os/include/genode_c_api/usb_client.h index e2f0811fab..ca25a04576 100644 --- a/repos/os/include/genode_c_api/usb_client.h +++ b/repos/os/include/genode_c_api/usb_client.h @@ -1,6 +1,7 @@ /* * \brief C-API Genode USB-client backend * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2023-06-29 */ @@ -15,133 +16,136 @@ #define __GENODE_C_API__USB_CLIENT_H_ #include -#include +#include +#include #ifdef __cplusplus extern "C" { #endif -typedef unsigned long genode_usb_client_handle_t; +/************************* + ** Lifetime management ** + *************************/ -struct genode_range_allocator; +typedef unsigned long genode_usb_client_dev_handle_t; -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; +/** + * Callback to announce a device + */ +typedef void* (*genode_usb_client_dev_add_t) + (genode_usb_client_dev_handle_t handle, char const *name, + genode_usb_speed_t speed); - /* - * Genode extensions (POD only) - */ - unsigned bus; - unsigned num ; - unsigned speed; -} __attribute__((packed)); +/** + * Callback to delete a device + */ +typedef void (*genode_usb_client_dev_del_t) + (genode_usb_client_dev_handle_t handle, void *opaque_data); + +void genode_usb_client_update(genode_usb_client_dev_add_t add, + genode_usb_client_dev_del_t del); + +/****************************************** + ** USB device and interface interaction ** + ******************************************/ + +typedef enum { + INVALID, + HALT, + NO_DEVICE, + NO_MEMORY, + TIMEOUT, + OK +} genode_usb_client_ret_val_t; + +/** + * Callback to produce out content of an USB request + */ +typedef void (*genode_usb_client_produce_out_t) + (void *opaque_data, genode_buffer_t buffer); + +/** + * Callback to consume in result of an USB request + */ +typedef void (*genode_usb_client_consume_in_t) + (void *opaque_data, genode_buffer_t buffer); + +/** + * Callback to produce out content of isochronous packet i + */ +typedef genode_uint32_t (*genode_usb_client_produce_out_isoc_t) + (void *opaque_data, genode_uint32_t i, genode_buffer_t buffer); + +/** + * Callback to consume in result of isochronous packet i + */ +typedef void (*genode_usb_client_consume_in_isoc_t) + (void *opaque_data, genode_uint32_t i, genode_buffer_t buffer); + +/** + * Callback to complete an USB request + */ +typedef void (*genode_usb_client_complete_t) + (void *opaque_data, genode_usb_client_ret_val_t result); + +genode_usb_client_ret_val_t +genode_usb_client_device_control(genode_usb_client_dev_handle_t handle, + genode_uint8_t request, + genode_uint8_t request_type, + genode_uint16_t value, + genode_uint16_t index, + unsigned long size, + void *opaque_data); + +void +genode_usb_client_device_update(genode_usb_client_produce_out_t out, + genode_usb_client_consume_in_t in, + genode_usb_client_produce_out_isoc_t out_isoc, + genode_usb_client_consume_in_isoc_t in_isoc, + genode_usb_client_complete_t complete); -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)); +typedef enum { BULK, IRQ, ISOC, FLUSH } genode_usb_client_iface_type_t; +void +genode_usb_client_claim_interface(genode_usb_client_dev_handle_t handle, + unsigned interface_num); -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_release_interface(genode_usb_client_dev_handle_t handle, + unsigned interface_num); -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); +genode_usb_client_ret_val_t +genode_usb_client_iface_transfer(genode_usb_client_dev_handle_t handle, + genode_usb_client_iface_type_t type, + genode_uint8_t index, + unsigned long size, + void *opaque_data); #ifdef __cplusplus } /* extern "C" */ +#endif /* __cplusplus */ + + +#ifdef __cplusplus #include +#include +#include -struct genode_range_allocator : Genode::Range_allocator { }; +namespace Genode_c_api { -static inline auto genode_range_allocator_ptr(Genode::Range_allocator &alloc) -{ - return static_cast(&alloc); + using namespace Genode; + + /** + * Initialize USB client c++ backend + */ + void initialize_usb_client(Env &env, + Allocator &alloc, + Signal_context_capability io_handler, + Signal_context_capability rom_handler); } + #endif /* __cplusplus */ #endif /* __GENODE_C_API__USB_CLIENT_H_ */ diff --git a/repos/os/include/usb/packet_handler.h b/repos/os/include/usb/packet_handler.h deleted file mode 100644 index 3ab175d41e..0000000000 --- a/repos/os/include/usb/packet_handler.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * \brief Packet stream helper - * \author Sebastian Sumpf - * \date 2014-12-08 - */ - -/* - * Copyright (C) 2014-2017 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 _INCLUDE__USB__PACKET_HANDLER_ -#define _INCLUDE__USB__PACKET_HANDLER_ - -#include - -namespace Usb { class Packet_handler; } - - -class Usb::Packet_handler -{ - private: - - Usb::Connection &_connection; - Genode::Entrypoint &_ep; - - Io_signal_handler _rpc_ack_avail { - _ep, *this, &Packet_handler::_packet_handler }; - - Io_signal_handler _rpc_ready_submit { - _ep, *this, &Packet_handler::_ready_handler }; - - bool _ready_submit = true; - bool _in_completion = false; - - void _packet_handler() - { - if (!_ready_submit) - return; - - while (packet_avail()) { - Packet_descriptor p = _connection.source()->get_acked_packet(); - - if (p.completion) { - _in_completion = true; - p.completion->complete(p); - _in_completion = false; - } else - release(p); - } - } - - void _ready_handler() - { - _ready_submit = true; - }; - - public: - - Packet_handler(Connection &connection, Genode::Entrypoint &ep) - : _connection(connection), _ep(ep) - { - /* connect 'ack_avail' to our rpc member */ - _connection.tx_channel()->sigh_ack_avail(_rpc_ack_avail); - _connection.tx_channel()->sigh_ready_to_submit(_rpc_ready_submit); - } - - - /*************************** - ** Packet stream wrapper ** - ***************************/ - - bool packet_avail() const - { - return _connection.source()->ack_avail(); - } - - void wait_for_packet() - { - if (packet_avail()) { _packet_handler(); } - else { _ep.wait_and_dispatch_one_io_signal(); } - } - - Packet_descriptor alloc(size_t size) - { - /* is size larger than packet stream */ - if (size > _connection.source()->bulk_buffer_size()) { - Genode::error("packet allocation of (", size, " bytes) too large, " - "buffer has ", _connection.source()->bulk_buffer_size(), - " bytes"); - throw Usb::Session::Tx::Source::Packet_alloc_failed(); - } - - while (true) { - try { - Packet_descriptor p = _connection.alloc_packet(size); - return p; - } catch (Usb::Session::Tx::Source::Packet_alloc_failed) { - /* block until some packets are freed */ - wait_for_packet(); - } - } - } - - void submit(Packet_descriptor &p) - { - /* check if submit queue is full */ - if (!_connection.source()->ready_to_submit()) { - _ready_submit = false; - - /* wait for ready_to_submit signal */ - while (!_ready_submit) - _ep.wait_and_dispatch_one_io_signal(); - } - - _connection.source()->submit_packet(p); - - /* - * If an acknowledgement available signal occurred in the meantime, - * retrieve packets, but avoid recursion if 'submit()' was called - * from the completion handler. - */ - if (packet_avail() && !_in_completion) - _packet_handler(); - } - - void *content(Packet_descriptor &p) - { - return _connection.source()->packet_content(p); - } - - void release(Packet_descriptor &p) - { - _connection.source()->release_packet(p); - } -}; - -#endif /* _INCLUDE__USB__PACKET_HANDLER_ */ diff --git a/repos/os/include/usb/types.h b/repos/os/include/usb/types.h deleted file mode 100644 index cfd070d167..0000000000 --- a/repos/os/include/usb/types.h +++ /dev/null @@ -1,223 +0,0 @@ -/** - * \brief Basic types for USB - * \author Sebastian Sumpf - * \date 2014-12-08 - */ - -/* - * Copyright (C) 2014-2017 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 _INCLUDE__USB__TYPES_H_ -#define _INCLUDE__USB__TYPES_H_ - -#include - -namespace Usb { - struct Device_descriptor; - struct Config_descriptor; - struct Interface_descriptor; - struct Endpoint_descriptor; - struct String; - struct Interface_extra; -} - -namespace Usb { - - enum Endpoint_type { - ENDPOINT_BULK = 0x2, - ENDPOINT_INTERRUPT = 0x3, - }; - - /** - * The following three enums are ORed together to form a control request - * type - */ - enum Endpoint_direction { - ENDPOINT_OUT = 0, - ENDPOINT_IN = 0x80, - }; - - enum Request_type { - TYPE_STANDARD = 0, - TYPE_CLASS = (1U << 5), - TYPE_VENDOR = (2U << 5), - TYPE_RESERVED = (3U << 5), - }; - - enum Recipient { - RECIPIENT_DEVICE = 0, - RECIPIENT_INTERFACE = 0x1, - RECIPIENT_ENDPOINT = 0x2, - RECIPIENT_OTHER = 0x3, - }; - - /** - * UTF-16 string - */ - typedef Genode::uint16_t utf16_t; -} - - -/** - * String containing UTF-16 plane 0 characters - */ -struct Usb::String -{ - utf16_t *string = nullptr; - unsigned length = 0; - - void copy(unsigned len, void *from, Genode::Allocator *md_alloc) - { - length = len; - - if (len == 0) - return; - - string = (utf16_t *)md_alloc->alloc(length * sizeof(utf16_t)); - Genode::memcpy(string, from, sizeof(utf16_t) * length); - } - - void free(Genode::Allocator *md_alloc) - { - if (!string) - return; - - md_alloc->free(string, length * sizeof(utf16_t)); - string = nullptr; - } - - /** - * Create char version - */ - char *to_char(char *buffer, unsigned len) - { - if (!length) - return (char *)""; - - len = Genode::min(length, len); - for (unsigned i = 0; i < len; i++) - buffer[i] = (char)(string[i] & 0xff); - - buffer[len] = 0; - return buffer; - } - - /** - * Print for debugging - */ - void print() - { - char buffer[128]; - Genode::log(Genode::Cstring(to_char(buffer, 128))); - } -}; - -/** - * USB hardware device descriptor - */ -struct Usb::Device_descriptor -{ - Genode::uint8_t length = 0; - Genode::uint8_t type = 0x1; - - Genode::uint16_t usb = 0; /* USB version in BCD (binary-coded decimal ) */ - Genode::uint8_t dclass = 0; - Genode::uint8_t dsubclass = 0; - Genode::uint8_t dprotocol = 0; - Genode::uint8_t max_packet_size = 0; /* of endpoint zero */ - - Genode::uint16_t vendor_id = 0; - Genode::uint16_t product_id = 0; - Genode::uint16_t device_release = 0; /* release number in BCD */ - - Genode::uint8_t manufactorer_index = 0; /* index of string describing manufacturer */ - Genode::uint8_t product_index = 0; - Genode::uint8_t serial_number_index = 0; - - Genode::uint8_t num_configs = 0; - - /** - * Genode extensions (POD only) - */ - unsigned bus = 0; - unsigned num = 0; - unsigned speed = 0; -} __attribute__((packed)); - -/** - * USB hardware configuration descriptor - */ -struct Usb::Config_descriptor -{ - Genode::uint8_t length = 0; - Genode::uint8_t type = 0x2; - - /* - * Total length of data returned for this configuration. Includes the - * combined length of all descriptors (configuration, interface, endpoint, - * and class or vendor specific) returned for this configuration. - */ - Genode::uint16_t total_length = 0; - Genode::uint8_t num_interfaces = 0; - - Genode::uint8_t config_value = 0; /* value used to set this configuration */ - Genode::uint8_t config_index = 0; /* index of string descriptor */ - - Genode::uint8_t attributes = 0; - Genode::uint8_t max_power = 0; /* maximum power consumption */ -} __attribute__((packed)); - -/** - * USB hardware interface descriptor - */ -struct Usb::Interface_descriptor -{ - Genode::uint8_t length = 0; - Genode::uint8_t type = 0x4; - - Genode::uint8_t number = 0; /* interface number */ - Genode::uint8_t alt_settings = 0; /* value used for setting alternate setting - using the 'number' field */ - Genode::uint8_t num_endpoints = 0; - - Genode::uint8_t iclass = 0; - Genode::uint8_t isubclass = 0; - Genode::uint8_t iprotocol = 0; - - Genode::uint8_t interface_index = 0; /* index of string descriptor */ - - /** - * Genode extensions (POD only) - */ - bool active = false; -} __attribute__((packed)); - - -/** - * Vendor specific data - */ -struct Usb::Interface_extra -{ - Genode::uint8_t length; - Genode::uint8_t data[32]; -}; - - -/** - * USB hardware endpoint descriptor - */ -struct Usb::Endpoint_descriptor -{ - Genode::uint8_t length = 0; - Genode::uint8_t type = 0x5; - Genode::uint8_t address = 0; - Genode::uint8_t attributes = 0; - Genode::uint16_t max_packet_size = 0; /* for this endpoint */ - Genode::uint8_t polling_interval = 0; -} __attribute__((packed)); - -#endif /* _INCLUDE__USB__TYPES_H_ */ diff --git a/repos/os/include/usb/usb.h b/repos/os/include/usb/usb.h deleted file mode 100644 index 6ad2b68ae4..0000000000 --- a/repos/os/include/usb/usb.h +++ /dev/null @@ -1,597 +0,0 @@ -/** - * \brief USB session wrapper - * \author Sebastian Sumpf - * \date 2014-12-08 - */ - -/* - * Copyright (C) 2014-2017 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 _INCLUDE__USB__USB_H_ -#define _INCLUDE__USB__USB_H_ - -#include -#include -#include -#include -#include -#include - -namespace Usb { - - /* debugging */ - bool constexpr verbose_descr = false; - - class Device; - class Config; - class Alternate_interface; - class Interface; - class Endpoint; - class Meta_data; - class Sync_completion; -} - - -class Usb::Meta_data -{ - protected: - - Genode::Allocator * const _md_alloc; - Connection &_connection; - Packet_handler &_handler; - - public: - - Meta_data(Genode::Allocator * const md_alloc, Connection &device, - Packet_handler &handler) - : _md_alloc(md_alloc), _connection(device), _handler(handler) { } -}; - -/** - * Completion for synchronous calls - */ -class Usb::Sync_completion : Completion -{ - private: - - bool _completed = false; - Packet_descriptor &_p; - - public: - - Sync_completion(Packet_handler &handler, Packet_descriptor &p) - : _p(p) - { - Completion *c = p.completion; - p.completion = this; - - handler.submit(p); - - while (!_completed) - handler.wait_for_packet(); - - if (c) - c->complete(p); - } - - void complete(Packet_descriptor &p) override - { - _p = p; - _completed = true; - } -}; - - -class Usb::Endpoint : public Endpoint_descriptor -{ - public: - - Endpoint(Endpoint_descriptor &endpoint_descr) - : Endpoint_descriptor(endpoint_descr) { } - - bool bulk() const { return (attributes & 0x3) == ENDPOINT_BULK; } - bool interrupt() const { return (attributes & 0x3) == ENDPOINT_INTERRUPT; } - - void dump() - { - if (verbose_descr) - Genode::log("\tEndpoint: " - "len: ", Genode::Hex(length), " " - "type: ", Genode::Hex(type), " " - "address: ", Genode::Hex(address), " " - "attributes: ", Genode::Hex(attributes)); - } -}; - - -class Usb::Alternate_interface : public Interface_descriptor, - public Meta_data -{ - private: - - enum { MAX_ENDPOINTS = 16 }; - Endpoint *_endpoints[MAX_ENDPOINTS]; - - /* - * Noncopyable - */ - Alternate_interface(Alternate_interface const &); - Alternate_interface &operator = (Alternate_interface const &); - - public: - - String interface_string { }; - - Alternate_interface(Interface_descriptor &interface_desc, - Meta_data &md) - : Interface_descriptor(interface_desc), Meta_data(md) - { - dump(); - - for (Genode::uint8_t i = 0; i < num_endpoints; i++) - { - Endpoint_descriptor descr; - _connection.endpoint_descriptor(number, alt_settings, i, &descr); - _endpoints[i] = new (_md_alloc) Endpoint(descr); - _endpoints[i]->dump(); - } - } - - ~Alternate_interface() - { - for (unsigned i = 0; i < num_endpoints; i++) - destroy(_md_alloc, _endpoints[i]); - - interface_string.free(_md_alloc); - } - - Endpoint &endpoint(unsigned index) - { - if (index >= num_endpoints) - throw Session::Invalid_endpoint(); - - return *_endpoints[index]; - } - - void dump() - { - if (!verbose_descr) - return; - - Genode::warning("Interface: " - "len: ", Genode::Hex(length), " " - "type: ", Genode::Hex(type), " " - "number: ", Genode::Hex(number), " " - "alt_settings: ", Genode::Hex(alt_settings)); - Genode::warning(" " - "num_endpoints: ", Genode::Hex(num_endpoints), " " - "class: ", Genode::Hex(iclass), " ", - "subclass: ", Genode::Hex(isubclass), " " - "protocol: ", Genode::Hex(iprotocol)); - } -}; - - -class Usb::Interface : public Meta_data -{ - private: - - friend class Config; - - enum { MAX_ALT = 10 }; - - Alternate_interface *_interface[MAX_ALT]; - unsigned _count = 0; - Alternate_interface *_current = nullptr; - bool _claimed = false; - - - void _check() - { - if (!_claimed) - throw Session::Interface_not_claimed(); - } - - protected: - - void _add(Alternate_interface *iface) - { - _interface[_count++] = iface; - - if (iface->active) - _current = iface; - } - - public: - - Interface(Meta_data &md) - : Meta_data(md) { } - - - /*************** - ** Accessors ** - ***************/ - - unsigned alternate_count() const { return _count; } - Alternate_interface ¤t() { return *_current; } - - Alternate_interface &alternate_interface(unsigned index) - { - if (index >= _count) - throw Session::Interface_not_found(); - - return *_interface[index]; - } - - Endpoint &endpoint(unsigned index) { return _current->endpoint(index); } - - - /*************************** - ** Packet stream helpers ** - ***************************/ - - Packet_descriptor alloc(size_t size) - { - return _handler.alloc(size); - } - - void submit(Packet_descriptor &p) - { - _handler.submit(p); - } - - void release(Packet_descriptor &p) - { - _handler.release(p); - } - - void *content(Packet_descriptor &p) - { - return _handler.content(p); - } - - /****************************** - ** Interface to USB service ** - ******************************/ - - /** - * Claim interface - */ - void claim() - { - _connection.claim_interface(_interface[0]->number); - _claimed = true; - } - - /** - * Release interface - */ - void release() - { - if (_claimed) - return; - - Packet_descriptor p = alloc(0); - p.type = Packet_descriptor::RELEASE_IF; - p.number = _interface[0]->number; - p.succeded = false; - - Sync_completion sync(_handler, p); - - if (p.succeded) - _claimed = false; - } - - void set_alternate_interface(Alternate_interface &alternate) - { - _check(); - - Packet_descriptor p = alloc(0); - p.type = Packet_descriptor::ALT_SETTING; - p.succeded = false; - p.interface.number = alternate.number; - p.interface.alt_setting = alternate.alt_settings; - - Sync_completion sync(_handler, p); - - if (p.succeded) - _current = _interface[p.interface.alt_setting]; - } - - void control_transfer(Packet_descriptor &p, uint8_t request_type, uint8_t request, - uint16_t value, uint16_t index, int timeout, - bool block = true, Completion *c = nullptr) - { - _check(); - - p.type = Usb::Packet_descriptor::CTRL; - p.succeded = false; - p.control.request = request; - p.control.request_type = request_type; - p.control.value = value; - p.control.index = index; - p.control.timeout = timeout; - p.completion = c; - - if(block) Sync_completion sync(_handler, p); - else _handler.submit(p); - } - - void bulk_transfer(Packet_descriptor &p, Endpoint &ep, - bool block = true, Completion *c = nullptr) - { - _check(); - - if (!ep.bulk()) - throw Session::Invalid_endpoint(); - - p.type = Usb::Packet_descriptor::BULK; - p.succeded = false; - p.transfer.ep = ep.address; - p.completion = c; - - if(block) Sync_completion sync(_handler, p); - else _handler.submit(p); - } - - void interrupt_transfer(Packet_descriptor &p, Endpoint &ep, - int polling_interval = - Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL, - bool block = true, Completion *c = nullptr) - { - _check(); - - if (!ep.interrupt()) - throw Session::Invalid_endpoint(); - - p.type = Usb::Packet_descriptor::IRQ; - p.succeded = false; - p.transfer.ep = ep.address; - p.transfer.polling_interval = polling_interval; - p.completion = c; - - if(block) Sync_completion sync(_handler, p); - else _handler.submit(p); - } -}; - - -class Usb::Config : public Config_descriptor, - public Meta_data -{ - private: - - enum { MAX_INTERFACES = 32 }; - - /* - * Noncopyable - */ - Config(Config const &); - Config &operator = (Config const &); - - Interface *_interfaces[MAX_INTERFACES]; - unsigned _total_interfaces = 0; - - public: - - String config_string { }; - - Config(Config_descriptor &config_desc, Meta_data &md) - : Config_descriptor(config_desc), Meta_data(md) - { - dump(); - - for (Genode::uint8_t i = 0; i < num_interfaces; i++) { - - Interface_descriptor descr; - _connection.interface_descriptor(i, 0, &descr); - _interfaces[descr.number] = new(_md_alloc) Interface(md); - - /* read number of alternative settings */ - unsigned alt_settings = _connection.alt_settings(i); - _total_interfaces += alt_settings; - - /* alt settings */ - for (unsigned j = 0; j < alt_settings; j++) { - _connection.interface_descriptor(i, j, &descr); - if (descr.number != i) - error("Interface number != index"); - - _interfaces[descr.number]->_add(new(_md_alloc) Alternate_interface(descr, md)); - } - } - } - - ~Config() - { - for (unsigned i = 0; i < num_interfaces; i++) - destroy(_md_alloc, _interfaces[i]); - - config_string.free(_md_alloc); - } - - Interface &interface(unsigned num) - { - - if (num >= num_interfaces) - throw Session::Interface_not_found(); - - return *_interfaces[num]; - } - - void dump() - { - if (verbose_descr) - log("Config: " - "len: ", Genode::Hex(length), " " - "type: ", Genode::Hex(type), " " - "total_length: ", Genode::Hex(total_length), " " - "num_intf: ", Genode::Hex(num_interfaces), " " - "config_value: ", Genode::Hex(config_value)); - } -}; - - -class Usb::Device : public Meta_data -{ - private: - - /* - * Noncopyable - */ - Device(Device const &); - Device &operator = (Device const &); - - Packet_handler _handler; - - void _clear() - { - if (!config) - return; - - manufactorer_string.free(_md_alloc); - product_string.free(_md_alloc); - serial_number_string.free(_md_alloc); - destroy(_md_alloc, config); - } - - public: - - enum Speed { - SPEED_UNKNOWN = 0, /* enumerating */ - SPEED_LOW, - SPEED_FULL, /* usb 1.1 */ - SPEED_HIGH, /* usb 2.0 */ - SPEED_WIRELESS, /* wireless (usb 2.5) */ - SPEED_SUPER, /* usb 3.0 */ - }; - - Device_descriptor device_descr { }; - - Config *config = nullptr; - - String manufactorer_string { }; - String product_string { }; - String serial_number_string { }; - - Device(Genode::Allocator * const md_alloc, Connection &connection, - Genode::Entrypoint &ep) - : Meta_data(md_alloc, connection, _handler), _handler(connection, ep) - { } - - Device_descriptor const *descriptor() { return &device_descr; } - Config *config_descriptor() { return config; } - - char const *speed_string(unsigned speed) - { - switch (speed) { - case SPEED_LOW : return "LOW"; - case SPEED_FULL : return "FULL"; - case SPEED_HIGH : return "HIGH"; - case SPEED_WIRELESS: return "WIRELESS"; - case SPEED_SUPER : return "SUPER"; - - default: return ""; - } - } - - void string_descriptor(uint8_t index, String *target) - { - Packet_descriptor p = _handler.alloc(128); - p.type = Packet_descriptor::STRING; - p.string.index = index; - p.string.length = 128; - - Sync_completion sync(_handler, p); - target->copy(p.string.length, _handler.content(p), _md_alloc); - } - - /** - * Re-read all descriptors (device, config, interface, and endpoints) - * Must be called before Usb::Device can be used (asynchronous) - */ - void update_config() - { - /* free info from previous call */ - _clear(); - - Config_descriptor config_descr; - - _connection.config_descriptor(&device_descr, &config_descr); - dump(); - - config = new (_md_alloc) Config(config_descr, *this); - - /* retrieve string descriptors */ - string_descriptor(device_descr.manufactorer_index, &manufactorer_string); - string_descriptor(device_descr.product_index, &product_string); - string_descriptor(device_descr.serial_number_index, &serial_number_string); - string_descriptor(config->config_index, &config->config_string); - - for (unsigned i = 0; i < config->num_interfaces; i++) { - Interface &iface = config->interface(i); - - for (unsigned j = 0; j < iface.alternate_count(); j++) - string_descriptor(iface.alternate_interface(j).interface_index, - &iface.alternate_interface(j).interface_string); - } - } - - /** - * Set configuration, no interfaces can be claimed (asynchronous) - */ - void set_configuration(uint8_t num) - { - if (!config) { - Genode::error("No current configuration found"); - return; - } - - if (!num || num > device_descr.num_configs) { - Genode::error("Valid configuration values: 1 ... ", device_descr.num_configs); - return; - } - - if (config && num == config->config_value) - return; - - Packet_descriptor p = _handler.alloc(0); - p.type = Packet_descriptor::CONFIG; - p.number = num; - - Sync_completion sync(_handler, p); - - if (p.succeded) - update_config(); - } - - Interface &interface(unsigned interface_num) - { - return config->interface(interface_num); - } - - void dump() - { - if (!verbose_descr) - return; - - using Genode::Hex; - - Genode::log("Device: " - "len: ", Hex(device_descr.length), " " - "type: " , Hex(device_descr.type), " " - "class: ", Hex(device_descr.dclass), " " - "sub-class: ", Hex(device_descr.dsubclass), " " - "proto: ", Hex(device_descr.dprotocol), " " - "max_packet: ", Hex(device_descr.max_packet_size)); - Genode::log(" " - "vendor: ", Hex(device_descr.vendor_id), " " - "product: ", Hex(device_descr.product_id), " " - "configs: ", Hex(device_descr.num_configs)); - } -}; - -#endif /* _INCLUDE__USB__USB_H_ */ diff --git a/repos/os/include/usb_session/capability.h b/repos/os/include/usb_session/capability.h index 6bbfafbd1a..ecb52de879 100644 --- a/repos/os/include/usb_session/capability.h +++ b/repos/os/include/usb_session/capability.h @@ -1,11 +1,12 @@ /** * \brief USB session capability * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2014-12-08 */ /* - * Copyright (C) 2014-2017 Genode Labs GmbH + * Copyright (C) 2014-2024 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. @@ -16,8 +17,13 @@ #include namespace Usb { - class Session; - typedef Genode::Capability Session_capability; + struct Interface_session; + struct Device_session; + class Session; + + using Interface_capability = Genode::Capability; + using Device_capability = Genode::Capability; + using Session_capability = Genode::Capability; } #endif /* _INCLUDE__USB_SESSION__CAPABILITY_H_ */ diff --git a/repos/os/include/usb_session/client.h b/repos/os/include/usb_session/client.h index 8f60861ee3..344711c497 100644 --- a/repos/os/include/usb_session/client.h +++ b/repos/os/include/usb_session/client.h @@ -1,11 +1,12 @@ /** * \brief USB session client implementation * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2014-12-08 */ /* - * Copyright (C) 2014-2017 Genode Labs GmbH + * Copyright (C) 2014-2024 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. @@ -15,108 +16,27 @@ #include -#include -#include #include -namespace Usb { - class Session_client; - class Interface_client; -} +namespace Usb { struct Client; } -class Usb::Session_client : public Genode::Rpc_client +struct Usb::Client : Genode::Rpc_client { - private: + Client(Session_capability session) + : Rpc_client(session) { } - Packet_stream_tx::Client _tx; + Rom_session_capability devices_rom() override { + return call(); } - public: + Device_capability acquire_device(Device_name const &name) override { + return call(name); } - /** - * Constructor - * - * \param session session capability - * \param tx_buffer_alloc allocator used for managing the - * transmission buffer - */ - Session_client(Session_capability session, - Genode::Range_allocator &tx_buffer_alloc, - Genode::Region_map &rm, - Genode::Signal_context_capability state_change) - : - Genode::Rpc_client(session), - _tx(call(), rm, tx_buffer_alloc) - { - if (state_change.valid()) - sigh_state_change(state_change); - } + Device_capability acquire_single_device() override { + return call(); } - /* - * Helper utility to always allocate correctly aligned USB packets - */ - Packet_descriptor alloc_packet(Genode::size_t size) - { - /* - * At least on ARM the minimal alignment for distinct - * DMA-capable USB URBs shall meet a maximum cache-line - * size of 128 bytes - */ - enum { URB_PAYLOAD_MIN_ALIGN_LOG2 = 7 }; - return source()->alloc_packet(size, URB_PAYLOAD_MIN_ALIGN_LOG2); - } - - /*************************** - ** USB session interface ** - ***************************/ - - bool plugged() override { return call(); } - Tx *tx_channel() override { return &_tx; } - Tx::Source *source() override { return _tx.source(); } - - void sigh_state_change(Genode::Signal_context_capability sigh) override { - call(sigh); } - - void config_descriptor(Device_descriptor *device_descr, - Config_descriptor *config_descr) override - { - call(device_descr, config_descr); - } - - unsigned alt_settings(unsigned index) override - { - return call(index); - } - - void interface_descriptor(unsigned index, unsigned alt_setting, - Interface_descriptor *interface_descr) override - { - call(index, alt_setting, interface_descr); - } - - bool interface_extra(unsigned index, unsigned alt_setting, - Interface_extra *interface_data) override - { - return call(index, alt_setting, interface_data); - } - - void endpoint_descriptor(unsigned interface_num, - unsigned alt_setting, - unsigned endpoint_num, - Endpoint_descriptor *endpoint_descr) override - { - call(interface_num, alt_setting, endpoint_num, endpoint_descr); - } - - void claim_interface(unsigned interface_num) override - { - call(interface_num); - } - - void release_interface(unsigned interface_num) override - { - call(interface_num); - } + void release_device(Device_capability device) override { + call(device); } }; #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 6e010d9c81..db94e54f5f 100644 --- a/repos/os/include/usb_session/connection.h +++ b/repos/os/include/usb_session/connection.h @@ -1,11 +1,12 @@ /* * \brief Client connection to USB server + * \author Stefan Kalkowski * \author Sebastian Sumpf * \date 2014-12-08 */ /* - * Copyright (C) 2014-2017 Genode Labs GmbH + * Copyright (C) 2014-2024 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. @@ -13,26 +14,104 @@ #ifndef _INCLUDE__USB_SESSION__CONNECTION_H_ #define _INCLUDE__USB_SESSION__CONNECTION_H_ -#include +#include #include -#include +#include +#include namespace Usb { struct Connection; } -struct Usb::Connection : Genode::Connection, Session_client +class Usb::Connection : public Genode::Connection, public Usb::Client { - Connection(Genode::Env &env, - Genode::Range_allocator *tx_block_alloc, - Label const &label = Label(), - Genode::size_t tx_buf_size = 512*1024, - Genode::Signal_context_capability sigh_state_changed = - Genode::Signal_context_capability()) - : - Genode::Connection(env, label, Ram_quota { 5*4096 + tx_buf_size }, - Args("tx_buf_size=", tx_buf_size)), - Session_client(cap(), *tx_block_alloc, env.rm(), sigh_state_changed) - { } + private: + + Env & _env; + Rom_session_client _rom { devices_rom() }; + Constructible _ds {}; + Io_signal_handler _handler { _env.ep(), *this, + &Connection::_handle_io }; + + void _try_attach() + { + _ds.destruct(); + try { _ds.construct(_env.rm(), _rom.dataspace()); } + catch (Attached_dataspace::Invalid_dataspace) { + warning("Invalid devices rom dataspace returned!");} + } + + void _handle_io() { } + + template + Device_capability _wait_for_device(FN const & fn) + { + for (;;) { + /* repeatedly check for availability of device */ + Device_capability cap = fn(); + if (cap.valid()) + return cap; + + _env.ep().wait_and_dispatch_one_io_signal(); + } + } + + public: + + Connection(Genode::Env &env, + Genode::size_t ram_quota = RAM_QUOTA) + : + Genode::Connection(env, Label(), + Ram_quota { ram_quota }, Args()), + Client(cap()), + _env(env) + { + _try_attach(); + + /* + * Initially register dummy handler, to be able to receive signals + * if _wait_for_device probes for a valid devices rom + */ + sigh(_handler); + } + + void update() + { + if (_ds.constructed() && _rom.update() == true) + return; + + _try_attach(); + } + + void sigh(Signal_context_capability sigh) { _rom.sigh(sigh); } + + template + void with_xml(FN const & fn) + { + update(); + try { + if (_ds.constructed() && _ds->local_addr()) { + Xml_node xml(_ds->local_addr(), _ds->size()); + fn(xml); + } + } catch (Xml_node::Invalid_syntax) { + warning("Devices rom has invalid XML syntax"); } + } + + Device_capability acquire_device(Device_name const &name) override + { + Ram_quota ram_quota(Device_session::TX_BUFFER_SIZE + 4096); + return retry_with_upgrade(ram_quota, Cap_quota{6}, [&] () { + return Client::acquire_device(name); }); + } + + Device_capability acquire_device() + { + return _wait_for_device([&] () { + Ram_quota ram_quota(Device_session::TX_BUFFER_SIZE + 4096); + return retry_with_upgrade(ram_quota, Cap_quota{6}, [&] () { + return Client::acquire_single_device(); }); + }); + } }; #endif /* _INCLUDE__USB_SESSION__CONNECTION_H_ */ diff --git a/repos/os/include/usb_session/device.h b/repos/os/include/usb_session/device.h new file mode 100644 index 0000000000..b272252a4d --- /dev/null +++ b/repos/os/include/usb_session/device.h @@ -0,0 +1,798 @@ +/* + * \brief USB device interface + * \author Stefan Kalkowski + * \date 2023-08-28 + */ + +/* + * Copyright (C) 2023-2024 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 _INCLUDE__USB_SESSION__DEVICE_H_ +#define _INCLUDE__USB_SESSION__DEVICE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Usb { + template class Urb_handler; + class Endpoint; + class Interface; + class Device; +} + + +class Usb::Endpoint +{ + public: + + enum Direction { OUT, IN }; + enum Type { CONTROL, ISOC, BULK, IRQ }; + + private: + + enum { MAX_NUMBER = 0xf, INVALID = 0xff }; + + struct Address : Register<8> + { + struct Number : Bitfield<0, 4> {}; + struct Direction : Bitfield<7, 1> {}; + }; + + struct Attributes : Register<8> + { + struct Type : Bitfield<0, 2> {}; + }; + + Address::access_t _address { INVALID }; + Attributes::access_t _attributes { INVALID }; + + public: + + struct Endpoint_not_avail : Exception {}; + + Endpoint(Address::access_t addr, Attributes::access_t attr) + : _address(addr), _attributes(attr) {} + + Endpoint(Interface &iface, Direction d, Type t); + + Endpoint() {} + + bool valid() const { + return _address != INVALID || _attributes != INVALID; } + + uint8_t address() const { + return _address; } + + uint8_t number() const { + return Address::Number::get(_address); } + + Type type() const { + return (Type) Attributes::Type::get(_attributes); } + + Direction direction() const { + return Address::Direction::get(_address) ? IN : OUT; } +}; + + +template +class Usb::Urb_handler +{ + protected: + + using Tx = typename SESSION::Tx; + using Packet_descriptor = typename SESSION::Packet_descriptor; + using Payload = typename Packet_descriptor::Payload; + + public: + + class Urb : Noncopyable + { + protected: + + friend class Urb_handler; + + using Direction = Endpoint::Direction; + using Isoc_header = genode_usb_isoc_transfer_header; + using Isoc_descriptor = genode_usb_isoc_descriptor; + + Urb_handler &_urb_handler; + Direction const _direction; + uint32_t const _isoc_packets; + size_t const _size; + Payload _payload { }; + bool _completed { false }; + + Constructible::Element> _tag {}; + + Fifo_element _pending_elem { *this }; + + virtual Packet_descriptor _create() const + { + Tagged_packet::Tag const tag { _tag->id().value }; + Packet_descriptor const p(_payload, tag); + return p; + } + + size_t _isoc_payload_offset() + { + return _isoc_packets ? sizeof(Isoc_header) + + _isoc_packets*sizeof(Isoc_descriptor) + : 0; + } + + template + void _submit(URB &urb, + Tx::Source &tx, + OUT_FN const &out_fn, + ISOC_OUT_FN const &isoc_out_fn) + { + if (!_tag.constructed()) + return; + + Packet_descriptor const p = _create(); + + if (!_isoc_packets && + _direction == Direction::OUT && + _size) { + Byte_range_ptr dst { (char*)tx.packet_content(p), _size }; + out_fn(urb, dst); + } + + if (_isoc_packets) { + addr_t payload = (addr_t)tx.packet_content(p); + size_t off = _isoc_payload_offset(); + + Isoc_header &hdr = *(Isoc_header*)(payload); + + hdr.number_of_packets = _isoc_packets; + + for (uint32_t i = 0; i < _isoc_packets; i++) { + Byte_range_ptr dst { (char*)(payload+off), _size-off }; + uint32_t psize = isoc_out_fn(urb, i, dst); + hdr.packets[i].actual_size = 0; + hdr.packets[i].size = psize; + off += psize; + } + } + + tx.try_submit_packet(p); + } + + template + void _in_results(URB &urb, + Packet_descriptor p, + Tx::Source &tx, + IN_FN const &in_fn, + ISOC_IN_FN const &isoc_in_fn) + { + if (!_isoc_packets) { + Const_byte_range_ptr src { (const char*)tx.packet_content(p), + p.payload_return_size }; + in_fn(urb, src); + return; + } + + addr_t payload = (addr_t)tx.packet_content(p); + size_t off = _isoc_payload_offset(); + + Isoc_header &hdr = *(Isoc_header*)(payload); + + for (uint32_t i = 0; i < _isoc_packets; i++) { + Const_byte_range_ptr src { (const char*)(payload+off), + hdr.packets[i].actual_size }; + isoc_in_fn(urb, i, src); + off += hdr.packets[i].size; + } + } + + Urb(Urb_handler &handler, + Direction direction, + size_t size = 0, + uint32_t isoc_packets = 0) + : + _urb_handler(handler), + _direction(direction), + _isoc_packets(isoc_packets), + _size(size + _isoc_payload_offset()) + { + _urb_handler._pending.enqueue(_pending_elem); + } + + public: + + virtual ~Urb() + { + if (pending()) + _urb_handler._pending.remove(_pending_elem); + else if (in_progress()) + warning("usb-session urb prematurely destructed"); + } + + bool in_progress() const { return _tag.constructed(); } + bool completed() const { return _completed; } + bool pending() const { return !in_progress() && !_completed; } + }; + + protected: + + enum { URB_ALLOC_GRANULARITY = 512 }; + + Packet_allocator _alloc; + Packet_stream_tx::Client _tx; + Id_space _tags { }; + Fifo> _pending { }; + + template + bool _try_process_ack(Tx::Source &, IN_FN const &, + ISOC_IN_FN const &, CPL_FN const &); + + template + bool _try_submit_pending_urb(Tx::Source &, OUT_FN const &, + ISOC_OUT_FN const &); + + public: + + Urb_handler(Capability cap, + Region_map &rm, Allocator &md_alloc) + : + _alloc(&md_alloc, URB_ALLOC_GRANULARITY), + _tx(cap, rm, _alloc) {} + + /** + * Handle the submission and completion of URBs + * + * \return true if progress was made + */ + template + bool update_urbs(OUT_FN const &out_fn, + IN_FN const &in_fn, + ISOC_OUT_FN const &isoc_out_fn, + ISOC_IN_FN const &isoc_in_fn, + CPL_FN const &complete_fn) + { + typename Tx::Source &tx = *_tx.source(); + + bool overall_progress = false; + + for (;;) { + + /* track progress of a single iteration */ + bool progress = false; + + /* process acknowledgements */ + while (_try_process_ack(tx, in_fn, isoc_in_fn, complete_fn)) + progress = true; + + /* try to submit pending requests */ + while (_try_submit_pending_urb(tx, out_fn, isoc_out_fn)) + progress = true; + + overall_progress |= progress; + + if (!progress) + break; + } + + if (overall_progress) + tx.wakeup(); + + return overall_progress; + } + + /** + * Call 'fn' with each urb as argument + * + * This method is intended for the destruction of the urbs associated + * with the handler before destructing the 'Urb_handler' object. + */ + template + void dissolve_all_urbs(FN const &fn) + { + _pending.dequeue_all([&] (Fifo_element &elem) { + fn(static_cast(elem.object())); }); + + auto discard_tag_and_apply_fn = [&] (Urb &urb) { + urb._tag.destruct(); + Packet_descriptor const p { urb._payload.offset, + urb._payload.bytes }; + fn(static_cast(urb)); + _tx.source()->release_packet(p); + }; + + while (_tags.template apply_any(discard_tag_and_apply_fn)); + } + + void sigh(Signal_context_capability cap) + { + _tx.sigh_ack_avail(cap); + _tx.sigh_ready_to_submit(cap); + } +}; + + +class Usb::Interface +: + Noncopyable, + Interface_capability, + public Interface_session +{ + public: + + struct Index + { + uint8_t number; + uint8_t alt_setting; + }; + + struct Type + { + uint8_t cla; + uint8_t subcla; + uint8_t prot; + }; + + class Urb : public Urb_handler::Urb + { + protected: + + using Base = Urb_handler::Urb; + + Packet_descriptor::Type _type; + Endpoint _ep; + + Packet_descriptor _create() const override + { + Packet_descriptor p = Base::_create(); + p.index = _ep.address(); + p.type = _type; + return p; + } + + public: + + Urb(Interface &iface, Endpoint &ep, + Packet_descriptor::Type type, + size_t size = 0, + uint32_t isoc_packets = 0) + : + Base(iface._urb_handler, ep.direction(), + size, isoc_packets), + _type(type), _ep(ep) { } + }; + + struct Alt_setting; + + private: + + friend class Endpoint; + friend class Urb; + + enum { MAX_EPS = 16 }; + + Device &_device; + Index _idx; + Urb_handler _urb_handler; + Endpoint _eps[2][MAX_EPS] { }; + + public: + + struct Interface_not_avail : Exception {}; + + Interface(Device &device, Index idx, size_t buffer_size); + Interface(Device &device, Type type, size_t buffer_size); + Interface(Device &device, size_t buffer_size); + ~Interface(); + + Index index() const { return _idx; } + + void sigh(Signal_context_capability cap) { + _urb_handler.sigh(cap); } + + template + bool update_urbs(OUT_FN const &out_fn, + IN_FN const &in_fn, + ISOC_OUT_FN const &isoc_out_fn, + ISOC_IN_FN const &isoc_in_fn, + CPL_FN const &complete_fn) + { + return _urb_handler.update_urbs(out_fn, in_fn, isoc_out_fn, + isoc_in_fn, complete_fn); + } + + template + bool update_urbs(OUT_FN const &out_fn, + IN_FN const &in_fn, + CPL_FN const &complete_fn) + { + auto isoc_out = [] (URB&, uint32_t, Byte_range_ptr&) { return 0; }; + auto isoc_in = [] (URB&, uint32_t, Const_byte_range_ptr&) { }; + return _urb_handler.update_urbs(out_fn, in_fn, isoc_out, + isoc_in, complete_fn); + } + + template + void dissolve_all_urbs(FN const &fn) { + _urb_handler.dissolve_all_urbs(fn); } + + template + void for_each_endpoint(FN const &fn) + { + for (unsigned d = Endpoint::OUT; d <= Endpoint::IN; d++) + for (unsigned n = 0; n < MAX_EPS; n++) + if (_eps[d][n].valid()) fn(_eps[d][n]); + } +}; + + +class Usb::Device +: + Noncopyable, + Device_capability, + public Device_session +{ + public: + + using Interface = Usb::Interface; + using Name = Usb::Session::Device_name; + + class Urb : public Urb_handler::Urb + { + public: + + using Type = Packet_descriptor::Request_type; + + protected: + + using Base = Urb_handler::Urb; + + uint8_t _request; + Type::access_t _request_type; + uint16_t _value; + uint16_t _index; + + Packet_descriptor _create() const override + { + Packet_descriptor p = Base::_create(); + p.request_type = _request_type; + p.request = _request; + p.value = _value; + p.index = _index; + return p; + } + + public: + + Urb(Device &device, + uint8_t request, Type::access_t request_type, + uint16_t value, uint16_t index, size_t size = 0) + : + Base(device._urb_handler, + Type::D::get(request_type) ? Endpoint::IN + : Endpoint::OUT, + size), + _request(request), _request_type(request_type), + _value(value), _index(index) {} + }; + + private: + + friend class Usb::Interface; + friend class Endpoint; + friend class Urb; + + ::Usb::Connection &_session; + Allocator &_md_alloc; + Region_map &_rm; + Name const _name { }; + + Urb_handler _urb_handler; + + Interface_capability _interface_cap(uint8_t num, size_t buf_size); + + Name _first_device_name(); + + template + void _for_each_iface(FN const & fn); + + Interface::Index _interface_index(Interface::Type); + + public: + + Device(Connection &usb_session, Allocator &md_alloc, + Region_map &rm, Name name); + + Device(Connection &session, Allocator &md_alloc, Region_map &rm); + + ~Device() { _session.release_device(*this); } + + void sigh(Signal_context_capability cap) { + _urb_handler.sigh(cap); } + + template + bool update_urbs(OUT_FN const &out_fn, + IN_FN const &in_fn, + CPL_FN const &complete_fn) + { + auto isoc_out = [] (URB&, uint32_t, Byte_range_ptr&) { return 0; }; + auto isoc_in = [] (URB&, uint32_t, Const_byte_range_ptr&) { }; + return _urb_handler.update_urbs(out_fn, in_fn, isoc_out, + isoc_in, complete_fn); + } + + template + void dissolve_all_urbs(FN const &fn) { + _urb_handler.dissolve_all_urbs(fn); } +}; + + +struct Usb::Interface::Alt_setting : Device::Urb +{ + using P = Device::Packet_descriptor; + using Rt = P::Request_type; + + Alt_setting(Device &dev, Interface &iface) + : + Device::Urb(dev, + P::Request::SET_INTERFACE, + Rt::value(P::Recipient::IFACE, P::Type::STANDARD, + P::Direction::IN), + iface.index().number, iface.index().alt_setting) {} +}; + + +template +template +bool +Usb::Urb_handler::_try_process_ack(Tx::Source &tx, + IN_FN const &in_fn, + ISOC_IN_FN const &isoc_in, + CPL_FN const &complete_fn) +{ + if (!tx.ack_avail()) + return false; + + Packet_descriptor const p = tx.try_get_acked_packet(); + + typename Id_space::Id const id { p.tag.value }; + + try { + _tags.template apply(id, [&] (Urb &urb) { + + if (urb._direction == Urb::Direction::IN && + p.return_value == Packet_descriptor::OK) + urb._in_results(static_cast(urb), p, tx, in_fn, isoc_in); + + urb._completed = true; + urb._tag.destruct(); + complete_fn(static_cast(urb), p.return_value); + }); + } catch (typename Id_space::Unknown_id) { + warning("spurious usb-session urb acknowledgement"); + } + + tx.release_packet(p); + return true; +} + + +template +template +bool +Usb::Urb_handler::_try_submit_pending_urb(Tx::Source &tx, + OUT_FN const &out_fn, + ISOC_FN const &isoc_fn) +{ + if (_pending.empty()) + return false; + + if (!tx.ready_to_submit()) + return false; + + /* + * Allocate space for the payload in the packet-stream buffer. + */ + + Payload payload { }; + try { + _pending.head([&] (Fifo_element const &elem) { + + Urb const &urb = elem.object(); + size_t const align = Tagged_packet::PACKET_ALIGNMENT; + payload = { + .offset = tx.alloc_packet(urb._size, align).offset(), + .bytes = urb._size + }; + }); + } catch (typename Tx::Source::Packet_alloc_failed) { + /* the packet-stream buffer is saturated */ + return false; + } + + /* + * All preconditions for the submission of the urb are satisfied. + * So the urb can go from the pending to the in-progress stage. + */ + + _pending.dequeue([&] (Genode::Fifo_element &elem) { + + Urb &urb = elem.object(); + + /* let the urb join the tag ID space, allocating a tag */ + urb._tag.construct(elem.object(), _tags); + + urb._payload = payload; + urb._submit(static_cast(elem.object()), tx, out_fn, isoc_fn); + }); + + return true; +} + + +inline Usb::Endpoint::Endpoint(Interface &iface, Direction d, Type t) +{ + bool found = false; + + iface.for_each_endpoint([&] (Endpoint ep) { + if (ep.type() == t && ep.direction() == d) { + _attributes = ep._attributes; + _address = ep._address; + found = true; + } + }); + + if (!found) + throw Endpoint_not_avail(); +}; + + +inline Usb::Interface::Interface(Device &device, Index idx, size_t buffer_size) +: + Interface_capability(device._interface_cap(idx.number, buffer_size)), + _device(device), _idx(idx), + _urb_handler(call(), device._rm, device._md_alloc) +{ + static constexpr uint16_t INVALID = 256; + + device._for_each_iface([&] (Xml_node node) { + if (node.attribute_value("number", INVALID) != idx.number) + return; + node.for_each_sub_node("endpoint", [&] (Xml_node node) { + Endpoint ep { node.attribute_value("address", 0), + node.attribute_value("attributes", 0) }; + if (!_eps[ep.direction()][ep.number()].valid()) + _eps[ep.direction()][ep.number()] = ep; + }); + }); +} + + +inline Usb::Interface::Interface(Device &device, Type type, size_t buffer_size) +: Interface(device, device._interface_index(type), buffer_size) { } + + +inline Usb::Interface::Interface(Device &device, size_t buffer_size) +: Interface(device, Index { 0, 0 }, buffer_size) { } + + +inline Usb::Interface::~Interface() { + _device.call(*this); } + + +inline Usb::Interface_capability +Usb::Device::_interface_cap(uint8_t num, size_t buf_size) +{ + return _session.retry_with_upgrade(Ram_quota{buf_size + 4096}, + Cap_quota{6}, [&] () { + return call(num, buf_size); }); +} + + +inline Usb::Device::Name Usb::Device::_first_device_name() +{ + Name ret; + _session.with_xml([&] (Xml_node & xml) { + xml.with_optional_sub_node("device", [&] (Xml_node node) { + ret = node.attribute_value("name", Name()); }); + }); + return ret; +} + + +template +void Usb::Device::_for_each_iface(FN const & fn) +{ + _session.with_xml([&] (Xml_node & xml) { + xml.for_each_sub_node("device", [&] (Xml_node node) { + if (node.attribute_value("name", Name()) == _name) + node.for_each_sub_node("config", [&] (Xml_node node) { + if (node.attribute_value("active", false)) + node.for_each_sub_node("interface", fn); + }); + }); + }); +} + + +inline Usb::Interface::Index +Usb::Device::_interface_index(Interface::Type t) +{ + static constexpr uint16_t INVALID = 256; + + uint16_t num = INVALID, alt = INVALID; + + _for_each_iface([&] (Xml_node node) { + uint16_t c = node.attribute_value("class", INVALID); + uint16_t s = node.attribute_value("subclass", INVALID); + uint16_t p = node.attribute_value("protocol", INVALID); + if (c == t.cla && s == t.subcla && p == t.prot) { + num = node.attribute_value("number", INVALID); + alt = node.attribute_value("alt_setting", INVALID); + } + }); + + if (num < INVALID && alt < INVALID) + return { (uint8_t)num, (uint8_t)alt }; + + throw Interface::Interface_not_avail(); +} + + +inline Usb::Device::Device(Connection &session, + Allocator &md_alloc, + Region_map &rm, + Name name) +: + Device_capability(session.acquire_device(name)), + _session(session), + _md_alloc(md_alloc), + _rm(rm), + _name(name), + _urb_handler(call(), rm, md_alloc) {} + + +inline Usb::Device::Device(Connection &session, + Allocator &md_alloc, + Region_map &rm) +: + Device_capability(session.acquire_device()), + _session(session), + _md_alloc(md_alloc), + _rm(rm), + _name(_first_device_name()), + _urb_handler(call(), rm, md_alloc) {} + +#endif /* _INCLUDE__USB_SESSION__DEVICE_H_ */ diff --git a/repos/os/include/usb_session/rpc_object.h b/repos/os/include/usb_session/rpc_object.h deleted file mode 100644 index 741a8ca879..0000000000 --- a/repos/os/include/usb_session/rpc_object.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * \brief Server RPC object with packet stream - * \author Sebastian Sumpf - * \date 2014-12-08 - */ - -/* - * Copyright (C) 2014-2017 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 _INCLUDE__USB_SESSION__RPC_OBJECT_H_ -#define _INCLUDE__USB_SESSION__RPC_OBJECT_H_ - -#include -#include -#include - -namespace Usb { class Session_rpc_object; } - - -class Usb::Session_rpc_object : public Genode::Rpc_object -{ - protected: - - Packet_stream_tx::Rpc_object _tx; - - public: - - /** - * Constructor - * - * \param tx_ds dataspace used as communication buffer - * for the tx packet stream - * \param ep entry point used for packet-stream channel - */ - Session_rpc_object(Genode::Dataspace_capability tx_ds, - Genode::Rpc_entrypoint &ep, - Genode::Region_map &rm) - : _tx(tx_ds, rm, ep) { } - - /** - * Return capability to packet-stream channel - * - * This method is called by the client via an RPC call at session - * construction time. - */ - Genode::Capability _tx_cap() { return _tx.cap(); } - - Tx::Sink *sink() { return _tx.sink(); } -}; - -#endif /* _INCLUDE__USB_SESSION__RPC_OBJECT_H_ */ diff --git a/repos/os/include/usb_session/types.h b/repos/os/include/usb_session/types.h new file mode 100644 index 0000000000..1194a579e9 --- /dev/null +++ b/repos/os/include/usb_session/types.h @@ -0,0 +1,114 @@ +/** + * \brief Basic types for USB (C/C++ compatible) + * \author Sebastian Sumpf + * \author Stefan Kalkowski + * \date 2014-12-08 + */ + +/* + * Copyright (C) 2014-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 _INCLUDE__USB__TYPES_H_ +#define _INCLUDE__USB__TYPES_H_ + +#include + + +enum Usb_speed { + GENODE_USB_SPEED_LOW, + GENODE_USB_SPEED_FULL, + GENODE_USB_SPEED_HIGH, + GENODE_USB_SPEED_SUPER, + GENODE_USB_SPEED_SUPER_PLUS, + GENODE_USB_SPEED_SUPER_PLUS_2X2, +}; +typedef enum Usb_speed genode_usb_speed_t; + + +struct genode_usb_isoc_descriptor +{ + genode_uint32_t size; + genode_uint32_t actual_size; +} __attribute__((packed)); + + +struct genode_usb_isoc_transfer_header +{ + genode_uint32_t number_of_packets; + struct genode_usb_isoc_descriptor packets[0]; +} __attribute__((packed)); + + +/** + * USB hardware device descriptor + */ +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 manufacturer_index; + genode_uint8_t product_index; + genode_uint8_t serial_number_index; + genode_uint8_t num_configs; +} __attribute__((packed)); + + +/** + * USB hardware configuration descriptor + */ +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)); + + +/** + * USB hardware interface descriptor + */ +struct genode_usb_interface_descriptor +{ + genode_uint8_t length; + genode_uint8_t type; + genode_uint8_t number; + genode_uint8_t alt_settings; + genode_uint8_t num_endpoints; + genode_uint8_t iclass; + genode_uint8_t isubclass; + genode_uint8_t iprotocol; + genode_uint8_t interface_index; +} __attribute__((packed)); + + +/** + * USB hardware endpoint descriptor + */ +struct genode_usb_endpoint_descriptor +{ + genode_uint8_t length; + genode_uint8_t type; + genode_uint8_t address; + genode_uint8_t attributes; + genode_uint16_t max_packet_size; + genode_uint8_t polling_interval; +} __attribute__((packed)); + +#endif /* _INCLUDE__USB__TYPES_H_ */ diff --git a/repos/os/include/usb_session/usb_session.h b/repos/os/include/usb_session/usb_session.h index 992c0527c8..3c5f72b531 100644 --- a/repos/os/include/usb_session/usb_session.h +++ b/repos/os/include/usb_session/usb_session.h @@ -1,11 +1,12 @@ /** - * \brief USB session for raw device connection + * \brief USB session for USB clients (mainly device drivers) + * \author Stefan Kalkowski * \author Sebastian Sumpf * \date 2014-12-08 */ /* - * Copyright (C) 2014-2017 Genode Labs GmbH + * Copyright (C) 2014-2024 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. @@ -13,244 +14,218 @@ #ifndef _INCLUDE__USB_SESSION__USB_SESSION_H_ #define _INCLUDE__USB_SESSION__USB_SESSION_H_ +#include #include #include +#include #include -#include - +#include namespace Usb { - using namespace Genode; - class Session; - struct Packet_descriptor; - struct Isoc_transfer; - struct Completion; + + struct Tagged_packet; + struct Interface_session; + struct Device_session; + class Session; } -/** - * USB packet type - */ -struct Usb::Packet_descriptor : Genode::Packet_descriptor +struct Usb::Tagged_packet : Genode::Packet_descriptor { - enum Type { STRING, CTRL, BULK, IRQ, ISOC, ALT_SETTING, CONFIG, RELEASE_IF, FLUSH_TRANSFERS }; - /* use the polling interval stated in the endpoint descriptor */ - enum { DEFAULT_POLLING_INTERVAL = -1 }; - - Type type { STRING }; - bool succeded { false }; - Completion *completion { nullptr }; - - union - { - struct - { - uint8_t index; - unsigned length; - } string; - - struct - { - uint8_t request; - uint8_t request_type; - uint16_t value; - uint16_t index; - int actual_size; /* returned */ - int timeout; - } control; - - struct - { - uint8_t ep; - int actual_size; /* returned */ - int polling_interval; /* for interrupt transfers */ - } transfer; - - struct - { - uint8_t number; - uint8_t alt_setting; - } interface; - - struct - { - uint8_t number; - }; - }; - - enum Error { - NO_ERROR, - INTERFACE_OR_ENDPOINT_ERROR, - MEMORY_ERROR, - NO_DEVICE_ERROR, - PACKET_INVALID_ERROR, - PROTOCOL_ERROR, - STALL_ERROR, - TIMEOUT_ERROR, - UNKNOWN_ERROR - }; - - Error error = NO_ERROR; + /* + * At least on ARM the minimal alignment for distinct + * DMA-capable USB URBs shall meet a maximum cache-line + * size of 128 bytes + */ + enum Alignment { PACKET_ALIGNMENT = 7 }; /** - * Return true if packet is a read transfer + * Payload location within the packet stream */ - bool read_transfer() { return transfer.ep & ENDPOINT_IN; } - - Packet_descriptor(off_t offset = 0, size_t size = 0) - : Genode::Packet_descriptor(offset, size) { } - - Packet_descriptor(Genode::Packet_descriptor p, Type type, Completion *completion = nullptr) - : Genode::Packet_descriptor(p.offset(), p.size()), type(type), completion(completion) { } -}; - - -/** - * Isochronous transfer metadata (located at start of stream packet) - */ -struct Usb::Isoc_transfer -{ - enum { MAX_PACKETS = 32 }; - - unsigned number_of_packets; - unsigned packet_size[MAX_PACKETS]; - unsigned actual_packet_size[MAX_PACKETS]; - - char *data() { return (char *)(this + 1); } - - static size_t size(unsigned data_size) + struct Payload { - return sizeof(Isoc_transfer) + data_size; - } + off_t offset; + size_t bytes; + }; + + struct Tag { + unsigned long value; + } tag { ~0UL }; + + enum Return_value { + UNHANDLED, NO_DEVICE, INVALID, TIMEOUT, HALT, OK + } return_value { UNHANDLED }; + + + Tagged_packet(off_t offset = 0, size_t size = 0) + : Genode::Packet_descriptor(offset, size) {} + + Tagged_packet(Payload p, Tag tag) + : + Genode::Packet_descriptor(p.offset, p.bytes), + tag(tag) {} }; -/** - * Completion for asynchronous communication - */ -struct Usb::Completion : Genode::Interface +struct Usb::Interface_session : Interface { - virtual void complete(Usb::Packet_descriptor &p) = 0; + struct Packet_descriptor : Tagged_packet + { + enum Type { + BULK, IRQ, ISOC, FLUSH + } type { FLUSH }; + + uint8_t index { 0 }; + size_t payload_return_size { 0 }; + + using Tagged_packet::Tagged_packet; + }; + + enum { TX_QUEUE_SIZE = 64 }; + + using Tx_policy = Packet_stream_policy; + using Tx = Packet_stream_tx::Channel; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC(Rpc_tx_cap, Capability, tx_cap); + GENODE_RPC_INTERFACE(Rpc_tx_cap); +}; + + +struct Usb::Device_session : Interface +{ + struct Packet_descriptor : Tagged_packet + { + enum Request : uint8_t { + GET_STATUS = 0x00, + CLEAR_FEATURE = 0x01, + SET_FEATURE = 0x03, + SET_ADDRESS = 0x05, + GET_DESCRIPTOR = 0x06, + SET_DESCRIPTOR = 0x07, + GET_CONFIGURATION = 0x08, + SET_CONFIGURATION = 0x09, + GET_INTERFACE = 0x0a, + SET_INTERFACE = 0x0b, + SYNCH_FRAME = 0x0c, + SET_SEL = 0x30, + SET_ISOCH_DELAY = 0x31, + }; + + uint8_t request { GET_STATUS }; + + enum Recipient { DEVICE, IFACE, ENDP, OTHER }; + enum Type { STANDARD, CLASS, VENDOR, RESERVED }; + enum Direction { OUT, IN }; + + struct Request_type : Register<8> + { + struct R : Bitfield<0, 5> { }; + struct T : Bitfield<5, 2> { }; + struct D : Bitfield<7, 1> { }; + + static access_t value(Recipient r, Type t, Direction d) + { + access_t ret = 0; + R::set(ret, r); + T::set(ret, t); + D::set(ret, d); + return ret; + } + }; + + Request_type::access_t request_type { 0 }; + + uint16_t value { 0 }; + uint16_t index { 0 }; + + size_t payload_return_size { 0 }; + size_t timeout { 0 }; + + using Tagged_packet::Tagged_packet; + }; + + enum { TX_QUEUE_SIZE = 8, TX_BUFFER_SIZE = 4096 }; + + using Tx_policy = Packet_stream_policy; + using Tx = Packet_stream_tx::Channel; + + + /********************* + ** RPC declaration ** + *********************/ + + GENODE_RPC_THROW(Rpc_acquire_interface, Interface_capability, + acquire_interface, + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps), + uint8_t, size_t); + GENODE_RPC(Rpc_release_interface, void, release_interface, + Interface_capability); + GENODE_RPC(Rpc_tx_cap, Capability, tx_cap); + GENODE_RPC_INTERFACE(Rpc_acquire_interface, Rpc_release_interface, + Rpc_tx_cap); }; struct Usb::Session : public Genode::Session { - /**************** - ** Exceptions ** - ****************/ - - class Device_not_found : public Exception { }; - class Interface_not_found : public Exception { }; - class Interface_already_claimed : public Exception { }; - class Interface_not_claimed : public Exception { }; - class Invalid_endpoint : public Exception { }; - - - /******************* - ** Packet stream ** - *******************/ - - enum { TX_QUEUE_SIZE = 64 }; - - typedef Packet_stream_policy Tx_policy; - - typedef Packet_stream_tx::Channel Tx; - - /** - * Request packet-transmission channel - */ - virtual Tx *tx_channel() { return 0; } - - /** - * Request client-side packet-stream interface of tx channel - */ - virtual Tx::Source *source() { return 0; } - - - /*********************** - ** Session interface ** - ***********************/ - /** * \noapi */ static const char *service_name() { return "Usb"; } - static constexpr unsigned CAP_QUOTA = 5; + static constexpr unsigned CAP_QUOTA = 8; + static constexpr unsigned RAM_QUOTA = 512 * 1024; + + virtual ~Session() {} + + using Device_name = String<64>; /** - * Send from the server to the client upon device state change + * Request ROM session containing information about available devices. + * + * \return capability to ROM dataspace */ - virtual void sigh_state_change(Signal_context_capability sigh) = 0; + virtual Rom_session_capability devices_rom() = 0; /** - * Is the device present + * Acquire device known by unique 'name' */ - virtual bool plugged() = 0; + virtual Device_capability acquire_device(Device_name const &name) = 0; /** - * Retrieve device and current configurations despcriptors + * Acquire the first resp. single device of this session */ - virtual void config_descriptor(Device_descriptor *device_descr, - Config_descriptor *config_descr) = 0; + virtual Device_capability acquire_single_device() = 0; /** - * Return number of alt settings for iterface + * Release all resources regarding the given 'device' session */ - virtual unsigned alt_settings(unsigned index) = 0; + virtual void release_device(Device_capability device) = 0; - /** - * Return interface descriptor for interface index/alternate setting tuple - */ - virtual void interface_descriptor(unsigned index, unsigned alt_setting, - Interface_descriptor *interface_descr) = 0; - virtual bool interface_extra(unsigned index, unsigned alt_setting, - Interface_extra *interface_data) = 0; + /********************* + ** RPC declaration ** + *********************/ - /** - * Return endpoint for interface index/alternate setting tuple - */ - virtual void endpoint_descriptor(unsigned interface_num, - unsigned alt_setting, - unsigned endpoint_num, - Endpoint_descriptor *endpoint_descr) = 0; - - /** - * Claim an interface number - */ - 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); - GENODE_RPC(Rpc_tx_cap, Capability, _tx_cap); - GENODE_RPC_THROW(Rpc_config_descr, void, config_descriptor, GENODE_TYPE_LIST(Device_not_found), - Device_descriptor *, Config_descriptor *); - GENODE_RPC_THROW(Rpc_alt_settings, unsigned, alt_settings, GENODE_TYPE_LIST(Device_not_found, - Interface_not_found), unsigned); - GENODE_RPC_THROW(Rpc_iface_descr, void, interface_descriptor, GENODE_TYPE_LIST(Device_not_found, - Interface_not_found), unsigned, unsigned, Interface_descriptor *); - GENODE_RPC_THROW(Rpc_iface_extra, bool, interface_extra, GENODE_TYPE_LIST(Device_not_found, - Interface_not_found), unsigned, unsigned, Interface_extra *); - GENODE_RPC_THROW(Rpc_ep_descr, void, endpoint_descriptor, GENODE_TYPE_LIST(Device_not_found, - Interface_not_found), unsigned, unsigned, unsigned, Endpoint_descriptor *); - GENODE_RPC_THROW(Rpc_claim_interface, void, claim_interface, GENODE_TYPE_LIST(Device_not_found, - Interface_not_found, Interface_already_claimed), unsigned); - GENODE_RPC_THROW(Rpc_release_interface, void, release_interface, GENODE_TYPE_LIST(Device_not_found, - Interface_not_found), unsigned); - GENODE_RPC_INTERFACE(Rpc_plugged, Rpc_sigh_state_change, Rpc_tx_cap, Rpc_config_descr, - Rpc_iface_descr, Rpc_iface_extra, Rpc_ep_descr, Rpc_alt_settings, - Rpc_claim_interface, Rpc_release_interface); + GENODE_RPC(Rpc_devices_rom, Rom_session_capability, devices_rom); + GENODE_RPC_THROW(Rpc_acquire_device, Device_capability, acquire_device, + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps), + Device_name const &); + GENODE_RPC_THROW(Rpc_acquire_single_device, Device_capability, + acquire_single_device, + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps)); + GENODE_RPC(Rpc_release_device, void, release_device, Device_capability); + GENODE_RPC_INTERFACE(Rpc_devices_rom, Rpc_acquire_device, + Rpc_acquire_single_device, Rpc_release_device); }; #endif /* _INCLUDE__USB_SESSION__USB_SESSION_H_ */ diff --git a/repos/os/recipes/api/usb_session/content.mk b/repos/os/recipes/api/usb_session/content.mk index d38e856189..19a189fb88 100644 --- a/repos/os/recipes/api/usb_session/content.mk +++ b/repos/os/recipes/api/usb_session/content.mk @@ -1,2 +1,2 @@ -MIRRORED_FROM_REP_DIR := include/usb_session include/usb +MIRRORED_FROM_REP_DIR := include/usb_session include $(REP_DIR)/recipes/api/session.inc diff --git a/repos/os/recipes/raw/drivers_interactive-pc/drivers.config b/repos/os/recipes/raw/drivers_interactive-pc/drivers.config index ca56b90c26..4ed857a160 100644 --- a/repos/os/recipes/raw/drivers_interactive-pc/drivers.config +++ b/repos/os/recipes/raw/drivers_interactive-pc/drivers.config @@ -124,7 +124,7 @@ - + @@ -140,7 +140,6 @@ - diff --git a/repos/os/run/usb_block.run b/repos/os/run/usb_block.run index 9da4d960e5..f4c2e7d813 100644 --- a/repos/os/run/usb_block.run +++ b/repos/os/run/usb_block.run @@ -8,8 +8,6 @@ if {[get_cmd_switch --autopilot] && ![have_include "power_on/qemu"]} { exit 0 } -proc usb_device_label {} { return "usb-1-2" } - create_boot_directory import_from_depot [depot_user]/src/[base_src] \ [depot_user]/pkg/test_usb_host_drv-[board] \ @@ -68,7 +66,7 @@ install_config { - + @@ -95,7 +93,11 @@ install_config { # Define USB host controller config # set fd [open [run_dir]/genode/usb_host_drv.config w] -puts $fd { } +puts $fd { + + + + } close $fd build_boot_image [build_artifacts] diff --git a/repos/os/src/app/usb_report_filter/README b/repos/os/src/app/usb_report_filter/README deleted file mode 100644 index 7dd3fca3f9..0000000000 --- a/repos/os/src/app/usb_report_filter/README +++ /dev/null @@ -1,78 +0,0 @@ -This directory contains a USB device report filter component. It filters the -device report coming from the USB driver by checking each device reported -against the given list of devices. Only approved devices are reported to a -consumer of the report coming from the filter component. - - -Configuration -~~~~~~~~~~~~~ - -A typical example configuration looks as follows: - -! -! -! -! -! -! -! - - -The component that may use the devices is identified by the 'client' node. -In addition to the 'vendor_id' and 'product_id' attribute a 'device' node -can contain a 'bus' and 'dev' attribute. If these attributes are present they -have a stronger significance than the 'vendor_id' and the 'product_id'. - -Whenever the 'usb_report_filter' component receives a new USB device report -from the driver it will generate a new driver configuration that contains -a policy entry for each matching device. After the driver's configuration has -been updated, the filter component will generate a new USB device report that -only contains the devices the component is allowed to access. - - -Example -~~~~~~~ - -In the following example we will give a VirtualBox instance access to a -Corsair Voyager USB stick: - -! -![...] -! -! -! -! - -! -! -! -! -! -! -! -! -! - -After the USB stick has been plugged in, the filter will generate the -following USB driver configuration: - -! -![...] -! -! -! -! -! - -After the driver has reloaded its configuration it will send a config report -that provokes the filter component to send the following USB device report to -VirtualBox: - -! -! -! - -In return, VirtualBox will try to access the USB device. Since the configuration -of the USB driver contains a matching policy entry the access attempt will -succeed. diff --git a/repos/os/src/app/usb_report_filter/main.cc b/repos/os/src/app/usb_report_filter/main.cc deleted file mode 100644 index 40a4890a47..0000000000 --- a/repos/os/src/app/usb_report_filter/main.cc +++ /dev/null @@ -1,431 +0,0 @@ -/* - * \brief Component that filters USB device reports - * \author Josef Soentgen - * \date 2016-01-13 - */ - -/* - * Copyright (C) 2016-2017 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. - */ - -/* Genode includes */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace Usb_filter { - - using Genode::Xml_node; - using Genode::Xml_generator; - using Genode::Attached_rom_dataspace; - using Genode::error; - using Genode::log; - using Genode::warning; - - struct Device_registry; - struct Main; -} - -static bool const verbose = false; - -static char const * const config_file = "usb_drv.config"; - - -class Usb_filter::Device_registry -{ - private: - - Genode::Env &_env; - Genode::Allocator &_alloc; - Genode::Directory &_root_dir; - - Genode::Reporter _reporter { _env, "usb_devices" }; - - Attached_rom_dataspace _devices_rom { _env, "devices" }; - Attached_rom_dataspace _usb_drv_config_rom { _env, "usb_drv_config" }; - - struct Entry : public Genode::List::Element - { - unsigned bus; - unsigned dev; - unsigned vendor; - unsigned product; - - Entry(unsigned b, unsigned d, unsigned v, unsigned p) - : bus(b), dev(d), vendor(v), product(p) { } - }; - - Genode::List _list { }; - - enum { MAX_LABEL_LEN = 512 }; - typedef Genode::String Label; - Label _client_label { }; - - template - void _for_each_entry(FUNC const &func) const - { - Entry const *e = _list.first(); - Entry const *next = nullptr; - for (; e; e = next) { - - /* - * Obtain next element prior calling the functor because - * the functor may remove the current element from the list. - */ - next = e->next(); - - func(*e); - } - } - - static unsigned _get_value(Xml_node node, char const * const attr) { - return node.attribute_value(attr, 0U); } - - static bool _config_has_device(Xml_node config, Entry const &entry) - { - bool result = false; - config.for_each_sub_node("device", [&] (Xml_node usb_device) { - - result |= (_get_value(usb_device, "bus") == entry.bus && - _get_value(usb_device, "dev") == entry.dev); - if (result) return; - - result |= (_get_value(usb_device, "vendor") == entry.vendor && - _get_value(usb_device, "product") == entry.product); - }); - - return result; - } - - static bool _devices_matches(Xml_node &device, Entry const & entry) - { - unsigned const bus = _get_value(device, "bus"); - unsigned const dev = _get_value(device, "dev"); - unsigned const vendor = _get_value(device, "vendor_id"); - unsigned const product = _get_value(device, "product_id"); - - return (bus == entry.bus && dev == entry.dev) || - (vendor == entry.vendor && product == entry.product); - } - - static void _gen_dev_attributes(Xml_generator &xml, Xml_node const &node) - { - auto copy_attr = [&] (auto name) - { - using Value = Genode::String<32>; - xml.attribute(name, node.attribute_value(name, Value())); - }; - - copy_attr("vendor_id"); - copy_attr("product_id"); - copy_attr("bus"); - copy_attr("dev"); - } - - static void _gen_policy_entry(Xml_generator &xml, Xml_node &node, - Entry const &, char const *label) - { - xml.node("policy", [&] { - unsigned const bus = _get_value(node, "bus"); - unsigned const dev = _get_value(node, "dev"); - - xml.attribute("label", Label(label, " -> usb-", bus, "-", dev)); - - _gen_dev_attributes(xml, node); - }); - } - - void _write_usb_drv_config(Xml_node const &drv_config, - Xml_node const &usb_devices) - { - using namespace Genode; - - bool const uhci_enabled = drv_config.attribute_value("uhci", false); - bool const ehci_enabled = drv_config.attribute_value("ehci", false); - bool const xhci_enabled = drv_config.attribute_value("xhci", false); - - char new_content[1024]; - Xml_generator xml(new_content, sizeof(new_content), "config", [&] { - if (uhci_enabled) xml.attribute("uhci", "yes"); - if (ehci_enabled) xml.attribute("ehci", "yes"); - if (xhci_enabled) xml.attribute("xhci", "yes"); - - /* copy other nodes */ - drv_config.for_each_sub_node([&] (Xml_node &node) { - if (!node.has_type("raw")) { - node.with_raw_node([&] (char const *start, size_t length) { - xml.append(start, length); }); - return; - } - }); - - if (!drv_config.has_sub_node("raw")) - log("enable raw support in usb_drv"); - - xml.node("raw", [&] { - xml.node("report", [&] { - xml.attribute("devices", "yes"); - }); - - char const * const label = _client_label.string(); - - usb_devices.for_each_sub_node("device", [&] (Xml_node &node) { - - auto add_policy_entry = [&] (Entry const &entry) { - if (!_devices_matches(node, entry)) return; - - _gen_policy_entry(xml, node, entry, label); - }; - _for_each_entry(add_policy_entry); - }); - }); - }); - - new_content[xml.used()] = 0; - if (verbose) - log("new usb_drv configuration:\n", Cstring(new_content)); - - try { - New_file new_file(_root_dir, config_file); - new_file.append(new_content, xml.used()); - } - catch (...) { - error("could not write '", config_file, "'"); - } - } - - void _write_usb_drv_config(Xml_node const &usb_devices) - { - using namespace Genode; - - try { - File_content::Limit limit { 64*1024 }; - File_content old_file { _alloc, _root_dir, config_file, - limit }; - - old_file.xml([&] (Xml_node const &old_drv_config) { - _write_usb_drv_config(old_drv_config, usb_devices); }); - - } catch (...) { - error("could not access '", config_file, "'"); - return; - } - } - - Genode::Signal_handler _devices_handler - { _env.ep(), *this, &Device_registry::_handle_devices }; - - void _handle_devices() - { - _devices_rom.update(); - - if (!_devices_rom.valid()) return; - - if (verbose) - log("device report:\n", _devices_rom.local_addr()); - - Xml_node usb_devices(_devices_rom.local_addr(), _devices_rom.size()); - - _write_usb_drv_config(usb_devices); - } - - bool _check_config(Xml_node &drv_config) - { - if (!drv_config.has_sub_node("raw")) { - error("could not access node"); - return false; - } - - auto check_policy = [&] (Entry const &entry) { - bool result = false; - drv_config.sub_node("raw").for_each_sub_node("policy", [&] (Xml_node &node) { - result |= (entry.bus == _get_value(node, "bus") && - entry.dev == _get_value(node, "dev")); - if (result) return; - - result |= (entry.vendor == _get_value(node, "vendor_id") && - entry.product == _get_value(node, "product_id")); - }); - - if (verbose && !result) - warning("No matching policy was created for " - "device ", entry.bus, "-", entry.dev, " " - "(", entry.vendor, ":", entry.product, ")"); - }; - _for_each_entry(check_policy); - - return true; - } - - static void _gen_device_entry(Xml_generator &xml, Xml_node &node, - Entry const &) - { - xml.node("device", [&] { - unsigned const bus = _get_value(node, "bus"); - unsigned const dev = _get_value(node, "dev"); - - xml.attribute("label", Label("usb-", bus, "-", dev)); - - _gen_dev_attributes(xml, node); - }); - } - - void _report_usb_devices() - { - using namespace Genode; - - if (!_devices_rom.valid()) return; - - /* - * XXX it might happen that the device list has changed after we are - * waiting for the usb_drv_config update - */ - Xml_node usb_devices(_devices_rom.local_addr(), _devices_rom.size()); - - Reporter::Xml_generator xml(_reporter, [&] () { - usb_devices.for_each_sub_node("device", [&] (Xml_node &node) { - - auto check_entry = [&] (Entry const &entry) { - if (!_devices_matches(node, entry)) return; - - _gen_device_entry(xml, node, entry); - }; - _for_each_entry(check_entry); - }); - }); - } - - Genode::Signal_handler _usb_drv_config_handler = - { _env.ep(), *this, &Device_registry::_handle_usb_drv_config }; - - void _handle_usb_drv_config() - { - _usb_drv_config_rom.update(); - - if (!_usb_drv_config_rom.valid()) return; - - Xml_node config(_usb_drv_config_rom.local_addr(), - _usb_drv_config_rom.size()); - - if (!_check_config(config)) return; - - /* report devices if the USB drivers has changed its policies */ - _report_usb_devices(); - } - - bool _entry_exists(unsigned bus, unsigned dev, - unsigned vendor, unsigned product) - { - bool result = false; - auto check_exists = [&] (Entry const &entry) { - result |= (bus && dev) && - (entry.bus == bus && entry.dev == dev); - result |= (vendor && product) && - (entry.vendor == vendor && entry.product == product); - }; - - _for_each_entry(check_exists); - - return result; - } - - - public: - - /** - * Constructor - */ - Device_registry(Genode::Env &env, Genode::Allocator &alloc, - Genode::Directory &root_dir) - : - _env(env), _alloc(alloc), _root_dir(root_dir) - { - _reporter.enabled(true); - - _devices_rom.sigh(_devices_handler); - - _usb_drv_config_rom.sigh(_usb_drv_config_handler); - } - - /** - * Update internal device registry - */ - void update_entries(Genode::Xml_node config) - { - auto remove_stale_entry = [&] (Entry const &entry) { - - if (_config_has_device(config, entry)) - return; - - _list.remove(const_cast(&entry)); - Genode::destroy(&_alloc, const_cast(&entry)); - }; - _for_each_entry(remove_stale_entry); - - auto add_new_entry = [&] (Xml_node const &node) { - - unsigned const bus = _get_value(node, "bus"); - unsigned const dev = _get_value(node, "dev"); - unsigned const vendor = _get_value(node, "vendor_id"); - unsigned const product = _get_value(node, "product_id"); - - if (_entry_exists(bus, dev, vendor, product)) return; - - Entry *entry = new (&_alloc) Entry(bus, dev, vendor, product); - - _list.insert(entry); - }; - config.for_each_sub_node("device", add_new_entry); - - try { - config.sub_node("client").attribute("label").value(_client_label); - } catch (...) { - error("could not update client label"); - } - } -}; - - -struct Usb_filter::Main -{ - Genode::Env &_env; - - Genode::Heap _heap { _env.ram(), _env.rm() }; - - Genode::Attached_rom_dataspace _config { _env, "config" }; - - Genode::Root_directory _root_dir { _env, _heap, _config.xml().sub_node("vfs") }; - - Genode::Signal_handler
_config_handler = - { _env.ep(), *this, &Main::_handle_config }; - - void _handle_config() - { - _config.update(); - device_registry.update_entries(_config.xml()); - } - - Device_registry device_registry { _env, _heap, _root_dir }; - - Main(Genode::Env &env) : _env(env) - { - _config.sigh(_config_handler); - - _handle_config(); - } -}; - - -void Component::construct(Genode::Env &env) { - static Usb_filter::Main main(env); } diff --git a/repos/os/src/app/usb_report_filter/target.mk b/repos/os/src/app/usb_report_filter/target.mk deleted file mode 100644 index 19043ea83c..0000000000 --- a/repos/os/src/app/usb_report_filter/target.mk +++ /dev/null @@ -1,3 +0,0 @@ -TARGET = usb_report_filter -SRC_CC = main.cc -LIBS = base vfs diff --git a/repos/os/src/drivers/usb_block/cbw_csw.h b/repos/os/src/drivers/usb_block/cbw_csw.h index 61d73affa2..ce1389dd37 100644 --- a/repos/os/src/drivers/usb_block/cbw_csw.h +++ b/repos/os/src/drivers/usb_block/cbw_csw.h @@ -19,6 +19,7 @@ namespace Usb { struct Cbw; struct Csw; + enum { ENDPOINT_IN = 0x80, ENDPOINT_OUT = 0 }; } @@ -28,6 +29,7 @@ using Genode::uint32_t; using Genode::uint64_t; using Genode::size_t; using Genode::addr_t; +using Genode::Byte_range_ptr; /***************************************************** @@ -72,12 +74,15 @@ struct Usb::Cbw : Genode::Mmio<0xf> struct Test_unit_ready : Usb::Cbw, Scsi::Test_unit_ready { - Test_unit_ready(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun) + Test_unit_ready(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + bool verbose) : Cbw(range, tag, 0, Usb::ENDPOINT_IN, lun, Scsi::Test_unit_ready::LENGTH), Scsi::Test_unit_ready(Cbw::range_at(15)) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -90,12 +95,15 @@ struct Test_unit_ready : Usb::Cbw, struct Request_sense : Usb::Cbw, Scsi::Request_sense { - Request_sense(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun) + Request_sense(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + bool verbose) : Cbw(range, tag, Scsi::Request_sense_response::LENGTH, Usb::ENDPOINT_IN, lun, Scsi::Request_sense::LENGTH), Scsi::Request_sense(Cbw::range_at(15)) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -108,11 +116,14 @@ struct Request_sense : Usb::Cbw, Scsi::Request_sense struct Start_stop : Usb::Cbw, Scsi::Start_stop { - Start_stop(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun) + Start_stop(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + bool verbose) : Cbw(range, tag, 0, Usb::ENDPOINT_IN, lun, Scsi::Start_stop::LENGTH), Scsi::Start_stop(Cbw::range_at(15)) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -125,12 +136,15 @@ struct Start_stop : Usb::Cbw, Scsi::Start_stop struct Inquiry : Usb::Cbw, Scsi::Inquiry { - Inquiry(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun) + Inquiry(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + bool verbose) : Cbw(range, tag, Scsi::Inquiry_response::LENGTH, Usb::ENDPOINT_IN, lun, Scsi::Inquiry::LENGTH), Scsi::Inquiry(Cbw::range_at(15)) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -143,12 +157,15 @@ struct Inquiry : Usb::Cbw, Scsi::Inquiry struct Read_capacity_10 : Usb::Cbw, Scsi::Read_capacity_10 { - Read_capacity_10(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun) + Read_capacity_10(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + bool verbose) : Cbw(range, tag, Scsi::Capacity_response_10::LENGTH, Usb::ENDPOINT_IN, lun, Scsi::Read_capacity_10::LENGTH), Scsi::Read_capacity_10(Cbw::range_at(15)) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -161,13 +178,15 @@ struct Read_capacity_10 : Usb::Cbw, Scsi::Read_capacity_10 struct Read_10 : Usb::Cbw, Scsi::Read_10 { - Read_10(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun, - uint32_t lba, uint16_t len, uint32_t block_size) + Read_10(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + uint32_t lba, uint16_t len, uint32_t block_size, bool verbose) : Cbw(range, tag, len * block_size, Usb::ENDPOINT_IN, lun, Scsi::Read_10::LENGTH), Scsi::Read_10(Cbw::range_at(15), lba, len) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -180,13 +199,15 @@ struct Read_10 : Usb::Cbw, Scsi::Read_10 struct Write_10 : Usb::Cbw, Scsi::Write_10 { - Write_10(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun, - uint32_t lba, uint16_t len, uint32_t block_size) + Write_10(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + uint32_t lba, uint16_t len, uint32_t block_size, bool verbose) : Cbw(range, tag, len * block_size, Usb::ENDPOINT_OUT, lun, Scsi::Write_10::LENGTH), Scsi::Write_10(Cbw::range_at(15), lba, len) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -199,12 +220,15 @@ struct Write_10 : Usb::Cbw, Scsi::Write_10 struct Read_capacity_16 : Usb::Cbw, Scsi::Read_capacity_16 { - Read_capacity_16(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun) + Read_capacity_16(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + bool verbose) : Cbw(range, tag, Scsi::Capacity_response_16::LENGTH, Usb::ENDPOINT_IN, lun, Scsi::Read_capacity_16::LENGTH), Scsi::Read_capacity_16(Cbw::range_at(15)) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -217,13 +241,15 @@ struct Read_capacity_16 : Usb::Cbw, Scsi::Read_capacity_16 struct Read_16 : Usb::Cbw, Scsi::Read_16 { - Read_16(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun, - uint64_t lba, uint32_t len, uint32_t block_size) + Read_16(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + uint64_t lba, uint32_t len, uint32_t block_size, bool verbose) : Cbw(range, tag, len * block_size, Usb::ENDPOINT_IN, lun, Scsi::Read_16::LENGTH), Scsi::Read_16(Cbw::range_at(15), (uint32_t)lba, (uint16_t)len) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -236,13 +262,15 @@ struct Read_16 : Usb::Cbw, Scsi::Read_16 struct Write_16 : Usb::Cbw, Scsi::Write_16 { - Write_16(Genode::Byte_range_ptr const &range, uint32_t tag, uint8_t lun, - uint64_t lba, uint32_t len, uint32_t block_size) + Write_16(Byte_range_ptr const &range, uint32_t tag, uint8_t lun, + uint64_t lba, uint32_t len, uint32_t block_size, bool verbose) : Cbw(range, tag, len * block_size, Usb::ENDPOINT_OUT, lun, Scsi::Write_16::LENGTH), Scsi::Write_16(Cbw::range_at(15), (uint32_t)lba, (uint16_t)len) - { if (verbose_scsi) dump(); } + { + if (verbose) dump(); + } void dump() { @@ -253,7 +281,7 @@ struct Write_16 : Usb::Cbw, Scsi::Write_16 }; -struct Usb::Csw : Genode::Mmio<0xd> +struct Usb::Csw : Genode::Const_mmio<0xd> { enum { LENGTH = 13 }; @@ -264,7 +292,7 @@ struct Usb::Csw : Genode::Mmio<0xd> enum { PASSED = 0, FAILED = 1, PHASE_ERROR = 2 }; struct Sts : Register<0xc, 8> { }; /* status */ - Csw(Genode::Byte_range_ptr const &range) : Mmio(range) { } + Csw(Genode::Const_byte_range_ptr const &range) : Const_mmio(range) { } uint32_t sig() const { return read(); } uint32_t tag() const { return read(); } diff --git a/repos/os/src/drivers/usb_block/main.cc b/repos/os/src/drivers/usb_block/main.cc index 5f029277f0..d4318a59f9 100644 --- a/repos/os/src/drivers/usb_block/main.cc +++ b/repos/os/src/drivers/usb_block/main.cc @@ -2,11 +2,12 @@ * \brief Usb session to Block session translator * \author Josef Soentgen * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2016-02-08 */ /* - * Copyright (C) 2016-2020 Genode Labs GmbH + * Copyright (C) 2016-2024 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. @@ -23,21 +24,19 @@ #include #include #include -#include - -/* used by cbw_csw.h so declare it here */ -static bool verbose_scsi = false; +#include /* local includes */ #include namespace Usb { - using namespace Genode; using namespace Block; using Response = Block::Request_stream::Response; - struct Block_driver; + struct No_device {}; + struct Io_error {}; + class Block_driver; struct Block_session_component; struct Main; } @@ -47,918 +46,675 @@ namespace Usb { ** USB Mass Storage (BBB) Block::Driver implementation ** *********************************************************/ -struct Usb::Block_driver : Usb::Completion +class Usb::Block_driver { - Env &env; - Entrypoint &ep; - - Signal_context_capability announce_sigh; - - /* - * Pending block request - */ - struct Block_request - { - Request block_request; - addr_t address { 0 }; - size_t size { 0 }; - bool completed { false }; - - Block_request(Request const &request, Request_stream::Payload const &payload) - : block_request(request) - { - payload.with_content(request, [&](void *addr, size_t sz) { - address = addr_t(addr); - size = sz; - }); - } - - bool read() const - { - return block_request.operation.type == Operation::Type::READ; - } - - bool write() const - { - return block_request.operation.type == Operation::Type::WRITE; - } - }; - - Constructible request { }; - - bool request_pending() const { return request.constructed(); } - - bool initialized = false; - bool device_plugged = false; - - /** - * Handle stage change signal - */ - void handle_state_change() - { - if (!usb.plugged()) { - log("Device unplugged"); - device_plugged = false; - return; - } - - if (initialized) { - error("Device was already initialized"); - return; - } - - log("Device plugged"); - - if (!initialize()) { - env.parent().exit(-1); - sleep_forever(); - return; - } - - /* all is well, announce the device */ - Signal_transmitter(announce_sigh).submit(); - } - - Signal_handler state_change_dispatcher = { - ep, *this, &Block_driver::handle_state_change }; - - /* - * Config ROM - */ - Attached_rom_dataspace config { env, "config" }; - - /* - * Read Usb session label from the configuration - */ - template - static Genode::String get_label(Xml_node node) - { - Genode::String usb_label { "usb_storage" }; - try { - node.attribute("label").value(usb_label); - return usb_label.string(); - } catch (...) { } - - return usb_label; - } - - enum Sizes : size_t { PACKET_STREAM_BUF_SIZE = 2 * (1UL << 20) }; - - /* - * USB session - */ - Allocator_avl alloc; - Usb::Connection usb { env, &alloc, - get_label<128>(config.xml()).string(), PACKET_STREAM_BUF_SIZE, - state_change_dispatcher }; - Usb::Device device; - Signal_handler
&block_request_handler; - - /* - * Reporter - */ - Reporter reporter { env, "devices" }; - bool _report_device = false; - - /* - * Block session - */ - Block::sector_t _block_count { 0 }; - uint32_t _block_size { 0 }; - - bool _writeable = false; - - bool force_cmd_16 = false; - - uint8_t active_interface = 0; - uint8_t active_lun = 0; - - enum { INVALID_ALT_SETTING = 256 }; - uint16_t active_alt_setting = 0; - - uint32_t active_tag = 0; - uint32_t new_tag() { return ++active_tag % 0xffffffu; } - - enum Tags { - INQ_TAG = 0x01, RDY_TAG = 0x02, CAP_TAG = 0x04, - REQ_TAG = 0x08, SS_TAG = 0x10 - }; - - uint8_t ep_in = 0; - uint8_t ep_out = 0; - - bool reset_device = false; - - /* - * Completion used while initializing the device - */ - struct Init_completion : Usb::Completion - { - bool inquiry = false; - bool unit_ready = false; - bool read_capacity = false; - bool request_sense = false; - - bool no_medium = false; - bool try_again = false; - bool start_stop = false; - - Usb::Device &device; - uint8_t interface; - - Block::sector_t block_count = 0; - uint32_t block_size = 0; - - char vendor[Scsi::Inquiry_response::Vid::ITEMS+1]; - char product[Scsi::Inquiry_response::Pid::ITEMS+1]; - - Init_completion(Usb::Device &device, uint8_t interface) - : device(device), interface(interface) { } - - void complete(Packet_descriptor &p) override - { - Usb::Interface iface = device.interface(interface); - - if (p.type != Packet_descriptor::BULK) { - error("Can only handle BULK packets"); - iface.release(p); - return; - } - - if (!p.succeded) { - error("init complete error: packet not succeeded"); - iface.release(p); - return; - } - - /* OUT transfer finished */ - if (!p.read_transfer()) { - iface.release(p); - return; - } - - int const actual_size = p.transfer.actual_size; - Byte_range_ptr data {(char *)iface.content(p), p.size()}; - - using namespace Scsi; - - switch (actual_size) { - case Inquiry_response::LENGTH: - { - Inquiry_response r(data); - if (verbose_scsi) r.dump(); - - if (!r.sbc()) { - warning("Device does not use SCSI Block Commands and may not work"); - } - - r.get_id(vendor, sizeof(vendor)); - r.get_id(product, sizeof(product)); - break; - } - case Capacity_response_10::LENGTH: - { - Capacity_response_10 r(data); - if (verbose_scsi) r.dump(); - - block_count = r.last_block() + 1; - block_size = r.block_size(); - break; - } - case Capacity_response_16::LENGTH: - { - Capacity_response_16 r(data); - if (verbose_scsi) r.dump(); - - block_count = r.last_block() + 1; - block_size = r.block_size(); - break; - } - case Request_sense_response::LENGTH: - { - Request_sense_response r(data); - if (verbose_scsi) r.dump(); - - uint8_t const asc = r.read(); - uint8_t const asq = r.read(); - - bool error = false; - - enum { MEDIUM_NOT_PRESENT = 0x3a, - NOT_READY_TO_READY_CHANGE = 0x28, - POWER_ON_OR_RESET_OCCURRED = 0x29, - LOGICAL_UNIT_NOT_READY = 0x04 }; - switch (asc) { - - case MEDIUM_NOT_PRESENT: - Genode::error("Not ready - medium not present"); - no_medium = true; - break; - - case NOT_READY_TO_READY_CHANGE: /* asq == 0x00 */ - case POWER_ON_OR_RESET_OCCURRED: /* asq == 0x00 */ - warning("Not ready - try again"); - try_again = true; - break; - - case LOGICAL_UNIT_NOT_READY: - - if (asq == 2) start_stop = true; /* initializing command required */ - else if (asq == 1) try_again = true; /* initializing in progress */ - else error = true; - - break; - - default: - error = true; - break; - } - - if (error) - Genode::error("Request_sense_response asc: ", - Hex(asc, Hex::PREFIX, Hex::PAD), - " asq: ", Hex(asq, Hex::PREFIX, Hex::PAD)); - break; - } - case Csw::LENGTH: - { - Csw csw(data); - - uint32_t const sig = csw.sig(); - if (sig != Csw::SIG) { - error("CSW signature does not match: ", - Hex(sig, Hex::PREFIX, Hex::PAD)); - break; - } - - uint32_t const tag = csw.tag(); - uint32_t const status = csw.sts(); - if (status != Csw::PASSED) { - error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD), - " tag: ", tag); - break; - } - - inquiry |= tag & INQ_TAG; - unit_ready |= tag & RDY_TAG; - read_capacity |= tag & CAP_TAG; - request_sense |= tag & REQ_TAG; - break; - } - default: break; - } - - iface.release(p); - } - } init { device, active_interface }; - - /** - * Send CBW - */ - void cbw(void *cb, Completion &c, bool block = false) - { - enum { CBW_VALID_SIZE = Cbw::LENGTH }; - Usb::Interface &iface = device.interface(active_interface); - Usb::Endpoint &ep = iface.endpoint(ep_out); - Usb::Packet_descriptor p = iface.alloc(CBW_VALID_SIZE); - memcpy(iface.content(p), cb, CBW_VALID_SIZE); - iface.bulk_transfer(p, ep, block, &c); - } - - /** - * Receive CSW - */ - void csw(Completion &c, bool block = false) - { - enum { CSW_VALID_SIZE = Csw::LENGTH }; - Usb::Interface &iface = device.interface(active_interface); - Usb::Endpoint &ep = iface.endpoint(ep_in); - Usb::Packet_descriptor p = iface.alloc(CSW_VALID_SIZE); - iface.bulk_transfer(p, ep, block, &c); - } - - /** - * Receive response - */ - void resp(size_t size, Completion &c, bool block = false) - { - Usb::Interface &iface = device.interface(active_interface); - Usb::Endpoint &ep = iface.endpoint(ep_in); - Usb::Packet_descriptor p = iface.alloc(size); - iface.bulk_transfer(p, ep, block, &c); - } - - /** - * Report block device - */ - void report_device(char const *vendor, char const *product, - Block::sector_t count, size_t size) - { - try { - Reporter::Xml_generator xml(reporter, [&] () { - xml.node("device", [&] () { - xml.attribute("vendor", vendor); - xml.attribute("product", product); - xml.attribute("block_count", count); - xml.attribute("block_size", size); - xml.attribute("writeable", _writeable); - }); - }); - } catch (...) { warning("Could not report block device"); } - } - - /** - * Initialize device - * - * All USB transfers in this method are done synchronously. First we reset - * the device (optional), then we query the max LUN. Afterwards we start - * sending CBWs. - * - * Since it might take some time for the device to get ready to use, we - * have to check the SCSI logical unit several times. - */ - bool initialize() - { - device.update_config(); - - Usb::Interface &iface = device.interface(active_interface); - try { iface.claim(); } - catch (Usb::Session::Interface_already_claimed) { - error("Device already claimed"); - return false; - } catch (Usb::Session::Interface_not_found) { - error("Interface not found"); - return false; - } - - enum { - ICLASS_MASS_STORAGE = 8, - ISUBCLASS_SCSI = 6, - IPROTO_BULK_ONLY = 80 + private: + + enum State { + ALT_SETTING, + RESET, + INQUIRY, + CHECK_MEDIUM, + READ_CAPACITY, + REPORT, + READY + } _state { ALT_SETTING }; + + enum Usb_request : uint8_t { + BULK_GET_MAX_LUN = 0xfe, + BULK_RESET = 0xff }; + struct Reset : Device::Urb + { + using P = Device::Packet_descriptor; + using Rt = P::Request_type; + + Reset(Device &dev, Interface &iface) + : + Device::Urb(dev, BULK_RESET, + Rt::value(P::Recipient::IFACE, P::Type::CLASS, + P::Direction::IN), + iface.index().number, 0) {} + }; + + Env &_env; + Entrypoint &_ep; + Allocator &_alloc; + + Connection _session { _env }; + Device _device { _session, _alloc, _env.rm() }; + /* - * Devices following the USB Attached SCSI specification - * normally expose the bulk-only transport in interface 0 alt 0 - * and the UAS endpoints in interface 0 alt 1. - * - * The default interface and thereby 'iface.current()', however, - * might point to the interface 0 alt 1 for such devices. - * - * In case the alternate setting was not explicitly configured - * we look for the first bulk-only setting. + * We have to decide which contructor to use + * for the Usb::Interface dependent on configuration values. + * Therefore, we use a pattern of constructible + reference here */ + Constructible _iface_memory_holder {}; + Interface &_interface; - if (active_alt_setting == INVALID_ALT_SETTING) { + Endpoint _ep_in { _interface, Endpoint::Direction::IN, + Endpoint::Type::BULK }; + Endpoint _ep_out { _interface, Endpoint::Direction::OUT, + Endpoint::Type::BULK }; - /* cap value in case there is no bulk-only */ - active_alt_setting = 0; + Interface::Alt_setting _alt_setting { _device, _interface }; + Constructible _reset {}; - for (uint16_t i = 0; i < iface.alternate_count(); i++) { - Alternate_interface &aif = iface.alternate_interface(i); - if (aif.iclass == ICLASS_MASS_STORAGE - && aif.isubclass == ISUBCLASS_SCSI - && aif.iprotocol == IPROTO_BULK_ONLY) { + using String = Genode::String<64>; - active_alt_setting = i; + Block::sector_t _block_count { 0 }; + uint32_t _block_size { 0 }; - Genode::log("Use probed alternate setting ", - active_alt_setting, " for interface ", - active_interface); - break; + String _vendor {}; + String _product {}; + + Reporter _reporter { _env, "devices" }; + + bool _writeable { false }; + bool _force_cmd_16 { false }; + uint8_t _active_lun { 0 }; + uint32_t _active_tag { 0 }; + bool _reset_device { false }; + bool _verbose_scsi { false }; + + uint32_t _new_tag() { return ++_active_tag % 0xffffffu; } + + enum Tag : uint32_t { + INQ_TAG = 0x01, + RDY_TAG = 0x02, + CAP_TAG = 0x04, + REQ_TAG = 0x08, + SS_TAG = 0x10 + }; + + void _report_device() + { + if (!_reporter.enabled()) + return; + + try { + Reporter::Xml_generator xml(_reporter, [&] () { + xml.node("device", [&] () { + xml.attribute("vendor", _vendor); + xml.attribute("product", _product); + xml.attribute("block_count", _block_count); + xml.attribute("block_size", _block_size); + xml.attribute("writeable", _writeable); + }); + }); + } catch (...) { warning("Could not report block device"); } + } + + Interface& _construct_interface(Xml_node & cfg) + { + using Type = Interface::Type; + using Index = Interface::Index; + + enum { INVALID = 256 }; + enum { + ICLASS_MASS_STORAGE = 8, + ISUBCLASS_SCSI = 6, + IPROTO_BULK_ONLY = 80 + }; + + static constexpr size_t PACKET_STREAM_BUF_SIZE = 2 * (1UL << 20); + + /* if interface is set by config, we assume user knows what to do */ + uint16_t _iface = cfg.attribute_value("interface", + INVALID); + uint8_t _alt = cfg.attribute_value("alt_setting", 0); + + if (_iface < INVALID) + _iface_memory_holder.construct(_device, + Index{ (uint8_t)_iface, _alt }, + PACKET_STREAM_BUF_SIZE); + else + _iface_memory_holder.construct(_device, + Type{ ICLASS_MASS_STORAGE, + ISUBCLASS_SCSI, + IPROTO_BULK_ONLY }, + PACKET_STREAM_BUF_SIZE); + return *_iface_memory_holder; + } + + void _no_write(Byte_range_ptr&) { } + void _no_read(Const_byte_range_ptr const&) { } + + void _inquiry(Byte_range_ptr &dst) { + Inquiry inq(dst, INQ_TAG, _active_lun, _verbose_scsi); } + + void _inquiry_result(Const_byte_range_ptr const &src) + { + using Response = Scsi::Inquiry_response; + + Response r(src, _verbose_scsi); + char v[Response::Vid::ITEMS+1]; + char p[Response::Pid::ITEMS+1]; + + if (!r.sbc()) + warning("Device does not use SCSI " + "Block Commands and may not work"); + + r.get_id(v, sizeof(v)); + r.get_id(p, sizeof(p)); + _vendor = String(v); + _product = String(p); + } + + void _unit_ready(Byte_range_ptr &dst) { + Test_unit_ready r(dst, RDY_TAG, _active_lun, _verbose_scsi); } + + void _sense(Byte_range_ptr &dst) { + Request_sense r(dst, REQ_TAG, _active_lun, _verbose_scsi); } + + void _sense_result(Const_byte_range_ptr const &src) + { + using namespace Scsi; + + Request_sense_response r(src, _verbose_scsi); + + uint8_t const asc = r.read(); + uint8_t const asq = r.read(); + + enum { MEDIUM_NOT_PRESENT = 0x3a, + NOT_READY_TO_READY_CHANGE = 0x28, + POWER_ON_OR_RESET_OCCURRED = 0x29, + LOGICAL_UNIT_NOT_READY = 0x04 }; + + switch (asc) { + case MEDIUM_NOT_PRESENT: + warning("Medium not present!"); + _medium_state.state = Medium_state::WAIT; + return; + case NOT_READY_TO_READY_CHANGE: /* asq == 0x00 */ + case POWER_ON_OR_RESET_OCCURRED: /* asq == 0x00 */ + warning("Medium not ready yet - try again"); + _medium_state.state = Medium_state::WAIT; + return; + case LOGICAL_UNIT_NOT_READY: + /* initializing command required */ + if (asq == 2) { + _medium_state.state = Medium_state::START_STOP; + return; + } + /* initializing in progress */ + if (asq == 1) { + _medium_state.state = Medium_state::WAIT; + return; + } + [[fallthrough]]; + default: + error("Request_sense_response asc: ", + Hex(asc), " asq: ", Hex(asq)); + }; + } + + void _start_stop(Byte_range_ptr &dst) { + Start_stop r(dst, SS_TAG, _active_lun, _verbose_scsi); } + + void _capacity(Byte_range_ptr &dst) + { + if (_force_cmd_16) + Read_capacity_16 r(dst, CAP_TAG, _active_lun, _verbose_scsi); + else + Read_capacity_10 r(dst, CAP_TAG, _active_lun, _verbose_scsi); + } + + void _capacity_result(Const_byte_range_ptr const &src) + { + if (_force_cmd_16) { + Scsi::Capacity_response_16 r(src, _verbose_scsi); + _block_count = r.last_block() + 1; + _block_size = r.block_size(); + } else { + Scsi::Capacity_response_10 r(src, _verbose_scsi); + if (r.last_block() < ~(uint32_t)0U) { + _block_count = r.last_block() + 1; + _block_size = r.block_size(); } } } - Alternate_interface &aif = - iface.alternate_interface((uint8_t)active_alt_setting); - iface.set_alternate_interface(aif); + void _block_write(Byte_range_ptr &dst) + { + if (_block_cmd.constructed()) + memcpy(dst.start, _block_cmd->address, dst.num_bytes); + } - Alternate_interface &alt_iface = iface.current(); + void _block_read(Const_byte_range_ptr const &src) + { + if (_block_cmd.constructed()) + memcpy(_block_cmd->address, src.start, src.num_bytes); + } + + void _block_command(Byte_range_ptr &dst) + { + if (!_block_cmd.constructed()) + return; + + Operation const &op = _block_cmd->block_request.operation; + uint64_t const n = (uint32_t) op.block_number; + uint16_t const c = (uint16_t) op.count; + + if (op.type == Operation::Type::READ) { + if (_force_cmd_16) + Read_16 r(dst, _active_tag, _active_lun, + n, c, _block_size, _verbose_scsi); + else + Read_10 r(dst, _active_tag, _active_lun, + (uint32_t)n, c, _block_size, _verbose_scsi); + } else { + if (_force_cmd_16) + Write_16 w(dst, _active_tag, _active_lun, + n, c, _block_size, _verbose_scsi); + else + Write_10 w(dst, _active_tag, _active_lun, + (uint32_t)n, c, _block_size, _verbose_scsi); + } + } + + struct Scsi_command + { + using Desc = Interface::Packet_descriptor; + using Urb = Interface::Urb; + + Block_driver &drv; + + void (Block_driver::*cmd) (Byte_range_ptr&); + void (Block_driver::*read) (Const_byte_range_ptr const&); + void (Block_driver::*write) (Byte_range_ptr&); + + uint32_t tag; + size_t size; + bool in; + + enum Cmdstate { + CBW, + DATA, + CSW, + DONE, + PROTOCOL_ERROR + } state { CBW }; + + Constructible urb { }; + + Scsi_command(Block_driver & drv, + void (Block_driver::*cmd)(Byte_range_ptr&), + void (Block_driver::*read)(Const_byte_range_ptr const&), + void (Block_driver::*write)(Byte_range_ptr&), + uint32_t tag, + size_t size, + bool in = true) + : + drv(drv), cmd(cmd), read(read), + write(write), tag(tag), size(size), in(in) {} + + void produce_out_content(Byte_range_ptr &dst) + { + if (state == CBW) { + (drv.*cmd)(dst); + return; + } + (drv.*write)(dst); + } + + void consume_in_result(Const_byte_range_ptr const &src) + { + if (state == DATA) { + (drv.*read)(src); + return; + } + + Csw csw(src); + if (csw.sig() != Csw::SIG) { + error("CSW signature does not match: ", + Hex(csw.sig(), Hex::PREFIX, Hex::PAD)); + state = PROTOCOL_ERROR; + return; + } + + if (!((csw.tag() & tag) && csw.sts() == Csw::PASSED)) { + warning("SCSI command failure, expected tag=", tag, + ", got tag=", csw.tag(), " status=", csw.sts()); + state = PROTOCOL_ERROR; + return; + } + } + + void completed(Desc::Return_value ret) + { + switch (ret) { + case Desc::OK: break; + case Desc::NO_DEVICE: throw No_device(); + default: throw Io_error(); + }; + + switch (state) { + case CBW: state = (size) ? DATA : CSW; break; + case DATA: state = CSW; break; + case CSW: state = DONE; break; + default: ; + }; + urb.destruct(); + } + + bool process(State next_state) + { + if (state == PROTOCOL_ERROR) + return false; + + Cmdstate s = state; + + if (!urb.constructed()) + switch (state) { + case CBW: urb.construct(drv._interface, drv._ep_out, + Desc::BULK, Cbw::LENGTH); break; + case DATA: urb.construct(drv._interface, + in ? drv._ep_in : drv._ep_out, + Desc::BULK, size); break; + case CSW: urb.construct(drv._interface, drv._ep_in, + Desc::BULK, Csw::LENGTH); break; + default: break; + }; + + drv._interface.update_urbs( + [this] (Urb &, Byte_range_ptr &dst) { + produce_out_content(dst); }, + [this] (Urb &, Const_byte_range_ptr &src) { + consume_in_result(src); }, + [this] (Urb&, + Interface::Packet_descriptor::Return_value r) { + completed(r); }); + + if (state == DONE) drv._state = next_state; + return s != state; + } + + bool done() { return state == DONE; } + bool failure() { return state == PROTOCOL_ERROR; } + }; + + struct Medium_state + { + enum State { + TEST, SENSE, START_STOP, WAIT, READY + } state { TEST }; + + Constructible cmd {}; + + Block_driver &drv; + + Timer::Connection timer { drv._env }; + + Medium_state(Block_driver &drv) : drv(drv) {} + + bool process(Block_driver::State next) + { + State s = state; + + if (!cmd.constructed()) + switch (state) { + case TEST: + cmd.construct(drv, + &Block_driver::_unit_ready, + &Block_driver::_no_read, + &Block_driver::_no_write, RDY_TAG, 0); + break; + case SENSE: + cmd.construct(drv, + &Block_driver::_sense, + &Block_driver::_sense_result, + &Block_driver::_no_write, REQ_TAG, + Scsi::Request_sense_response::LENGTH); + break; + case START_STOP: + cmd.construct(drv, + &Block_driver::_start_stop, + &Block_driver::_no_read, + &Block_driver::_no_write, SS_TAG, 0); + break; + case WAIT: + timer.msleep(1000); + state = TEST; + return true; + default: return false; + }; + + bool ret = cmd->process(CHECK_MEDIUM); + + if (cmd->done() || cmd->failure()) { + switch (state) { + case TEST: + state = (cmd->done()) ? READY : SENSE; + if (cmd->done()) drv._state = next; + break; + case START_STOP: + if (cmd->done()) state = WAIT; + break; + default: ; + }; + cmd.destruct(); + } + + return ret || s != state; + } + + bool done() { return state == READY; } + }; + + struct Block_command + { + Scsi_command cmd; + Request block_request; + void * const address; + size_t size; + + Block_command(Block_driver &drv, Request request, + void *addr, size_t sz, uint32_t tag) + : + cmd(drv, + &Block_driver::_block_command, + &Block_driver::_block_read, + &Block_driver::_block_write, + tag, sz, + request.operation.type == Block::Operation::Type::READ), + block_request(request), address(addr), size(sz) + { + if (!address && !size) cmd.state = Scsi_command::DONE; + } + + Block_command(const Block_command&) = delete; + void operator=(const Block_command&) = delete; + + bool process(State next_state) + { + bool ret = cmd.process(next_state); + if (cmd.done()) block_request.success = true; + return ret; + } + }; + + Scsi_command _inquiry_cmd { *this, + &Block_driver::_inquiry, + &Block_driver::_inquiry_result, + &Block_driver::_no_write, INQ_TAG, + Scsi::Inquiry_response::LENGTH }; + + Medium_state _medium_state { *this }; + + Scsi_command _capacity_10_cmd { *this, + &Block_driver::_capacity, + &Block_driver::_capacity_result, + &Block_driver::_no_write, CAP_TAG, + Scsi::Capacity_response_10::LENGTH }; + + Scsi_command _capacity_16_cmd { *this, + &Block_driver::_capacity, + &Block_driver::_capacity_result, + &Block_driver::_no_write, CAP_TAG, + Scsi::Capacity_response_16::LENGTH }; + + Constructible _block_cmd { }; + + bool _capacity(State next_state) + { + if (!_force_cmd_16) { + bool ret = _capacity_10_cmd.process(next_state); + if (!_capacity_10_cmd.done() || _block_count) + return ret; + _force_cmd_16 = true; + _state = READ_CAPACITY; + } + return _capacity_16_cmd.process(next_state); + } + + public: + + void completed(Device::Packet_descriptor::Return_value ret) + { + using Desc = Device::Packet_descriptor; + + switch (ret) { + case Desc::OK: break; + case Desc::NO_DEVICE: throw No_device(); + default: throw Io_error(); + }; + + switch (_state) { + case ALT_SETTING: + if (_reset_device) _reset.construct(_device, _interface); + _state = _reset_device ? RESET : INQUIRY; + return; + case RESET: + _reset.destruct(); + _state = INQUIRY; + return; + default: + warning("Control URB received after initialization"); + }; + } + + void apply_config(Xml_node node) + { + _writeable = node.attribute_value("writeable", false); + _active_lun = node.attribute_value("lun", 0); + _reset_device = node.attribute_value("reset_device", false); + _verbose_scsi = node.attribute_value("verbose_scsi", false); + + _reporter.enabled(node.attribute_value("report", false)); + } + + bool handle_io() + { + using Urb = Device::Urb; + using Value = Device::Packet_descriptor::Return_value; + + auto out = [] (Urb&, Byte_range_ptr&) { }; + auto in = [] (Urb&, Const_byte_range_ptr&) { }; + auto cpl = [this] (Urb&, Value r) { completed(r); }; + + switch (_state) { + case ALT_SETTING: [[fallthrough]]; + case RESET: return _device.update_urbs(out, in, cpl); + case INQUIRY: return _inquiry_cmd.process(CHECK_MEDIUM); + case CHECK_MEDIUM: return _medium_state.process(READ_CAPACITY); + case READ_CAPACITY: return _capacity(REPORT); + case REPORT: _report_device(); _state = READY; [[fallthrough]]; + case READY: + if (_block_cmd.constructed()) + return _block_cmd->process(READY); + }; - if (alt_iface.iclass != ICLASS_MASS_STORAGE - || alt_iface.isubclass != ISUBCLASS_SCSI - || alt_iface.iprotocol != IPROTO_BULK_ONLY) { - error("No mass storage SCSI bulk-only device"); return false; } - for (uint8_t i = 0; i < alt_iface.num_endpoints; i++) { - Endpoint ep = alt_iface.endpoint(i); - if (!ep.bulk()) - continue; - if (ep.address & Usb::ENDPOINT_IN) - ep_in = i; - else - ep_out = i; + bool device_ready() { return _state == READY; } + + Block_driver(Env &env, Allocator &alloc, Signal_context_capability sigh, + Xml_node config) + : + _env(env), _ep(env.ep()), _alloc(alloc), + _interface(_construct_interface(config)) + { + _device.sigh(sigh); + _interface.sigh(sigh); + + apply_config(config); + handle_io(); } - try { + + /******************************************* + ** interface to Block_session_component ** + *******************************************/ + + Block::Session::Info info() const + { + return { .block_size = _block_size, + .block_count = _block_count, + .align_log2 = log2(_block_size), + .writeable = _writeable }; + } + + Response submit(Request const &block_request, + Request_stream::Payload const &payload) + { + if (_state != READY) + return Response::REJECTED; + + using Type = Operation::Type; /* - * reset - * - * This command caused write command errors on a - * 'SanDisk Cruzer Force' (0781:557d) USB stick, so it is - * omitted by default. + * Check if there is already a request pending and wait + * until it has finished. We do this check here to implement + * 'SYNC' as barrier that waits for out-standing requests. */ - if (reset_device) { - Usb::Packet_descriptor p = iface.alloc(0); - iface.control_transfer(p, 0x21, 0xff, 0, active_interface, 100); - if (!p.succeded) { - error("Could not reset device"); - iface.release(p); - throw -1; - } - iface.release(p); + if (_block_cmd.constructed()) + return Response::RETRY; + + Operation const &op = block_request.operation; + + /* read only */ + if (_writeable == false && op.type == Type::WRITE) + return Response::REJECTED; + + /* range check */ + block_number_t const last = op.block_number + op.count; + if (last > _block_count) + return Response::REJECTED; + + /* we only support 32-bit block numbers in 10-Cmd mode */ + if (!_force_cmd_16 && last >= ~0U) + return Response::REJECTED; + + void * addr = nullptr; + size_t size = 0; + payload.with_content(block_request, [&](void *a, size_t sz) { + addr = a; size = sz; }); + + _block_cmd.construct(*this, block_request, addr, size, _new_tag()); + + /* operations currently handled as successful NOP */ + if (_block_cmd.constructed() && + (block_request.operation.type == Type::TRIM || + block_request.operation.type == Type::SYNC)) { + _block_cmd->block_request.success = true; + return Response::ACCEPTED; } - /* - * Let us do GetMaxLUN and simply ignore the return value because none - * of the devices that were tested did infact report another value than 0. - */ - Usb::Packet_descriptor p = iface.alloc(1); - iface.control_transfer(p, 0xa1, 0xfe, 0, active_interface, 100); - uint8_t max_lun = *(uint8_t*)iface.content(p); - if (p.succeded && max_lun == 0) { max_lun = 1; } - iface.release(p); - - /* - * Query device - */ - - char cbw_buffer[Cbw::LENGTH]; - Byte_range_ptr cbw_buf_range {(char *)cbw_buffer, Cbw::LENGTH}; - - /* - * We should probably execute the SCSI REPORT_LUNS command first - * but we will receive LOGICAL UNIT NOT SUPPORTED if we try to - * access an invalid unit. The user has to specify the LUN in - * the configuration anyway. - */ - - /* Scsi::Opcode::INQUIRY */ - Inquiry inq(cbw_buf_range, INQ_TAG, active_lun); - - cbw(cbw_buffer, init, true); - resp(Scsi::Inquiry_response::LENGTH, init, true); - csw(init, true); - - if (!init.inquiry) { - warning("Inquiry_cmd failed"); - throw -1; - } - - /* Scsi::Opcode::TEST_UNIT_READY */ - { - Timer::Connection timer { env }; - /* - * It might take some time for devices to get ready (e.g. the ZTE Open C - * takes 3 retries to actually present us a medium and another try to - * let us use the medium. - */ - enum { MAX_RETRIES = 10 }; - int retries; - for (retries = 0; retries < MAX_RETRIES; retries++) { - Test_unit_ready unit_ready(cbw_buf_range, RDY_TAG, active_lun); - - cbw(cbw_buffer, init, true); - csw(init, true); - - if (!init.unit_ready) { - Request_sense sense(cbw_buf_range, REQ_TAG, active_lun); - - cbw(cbw_buffer, init, true); - resp(Scsi::Request_sense_response::LENGTH, init, true); - csw(init, true); - if (!init.request_sense) { - warning("Request_sense failed"); - throw -1; - } - - if (init.no_medium) { - /* do nothing for now */ - } else if (init.try_again) { - init.try_again = false; - } else if (init.start_stop) { - - init.start_stop = false; - Start_stop start_stop(cbw_buf_range, SS_TAG, active_lun); - - cbw(cbw_buffer, init, true); - csw(init, true); - - } else break; - } else break; - - timer.msleep(1000); - } - if (retries == MAX_RETRIES) { - warning("Test_unit_ready_cmd failed"); - throw -1; - } - } - - /* - * Some devices (e.g. corsair voyager usb stick (1b1c:1a03)) failed - * when the 16-byte command was tried first and when the 10-byte - * command was tried afterwards as a fallback. - * - * For this reason, we use the 10-byte commands by default and the - * 16-byte commands only when the capacity of the device requires - * it. - */ - - /* Scsi::Opcode::READ_CAPACITY_10 */ - Read_capacity_10 read_cap(cbw_buf_range, CAP_TAG, active_lun); - - cbw(cbw_buffer, init, true); - resp(Scsi::Capacity_response_10::LENGTH, init, true); - csw(init, true); - - if (!init.read_capacity) { - warning("Read_capacity_cmd failed"); - throw -1; - } - - /** - * The READ_CAPACITY_10 count is 32-bit last() block + 1. - * If the maximum value is reached READ_CAPACITY_16 has to be used. - */ - if (init.block_count > ~(uint32_t)0U) { - - Read_capacity_16 read_cap(cbw_buf_range, CAP_TAG, active_lun); - - init.read_capacity = false; - - cbw(cbw_buffer, init, true); - resp(Scsi::Capacity_response_16::LENGTH, init, true); - csw(init, true); - - if (!init.read_capacity) { - warning("Read_capacity_cmd failed"); - throw -1; - } - - force_cmd_16 = true; - } - - _block_size = init.block_size; - _block_count = init.block_count; - - initialized = true; - device_plugged = true; - - char vendor[32]; - char product[32]; - - device.manufactorer_string.to_char(vendor, sizeof(vendor)); - device.product_string.to_char(product, sizeof(product)); - - log("Found USB device: ", (char const*)vendor, " (", - (char const*)product, ") block size: ", _block_size, - " count: ", _block_count); - - if (_report_device) - report_device(init.vendor, init.product, - init.block_count, init.block_size); - return true; - } catch (int) { - /* handle command failures */ - error("Could not initialize storage device"); - } catch (...) { - /* handle Usb::Session failures */ - error("Could not initialize storage device"); - } - return false; - } - - /** - * Execute pending request - * - * Called after the CBW has been successfully received by the device - * to initiate read/write transaction. - */ - bool execute_pending_request() - { - Usb::Interface &iface = device.interface(active_interface); - Usb::Endpoint ep = iface.endpoint(request->read() ? ep_in : ep_out); - Usb::Packet_descriptor p = iface.alloc(request->size); - - if (request->write()) - memcpy(iface.content(p), (void *)request->address, request->size); - - iface.bulk_transfer(p, ep, false, this); - - return true; - } - - void wakeup_client(bool success) - { - request->block_request.success = success; - request->completed = true; - - /* - * An I/O operation completed, thus trigger block-request handling on - * component service level. - */ - block_request_handler.local_submit(); - } - - /** - * Handle packet completion - * - * This method is called several times while doing one transaction. First - * the CWB is sent, than the payload read or written. At the end, the CSW - * is requested. - */ - void complete(Packet_descriptor &p) override - { - Usb::Interface iface = device.interface(active_interface); - - if (p.type != Packet_descriptor::BULK) { - error("No BULK packet"); - iface.release(p); - return; - } - - if (!p.succeded) { - error("complete error: packet not succeded"); - if (request_pending()) { - error("request pending: tag: ", active_tag, " read: ", - request->read(), " buffer: ", Hex(request->address), - " lba: ", request->block_request.operation.block_number, - " size: ", request->size); - wakeup_client(false); - } - iface.release(p); - return; - } - - static bool request_executed = false; - if (!p.read_transfer()) { - /* send read/write request */ - if (request_pending()) { - - /* - * The CBW was successfully sent to the device, now read/write the - * actual content. - */ - if (!request_executed) { - request_executed = execute_pending_request(); - } else { - /* the content was successfully written, get the CSW */ - csw(*this); - } - } - - iface.release(p); - return; - } - - int actual_size = p.transfer.actual_size; - if (actual_size < 0) { - error("Transfer actual size: ", actual_size); - actual_size = 0; - } - - /* the size indicates an IN I/O packet */ - if ((uint32_t)actual_size >= _block_size) { - if (request_pending()) { - - /* the content was successfully read, get the CSW */ - memcpy((void *)request->address, iface.content(p), actual_size); - csw(*this); - } - - iface.release(p); - return; - } - - /* when ending up here, we should have gotten an CSW packet */ - if (actual_size != Csw::LENGTH) - warning("This is not the actual size you are looking for"); - - do { - Csw csw({(char *)iface.content(p), p.size()}); - - uint32_t const sig = csw.sig(); - if (sig != Csw::SIG) { - error("CSW signature does not match: ", - Hex(sig, Hex::PREFIX, Hex::PAD)); - break; - } - - uint32_t const tag = csw.tag(); - if (tag != active_tag) { - error("CSW tag mismatch. Got ", tag, " expected: ", - active_tag); - break; - } - - uint32_t const status = csw.sts(); - if (status != Csw::PASSED) { - error("CSW failed: ", Hex(status, Hex::PREFIX, Hex::PAD), - " read: ", request->read(), " buffer: ", (void *)request->address, - " lba: ", request->block_request.operation.block_number, - " size: ", request->size); - break; - } - - uint32_t const dr = csw.dr(); - if (dr) { - warning("CSW data residue: ", dr, " not considered"); - } - - /* ack Block::Packet_descriptor */ - request_executed = false; - wakeup_client(true); - } while (0); - - iface.release(p); - } - - /** - * Parse configuration - */ - void parse_config(Xml_node node) - { - _writeable = node.attribute_value("writeable", false); - _report_device = node.attribute_value("report", false); - active_interface = node.attribute_value("interface", 0); - active_lun = node.attribute_value("lun", 0); - reset_device = node.attribute_value("reset_device", false); - verbose_scsi = node.attribute_value("verbose_scsi", false); - - active_alt_setting = - node.attribute_value("alt_setting", INVALID_ALT_SETTING); - } - - /** - * Constructor - * - * \param alloc allocator used by Usb::Connection - * \param ep Server::Endpoint - * \param sigh signal context used for annoucing Block service - */ - Block_driver(Env &env, Allocator &alloc, - Genode::Signal_context_capability sigh, - Signal_handler
&block_request_handler) - : - env(env), ep(env.ep()), announce_sigh(sigh), alloc(&alloc), - device(&alloc, usb, ep), block_request_handler(block_request_handler) - { - parse_config(config.xml()); - reporter.enabled(true); - - /* USB device gets initialized by handle_state_change() */ - handle_state_change(); - } - - ~Block_driver() - { - Usb::Interface &iface = device.interface(active_interface); - iface.release(); - } - - /** - * Send CBW - */ - void send_cbw(block_number_t lba, block_count_t count, bool read) - { - uint32_t const t = new_tag(); - - /** - * Assuming a minimal packet size of 512. Total packet stream buffer - * should not exceed 16-bit block count value. - */ - static_assert((PACKET_STREAM_BUF_SIZE / 512UL) < (uint16_t)~0UL); - uint16_t c = (uint16_t) count; - - /** - * We check for lba fitting 32-bit value for 10-Cmd mode - * before entering this function - */ - char cb[Cbw::LENGTH]; - Byte_range_ptr cb_range {(char *)cb, Cbw::LENGTH}; - if (read) { - if (force_cmd_16) Read_16 r(cb_range, t, active_lun, lba, c, _block_size); - else Read_10 r(cb_range, t, active_lun, (uint32_t)lba, c, _block_size); - } else { - if (force_cmd_16) Write_16 w(cb_range, t, active_lun, lba, c, _block_size); - else Write_10 w(cb_range, t, active_lun, (uint32_t)lba, c, _block_size); - } - - cbw(cb, *this); - } - - /******************************************* - ** interface to Block_session_component ** - *******************************************/ - - Block::Session::Info info() const - { - return { .block_size = _block_size, - .block_count = _block_count, - .align_log2 = log2(_block_size), - .writeable = _writeable }; - } - - Response submit(Request const &block_request, - Request_stream::Payload const &payload) - { - if (device_plugged == false) - return Response::REJECTED; - - /* - * Check if there is already a request pending and wait - * until it has finished. We do this check here to implement - * 'SYNC' as barrier that waits for out-standing requests. - */ - if (request_pending()) - return Response::RETRY; - - Operation const &op = block_request.operation; - - /* read only */ - if (info().writeable == false && - op.type == Operation::Type::WRITE) - return Response::REJECTED; - - /* range check */ - block_number_t const last = op.block_number + op.count; - if (last > info().block_count) - return Response::REJECTED; - - /* we only support 32-bit block numbers in 10-Cmd mode */ - if (!force_cmd_16 && last >= ~0U) - return Response::REJECTED; - - request.construct(block_request, payload); - - /* operations currently handled as successful NOP */ - if (request->block_request.operation.type == Operation::Type::TRIM - || request->block_request.operation.type == Operation::Type::SYNC) { - request->block_request.success = true; - request->completed = true; return Response::ACCEPTED; } - /* execute */ - send_cbw(op.block_number, op.count, request->read()); - - return Response::ACCEPTED; - } - - template - void with_completed(FUNC const &fn) - { - if (request_pending() == false || request->completed == false) return; - - fn(request->block_request); - - request.destruct(); - } + template + void with_completed(FUNC const &fn) + { + if (_block_cmd.constructed() && + _block_cmd->block_request.success) { + fn(_block_cmd->block_request); + _block_cmd.destruct(); + } + } }; @@ -988,59 +744,79 @@ struct Usb::Block_session_component : Rpc_object, struct Usb::Main : Rpc_object> { - Env &env; - Heap heap { env.ram(), env.rm() }; + Env &env; + Heap heap { env.ram(), env.rm() }; + + Attached_rom_dataspace config { env, "config" }; Constructible block_ds { }; Constructible block_session { }; - Signal_handler
announce_dispatcher { - env.ep(), *this, &Usb::Main::announce }; + Signal_handler
config_handler { env.ep(), *this, + &Main::update_config }; + Signal_handler
io_handler { env.ep(), *this, &Main::handle}; - Signal_handler
request_handler { - env.ep(), *this, &Main::handle_requests }; + Block_driver driver { env, heap, io_handler, config.xml() }; - Block_driver driver { env, heap, announce_dispatcher, request_handler }; + enum State { INIT, ANNOUNCED } state { INIT }; - void announce() + void update_config() { - env.parent().announce(env.ep().manage(*this)); + config.update(); + driver.apply_config(config.xml()); } - /** - * There can only be one request in flight for this driver, so keep it simple - */ - void handle_requests() + void handle() { - if (!block_session.constructed()) return; + try { + bool progress = true; - bool progress; - do { - progress = false; + while (progress) { + while (driver.handle_io()) ; - /* ack and release possibly pending packet */ - block_session->try_acknowledge([&] (Block_session_component::Ack &ack) { - driver.with_completed([&] (Block::Request const &request) { - ack.submit(request); - progress = true; + if (!driver.device_ready()) + return; + + if (state == INIT) { + env.parent().announce(env.ep().manage(*this)); + state = ANNOUNCED; + } + + if (!block_session.constructed()) + return; + + progress = false; + + /* ack and release possibly pending packet */ + block_session->try_acknowledge([&] (Block_session_component::Ack &ack) { + driver.with_completed([&] (Block::Request const &request) { + ack.submit(request); + progress = true; + }); }); - }); - block_session->with_requests([&] (Request const request) { + block_session->with_requests([&] (Request const request) { - Response response = Response::RETRY; + Response response = Response::RETRY; - block_session->with_payload([&] (Request_stream::Payload const &payload) { - response = driver.submit(request, payload); + block_session->with_payload([&] (Request_stream::Payload const &payload) { + response = driver.submit(request, payload); + }); + if (response != Response::RETRY) + progress = true; + + return response; }); - if (response != Response::RETRY) - progress = true; + } - return response; - }); - } while (progress); - - block_session->wakeup_client_if_needed(); + block_session->wakeup_client_if_needed(); + } catch (Io_error&) { + error("An unrecoverable USB error occured, will halt!"); + sleep_forever(); + } catch (No_device&) { + warning("The device has vanished, will halt."); + sleep_forever(); + } } @@ -1067,8 +843,7 @@ struct Usb::Main : Rpc_object> } block_ds.construct(env.ram(), env.rm(), ds_size); - block_session.construct(env, block_ds->cap(), request_handler, - driver.info()); + block_session.construct(env, block_ds->cap(), io_handler, driver.info()); return block_session->cap(); } @@ -1084,7 +859,7 @@ struct Usb::Main : Rpc_object> block_ds.destruct(); } - Main(Env &env) : env(env) { } + Main(Env &env) : env(env) { config.sigh(config_handler); } }; diff --git a/repos/os/src/drivers/usb_block/mmio.h b/repos/os/src/drivers/usb_block/mmio.h new file mode 100644 index 0000000000..032f62daee --- /dev/null +++ b/repos/os/src/drivers/usb_block/mmio.h @@ -0,0 +1,74 @@ +/* + * \brief Extension of MMIO framework with const read-only MMIO regions + * \author Stefan Kalkowski + * \date 2024-02-21 + */ + +/* + * Copyright (C) 2024 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 _USB_BLOCK__MMIO_H_ +#define _USB_BLOCK__MMIO_H_ + +#include + +namespace Genode { + class Const_mmio_plain_access; + template class Const_mmio; +}; + + +/** + * Plain access implementation for MMIO + */ +class Genode::Const_mmio_plain_access +{ + friend Register_set_plain_access; + + private: + + Const_byte_range_ptr const _range; + + template + inline ACCESS_T _read(off_t const &offset) const { + return *(ACCESS_T volatile *)(_range.start + offset); } + + public: + + Const_mmio_plain_access(Const_byte_range_ptr const &range) + : _range(range.start, range.num_bytes) { } + + Const_byte_range_ptr range_at(off_t offset) const { + return {_range.start + offset, _range.num_bytes - offset}; } + + Const_byte_range_ptr range() const { return range_at(0); } + + addr_t base() const { return (addr_t)range().start; } +}; + + +template +struct Genode::Const_mmio : Const_mmio_plain_access, + Register_set +{ + static constexpr size_t SIZE = MMIO_SIZE; + + class Range_violation : Exception { }; + + Const_mmio(Const_byte_range_ptr const &range) + : + Const_mmio_plain_access(range), + Register_set(*static_cast(this)) + { + if (range.num_bytes < SIZE) { + error("MMIO range is unexpectedly too small"); + throw Range_violation { }; + } + } +}; + +#endif /* _USB_BLOCK__MMIO_H_ */ diff --git a/repos/os/src/drivers/usb_block/scsi.h b/repos/os/src/drivers/usb_block/scsi.h index 88ecf28255..695f8305fd 100644 --- a/repos/os/src/drivers/usb_block/scsi.h +++ b/repos/os/src/drivers/usb_block/scsi.h @@ -16,7 +16,8 @@ #include #include -#include + +#include namespace Scsi { @@ -84,7 +85,7 @@ namespace Scsi { * SCSI command responses ** ***************************/ -struct Scsi::Inquiry_response : Genode::Mmio<0x24> +struct Scsi::Inquiry_response : Genode::Const_mmio<0x24> { /* * Minimum response length is 36 bytes. @@ -105,7 +106,8 @@ struct Scsi::Inquiry_response : Genode::Mmio<0x24> struct Pid : Register_array<0x10, 8, 16, 8> { }; /* product identification */ struct Rev : Register_array<0x20, 8, 4, 8> { }; /* product revision level */ - Inquiry_response(Byte_range_ptr const &range) : Mmio(range) { } + Inquiry_response(Const_byte_range_ptr const &range, bool verbose) + : Const_mmio(range) { if (verbose) dump(); } bool sbc() const { return read
() == 0x00; } bool removable() const { return read(); } @@ -132,7 +134,7 @@ struct Scsi::Inquiry_response : Genode::Mmio<0x24> }; -struct Scsi::Request_sense_response : Genode::Mmio<0x13> +struct Scsi::Request_sense_response : Genode::Const_mmio<0x13> { enum { LENGTH = 18 }; @@ -153,7 +155,8 @@ struct Scsi::Request_sense_response : Genode::Mmio<0x13> struct Fru : Register<0xe, 8> { }; /* field replaceable unit code */ struct Sks : Register<0xf, 32> { }; /* sense key specific (3 byte) */ - Request_sense_response(Byte_range_ptr const &range) : Mmio(range) { } + Request_sense_response(Const_byte_range_ptr const &range, bool verbose) + : Const_mmio(range) { if (verbose) dump(); } void dump() { @@ -167,14 +170,15 @@ struct Scsi::Request_sense_response : Genode::Mmio<0x13> }; -struct Scsi::Capacity_response_10 : Genode::Mmio<0x8> +struct Scsi::Capacity_response_10 : Genode::Const_mmio<0x8> { enum { LENGTH = 8 }; struct Lba : Register<0x0, 32> { }; struct Bs : Register<0x4, 32> { }; - Capacity_response_10(Byte_range_ptr const &range) : Mmio(range) { } + Capacity_response_10(Const_byte_range_ptr const &range, bool verbose) + : Const_mmio(range) { if (verbose) dump(); } uint32_t last_block() const { return be(read()); } uint32_t block_size() const { return be(read()); } @@ -188,14 +192,15 @@ struct Scsi::Capacity_response_10 : Genode::Mmio<0x8> }; -struct Scsi::Capacity_response_16 : Genode::Mmio<0xc> +struct Scsi::Capacity_response_16 : Genode::Const_mmio<0xc> { enum { LENGTH = 32 }; struct Lba : Register<0x0, 64> { }; struct Bs : Register<0x8, 32> { }; - Capacity_response_16(Byte_range_ptr const &range) : Mmio(range) { } + Capacity_response_16(Const_byte_range_ptr const &range, bool verbose) + : Const_mmio(range) { if (verbose) dump(); } uint64_t last_block() const { return be(read()); } uint32_t block_size() const { return be(read()); } diff --git a/repos/os/src/lib/genode_c_api/usb.cc b/repos/os/src/lib/genode_c_api/usb.cc index 3d42b17cb5..78a54b979e 100644 --- a/repos/os/src/lib/genode_c_api/usb.cc +++ b/repos/os/src/lib/genode_c_api/usb.cc @@ -14,706 +14,1455 @@ #include #include #include +#include +#include +#include #include +#include #include #include -#include +#include +#include #include #include using namespace Genode; - -struct Device { - genode_usb_vendor_id_t vendor; - genode_usb_product_id_t product; - genode_usb_class_num_t cla; - genode_usb_bus_num_t bus; - genode_usb_dev_num_t dev; - genode_usb_session * usb_session { nullptr }; - - Device(genode_usb_vendor_id_t vendor, - genode_usb_product_id_t product, - genode_usb_class_num_t cla, - genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev) - : vendor(vendor), product(product), - cla(cla), bus(bus), dev(dev) {} - - using Label = String<64>; - - Label label() { return Label("usb-", bus, "-", dev); } -}; +using namespace Usb; -class Session_id +using String_item = String<64>; + +static String_item string_item(genode_usb_dev_string_item_t func, void *opaque_data) { + char buf[String_item::capacity()] { }; + + func({ buf, sizeof(buf) }, opaque_data); + + char *first = buf; + char *last = buf + sizeof(buf) - 1; + + /* skip leading whitespace */ + while (*first == ' ') ++first; + + /* trim trailing whitespace */ + while (last >= first && (*last == 0 || *last == ' ')) + --last; + + return { Cstring(first, last + 1 - first) }; +} + + +/** + * The API of the Registry class is favored in contrast to the ancient List, + * but due to different potentially blocking user-level-scheduled threads, + * e.g., within DDE Linux we cannot use a mutex-guarded registry. + * Those threads are logical ones, and do not have real concurrency. Moreover, + * only one and the same threads insert/remove/handle a single device. + * Therefore, thread safety is not an issue. + */ +template +struct Reg_list +{ + public: + + class Element : public List::Element + { + private: + + friend class Reg_list; + + Reg_list &_registry; + T &_object; + + /* + * Noncopyable + */ + Element(Element const &); + Element &operator = (Element const &); + + public: + + Element(Reg_list &r, T &o); + ~Element(); + }; + private: - genode_usb_session_handle_t const _id; - unsigned _ref_cnt { 0 }; + List _elements { }; public: - Session_id(genode_usb_session_handle_t id) : _id(id) {} + template + void for_each(FN const & fn) + { + Element *e = _elements.first(), *next = nullptr; + for ( ; e; e = next) { + next = e->next(); + fn(e->_object); + } + } - genode_usb_session_handle_t id() { return _id; } + template + void for_each(FN const & fn) const + { + Element const *e = _elements.first(), *next = nullptr; + for ( ; e; e = next) { + next = e->next(); + fn(e->_object); + } + } +}; - void inc() { _ref_cnt++; } - void dec() { _ref_cnt--; } - bool used() { return _ref_cnt; } + +template Reg_list::Element::Element(Reg_list &r, T &o) +: _registry(r), _object(o) { r._elements.insert(this); } + + + +template Reg_list::Element::~Element() { + _registry._elements.remove(this); } + + +struct genode_usb_endpoint : Reg_list::Element +{ + genode_usb_endpoint_descriptor desc; + + genode_usb_endpoint(Reg_list ®istry, + genode_usb_endpoint_descriptor desc) + : + Reg_list::Element(registry, *this), + desc(desc) {} +}; + + +struct genode_usb_interface : Reg_list::Element +{ + String_item const info; + genode_usb_interface_descriptor const desc; + bool active; + Reg_list endpoints {}; + + genode_usb_interface(Reg_list ®istry, + String_item const &info, + genode_usb_interface_descriptor desc, + bool active) + : + Reg_list::Element(registry, *this), + info(info), desc(desc), active(active) {} +}; + + +struct genode_usb_configuration : Reg_list::Element +{ + genode_usb_config_descriptor const desc; + bool active; + Reg_list interfaces {}; + + genode_usb_configuration(Reg_list ®istry, + genode_usb_config_descriptor desc, + bool active) + : + Reg_list::Element(registry, *this), + desc(desc), active(active) {} +}; + + +struct genode_usb_device : Reg_list::Element +{ + genode_usb_bus_num_t const bus; + genode_usb_dev_num_t const dev; + genode_usb_speed_t const speed; + String_item const manufacturer; + String_item const product; + genode_usb_device_descriptor const desc; + Reg_list configs {}; + + genode_usb_device(Reg_list ®istry, + genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_speed_t speed, + String_item const &manufacturer, + String_item const &product, + genode_usb_device_descriptor desc) + : + Reg_list::Element(registry, *this), + bus(bus), dev(dev), speed(speed), + manufacturer(manufacturer), product(product), desc(desc) {} + + using Label = String<64>; + + Label label() const { return Label("usb-", bus, "-", dev); } + + String<32> speed_to_string() const + { + switch (speed) { + case GENODE_USB_SPEED_LOW: return "low"; + case GENODE_USB_SPEED_FULL: return "full"; + case GENODE_USB_SPEED_HIGH: return "high"; + case GENODE_USB_SPEED_SUPER: return "super"; + case GENODE_USB_SPEED_SUPER_PLUS: return "super_plus"; + case GENODE_USB_SPEED_SUPER_PLUS_2X2: return "super_plus_2x2"; + }; + + return "full"; + } + + void generate(Xml_generator & xml, bool acquired) const; +}; + + +struct Dma_allocator : Interface +{ + virtual genode_shared_dataspace * alloc_dma_dataspace(size_t size) = 0; + + virtual void free_dma_dataspace(genode_shared_dataspace * ds, size_t size) = 0; +}; + + +template +class Packet_handler +: + public Genode::Rpc_object, + private Reg_list::Element +{ + protected: + + enum State { CONNECTED, DISCONNECTED }; + + using Packet_descriptor = typename SESSION::Packet_descriptor; + using Tx = typename SESSION::Tx; + static constexpr size_t MAX_PACKETS = SESSION::TX_QUEUE_SIZE; + + Env &_env; + Dma_allocator &_alloc; + State _state { CONNECTED }; + size_t _buf_size; + genode_shared_dataspace &_ds; + Packet_stream_tx::Rpc_object _tx; + + Constructible _packets[MAX_PACKETS] { }; + + Capability _cap; + + enum Index_error { OUT_OF_BOUNDS }; + using Index_result = Attempt; + + Index_result _idx_avail() + { + size_t idx = 0; + for (auto const &p : _packets) { + if (!p.constructed()) + break; + ++idx; + } + if (idx == MAX_PACKETS) + return OUT_OF_BOUNDS; + return idx; + } + + bool _packet_avail() + { + unsigned packets_in_flight = 0; + for (auto const &p : _packets) + if (p.constructed()) packets_in_flight++; + + return _tx.sink()->packet_avail() && + (_tx.sink()->ack_slots_free() > packets_in_flight); + } + + template + void _for_each_packet(FN const & fn) + { + while (true) { + size_t idx = ~0UL; + _idx_avail().with_result([&] (size_t i) { idx = i; }, + [&] (Index_error) {}); + + if (idx > MAX_PACKETS || !_packet_avail()) + break; + + Packet_descriptor p = _tx.sink()->try_get_packet(); + if (!_tx.sink()->packet_valid(p)) + break; + + _packets[idx].construct(p); + fn(_packets[idx]); + } + } + + enum Packet_error { NO_PACKET }; + using Finish_packet_result = Attempt; + + Finish_packet_result _finish_packet(genode_usb_request_handle_t handle) + { + if ((addr_t)handle < (addr_t)&_packets || + (addr_t)handle > ((addr_t)&_packets + sizeof(_packets))) + return NO_PACKET; + + Constructible & cp = + *reinterpret_cast*>(handle); + if (!cp.constructed()) + return NO_PACKET; + + Packet_descriptor p = *cp; + cp.destruct(); + return p; + } + + void _ack(Packet_descriptor::Return_value v, + size_t actual_size, + Packet_descriptor p) + { + p.return_value = v; + p.payload_return_size = actual_size; + if (!_tx.sink()->try_ack_packet(p)) + error("USB client's ack queue run full, looses packet ack!"); + } + + virtual void + _handle_request(Constructible &cp, + genode_buffer_t payload, + genode_usb_req_callback_t const callback, + void *opaque_data) = 0; + public: + + Packet_handler(Env &env, + Dma_allocator &alloc, + Reg_list ®istry, + size_t buf_size, + T &object, + Signal_context_capability sigh_cap) + : + Reg_list::Element(registry, object), + _env(env), + _alloc(alloc), + _buf_size(buf_size), + _ds(*alloc.alloc_dma_dataspace(buf_size)), + _tx(genode_shared_dataspace_capability(&_ds), env.rm(), + env.ep().rpc_ep()), + _cap(env.ep().rpc_ep().manage(this)) + { + _tx.sigh_packet_avail(sigh_cap); + _tx.sigh_ready_to_ack(sigh_cap); + } + + + ~Packet_handler() + { + _env.ep().rpc_ep().dissolve(this); + + if (_tx.dataspace().valid()) + _alloc.free_dma_dataspace(&_ds, _buf_size); + } + + Capability session_cap() { return _cap; } + Capability tx_cap() { return _tx.cap(); } + + bool connected() const { return _state == CONNECTED; } + + virtual void disconnect() { _state = DISCONNECTED; } + + virtual void wakeup() { _tx.sink()->wakeup(); } + + virtual bool request(genode_usb_req_callback_t const callback, + void *opaque_data) + { + bool ret = false; + _for_each_packet([&] (Constructible &cp) { + void * addr = _tx.sink()->packet_content(*cp); + if (addr) + addr = (void*)(genode_shared_dataspace_local_address(&_ds) + + ((addr_t)addr - + (addr_t)_tx.sink()->ds_local_base())); + genode_buffer_t buf { addr, addr ? cp->size() : 0 }; + _handle_request(cp, buf, callback, opaque_data); + ret = true; + }); + return ret; + } + + virtual void handle_disconnected() + { + if (_state != DISCONNECTED) + return; + + while (_packet_avail()) { + Packet_descriptor p = _tx.sink()->try_get_packet(); + if (_tx.sink()->packet_valid(p)) + _ack(Packet_descriptor::NO_DEVICE, 0, p); + } + } +}; + + +class Session_component; + +class Interface_component +: public Packet_handler +{ + private: + + using Base = Packet_handler; + + friend class Device_component; + + enum { MAX_EPS = 30, UNUSED = 255 }; + + uint8_t _iface_idx; + uint8_t _ep_addresses[MAX_EPS] { UNUSED }; + + void + _handle_request(Constructible &cp, + genode_buffer_t payload, + genode_usb_req_callback_t const callback, + void *opaque_data) override; + + public: + + Interface_component(Env &env, + Reg_list ®istry, + Session_component &session, + genode_usb_device::Label label, + size_t buf_size, + Signal_context_capability sigh_cap, + uint8_t iface_idx); + + bool handle_response(genode_usb_request_handle_t request_id, + genode_usb_request_ret_t return_value, + uint32_t *actual_sizes); +}; + + +class Device_component +: public Packet_handler +{ + private: + + using Base = Packet_handler; + + friend class Session_component; + + Env &_env; + Heap &_heap; + Session_component &_session; + bool const _controls; + genode_usb_device::Label const _device_label; + Reg_list _interfaces {}; + Signal_context_capability _sigh_cap; + bool _warn_once { true }; + + void + _handle_request(Constructible &cp, + genode_buffer_t payload, + genode_usb_req_callback_t const callback, + void *opaque_data) override; + + public: + + Device_component(Env &env, + Heap &heap, + Reg_list ®istry, + Session_component &session, + bool const controls, + genode_usb_device::Label const &device, + Signal_context_capability sigh_cap); + ~Device_component(); + + bool request(genode_usb_req_callback_t const callback, + void *opaque_data) override; + bool handle_response(genode_usb_request_handle_t request_id, + genode_usb_request_ret_t return_value, + uint32_t *actual_sizes); + void disconnect() override; + void wakeup() override; + void handle_disconnected() override; + + + /****************************** + ** Device_session interface ** + ******************************/ + + Interface_capability acquire_interface(uint8_t index, size_t buf_size); + void release_interface(Interface_capability cap); }; class Root; - -class genode_usb_session : public Usb::Session_rpc_object +class Session_component +: + public Dma_allocator, + public Session_object, + private Reg_list::Element, + private Dynamic_rom_session::Xml_producer { private: - friend class ::Root; + friend class Device_component; - enum { MAX_PACKETS_IN_FLIGHT = 32 }; + Env &_env; + ::Root &_root; + Reg_list &_sessions; + Reg_list &_devices; + Attached_rom_dataspace &_config; + Signal_context_capability _sigh_cap; + genode_shared_dataspace_alloc_attach_t _alloc_fn; + genode_shared_dataspace_free_t _free_fn; + genode_usb_dev_release_t _release_fn; - Constructible packets[MAX_PACKETS_IN_FLIGHT] { }; + Constrained_ram_allocator _env_ram { _env.pd(), + _ram_quota_guard(), + _cap_quota_guard() }; + Heap _heap { _env_ram, _env.rm() }; + Dynamic_rom_session _rom_session { _env.ep(), _env_ram, + _env.rm(), *this }; - ::Root & _root; - genode_shared_dataspace * _ds; - Signal_context_capability _sigh_state_cap {}; - Session_id & _id; - Session_label const _label; - List_element _le { this }; + Reg_list _device_sessions {}; - unsigned _packets_in_flight(); - void _ack(int err, Usb::Packet_descriptor p); - - genode_usb_buffer _buffer_of_packet(Usb::Packet_descriptor p); + enum State { ACTIVE, IN_DESTRUCTION } _state { ACTIVE }; /* * Non_copyable */ - genode_usb_session(const genode_usb_session&); - genode_usb_session & operator=(const genode_usb_session&); + Session_component(const Session_component&); + Session_component & operator=(const Session_component&); + + template + void _device_policy(genode_usb_device const & d, FN const &fn); + + bool _matches(genode_usb_device const & device); + + Device_capability _acquire(genode_usb_device::Label const &device, + bool const controls); + void _release(Device_component &dc); public: - genode_usb_session(::Root & root, - genode_shared_dataspace * ds, - Env & env, - Session_id & id, - Signal_context_capability cap, - Session_label label); + Session_component(Env &env, + ::Root &root, + Reg_list ®istry, + Reg_list &devices, + Attached_rom_dataspace &config, + Signal_context_capability sigh_cap, + genode_shared_dataspace_alloc_attach_t alloc_fn, + genode_shared_dataspace_free_t free_fn, + genode_usb_dev_release_t release_fn, + Label const &label, + Resources const &resources, + Diag const &diag); - virtual ~genode_usb_session() {} + ~Session_component(); - /* - * 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); + void announce_device(genode_usb_device const & device); + void discontinue_device(genode_usb_device const & device); + void update_policy(); + void update_devices_rom(); - /* - * 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); + bool acquired(genode_usb_device const &dev); + bool request(genode_usb_device const &dev, + genode_usb_req_callback_t const callback, + void *opaque_data); + bool handle_response(genode_usb_request_handle_t request_id, + genode_usb_request_ret_t return_value, + uint32_t *actual_sizes); + void handle_disconnected(); + void wakeup(); - /* - * Notify session about state changes - */ - void notify(); + bool matches(genode_usb_device::Label label, uint8_t iface_idx); - /* - * Acknowledge all remaining requests in the packet stream - */ - void flush_packet_stream(); + template + void for_each_ep(genode_usb_device::Label label, + uint8_t iface_idx, FN const &fn); + + void set_interface(genode_usb_device::Label label, + uint16_t num, uint16_t alt); + void set_configuration(genode_usb_device::Label label, + uint16_t num); + + /***************************** + ** Dma_allocator interface ** + *****************************/ + + genode_shared_dataspace * alloc_dma_dataspace(size_t size) override + { + Ram_quota const needed_ram { size + 4096 }; + + if (!_ram_quota_guard().have_avail(needed_ram)) + throw Out_of_ram(); + + _cap_quota_guard().replenish(Cap_quota{2}); + _ram_quota_guard().replenish(needed_ram); + + return _alloc_fn(size); + } + + void free_dma_dataspace(genode_shared_dataspace *ds, size_t size) override + { + _cap_quota_guard().replenish(Cap_quota{2}); + _ram_quota_guard().replenish(Ram_quota{size+4096}); + _free_fn(ds); + } /*************************** ** 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; + Rom_session_capability devices_rom() override; + Device_capability acquire_device(Device_name const &name) override; + Device_capability acquire_single_device() override; + void release_device(Device_capability) override; + + + /******************************************* + ** Dynamic_rom_session::Xml_producer API ** + *******************************************/ + + void produce_xml(Xml_generator &xml) override; }; -class Root : public Root_component +class Root : Sliced_heap, public Root_component { private: - enum { MAX_DEVICES = 32, MAX_SESSIONS = MAX_DEVICES }; + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Signal_context_capability _sigh_cap; + Attached_rom_dataspace _config { _env, "config" }; + Signal_handler _config_handler { _env.ep(), *this, + &Root::_config_update }; + Reporter _config_reporter { _env, "config" }; + Reg_list _devices {}; + bool _announced { false }; - Env & _env; - Signal_context_capability _sigh_cap; - Attached_rom_dataspace _config { _env, "config" }; - Signal_handler _config_handler { _env.ep(), *this, - &Root::_config_update }; - Reporter _config_reporter { _env, "config" }; - Constructible _session_ids[MAX_SESSIONS]; - Constructible _devices[MAX_DEVICES]; - bool _announced { false }; + Constructible _device_reporter {}; + Reg_list _sessions {}; - Constructible _device_reporter {}; - List> _sessions {}; + genode_shared_dataspace_alloc_attach_t _alloc_fn; + genode_shared_dataspace_free_t _free_fn; + genode_usb_dev_release_t _release_fn; Root(const Root&); Root & operator=(const Root&); - genode_usb_session * _create_session(const char * args, + Session_component * _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 (auto &device : _devices) - if (device.constructed()) - fn(*device); - } - - 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); + void _upgrade_session(Session_component *, const char *) override; void _config_update(); void _announce_service(); public: - Root(Env & env, Allocator & alloc, Signal_context_capability); + Root(Env &env, + Signal_context_capability sigh_cap, + genode_shared_dataspace_alloc_attach_t alloc_fn, + genode_shared_dataspace_free_t free_fn, + genode_usb_dev_release_t release_fn); - void announce_device(genode_usb_vendor_id_t vendor, - genode_usb_product_id_t product, - genode_usb_class_num_t cla, - genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev); + /* + * Update report about existing USB devices + */ + void report(); + + void device_add_endpoint(struct genode_usb_interface *iface, + genode_usb_endpoint_descriptor desc); + void device_add_interface(struct genode_usb_configuration *cfg, + genode_usb_dev_string_item_t info_string, + genode_usb_interface_descriptor desc, + genode_usb_dev_add_endp_t callback, + void *opaque_data, + bool active); + void device_add_configuration(struct genode_usb_device *dev, + genode_usb_config_descriptor desc, + genode_usb_dev_add_iface_t callback, + void *opaque_data, + bool active); + void announce_device(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_speed_t speed, + genode_usb_dev_string_item_t manufacturer_string, + genode_usb_dev_string_item_t product_string, + genode_usb_device_descriptor desc, + genode_usb_dev_add_config_t callback, + void *opaque_data); void discontinue_device(genode_usb_bus_num_t bus, genode_usb_dev_num_t 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(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev); + bool acquired(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev); + + bool request(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_req_callback_t const callback, + void *opaque_data); + + void handle_response(genode_usb_request_handle_t request_id, + genode_usb_request_ret_t return_value, + uint32_t *actual_sizes); /* - * Finds the bus/device numbers of the device associated for a - * given session - * - * \returns true if a device is associated, otherwise false + * Acknowledge requests from sessions with disconnected devices */ - bool device_associated(genode_usb_session * session, - genode_usb_bus_num_t & bus, - genode_usb_dev_num_t & dev); + void handle_disconnected_sessions(); - /* - * Apply a functor to the session with the given handle - */ - template - void session(genode_usb_session_handle_t id, FUNC const & fn); - - /* - * Acknowledge requests from sessions without device - */ - void handle_empty_sessions(); - - void decrement_session_id(genode_usb_session_handle_t id); + void wakeup(); }; -static ::Root * _usb_root = nullptr; -static genode_usb_rpc_callbacks * _callbacks = nullptr; +static ::Root * _usb_root = 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(); -} - - -void genode_usb_session::flush_packet_stream() -{ - /* ack packets in flight */ - for (auto &p : packets) { - if (!p.constructed()) - continue; - _ack(Usb::Packet_descriptor::NO_DEVICE_ERROR, *p); - p.destruct(); - } - - /* ack all packets in request stream */ - while (sink()->packet_avail() && sink()->ack_slots_free()) - _ack(Usb::Packet_descriptor::NO_DEVICE_ERROR, sink()->get_packet()); -} - - -bool genode_usb_session::plugged() -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t dev; - return _root.device_associated(this, bus, dev); -} - - -void genode_usb_session::config_descriptor(Usb::Device_descriptor * device_descr, - Usb::Config_descriptor * config_descr) -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t 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) -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t 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) -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t 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) -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t 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(); - - if (len >= 0xff) - error("Unsupported length of alt_setting iface extra!"); - - interface_data->length = (uint8_t)len; - return len; -} - - -void genode_usb_session::endpoint_descriptor(unsigned interface_num, - unsigned alt_setting, - unsigned endpoint_num, - Usb::Endpoint_descriptor * endp) -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t 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 iface) -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t dev; - if (!_root.device_associated(this, bus, dev)) - throw Device_not_found(); - - if (_callbacks->claim_fn(bus, dev, iface)) - throw Interface_not_found(); -} - - -void genode_usb_session::release_interface(unsigned iface) -{ - genode_usb_bus_num_t bus; - genode_usb_dev_num_t dev; - if (!_root.device_associated(this, bus, dev)) - throw Device_not_found(); - - if (_callbacks->release_fn(bus, dev, iface)) - throw Interface_not_found(); -} - - -unsigned genode_usb_session::_packets_in_flight() -{ - unsigned ret = 0; - for (auto const &p : packets) - if (p.constructed()) ret++; - return ret; -} - - -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; - try { - sink()->acknowledge_packet(p); - } catch(...) { - warning("USB client's ack queue run full, looses packet ack!"); - } -} - - -genode_usb_buffer genode_usb_session::_buffer_of_packet(Usb::Packet_descriptor p) -{ - void * addr = nullptr; - void * packet_content = sink()->packet_content(p); - if (packet_content) { - addr_t offset = (addr_t)packet_content - (addr_t)sink()->ds_local_base(); - - addr = (void*)(genode_shared_dataspace_local_address(_ds) + offset); - } - - return { addr, addr ? p.size() : 0 }; -} - - -bool genode_usb_session::request(genode_usb_request_callbacks & req, void * data) -{ - using Packet_descriptor = Usb::Packet_descriptor; - - genode_usb_request_handle_t idx = 0; - - /* find free packet slot */ - for (auto const &p : packets) { - if (!p.constructed()) - break; - ++idx; - } - if (idx == MAX_PACKETS_IN_FLIGHT) - return false; - - Packet_descriptor p; - - /* get next packet from request stream */ - while (true) { - if (!sink()->packet_avail() || - (sink()->ack_slots_free() <= _packets_in_flight())) - return false; - - p = sink()->get_packet(); - if (sink()->packet_valid(p)) - break; - - _ack(Packet_descriptor::PACKET_INVALID_ERROR, p); - } - - genode_usb_buffer payload = _buffer_of_packet(p); - - packets[idx].construct(p); - _id.inc(); /* increment the session ids usage */ - - switch (p.type) { - case Packet_descriptor::STRING: - req.string_fn((genode_usb_request_string*)&p.string, - _id.id(), idx, payload, data); - break; - case Packet_descriptor::CTRL: - req.urb_fn({ CTRL, &p.control }, _id.id(), idx, payload, data); - break; - case Packet_descriptor::BULK: - req.urb_fn({ BULK, &p.transfer }, _id.id(), idx, payload, data); - break; - case Packet_descriptor::IRQ: - req.urb_fn({ IRQ, &p.transfer }, _id.id(), idx, payload, data); - break; - case Packet_descriptor::ISOC: - req.urb_fn({ ISOC, &p.transfer }, _id.id(), idx, payload, data); - break; - case Packet_descriptor::ALT_SETTING: - req.altsetting_fn(p.interface.number, p.interface.alt_setting, - _id.id(), idx, data); - break; - case Packet_descriptor::CONFIG: - req.config_fn(p.number, _id.id(), idx, data); - break; - case Packet_descriptor::RELEASE_IF: - warning("Release interface gets ignored!"); - _id.dec(); - packets[idx].destruct(); - break; - case Packet_descriptor::FLUSH_TRANSFERS: - req.flush_fn(p.number, _id.id(), idx, data); - break; - }; - - return true; -} - - -void genode_usb_session::handle_response(genode_usb_request_handle_t id, - genode_usb_response_t callback, - void * callback_data) -{ - using Packet_descriptor = Usb::Packet_descriptor; - - Packet_descriptor p = *packets[id]; - genode_usb_buffer payload = _buffer_of_packet(p); - switch (p.type) { - case Packet_descriptor::CTRL: - _ack(callback({ CTRL, &p.control }, payload, callback_data), p); - break; - case Packet_descriptor::BULK: - _ack(callback({ BULK, &p.transfer }, payload, callback_data), p); - break; - case Packet_descriptor::IRQ: - _ack(callback({ IRQ, &p.transfer }, payload, callback_data), p); - break; - case Packet_descriptor::ISOC: - _ack(callback({ ISOC, &p.transfer }, payload, callback_data), p); - break; - default: - _ack(callback({ NONE, nullptr }, { nullptr, 0 }, callback_data), p); - }; - packets[id].destruct(); -} - - -genode_usb_session::genode_usb_session(::Root & root, - genode_shared_dataspace * ds, - Env & env, - Session_id & id, - Signal_context_capability cap, - Session_label const label) -: - Usb::Session_rpc_object(genode_shared_dataspace_capability(ds), - env.ep().rpc_ep(), env.rm()), - _root(root), - _ds(ds), - _id(id), - _label(label) -{ - _tx.sigh_packet_avail(cap); - - id.inc(); -} - - -bool ::Root::_matches(Device & d, genode_usb_session & s) -{ - try { - Session_policy const policy(s._label, _config.xml()); - - genode_usb_vendor_id_t vendor = - policy.attribute_value("vendor_id", 0); - genode_usb_product_id_t product = - policy.attribute_value("product_id", 0); - genode_usb_bus_num_t bus = - policy.attribute_value("bus", 0); - genode_usb_dev_num_t dev = - policy.attribute_value("dev", 0); - genode_usb_class_num_t 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()); - } catch(Session_policy::No_policy_defined) {} - - 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(); - - if (tx_buf_size > ram_quota) { - warning("Insufficient RAM quota, got ", ram_quota, " need ", - tx_buf_size); - throw Insufficient_ram_quota(); - } - - unsigned i = 0; - for (; i < MAX_SESSIONS; i++) - if (!_session_ids[i]->used()) - break; - if (i >= MAX_SESSIONS) { - warning("Maximum of sessions reached!"); - throw Service_denied(); - } - - genode_shared_dataspace * ds = _callbacks->alloc_fn(tx_buf_size); - genode_usb_session * ret = new (md_alloc()) - genode_usb_session(*this, ds, _env, *_session_ids[i], _sigh_cap, 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) { - _callbacks->release_all_fn(d.bus, d.dev); - d.usb_session = nullptr; - } - }); - - session->_id.dec(); - genode_shared_dataspace * ds = session->_ds; - _sessions.remove(&session->_le); - Genode::destroy(md_alloc(), session); - _callbacks->free_fn(ds); -} - - -void ::Root::_report() +void genode_usb_device::generate(Xml_generator & xml, bool acquired) const { using Value = String<64>; + auto per_endp = [&] (genode_usb_endpoint const & endp) + { + xml.node("endpoint", [&] { + xml.attribute("address", Value(Hex(endp.desc.address))); + xml.attribute("attributes", Value(Hex(endp.desc.attributes))); + xml.attribute("max_packet_size", + Value(Hex(endp.desc.max_packet_size))); + }); + }; + + auto per_iface = [&] (genode_usb_interface const & iface) + { + xml.node("interface", [&] { + xml.attribute("active", iface.active); + xml.attribute("number", Value(Hex(iface.desc.number))); + if (*iface.info.string()) xml.attribute("info", iface.info); + xml.attribute("alt_setting", Value(Hex(iface.desc.alt_settings))); + xml.attribute("class", Value(Hex(iface.desc.iclass))); + xml.attribute("subclass", Value(Hex(iface.desc.isubclass))); + xml.attribute("protocol", Value(Hex(iface.desc.iprotocol))); + iface.endpoints.for_each(per_endp); + }); + }; + + auto per_config = [&] (genode_usb_configuration const & cfg) + { + xml.node("config", [&] { + xml.attribute("active", cfg.active); + xml.attribute("value", Value(Hex(cfg.desc.config_value))); + cfg.interfaces.for_each(per_iface); + }); + }; + + xml.node("device", [&] { + xml.attribute("name", label()); + xml.attribute("class", Value(Hex(desc.dclass))); + if (*manufacturer.string()) xml.attribute("manufacturer", manufacturer); + if (*product.string()) xml.attribute("product", product); + xml.attribute("vendor_id", Value(Hex(desc.vendor_id))); + xml.attribute("product_id", Value(Hex(desc.product_id))); + xml.attribute("speed", speed_to_string()); + if (acquired) xml.attribute("acquired", true); + configs.for_each(per_config); + }); +} + + +void +Interface_component::_handle_request(Constructible &cpd, + genode_buffer_t payload, + genode_usb_req_callback_t const cbs, + void *opaque_data) +{ + genode_usb_request_handle_t handle = &cpd; + bool granted = false; + + for (unsigned i = 0; i < MAX_EPS && !granted; i++) + if (_ep_addresses[i] == cpd->index) granted = true; + + if (!granted) { + handle_response(handle, INVALID, nullptr); + return; + } + + switch (cpd->type) { + case Packet_descriptor::BULK: + cbs->bulk_fn(handle, cpd->index, payload, opaque_data); + break; + case Packet_descriptor::IRQ: + cbs->irq_fn(handle, cpd->index, payload, opaque_data); + break; + case Packet_descriptor::ISOC: + { + genode_usb_isoc_transfer_header & hdr = + *reinterpret_cast(payload.addr); + genode_buffer_t isoc_payload { + (void*) ((addr_t)payload.addr + + sizeof(genode_usb_isoc_transfer_header) + + (hdr.number_of_packets * + sizeof(genode_usb_isoc_descriptor))), + payload.size - sizeof(genode_usb_isoc_transfer_header) + + hdr.number_of_packets * sizeof(genode_usb_isoc_descriptor) }; + cbs->isoc_fn(handle, cpd->index, hdr.number_of_packets, + hdr.packets, isoc_payload, opaque_data); + break; + } + case Packet_descriptor::FLUSH: + cbs->flush_fn(cpd->index, handle, opaque_data); + break; + }; +} + + +bool +Interface_component::handle_response(genode_usb_request_handle_t handle, + genode_usb_request_ret_t value, + uint32_t *actual_sizes) +{ + bool ret = false; + _finish_packet(handle).with_result( + [&] (Packet_descriptor p) { + ret = true; + Packet_descriptor::Return_value v {}; + switch (value) { + case OK: v = Packet_descriptor::OK; break; + case NO_DEVICE: v = Packet_descriptor::NO_DEVICE; break; + case INVALID: v = Packet_descriptor::INVALID; break; + case HALT: v = Packet_descriptor::HALT; break; + case TIMEOUT: + error("timeout shouldn't be returned for transfer URBs"); + }; + + /* return actual isoc frames sizes */ + if ((value == OK) && + (p.type == Packet_descriptor::ISOC)) { + void * data = _tx.sink()->packet_content(p); + genode_usb_isoc_transfer_header & hdr = + *reinterpret_cast(data); + for (size_t i = 0; i < hdr.number_of_packets; i++) + hdr.packets[i].actual_size = actual_sizes[i+1]; + } + + _ack(v, (value == OK) ? actual_sizes[0] : 0, p); + }, + [&] (Packet_error) {}); + return ret; +} + + +Interface_component::Interface_component(Env &env, + Reg_list ®istry, + Session_component &session, + genode_usb_device::Label label, + size_t buf_size, + Signal_context_capability sigh_cap, + uint8_t iface_idx) +: + Base(env, session, registry, buf_size, *this, sigh_cap), + _iface_idx(iface_idx) +{ + unsigned idx = 0; + session.for_each_ep(label, iface_idx, [&] (genode_usb_endpoint const &ep) { + if (idx < MAX_EPS) _ep_addresses[idx++] = ep.desc.address; }); +} + + +void +Device_component::_handle_request(Constructible &cpd, + genode_buffer_t payload, + genode_usb_req_callback_t const cbs, + void *opaque_data) +{ + using P = Packet_descriptor; + + genode_usb_request_handle_t handle = &cpd; + bool granted = false; + + switch (cpd->request) { + case P::GET_STATUS: [[fallthrough]]; + case P::GET_DESCRIPTOR: [[fallthrough]]; + case P::GET_CONFIGURATION: [[fallthrough]]; + case P::GET_INTERFACE: + granted = true; + break; + case P::SET_INTERFACE: + _interfaces.for_each([&] (Interface_component & ic) { + if (ic._iface_idx == cpd->index) granted = true; }); + if (granted) break; + [[fallthrough]]; + default: granted = _controls; + }; + + if (!granted) { + uint32_t ret = 0; + handle_response(handle, INVALID, &ret); + if (_warn_once) { + warning("Invalid restricted control URB to device ", _device_label, + " from session ", _session.label()); + _warn_once = false; + } + return; + } + + cbs->ctrl_fn(handle, cpd->request, cpd->request_type, cpd->value, + cpd->index, cpd->timeout, payload, opaque_data); +} + + +Interface_capability Device_component::acquire_interface(uint8_t index, + size_t buf_size) +{ + if (!_session.matches(_device_label, index)) + return Interface_capability(); + + Interface_component * ic = new (_heap) + Interface_component(_env, _interfaces, _session, _device_label, + buf_size, _sigh_cap, index); + return ic->session_cap(); +} + + +void Device_component::release_interface(Interface_capability cap) +{ + if (!cap.valid()) + return; + + _interfaces.for_each([&] (Interface_component & ic) { + if (cap.local_name() == ic.cap().local_name()) + destroy(_heap, &ic); }); +} + + +bool Device_component::request(genode_usb_req_callback_t const callback, + void *opaque_data) +{ + bool ret = false; + + _interfaces.for_each([&] (Interface_component & ic) { + if (ic.request(callback, opaque_data)) ret = true; }); + + return ret ? ret : Base::request(callback, opaque_data); +} + + +bool +Device_component::handle_response(genode_usb_request_handle_t handle, + genode_usb_request_ret_t value, + uint32_t *actual_sizes) +{ + bool ret = false; + _finish_packet(handle).with_result( + [&] (Packet_descriptor p) { + ret = true; + Packet_descriptor::Return_value v = + Packet_descriptor::INVALID; + switch (value) { + case OK: v = Packet_descriptor::OK; break; + case NO_DEVICE: v = Packet_descriptor::NO_DEVICE; break; + case INVALID: v = Packet_descriptor::INVALID; break; + case TIMEOUT: v = Packet_descriptor::TIMEOUT; break; + case HALT: v = Packet_descriptor::HALT; break; + }; + _ack(v, (value == OK) ? actual_sizes[0] : 0, p); + + if (value != OK) + return; + + if (p.request == Packet_descriptor::SET_INTERFACE && + p.request_type == Packet_descriptor::IFACE) { + _session.set_interface(_device_label, p.index, p.value); + return; + } + + if (p.request == Packet_descriptor::SET_CONFIGURATION && + p.request_type == Packet_descriptor::DEVICE) { + _session.set_configuration(_device_label, p.value); + return; + } + }, + [&] (Packet_error) {}); + + if (!ret) + _interfaces.for_each([&] (Interface_component & ic) { + if (ret) return; + if (ic.handle_response(handle, value, actual_sizes)) ret = true; + }); + + return ret; +} + + +void Device_component::disconnect() +{ + Base::disconnect(); + + _interfaces.for_each([&] (Interface_component & ic) { + ic.disconnect(); }); +} + + +void Device_component::handle_disconnected() +{ + Base::handle_disconnected(); + + _interfaces.for_each([&] (Interface_component & ic) { + ic.handle_disconnected(); }); +} + + +void Device_component::wakeup() +{ + Base::wakeup(); + + _interfaces.for_each([&] (Interface_component & ic) { + ic.wakeup(); }); +} + + +Device_component::Device_component(Env &env, + Heap &heap, + Reg_list ®istry, + Session_component &session, + bool const controls, + genode_usb_device::Label const &device, + Signal_context_capability sigh_cap) +: + Packet_handler(env, session, registry, + TX_BUFFER_SIZE, + *this, sigh_cap), + _env(env), + _heap(heap), + _session(session), + _controls(controls), + _device_label(device), + _sigh_cap(sigh_cap) {} + + +Device_component::~Device_component() +{ + _interfaces.for_each([&] (Interface_component & ic) { + destroy(_heap, &ic); }); +} + + +template +void Session_component::_device_policy(genode_usb_device const &d, + FN const &fn) +{ + using Label = genode_usb_device::Label; + + try { + Session_policy const policy(label(), _config.xml()); + + policy.for_each_sub_node("device", [&] (Xml_node & node) { + uint16_t vendor = node.attribute_value("vendor_id", 0); + uint16_t product = node.attribute_value("product_id", 0); + Label label = node.attribute_value("name", Label()); + + bool match = (((vendor == d.desc.vendor_id) && + (product == d.desc.product_id)) || + (d.label() == label)); + + if (!match) { + uint8_t cla = node.attribute_value("class", 0); + d.configs.for_each([&] (genode_usb_configuration const &c) { + if (!c.active) + return; + c.interfaces.for_each([&] (genode_usb_interface const &i) { + if (i.desc.iclass == cla) match = true; }); + }); + } + + if (match) fn(node); + }); + } catch(Session_policy::No_policy_defined) {} +} + + +bool Session_component::_matches(genode_usb_device const & d) +{ + bool ret = false; + _device_policy(d, [&] (Xml_node) { ret = true; }); + return ret; +} + + +Device_capability +Session_component::_acquire(genode_usb_device::Label const &name, bool controls) +{ + Device_component * dc = new (_heap) + Device_component(_env, _heap, _device_sessions, *this, + controls, name, _sigh_cap); + return dc->session_cap(); +} + + +void Session_component::_release(Device_component &dc) +{ + genode_usb_device::Label name = dc._device_label; + destroy(_heap, &dc); + + _devices.for_each([&] (genode_usb_device & device) { + if (device.label() != name) + return; + _sessions.for_each([&] (Session_component &sc) { + if (sc._matches(device)) sc.update_devices_rom(); }); + _root.report(); + }); +} + + +void Session_component::set_interface(genode_usb_device::Label label, + uint16_t num, uint16_t alt) +{ + bool changed = false; + _devices.for_each([&] (genode_usb_device & d) { + if (d.label() != label) + return; + d.configs.for_each([&] (genode_usb_configuration & c) { + if (!c.active) + return; + c.interfaces.for_each([&] (genode_usb_interface & i) { + if (i.desc.number != num) + return; + + if (i.active != (i.desc.alt_settings == alt)) { + i.active = (i.desc.alt_settings == alt); + changed = true; + } + }); + }); + }); + + if (changed) { + update_devices_rom(); + _root.report(); + } +} + + +void Session_component::set_configuration(genode_usb_device::Label label, + uint16_t num) +{ + bool changed = false; + _devices.for_each([&] (genode_usb_device & d) { + if (d.label() != label) + return; + d.configs.for_each([&] (genode_usb_configuration & c) { + if (c.active != (c.desc.config_value == num)) { + c.active = (c.desc.config_value == num); + changed = true; + } + }); + }); + + if (changed) { + update_devices_rom(); + _root.report(); + } +} + + +bool Session_component::matches(genode_usb_device::Label label, uint8_t iface) +{ + /* + * If no interface is defined in the session policy, + * all interfaces are allowed, otherwise check for the iface number + */ + bool ret = false; + _devices.for_each([&] (genode_usb_device const & d) { + if (d.label() != label) + return; + _device_policy(d, [&] (Xml_node dev_node) { + if (!dev_node.has_sub_node("interface")) + ret = true; + else + dev_node.for_each_sub_node("interface", [&] (Xml_node & node) { + if (node.attribute_value("number", 255) == iface) + ret = true; + }); + }); + }); + return ret; +} + + +template +void Session_component::for_each_ep(genode_usb_device::Label label, + uint8_t iface_idx, FN const & fn) +{ + _devices.for_each([&] (genode_usb_device const & d) { + if (d.label() != label) + return; + d.configs.for_each([&] (genode_usb_configuration const & cfg) { + if (!cfg.active) + return; + cfg.interfaces.for_each([&] (genode_usb_interface const & iface) { + if (iface.desc.number == iface_idx) + iface.endpoints.for_each(fn); + }); + }); + }); +} + + +void Session_component::announce_device(genode_usb_device const & device) +{ + if (_matches(device)) update_devices_rom(); +} + + +void Session_component::discontinue_device(genode_usb_device const & device) +{ + _device_sessions.for_each([&] (Device_component & dc) { + if (dc._device_label != device.label()) + return; + dc.disconnect(); + update_devices_rom(); + }); +} + + +void Session_component::update_policy() +{ + _device_sessions.for_each([&] (Device_component & dc) { + _devices.for_each([&] (genode_usb_device const & device) { + if (device.label() != dc._device_label) + return; + if (!_matches(device)) { + dc.disconnect(); + _release_fn(device.bus, device.dev); + } + }); + }); + update_devices_rom(); +} + + +void Session_component::update_devices_rom() +{ + _rom_session.trigger_update(); +} + + +Rom_session_capability Session_component::devices_rom() +{ + return _rom_session.cap(); +} + + +bool Session_component::acquired(genode_usb_device const &dev) +{ + if (_state == IN_DESTRUCTION) + return false; + + bool ret = false; + _device_sessions.for_each([&] (Device_component & dc) { + if (dc._device_label == dev.label()) ret = dc.connected(); }); + return ret; +} + + +bool Session_component::request(genode_usb_device const &dev, + genode_usb_req_callback_t const callback, + void *opaque_data) +{ + bool ret = false; + _device_sessions.for_each([&] (Device_component & dc) { + if (dc._device_label == dev.label()) + if (dc.request(callback, opaque_data)) ret = true; + }); + return ret; +} + + +bool +Session_component::handle_response(genode_usb_request_handle_t handle, + genode_usb_request_ret_t v, + uint32_t *actual_sizes) +{ + bool handled = false; + _device_sessions.for_each([&] (Device_component & dc) { + if (!handled) handled = dc.handle_response(handle, v, actual_sizes); }); + return handled; +} + + +void Session_component::handle_disconnected() +{ + _device_sessions.for_each([&] (Device_component & dc) { + dc.handle_disconnected(); }); +} + + +Device_capability Session_component::acquire_device(Device_name const &name) +{ + Device_capability cap; + bool found = false; + + _devices.for_each([&] (genode_usb_device & device) { + if (device.label() != name || !_matches(device)) + return; + + found = true; + _sessions.for_each([&] (Session_component &sc) { + if (sc.acquired(device)) found = false; }); + + if (!found) { + warning("USB device ", name, + "already acquired by another session"); + } + + cap = _acquire(device.label(), true); + _sessions.for_each([&] (Session_component &sc) { + if (sc._matches(device)) sc.update_devices_rom(); }); + _root.report(); + }); + + if (!found) _acquire("INVALID_DEVICE", false); + return cap; +} + + +Device_capability Session_component::acquire_single_device() +{ + Device_capability cap; + _devices.for_each([&] (genode_usb_device & device) { + if (cap.valid() || !_matches(device)) + return; + + bool acquired = false; + _sessions.for_each([&] (Session_component &sc) { + if (sc.acquired(device)) acquired = true; }); + + if (acquired) + return; + + cap = _acquire(device.label(), true); + _sessions.for_each([&] (Session_component &sc) { + if (sc._matches(device)) sc.update_devices_rom(); }); + _root.report(); + }); + return cap; +} + + +void Session_component::release_device(Device_capability cap) +{ + if (!cap.valid()) + return; + + _device_sessions.for_each([&] (Device_component & dc) { + if (cap.local_name() == dc.cap().local_name()) + _release(dc); }); +} + + +void Session_component::produce_xml(Xml_generator &xml) +{ + _devices.for_each([&] (genode_usb_device const & device) { + if (!_matches(device)) + return; + + bool acquired_by_other_session = false; + _sessions.for_each([&] (Session_component &sc) { + if (sc.acquired(device) && &sc != this) + acquired_by_other_session = true; + }); + if (acquired_by_other_session) + return; + + device.generate(xml, acquired(device)); + }); +} + + +void Session_component::wakeup() +{ + _device_sessions.for_each([&] (Device_component & dc) { dc.wakeup(); }); +} + + +Session_component::Session_component(Env &env, + ::Root &root, + Reg_list ®istry, + Reg_list &devices, + Attached_rom_dataspace &config, + Signal_context_capability sigh_cap, + genode_shared_dataspace_alloc_attach_t alloc_fn, + genode_shared_dataspace_free_t free_fn, + genode_usb_dev_release_t release_rn, + Label const &label, + Resources const &resources, + Diag const &diag) +: + Session_object(env.ep(), resources, label, diag), + Reg_list::Element(registry, *this), + Dynamic_rom_session::Xml_producer("devices"), + _env(env), + _root(root), + _sessions(registry), + _devices(devices), + _config(config), + _sigh_cap(sigh_cap), + _alloc_fn(alloc_fn), + _free_fn(free_fn), + _release_fn(release_rn) { } + + +Session_component::~Session_component() +{ + _state = IN_DESTRUCTION; + _device_sessions.for_each([&] (Device_component & dc) { + _devices.for_each([&] (genode_usb_device & device) { + if (device.label() == dc._device_label) + _release_fn(device.bus, device.dev); }); + + _release(dc); + }); +} + + +Session_component * ::Root::_create_session(const char * args, + Affinity const &) +{ + Session_component * sc = nullptr; + try { + Session_label const label { session_label_from_args(args) }; + Session_policy const policy { label, _config.xml() }; + + sc = new (md_alloc()) + Session_component(_env, *this, _sessions, _devices, _config, + _sigh_cap, _alloc_fn, _free_fn, _release_fn, + label, session_resources_from_args(args), + session_diag_from_args(args)); + } catch (Session_policy::No_policy_defined) { + error("Invalid session request, no matching policy for ", + "'", label_from_args(args).string(), "'"); + throw Service_denied(); + } catch (...) { + if (sc) { Genode::destroy(md_alloc(), sc); } + throw; + } + return sc; +} + + +void ::Root::_upgrade_session(Session_component * sc, const char * args) +{ + sc->upgrade(ram_quota_from_args(args)); + sc->upgrade(cap_quota_from_args(args)); +} + + +void ::Root::report() +{ if (!_device_reporter.constructed()) return; _device_reporter->generate([&] (Reporter::Xml_generator &xml) { - _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))); - - Usb::Device_descriptor device_descr { }; - Usb::Config_descriptor config_descr { }; - - if (!_callbacks->cfg_desc_fn(d.bus, d.dev, - (void*) &device_descr, - &config_descr)) - return; - - for (unsigned idx = 0; idx < config_descr.num_interfaces; idx++) { - - int const num_altsetting = _callbacks->alt_settings_fn(d.bus, d.dev, idx); - if (num_altsetting < 0) - continue; - - for (int adx = 0; adx < num_altsetting; adx++) { - - int active = false; - Usb::Interface_descriptor iface_desc { }; - if (_callbacks->iface_desc_fn(d.bus, d.dev, idx, adx, - (void*) &iface_desc, - sizeof (Usb::Interface_descriptor), - &active)) - continue; - - if (!active) - continue; - - xml.node("interface", [&] { - xml.attribute("class", Value(Hex(iface_desc.iclass))); - xml.attribute("protocol", Value(Hex(iface_desc.iprotocol))); - }); - } - } - }); + _devices.for_each([&] (genode_usb_device & d) { + bool acquired = false; + _sessions.for_each([&] (Session_component &sc) { + if (sc.acquired(d)) acquired = true; }); + d.generate(xml, acquired); }); }); } @@ -751,6 +1500,9 @@ void ::Root::_config_update() }); _announce_service(); + + _sessions.for_each([&] (Session_component & sc) { + sc.update_policy(); }); } @@ -766,216 +1518,268 @@ void ::Root::_announce_service() } -void ::Root::announce_device(genode_usb_vendor_id_t vendor, - genode_usb_product_id_t product, - genode_usb_class_num_t cla, - genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev) +void ::Root::device_add_endpoint(struct genode_usb_interface *iface, + genode_usb_endpoint_descriptor desc) { - for (auto &device : _devices) { - if (device.constructed()) - continue; + new (_heap) genode_usb_endpoint(iface->endpoints, desc); +} - device.construct(vendor, product, cla, bus, dev); - _announce_service(); - _report(); - _for_each_session([&] (genode_usb_session & s) { - if (device->usb_session || !_matches(*device, s)) - return; - device->usb_session = &s; - s.notify(); - }); - return; - } +void ::Root::device_add_interface(struct genode_usb_configuration *cfg, + genode_usb_dev_string_item_t info_string, + genode_usb_interface_descriptor desc, + genode_usb_dev_add_endp_t callback, + void *opaque_data, + bool active) +{ + String_item info { string_item(info_string, opaque_data) }; - error("Could not announce driver for new USB device, no slot left!"); + genode_usb_interface *iface = new (_heap) + genode_usb_interface(cfg->interfaces, info, desc, active); + for (unsigned i = desc.num_endpoints; i > 0; i--) + callback(iface, i-1, opaque_data); +} + + +void ::Root::device_add_configuration(struct genode_usb_device *dev, + genode_usb_config_descriptor desc, + genode_usb_dev_add_iface_t callback, + void *opaque_data, + bool active) +{ + genode_usb_configuration *config = new (_heap) + genode_usb_configuration(dev->configs, desc, active); + for (unsigned i = desc.num_interfaces; i > 0; i--) + callback(config, i-1, opaque_data); +} + + +void ::Root::announce_device(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_speed_t speed, + genode_usb_dev_string_item_t manufacturer_string, + genode_usb_dev_string_item_t product_string, + genode_usb_device_descriptor desc, + genode_usb_dev_add_config_t callback, + void *opaque_data) +{ + String_item manufacturer { string_item(manufacturer_string, opaque_data) }; + String_item product { string_item(product_string, opaque_data) }; + + genode_usb_device *device = new (_heap) + genode_usb_device(_devices, bus, dev, speed, manufacturer, product, desc); + for (unsigned i = desc.num_configs; i > 0; i--) + callback(device, i-1, opaque_data); + _announce_service(); + report(); + _sessions.for_each([&] (Session_component & sc) { + sc.announce_device(*device); }); } void ::Root::discontinue_device(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev) { - for (auto &device : _devices) { - if (!device.constructed() || - device->bus != bus || - device->dev != dev) - continue; + _devices.for_each([&] (genode_usb_device & device) + { + if (device.bus != bus || device.dev != dev) + return; - if (device->usb_session) { - device->usb_session->notify(); - device->usb_session->flush_packet_stream(); - } + _sessions.for_each([&] (Session_component & sc) { + sc.discontinue_device(device); }); - device.destruct(); - _report(); - return; - } -} - - -genode_usb_session_handle_t ::Root::session(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev) -{ - genode_usb_session * session = nullptr; - _for_each_device([&] (Device & d) { - if (d.bus == bus && d.dev == dev) - session = d.usb_session; + device.configs.for_each([&] (genode_usb_configuration & cfg) { + cfg.interfaces.for_each([&] (genode_usb_interface & iface) { + iface.endpoints.for_each([&] (genode_usb_endpoint & endp) { + Genode::destroy(_heap, &endp); }); + Genode::destroy(_heap, &iface); + }); + Genode::destroy(_heap, &cfg); + }); + Genode::destroy(_heap, &device); }); - - return session ? session->_id.id() : 0; + report(); } -template -void ::Root::session(genode_usb_session_handle_t id, FUNC const & fn) -{ - genode_usb_session * session = nullptr; - - _for_each_session([&] (genode_usb_session & s) { - if (s._id.id() == id) session = &s; - }); - - /* - * We've to execute the functor outside the session iteration, - * because the functor might block and the actual session - * can be destroyed in the meantime, which will lead to - * a corrupted next() pointer. - */ - if (session) fn(*session); -} - - -bool ::Root::device_associated(genode_usb_session * session, - genode_usb_bus_num_t & bus, - genode_usb_dev_num_t & dev) +bool ::Root::acquired(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev) { bool ret = false; - _for_each_device([&] (Device & d) { - if (d.usb_session == session) { - bus = d.bus; - dev = d.dev; - ret = true; - } + _devices.for_each([&] (genode_usb_device & device) { + if (device.bus == bus && device.dev == dev) + _sessions.for_each([&] (Session_component & sc) { + if (sc.acquired(device)) ret = true; }); }); return ret; } -void ::Root::handle_empty_sessions() +bool ::Root::request(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_req_callback_t const callback, + void *opaque_data) { - _for_each_session([&] (genode_usb_session & s) { - bool associated = false; - _for_each_device([&] (Device & d) { - if (d.usb_session == &s) associated = true; }); - if (!associated) - s.flush_packet_stream(); + bool ret = false; + _devices.for_each([&] (genode_usb_device & device) + { + if (device.bus != bus || device.dev != dev) + return; + + _sessions.for_each([&] (Session_component & sc) { + if (sc.request(device, callback, opaque_data)) ret = true; }); + }); + return ret; +} + + +void ::Root::handle_response(genode_usb_request_handle_t id, + genode_usb_request_ret_t ret, + uint32_t *actual_sizes) +{ + bool handled = false; + _sessions.for_each([&] (Session_component & sc) { + if (!handled) handled = sc.handle_response(id, ret, actual_sizes); }); } -void ::Root::decrement_session_id(genode_usb_session_handle_t id) +void ::Root::handle_disconnected_sessions() { - if (id > 0 && id <= MAX_SESSIONS) - _session_ids[id-1]->dec(); + _sessions.for_each([&] (Session_component & sc) { + sc.handle_disconnected(); }); } -::Root::Root(Env & env, Allocator & alloc, Signal_context_capability cap) -: - Root_component(env.ep(), alloc), - _env(env), _sigh_cap(cap) +void ::Root::wakeup() { - /* Reserve id zero which is invalid */ - for (unsigned i = 0; i < MAX_SESSIONS; i++) - _session_ids[i].construct((genode_usb_session_handle_t)(i+1)); + _sessions.for_each([&] (Session_component & sc) { sc.wakeup(); }); +} + +::Root::Root(Env &env, + Signal_context_capability cap, + genode_shared_dataspace_alloc_attach_t alloc_fn, + genode_shared_dataspace_free_t free_fn, + genode_usb_dev_release_t release_fn) +: + Sliced_heap(env.ram(), env.rm()), + Root_component(env.ep(), *this), + _env(env), _sigh_cap(cap), _alloc_fn(alloc_fn), + _free_fn(free_fn), _release_fn(release_fn) +{ _config.sigh(_config_handler); _config_update(); } -extern "C" void genode_usb_init(genode_env * env_ptr, - genode_allocator * alloc_ptr, - genode_signal_handler * sigh_ptr, - genode_usb_rpc_callbacks * callbacks) +void +Genode_c_api::initialize_usb_service(Env &env, + Signal_context_capability sigh, + genode_shared_dataspace_alloc_attach_t alloc_fn, + genode_shared_dataspace_free_t free_fn, + genode_usb_dev_release_t release_fn) { - static ::Root root(*static_cast(env_ptr), - *static_cast(alloc_ptr), - cap(sigh_ptr)); - _callbacks = callbacks; - _usb_root = &root; + static ::Root root(env, sigh, alloc_fn, free_fn, release_fn); + _usb_root = &root; } -extern "C" void genode_usb_announce_device(genode_usb_vendor_id_t vendor, - genode_usb_product_id_t product, - genode_usb_class_num_t cla, - genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev) +extern "C" +void genode_usb_device_add_endpoint(struct genode_usb_interface *iface, + genode_usb_endpoint_descriptor desc) { - if (!_usb_root) - return; + if (_usb_root) _usb_root->device_add_endpoint(iface, desc); +} - _usb_root->announce_device(vendor, product, cla, bus, dev); + +extern "C" +void genode_usb_device_add_interface(struct genode_usb_configuration *cfg, + genode_usb_dev_string_item_t info_string, + genode_usb_interface_descriptor desc, + genode_usb_dev_add_endp_t callback, + void *opaque_data, + bool active) +{ + if (_usb_root) + _usb_root->device_add_interface(cfg, info_string, desc, callback, + opaque_data, active); +} + + +extern "C" +void genode_usb_device_add_configuration(struct genode_usb_device *dev, + genode_usb_config_descriptor desc, + genode_usb_dev_add_iface_t callback, + void *opaque_data, + bool active) +{ + if (_usb_root) + _usb_root->device_add_configuration(dev, desc, callback, + opaque_data, active); +} + + + +extern "C" +void genode_usb_announce_device(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_speed_t speed, + genode_usb_dev_string_item_t manufacturer_string, + genode_usb_dev_string_item_t product_string, + genode_usb_device_descriptor desc, + genode_usb_dev_add_config_t callback, + void *opaque_data) +{ + if (_usb_root) + _usb_root->announce_device(bus, dev, speed, + manufacturer_string, product_string, + desc, callback, opaque_data); } extern "C" void genode_usb_discontinue_device(genode_usb_bus_num_t bus, genode_usb_dev_num_t dev) +{ + if (_usb_root) _usb_root->discontinue_device(bus, dev); +} + + +extern "C" bool +genode_usb_device_acquired(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev) +{ + return _usb_root ? _usb_root->acquired(bus, dev) : false; +} + + +extern "C" bool +genode_usb_request_by_bus_dev(genode_usb_bus_num_t bus, + genode_usb_dev_num_t dev, + genode_usb_req_callback_t const callback, + void *opaque_data) +{ + return _usb_root ? _usb_root->request(bus, dev, callback, opaque_data) + : false; +} + + +extern "C" void +genode_usb_ack_request(genode_usb_request_handle_t request_id, + genode_usb_request_ret_t ret, + uint32_t *actual_sizes) { if (_usb_root) - _usb_root->discontinue_device(bus, dev); + _usb_root->handle_response(request_id, ret, actual_sizes); } -extern "C" genode_usb_session_handle_t -genode_usb_session_by_bus_dev(genode_usb_bus_num_t bus, - genode_usb_dev_num_t dev) +extern "C" void genode_usb_notify_peers() { - genode_usb_session_handle_t ret = _usb_root ? _usb_root->session(bus, dev) : 0; - return ret; + if (_usb_root) _usb_root->wakeup(); } -extern "C" int -genode_usb_request_by_session(genode_usb_session_handle_t id, - struct genode_usb_request_callbacks * const req, - void * data) +extern "C" void genode_usb_handle_disconnected_sessions() { - 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); }); - - _usb_root->decrement_session_id(session_id); -} - - -extern "C" void genode_usb_notify_peers() { } - - -extern "C" void genode_usb_handle_empty_sessions() -{ - if (!_usb_root) - return; - - _usb_root->handle_empty_sessions(); + if (_usb_root) _usb_root->handle_disconnected_sessions(); } diff --git a/repos/os/src/lib/genode_c_api/usb_client.cc b/repos/os/src/lib/genode_c_api/usb_client.cc index 1af5e68b78..90ad4fd411 100644 --- a/repos/os/src/lib/genode_c_api/usb_client.cc +++ b/repos/os/src/lib/genode_c_api/usb_client.cc @@ -1,6 +1,7 @@ /* * \brief Genode USB client provider C-API * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2023-06-29 */ @@ -12,329 +13,624 @@ */ #include -#include #include -#include +#include #include +#include +#include +#include + +#include +#include using namespace Genode; -struct Usb_client; -struct Usb_completion; +struct Device; -using Usb_id_space = Id_space; - - -struct Usb_completion : Usb::Completion +struct Endpoint : List_model::Element { - Usb::Packet_descriptor packet { }; + uint8_t const address; + uint8_t const attributes; + uint8_t const max_packet_size; - genode_usb_client_request_packet *request = nullptr; + Endpoint(Xml_node const &n) + : + address(n.attribute_value("address", 0xff)), + attributes(n.attribute_value("attributes", 0xff)), + max_packet_size(n.attribute_value("max_packet_size", 0)) {} - 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; + bool matches(Xml_node const &node) const { + return address == node.attribute_value("address", 0xff); } - 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); - } + static bool type_matches(Xml_node const &node) { + return node.has_type("endpoint"); } }; -struct Usb_client : Usb::Connection +class Interface : public List_model<::Interface>::Element { - enum { PACKET_SLOTS = Usb::Session::TX_QUEUE_SIZE }; + public: - Usb_completion completions[PACKET_SLOTS]; - Bit_allocator slots { }; - Usb_id_space::Element const elem; + class Urb : Usb::Endpoint, public Usb::Interface::Urb + { + private: - 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) - { } + friend class ::Interface; + using Pdesc = Usb::Interface::Packet_descriptor; - genode_usb_client_handle_t handle() const { return elem.id().value; } + static Pdesc::Type _type(genode_usb_client_iface_type_t t) + { + switch(t) { + case ::BULK: return Pdesc::BULK; + case ::IRQ: return Pdesc::IRQ; + case ::ISOC: return Pdesc::ISOC; + case ::FLUSH: return Pdesc::FLUSH; + }; + return Pdesc::FLUSH; + } - void free_completion(Usb_completion *completion) - { - unsigned long slot_idx = (unsigned long)(completion - completions); - slots.free(slot_idx); - } + struct Driver_data { void * const data; } _driver_data; - 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 + public: + + Urb(::Interface &iface, + ::Endpoint &endp, + genode_usb_client_iface_type_t type, + size_t size, + void *opaque_data) + : + Usb::Endpoint(endp.address, endp.attributes), + Usb::Interface::Urb(iface._session(), *this, _type(type), + size), + _driver_data{opaque_data} {} + }; + + private: + + Device &_device; + Constructible _iface {}; + List_model _endpoints {}; + uint8_t const _number; + uint8_t const _alt_setting; + bool _active; + Tslab _slab; + + /* The interface's buffer size might be configureable in the future */ + size_t _buf_size { 4096 * 128 }; + + Usb::Interface &_session(); + + public: + + Interface(Device &device, Xml_node const &n, Allocator &alloc) + : + _device(device), + _number(n.attribute_value("number", 0xff)), + _alt_setting(n.attribute_value("alt_setting", 0xff)), + _active(n.attribute_value("active", false)), + _slab(alloc) {} + + ~Interface() + { + if (_iface.constructed()) + _iface->dissolve_all_urbs([&] (Urb &urb) { + destroy(_slab, &urb); }); + } + + uint8_t number() const { return _number; }; + uint8_t alt_setting() const { return _alt_setting; }; + bool active() const { return _active; } + + void active(bool active) { _active = active; } + + Allocator &slab() { return _slab; } + + bool matches(Xml_node const &n) const + { + uint8_t nr = n.attribute_value("number", 0xff); + uint8_t alt = n.attribute_value("alt_setting", 0xff); + return _number == nr && _alt_setting == alt; + } + + static bool type_matches(Xml_node const &node) { + return node.has_type("interface"); } + + void update(Allocator &alloc, Xml_node const &node) + { + _active = node.attribute_value("active", false); + _endpoints.update_from_xml(node, + + /* create */ + [&] (Xml_node const &node) -> Endpoint & { + return *new (alloc) Endpoint(node); }, + + /* destroy */ + [&] (Endpoint &endp) { + destroy(alloc, &endp); }, + + /* update */ + [&] (Endpoint &, Xml_node const &) { } + ); + } + + void update(genode_usb_client_produce_out_t out, + genode_usb_client_consume_in_t in, + genode_usb_client_produce_out_isoc_t out_isoc, + genode_usb_client_consume_in_isoc_t in_isoc, + genode_usb_client_complete_t complete) + { + if (!_iface.constructed()) + return; + + _iface->update_urbs( + + /* produce out content */ + [&] (Urb &urb, Byte_range_ptr &dst) { + out(urb._driver_data.data, { dst.start, dst.num_bytes }); + }, + + /* consume in results */ + [&] (Urb &urb, Const_byte_range_ptr &src) { + in(urb._driver_data.data, + { (void*)src.start, src.num_bytes }); + }, + + /* produce out content */ + [&] (Urb &urb, uint32_t idx, Byte_range_ptr &dst) { + return out_isoc(urb._driver_data.data, idx, + { dst.start, dst.num_bytes }); + }, + + /* consume in results */ + [&] (Urb &urb, uint32_t idx, Const_byte_range_ptr &src) { + in_isoc(urb._driver_data.data, idx, + { (void*)src.start, src.num_bytes }); + }, + + /* complete USB request */ + [&] (Urb &urb, Usb::Tagged_packet::Return_value v) { + using Retval = Usb::Tagged_packet::Return_value; + genode_usb_client_ret_val_t ret; + + switch (v) { + case Retval::NO_DEVICE: ret = NO_DEVICE; break; + case Retval::INVALID: ret = INVALID; break; + case Retval::HALT: ret = HALT; break; + case Retval::OK: ret = OK; break; + default: + error("unhandled packet or timeout should not happen!"); + ret = INVALID; + }; + complete(urb._driver_data.data, ret); + destroy(_slab, &urb); + }); + } + + template + void with_endpoint(uint8_t index, FN const &fn) + { + _endpoints.for_each([&] (Endpoint &endp) { + if (endp.address == index) fn(endp); + }); + } + + void delete_all_urbs(genode_usb_client_complete_t complete) + { + _iface->dissolve_all_urbs([&] (Urb &urb) { + complete(urb._driver_data.data, NO_DEVICE); + destroy(_slab, &urb); + }); + } +}; + + +class Device : public List_model::Element +{ + public: + + using Name = String<64>; + using Speed = String<32>; + + struct Urb : Usb::Device::Urb + { + using Descriptor = Usb::Device::Packet_descriptor; + using Request_type = Descriptor::Request_type; + + struct Driver_data { void * const data; } _driver_data; + + Urb(Device & device, + uint8_t request, + uint8_t request_type, + uint16_t value, + uint16_t index, + size_t size, + void *opaque_data) + : + Usb::Device::Urb(device._device, request, + (Request_type::access_t)request_type, + value, index, size), + _driver_data{opaque_data} {} + + bool set_interface() const; + + uint16_t index() const { return _index; } + uint16_t value() const { return _value; } + }; + + private: + + Name const _name; + Speed const _speed; + Id_space::Element const _elem; + Usb::Device _device; + Signal_context_capability _sigh_cap; + void *_driver_data { nullptr }; + List_model<::Interface> _ifaces {}; + Tslab _slab; + + enum State { AVAIL, REMOVED } _state { AVAIL }; + + /** + * Noncopyable */ - 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; + Device(Device const &); + Device &operator = (Device const &); + + friend class Session; + + void _delete_all_urbs(genode_usb_client_complete_t complete) + { + _device.dissolve_all_urbs([&] (Urb &urb) { + complete(urb._driver_data.data, NO_DEVICE); + destroy(_slab, &urb); + }); + + _ifaces.for_each([&] (::Interface &iface) { + iface.delete_all_urbs(complete); }); } - return completion; - } + public: - void release(Usb_completion *completion) + Device(Name &name, + Speed &speed, + Usb::Connection &usb, + Allocator &alloc, + Region_map &rm, + Id_space &space, + Signal_context_capability cap) + : + _name(name), + _speed(speed), + _elem(*this, space), + _device(usb, alloc, rm, name), + _sigh_cap(cap), + _slab(alloc) + { + _device.sigh(_sigh_cap); + } + + ~Device() + { + _device.dissolve_all_urbs([&] (Urb &urb) { + destroy(_slab, &urb); }); + } + + Usb::Device &session() { return _device; } + + Name name() { return _name; } + + Usb_speed speed() + { + if (_speed == "low") return GENODE_USB_SPEED_LOW; + if (_speed == "full") return GENODE_USB_SPEED_FULL; + if (_speed == "high") return GENODE_USB_SPEED_HIGH; + if (_speed == "super") return GENODE_USB_SPEED_SUPER; + if (_speed == "super_plus") return GENODE_USB_SPEED_SUPER_PLUS; + if (_speed == "super_plus_2x2") + return GENODE_USB_SPEED_SUPER_PLUS_2X2; + return GENODE_USB_SPEED_FULL; + } + + Signal_context_capability sigh_cap() { return _sigh_cap; } + + genode_usb_client_dev_handle_t handle() const { + return _elem.id().value; } + + void driver_data(void *data) { _driver_data = data; } + void* driver_data() { return _driver_data; } + + Allocator &slab() { return _slab; } + + bool matches(Xml_node const &node) const { + return _name == node.attribute_value("name", Name()); } + + static bool type_matches(Xml_node const &node) { + return node.has_type("device"); } + + void set_interface(uint16_t index, uint16_t value); + + void update(Allocator &alloc, Xml_node const &node) + { + Xml_node active_config = node; + + node.for_each_sub_node("config", [&] (Xml_node const &node) { + if (node.attribute_value("active", false)) + active_config = node; + }); + + _ifaces.update_from_xml(active_config, + + /* create */ + [&] (Xml_node const &node) -> ::Interface & { + return *new (alloc) ::Interface(*this, node, alloc); }, + + /* destroy */ + [&] (::Interface &iface) { + iface.update(alloc, Xml_node("")); + destroy(alloc, &iface); }, + + /* update */ + [&] (::Interface &iface, Xml_node const &node) { + iface.update(alloc, node); } + ); + } + + void update(genode_usb_client_produce_out_t out, + genode_usb_client_consume_in_t in, + genode_usb_client_produce_out_isoc_t out_isoc, + genode_usb_client_consume_in_isoc_t in_isoc, + genode_usb_client_complete_t complete) + { + if (_state == REMOVED) { + _delete_all_urbs(complete); + return; + } + + _device.update_urbs( + + /* produce out content */ + [&] (Urb &urb, Byte_range_ptr &dst) { + out(urb._driver_data.data, { (void*)dst.start, + dst.num_bytes }); + }, + + /* consume in results */ + [&] (Urb &urb, Const_byte_range_ptr &src) { + in(urb._driver_data.data, { (void*)src.start, + src.num_bytes }); + }, + + /* complete USB request */ + [&] (Urb &urb, Usb::Tagged_packet::Return_value v) + { + using Retval = Usb::Tagged_packet::Return_value; + genode_usb_client_ret_val_t ret; + + switch (v) { + case Retval::NO_DEVICE: ret = NO_DEVICE; break; + case Retval::INVALID: ret = INVALID; break; + case Retval::HALT: ret = HALT; break; + case Retval::TIMEOUT: ret = TIMEOUT; break; + case Retval::OK: + ret = OK; + if (urb.set_interface()) + set_interface(urb.index(), urb.value()); + break; + default: + error("unhandled packet should not happen!"); + ret = INVALID; + }; + + complete(urb._driver_data.data, ret); + destroy(_slab, &urb); + }); + + _ifaces.for_each([&] (::Interface &iface) { + iface.update(out, in, out_isoc, in_isoc, complete); }); + } + + template + void with_active_interfaces(FN const &fn) + { + _ifaces.for_each([&] (::Interface &iface) { + if (iface.active()) fn(iface); }); + } +}; + + +struct Session +{ + Env &_env; + Allocator &_alloc; + Signal_context_capability _handler_cap; + Usb::Connection _usb { _env }; + List_model _model {}; + Id_space _space {}; + + Session(Env &env, Allocator &alloc, Signal_context_capability io_cap, + Signal_context_capability rom_cap) + : + _env(env), _alloc(alloc), _handler_cap(io_cap) { - source()->release_packet(completion->packet); - free_completion(completion); + _usb.sigh(rom_cap); + } + + ~Session() { + _model.for_each([&] (Device &dev) { destroy(_alloc, &dev); }); } + + void update(genode_usb_client_dev_add_t add, + genode_usb_client_dev_del_t del) + { + _usb.with_xml([&] (Xml_node node) { + _model.update_from_xml(node, + + /* create */ + [&] (Xml_node const &node) -> Device & + { + Device::Name name = + node.attribute_value("name", Device::Name()); + Device::Speed speed = + node.attribute_value("speed", Device::Speed()); + Device &dev = *new (_alloc) + Device(name, speed, _usb, _alloc, _env.rm(), + _space, _handler_cap); + return dev; + }, + + /* destroy */ + [&] (Device &dev) + { + dev._state = Device::REMOVED; + if (dev.driver_data()) del(dev.handle(), dev.driver_data()); + dev.update(_alloc, Xml_node("")); + destroy(_alloc, &dev); + }, + + /* update */ + [&] (Device &dev, Xml_node const &node) + { + dev.update(_alloc, node); + } + ); + }); + + /* add new devices for C-API client after it got successfully added */ + _model.for_each([&] (Device &dev) { + if (!dev.driver_data()) + dev.driver_data(add(dev.handle(), dev.name().string(), + dev.speed())); + }); } }; -static Usb_id_space & usb_space() +Usb::Interface &::Interface::_session() { - static Usb_id_space instance { }; + if (!_iface.constructed()) { + _iface.construct(_device.session(), + Usb::Interface::Index{_number, _alt_setting}, + _buf_size); + _iface->sigh(_device.sigh_cap()); + } + return *_iface; +}; - return instance; + +bool Device::Urb::set_interface() const +{ + return (_request == Descriptor::SET_INTERFACE) && + (Request_type::R::get(_request_type) == Descriptor::IFACE) && + (Request_type::T::get(_request_type) == Descriptor::STANDARD); } -template -int usb_client_apply(genode_usb_client_handle_t handle, FUNC const &fn) +void Device::set_interface(uint16_t index, uint16_t value) +{ + _ifaces.for_each([&] (::Interface &iface) { + if (iface.number() != index) + return; + iface.active(iface.alt_setting() == value); + }); +} + + +static ::Session * _usb_session = nullptr; + + +void Genode_c_api::initialize_usb_client(Env &env, + Allocator &alloc, + Signal_context_capability io_handler, + Signal_context_capability rom_handler) +{ + static ::Session session(env, alloc, io_handler, rom_handler); + _usb_session = &session; +}; + + +extern "C" +void genode_usb_client_update(genode_usb_client_dev_add_t add, + genode_usb_client_dev_del_t del) +{ + if (_usb_session) _usb_session->update(add, del); +} + + +extern "C" +genode_usb_client_ret_val_t +genode_usb_client_device_control(genode_usb_client_dev_handle_t handle, + genode_uint8_t request, + genode_uint8_t request_type, + genode_uint16_t value, + genode_uint16_t index, + unsigned long size, + void *opaque_data) { - 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; + if (!_usb_session) + return NO_DEVICE; + + return _usb_session->_space.apply({ handle }, + [&] (Device & device) { + new (device.slab()) + Device::Urb(device, request, request_type, + value, index, size, opaque_data); + return OK; + }); + } catch(Id_space::Unknown_id&) { + return NO_DEVICE; + } catch(...) { + return NO_MEMORY; } - 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) +extern "C" +void +genode_usb_client_device_update(genode_usb_client_produce_out_t out, + genode_usb_client_consume_in_t in, + genode_usb_client_produce_out_isoc_t out_isoc, + genode_usb_client_consume_in_isoc_t in_isoc, + genode_usb_client_complete_t complete) { - Env &_env = *static_cast(env); - Range_allocator *_alloc = static_cast(alloc); - Allocator *_md_alloc = static_cast(md_alloc); + try { + if (!_usb_session) + return; + _usb_session->_model.for_each([&] (Device & device) { + device.update(out, in, out_isoc, in_isoc, complete); }); - Usb_client *client = new (_md_alloc) Usb_client(_env, usb_space(), label, - _alloc, cap(handler)); - return client->handle(); + } catch(...) { } } -void genode_usb_client_destroy(genode_usb_client_handle_t handle, - struct genode_allocator *md_alloc) +extern "C" +genode_usb_client_ret_val_t +genode_usb_client_iface_transfer(genode_usb_client_dev_handle_t handle, + genode_usb_client_iface_type_t type, + genode_uint8_t index, + unsigned long size, + void *opaque_data) { - 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); - }); -}; + try { + if (!_usb_session) + return NO_DEVICE; + genode_usb_client_ret_val_t ret = NO_DEVICE; -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); - } - }); + _usb_session->_space.apply({ handle }, + [&] (Device & device) { + device.with_active_interfaces([&] (::Interface &iface) { + iface.with_endpoint(index, [&] (Endpoint &endp) { + new (iface.slab()) + ::Interface::Urb(iface, endp, type, size, opaque_data); + ret = OK; + }); + }); + }); + + return ret; + } catch(Id_space::Unknown_id&) { + return NO_DEVICE; + } catch(...) { + return NO_MEMORY; + } } diff --git a/repos/os/src/server/black_hole/usb.h b/repos/os/src/server/black_hole/usb.h index 676e938b4f..1c956d98d6 100644 --- a/repos/os/src/server/black_hole/usb.h +++ b/repos/os/src/server/black_hole/usb.h @@ -18,7 +18,8 @@ #include /* os includes */ -#include +#include +#include namespace Black_hole { @@ -30,42 +31,44 @@ namespace Black_hole { } -class Black_hole::Usb_session : public Usb::Session_rpc_object +class Black_hole::Usb_session : public Session_object, + private Dynamic_rom_session::Xml_producer { + private: + + Env &_env; + Dynamic_rom_session _rom_session { _env.ep(), _env.ram(), + _env.rm(), *this }; + public: - Usb_session(Ram_dataspace_capability tx_ds, - Entrypoint &ep, - Region_map &rm) + Usb_session(Env &env, + Label const &label, + Resources const &resources, + Diag const &diag) + : - Session_rpc_object { tx_ds, ep.rpc_ep(), rm } - { } + Session_object(env.ep(), resources, label, diag), + Dynamic_rom_session::Xml_producer("devices"), + _env(env) { } - void sigh_state_change(Signal_context_capability /* sigh */) override { } + Rom_session_capability devices_rom() override { + return _rom_session.cap(); } - bool plugged() override { return false; } + Device_capability acquire_device(Device_name const &) override { + return Device_capability(); } - void config_descriptor(Device_descriptor * /* device_descr */, - Config_descriptor * /* config_descr */) override { } + Device_capability acquire_single_device() override { + return Device_capability(); } - unsigned alt_settings(unsigned /* index */) override { return 0; } + void release_device(Device_capability) override {} - void interface_descriptor(unsigned /* index */, - unsigned /* alt_setting */, - Interface_descriptor * /* interface_descr */) override { } - bool interface_extra(unsigned /* index */, - unsigned /* alt_setting */, - Interface_extra * /* interface_data */) override { return false; } + /******************************************* + ** Dynamic_rom_session::Xml_producer API ** + *******************************************/ - void endpoint_descriptor(unsigned /* interface_num */, - unsigned /* alt_setting */, - unsigned /* endpoint_num */, - Endpoint_descriptor * /* endpoint_descr */) override { } - - void claim_interface(unsigned /* interface_num */) override { } - - void release_interface(unsigned /* interface_num */) override { } + void produce_xml(Xml_generator &) override {} }; @@ -79,18 +82,11 @@ class Black_hole::Usb_root : public Root_component Usb_session *_create_session(char const *args) override { - size_t const ram_quota { - Arg_string::find_arg(args, "ram_quota" ).ulong_value(0) }; - - size_t const tx_buf_size { - Arg_string::find_arg(args, "tx_buf_size").ulong_value(0) }; - - if (ram_quota < tx_buf_size) { - throw Insufficient_ram_quota { }; - } - Ram_dataspace_capability tx_ds { _env.ram().alloc(tx_buf_size) }; return new (md_alloc()) - Usb_session { tx_ds, _env.ep(), _env.rm() }; + Usb_session { _env, + session_label_from_args(args), + session_resources_from_args(args), + session_diag_from_args(args) }; } public: diff --git a/repos/os/src/test/black_hole/main.cc b/repos/os/src/test/black_hole/main.cc index 92a4547822..7aff1e7afb 100644 --- a/repos/os/src/test/black_hole/main.cc +++ b/repos/os/src/test/black_hole/main.cc @@ -345,7 +345,7 @@ class Black_hole_test::Usb_test Env &_env; Allocator_avl _alloc; - Usb::Connection _connection { _env, &_alloc }; + Usb::Connection _connection { _env }; bool _finished { false }; public: diff --git a/repos/pc/src/drivers/usb_host/pc/README b/repos/pc/src/drivers/usb_host/pc/README new file mode 100644 index 0000000000..6102339439 --- /dev/null +++ b/repos/pc/src/drivers/usb_host/pc/README @@ -0,0 +1,95 @@ +USB host controller driver +########################## + +This driver is capable to serve different controller types, which a platform +offers, like OHCI, UHCI, EHCI, and XHCI. + +It can report the configuration it currently has, as well as a report +of all USB devices it has currently recognized. Both reports can be enabled +separatly via the config node of the driver: + +! +! +! +! +! +! +! +! + +The devices report contains most relevant information about an USB device, +beside of its product/vendor information, it also contains information +about configurations, interface, and endpoints available. + +Example report: + +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! +! + +An USB device can get assigned to a client request via policy nodes +within the configuration of the USB host controller driver. One can +assign devices based on their name, a vendor-product-tuple, or by +defining the class-value for all USB device interfaces that matches +that class. Here are some examples: + +! +! +! +! +! +! +! +! +! +! +! +! + +Currently, if an USB device is assigned to more than one client, only the first client +that acquires it gains access to the device. Once the device got acquired, it vanishs +from the devices ROM of the other clients. + + +BIOS Handoff +~~~~~~~~~~~~ + +Per default the driver performs a hand off of the USB controller from the +BIOS, since it still may access the controller when booting, for example, from +an USB device. The BIOS hand off induces the execution of BIOS/SMM USB driver +code and potentially DMA operations. Unfortunately, some ACPI tables report +wrong RMRR information, which implicates IOMMU faults on illegal DMA +operations and consequently the hand off may fail after noticeably long +timeouts. Therefore, the hand off can be disabled in the USB driver +configuration like follows. + +! diff --git a/repos/pc/src/drivers/usb_host/pc/main.cc b/repos/pc/src/drivers/usb_host/pc/main.cc index d5c5ba9c86..5f09d02a51 100644 --- a/repos/pc/src/drivers/usb_host/pc/main.cc +++ b/repos/pc/src/drivers/usb_host/pc/main.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -48,7 +49,6 @@ struct Main Env & env; Signal_handler
signal_handler { env.ep(), *this, &Main::handle_signal }; - Sliced_heap sliced_heap { env.ram(), env.rm() }; void handle_signal() { @@ -67,13 +67,11 @@ struct Main } Lx_kit::initialize(env, signal_handler); - env.exec_static_constructors(); - - genode_usb_init(genode_env_ptr(env), - genode_allocator_ptr(sliced_heap), - genode_signal_handler_ptr(signal_handler), - &lx_emul_usb_rpc_callbacks); + Genode_c_api::initialize_usb_service(env, signal_handler, + lx_emul_shared_dma_buffer_allocate, + lx_emul_shared_dma_buffer_free, + lx_emul_usb_release_device); lx_emul_start_kernel(nullptr); } }; diff --git a/repos/ports/recipes/pkg/vbox5-nova-capture/runtime b/repos/ports/recipes/pkg/vbox5-nova-capture/runtime index 8f9cfd4825..6bfb2e84ca 100644 --- a/repos/ports/recipes/pkg/vbox5-nova-capture/runtime +++ b/repos/ports/recipes/pkg/vbox5-nova-capture/runtime @@ -14,7 +14,6 @@ - @@ -72,7 +71,6 @@ - diff --git a/repos/ports/recipes/pkg/vbox5-nova-sculpt/runtime b/repos/ports/recipes/pkg/vbox5-nova-sculpt/runtime index 46e3ad6138..d4f7f44ce8 100644 --- a/repos/ports/recipes/pkg/vbox5-nova-sculpt/runtime +++ b/repos/ports/recipes/pkg/vbox5-nova-sculpt/runtime @@ -13,7 +13,6 @@ - diff --git a/repos/ports/recipes/pkg/vbox6-capture/runtime b/repos/ports/recipes/pkg/vbox6-capture/runtime index ca4433c8c2..ffa713c9d4 100644 --- a/repos/ports/recipes/pkg/vbox6-capture/runtime +++ b/repos/ports/recipes/pkg/vbox6-capture/runtime @@ -17,7 +17,6 @@ - @@ -81,7 +80,6 @@ - diff --git a/repos/ports/recipes/pkg/vbox6/runtime b/repos/ports/recipes/pkg/vbox6/runtime index de5c9ea560..a95e08eeeb 100644 --- a/repos/ports/recipes/pkg/vbox6/runtime +++ b/repos/ports/recipes/pkg/vbox6/runtime @@ -16,7 +16,6 @@ - @@ -78,7 +77,6 @@ - diff --git a/repos/ports/recipes/raw/vbox5-nova-sculpt/init.config b/repos/ports/recipes/raw/vbox5-nova-sculpt/init.config index 4d623c5bd0..13331a0dd9 100644 --- a/repos/ports/recipes/raw/vbox5-nova-sculpt/init.config +++ b/repos/ports/recipes/raw/vbox5-nova-sculpt/init.config @@ -38,7 +38,6 @@ - diff --git a/repos/ports/run/netperf.inc b/repos/ports/run/netperf.inc index 7e353422a2..8b474a783f 100644 --- a/repos/ports/run/netperf.inc +++ b/repos/ports/run/netperf.inc @@ -445,9 +445,13 @@ if { $use_usb_driver } { set fd [open [run_dir]/genode/usb_host_drv.config w] append usb_config {} append_if [have_board rpi] usb_config { - } + + + } append_if [have_spec x86] usb_config { - } + + + } append usb_config {} puts $fd $usb_config close $fd diff --git a/repos/ports/run/vbox5_genode_usb_hid_raw.run b/repos/ports/run/vbox5_genode_usb_hid_raw.run index b2878e5e30..cf5098c4ab 100644 --- a/repos/ports/run/vbox5_genode_usb_hid_raw.run +++ b/repos/ports/run/vbox5_genode_usb_hid_raw.run @@ -13,8 +13,6 @@ if { [have_include "power_on/qemu"] || ![have_spec nova] || ![have_spec x86_64]} exit 0 } -build { app/usb_report_filter } - create_boot_directory import_from_depot [depot_user]/src/[base_src] \ [depot_user]/src/acpi_drv \ @@ -61,13 +59,10 @@ install_config { - - - - - - - + + + + @@ -108,75 +103,17 @@ install_config { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - @@ -240,7 +177,6 @@ install_config { - @@ -275,7 +211,7 @@ exec -ignorestderr \ exec ln -sf ${genode_dir}/repos/ports/run/vm_genode_usb_hid_raw.vbox bin/ exec ln -sf ../../usb_hid_raw.iso bin/ -build_boot_image { usb_report_filter usb_hid_raw.iso vm_genode_usb_hid_raw.vbox } +build_boot_image { usb_hid_raw.iso vm_genode_usb_hid_raw.vbox } if { ![get_cmd_switch --autopilot] } { run_genode_until forever } @@ -291,10 +227,11 @@ run_genode_until {.*\[init -\> event_dump\] Input event #11.*\n} 60 [output_spaw # pay only attention to the output of init and its children grep_output {^\[init } +unify_output {(?n)^\[init -\> log_terminal\] \[init -\> usb_hid_drv\] usb usb-[0-9]-[0-9]: input irq status -19 received} "" unify_output {(?n) on usb-usbbus.*$} "" unify_output {(?n) at usb-usbbus.*\)} ")" unify_output {(?n)hid-generic.*input:} "hid-generic: input:" -unify_output {(?n)usb-[0-9]-[0-9]} "usb-X-X" +unify_output {(?n)usb-[0-9]-[0-9]: USB disconnect, device number [0-9]} "usb-X-X: USB disconnect, device number X" unify_output {(?n)device number [0-9]+} "device number X" unify_output {(?n)input[0-9]} "inputX" unify_output {(?n) as /devices/.*} "" @@ -313,13 +250,12 @@ compare_output_to { [init -> log_terminal] [init -> usb_hid_drv] usb usb-X-X: USB disconnect, device number X [init -> log_terminal] [init -> usb_hid_drv] Disconnected device: inputX [init -> log_terminal] [init -> usb_hid_drv] Disconnected device: inputX -[init -> virtualbox] Attach USB device usb-X-X -[init -> log_terminal] [init -> usb_hid_drv] input: HID 03eb:204d -[init -> log_terminal] [init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) -[init -> log_terminal] [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Keyboard [HID 03eb:204d] -[init -> log_terminal] [init -> usb_hid_drv] input: HID 03eb:204d -[init -> log_terminal] [init -> usb_hid_drv] Connected device: inputX (HID 03eb:204d) MOUSE -[init -> log_terminal] [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Mouse [HID 03eb:204d] +[init -> log_terminal] [init -> usb_hid_drv] input: Dean Camera LUFA Mouse and Keyboard Demo +[init -> log_terminal] [init -> usb_hid_drv] Connected device: inputX (Dean Camera LUFA Mouse and Keyboard Demo) +[init -> log_terminal] [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Keyboard [Dean Camera LUFA Mouse and Keyboard Demo] +[init -> log_terminal] [init -> usb_hid_drv] input: Dean Camera LUFA Mouse and Keyboard Demo +[init -> log_terminal] [init -> usb_hid_drv] Connected device: inputX (Dean Camera LUFA Mouse and Keyboard Demo) MOUSE +[init -> log_terminal] [init -> usb_hid_drv] hid-generic: input: USB HID v1.11 Mouse [Dean Camera LUFA Mouse and Keyboard Demo] [init -> log_terminal] [init -> event_dump] Input event #5 PRESS KEY_X 65534 key count: 1 [init -> log_terminal] [init -> event_dump] Input event #6 RELEASE KEY_X key count: 0 [init -> log_terminal] [init -> event_dump] Input event #7 REL_MOTION -1+1 key count: 0 diff --git a/repos/ports/run/vbox_share.inc b/repos/ports/run/vbox_share.inc index a1139546b4..37f8a92473 100644 --- a/repos/ports/run/vbox_share.inc +++ b/repos/ports/run/vbox_share.inc @@ -275,9 +275,6 @@ set config_of_app { - - - diff --git a/repos/ports/run/vbox_win.inc b/repos/ports/run/vbox_win.inc index 5828d56dcd..064504e9cc 100644 --- a/repos/ports/run/vbox_win.inc +++ b/repos/ports/run/vbox_win.inc @@ -144,9 +144,7 @@ set config_of_app { } append config_of_app { - - - } +} append_if [expr $use_cpu_load] config_of_app { } diff --git a/repos/ports/run/virtualbox_auto.inc b/repos/ports/run/virtualbox_auto.inc index 2c676948ec..641ae93364 100644 --- a/repos/ports/run/virtualbox_auto.inc +++ b/repos/ports/run/virtualbox_auto.inc @@ -47,6 +47,10 @@ if {$use_usb} { [depot_user]/src/usb_hid_drv } +if {!$use_usb} { + import_from_depot [depot_user]/src/black_hole +} + if {$use_rumpfs} { import_from_depot [depot_user]/src/rump } @@ -61,7 +65,6 @@ append build_components { } lappend_if [expr $use_serial] build_components server/log_terminal -lappend_if [expr $use_usb] build_components app/usb_report_filter lappend_if [expr $use_ram_fs || $use_usb] build_components lib/vfs_import lappend_if [expr $use_cpu_load] build_components app/trace_subject_reporter lappend_if [expr $use_cpu_load] build_components app/cpu_load_display @@ -332,99 +335,43 @@ append_if [expr $use_cpu_load] config { } append_if [expr $use_usb] config { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -434,6 +381,15 @@ append_if [expr $use_usb] config { } +append_if [expr !$use_usb] config { + + + + + + +} + append_if [expr $use_serial] config { @@ -453,6 +409,3 @@ install_config $config lappend boot_modules {*}[build_artifacts] lappend_if [expr $use_ram_fs && !$use_overlay_from_disk] boot_modules $overlay_image - -# platform-specific modules -lappend_if [expr $use_usb] boot_modules usb_report_filter