From 7ec08af6d912759548d6285ab5254eeb0cddf87f Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Thu, 14 Sep 2023 10:55:11 +0200 Subject: [PATCH] usb: session renewal & new client API Replace the USB session API by one that provides a devices ROM only, which contains information about all USB devices available for this client, as well as methods to acquire and release a single device. The acquisition of an USB device returns the capability to a device session that includes a packet stream buffer to communicate control transfers in between the client and the USB host controller driver. Moreover, additional methods to acquire and release an USB interface can be used. The acquisition of an USB interface returns the capability to an interface session that includes a packet stream buffer to communicate either bulk, interrupt, or isochronous transfers in between the client and the USB host controller driver. This commit implements the API changes in behalf of the Genode C API's USB server and client side. Addtionally, it provides Usb::Device, Usb::Interface, and Usb::Endpoint utilities that can be used by native C++ clients to use the new API and hide the sophisticated packet stream API. The adaptations necessary target the following areas: * lx_emul layer for USB host and client side * Linux USB host controller driver port for PC * Linux USB client ports: usb_hid_drv and usb_net_drv, additionally reduce the Linux tasks used inside these drivers * Native usb_block_drv * black_hole component * Port of libusb, including smartcard and usb_webcam driver depending on it * Port of Qemu XHCI model library, including vbox5 & vbox6 depending on it * Adapt all run-scripts and drivers_interactive recipes to work with the new policy rules of the USB host controller driver Fix genodelabs/genode#5021 --- repos/dde_linux/run/usb_hid_raw.run | 19 +- repos/dde_linux/run/usb_hid_reconnect.run | 6 +- repos/dde_linux/run/usb_net.run | 7 +- repos/dde_linux/src/drivers/usb_hid/README | 18 +- repos/dde_linux/src/drivers/usb_hid/dummies.c | 72 +- repos/dde_linux/src/drivers/usb_hid/lx_emul.c | 29 - repos/dde_linux/src/drivers/usb_hid/lx_user.c | 43 +- repos/dde_linux/src/drivers/usb_hid/main.cc | 368 +-- .../usb_hid/spec/arm_v6/generated_dummies.c | 93 +- .../drivers/usb_hid/spec/arm_v6/source.list | 3 + .../usb_hid/spec/arm_v7/generated_dummies.c | 93 +- .../drivers/usb_hid/spec/arm_v7/source.list | 3 + .../usb_hid/spec/arm_v8/generated_dummies.c | 93 +- .../drivers/usb_hid/spec/arm_v8/source.list | 3 + .../usb_hid/spec/x86_32/generated_dummies.c | 85 +- .../drivers/usb_hid/spec/x86_32/source.list | 3 + .../usb_hid/spec/x86_64/generated_dummies.c | 85 +- .../drivers/usb_hid/spec/x86_64/source.list | 3 + .../dde_linux/src/drivers/usb_hid/target.inc | 10 +- repos/dde_linux/src/drivers/usb_hid/usb_hid.h | 9 +- repos/dde_linux/src/drivers/usb_net/dummies.c | 86 +- repos/dde_linux/src/drivers/usb_net/lx_emul.c | 351 --- repos/dde_linux/src/drivers/usb_net/lx_user.c | 18 +- repos/dde_linux/src/drivers/usb_net/main.cc | 177 +- .../usb_net/spec/arm_v6/generated_dummies.c | 69 +- .../drivers/usb_net/spec/arm_v6/source.list | 3 + .../usb_net/spec/arm_v7/generated_dummies.c | 69 +- .../drivers/usb_net/spec/arm_v7/source.list | 3 + .../usb_net/spec/arm_v8/generated_dummies.c | 69 +- .../drivers/usb_net/spec/arm_v8/source.list | 3 + .../usb_net/spec/x86_32/generated_dummies.c | 61 - .../drivers/usb_net/spec/x86_32/source.list | 3 + .../usb_net/spec/x86_64/generated_dummies.c | 61 - .../drivers/usb_net/spec/x86_64/source.list | 3 + .../dde_linux/src/drivers/usb_net/target.inc | 6 +- .../src/include/lx_emul/input_leds.h | 1 + repos/dde_linux/src/include/lx_emul/usb.h | 5 +- .../src/include/lx_emul/usb_client.h | 7 +- .../lx_emul/shadow/drivers/input/input-leds.c | 48 +- repos/dde_linux/src/lib/lx_emul/usb.c | 1097 +++----- .../virt/shadow/drivers/usb/core/buffer.c | 15 + .../virt/shadow/drivers/usb/core/hcd.c | 89 + .../virt/shadow/drivers/usb/core/message.c | 451 ---- .../virt/shadow/drivers/usb/core/urb.c | 218 -- .../virt/shadow/drivers/usb/core/urb_helper.h | 43 - .../src/lib/lx_emul/virt/usb_client.c | 295 ++- repos/gems/sculpt/default-pc.sculpt | 2 +- repos/gems/sculpt/drivers/pc | 2 +- repos/gems/sculpt/launcher/usb_devices_rom | 8 - repos/gems/src/app/driver_manager/main.cc | 46 +- .../recipes/raw/usb_webcam/usb_webcam.config | 4 +- repos/libports/run/acpica.run | 3 +- repos/libports/run/smartcard.run | 4 +- repos/libports/run/webcam.inc | 11 +- .../libports/src/lib/libusb/genode_usb_raw.cc | 743 +++--- repos/libports/src/lib/qemu-usb/host.cc | 1743 ++++++------ .../src/lib/qemu-usb/include/qemu_emul.h | 2 +- repos/libports/src/lib/qemu-usb/qemu_emul.cc | 4 +- .../libports/src/lib/vfs/libusb/vfs_libusb.cc | 90 +- repos/os/include/genode_c_api/base.h | 7 + repos/os/include/genode_c_api/usb.h | 346 +-- repos/os/include/genode_c_api/usb_client.h | 216 +- repos/os/include/usb/packet_handler.h | 139 - repos/os/include/usb/types.h | 223 -- repos/os/include/usb/usb.h | 597 ----- repos/os/include/usb_session/capability.h | 12 +- repos/os/include/usb_session/client.h | 108 +- repos/os/include/usb_session/connection.h | 109 +- repos/os/include/usb_session/device.h | 798 ++++++ repos/os/include/usb_session/rpc_object.h | 54 - repos/os/include/usb_session/types.h | 114 + repos/os/include/usb_session/usb_session.h | 367 ++- repos/os/recipes/api/usb_session/content.mk | 2 +- .../raw/drivers_interactive-pc/drivers.config | 3 +- repos/os/run/usb_block.run | 10 +- repos/os/src/app/usb_report_filter/README | 78 - repos/os/src/app/usb_report_filter/main.cc | 431 --- repos/os/src/app/usb_report_filter/target.mk | 3 - repos/os/src/drivers/usb_block/cbw_csw.h | 80 +- repos/os/src/drivers/usb_block/main.cc | 1629 +++++------- repos/os/src/drivers/usb_block/mmio.h | 74 + repos/os/src/drivers/usb_block/scsi.h | 23 +- repos/os/src/lib/genode_c_api/usb.cc | 2334 +++++++++++------ repos/os/src/lib/genode_c_api/usb_client.cc | 852 ++++-- repos/os/src/server/black_hole/usb.h | 70 +- repos/os/src/test/black_hole/main.cc | 2 +- repos/pc/src/drivers/usb_host/pc/README | 95 + repos/pc/src/drivers/usb_host/pc/main.cc | 12 +- .../recipes/pkg/vbox5-nova-capture/runtime | 2 - .../recipes/pkg/vbox5-nova-sculpt/runtime | 1 - repos/ports/recipes/pkg/vbox6-capture/runtime | 2 - repos/ports/recipes/pkg/vbox6/runtime | 2 - .../recipes/raw/vbox5-nova-sculpt/init.config | 1 - repos/ports/run/netperf.inc | 8 +- repos/ports/run/vbox5_genode_usb_hid_raw.run | 98 +- repos/ports/run/vbox_share.inc | 3 - repos/ports/run/vbox_win.inc | 4 +- repos/ports/run/virtualbox_auto.inc | 123 +- 98 files changed, 6937 insertions(+), 9053 deletions(-) create mode 100644 repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/buffer.c create mode 100644 repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/hcd.c delete mode 100644 repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/message.c delete mode 100644 repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb.c delete mode 100644 repos/dde_linux/src/lib/lx_emul/virt/shadow/drivers/usb/core/urb_helper.h delete mode 100644 repos/gems/sculpt/launcher/usb_devices_rom delete mode 100644 repos/os/include/usb/packet_handler.h delete mode 100644 repos/os/include/usb/types.h delete mode 100644 repos/os/include/usb/usb.h create mode 100644 repos/os/include/usb_session/device.h delete mode 100644 repos/os/include/usb_session/rpc_object.h create mode 100644 repos/os/include/usb_session/types.h delete mode 100644 repos/os/src/app/usb_report_filter/README delete mode 100644 repos/os/src/app/usb_report_filter/main.cc delete mode 100644 repos/os/src/app/usb_report_filter/target.mk create mode 100644 repos/os/src/drivers/usb_block/mmio.h create mode 100644 repos/pc/src/drivers/usb_host/pc/README 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