diff --git a/bdk/usb/usb_gadget_hid.c b/bdk/usb/usb_gadget_hid.c index dc7681a0..66c8eef2 100644 --- a/bdk/usb/usb_gadget_hid.c +++ b/bdk/usb/usb_gadget_hid.c @@ -1,7 +1,7 @@ /* * USB Gadget HID driver for Tegra X1 * - * Copyright (c) 2019-2022 CTCaer + * Copyright (c) 2019-2025 CTCaer * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -80,6 +80,8 @@ enum { static jc_cal_t jc_cal_ctx; static usb_ops_t usb_ops; +static void *rpt_buffer = (u8 *)USB_EP_BULK_IN_BUF_ADDR; + static bool _jc_calibration(const jc_gamepad_rpt_t *jc_pad) { // Calibrate left stick. @@ -347,7 +349,7 @@ static bool _fts_touch_read(touchpad_report_t *rpt) static u8 _hid_transfer_start(usb_ctxt_t *usbs, u32 len) { - u8 status = usb_ops.usb_device_ep1_in_write((u8 *)USB_EP_BULK_IN_BUF_ADDR, len, NULL, USB_XFER_SYNCED_CMD); + u8 status = usb_ops.usb_device_ep1_in_write(rpt_buffer, len, NULL, USB_XFER_SYNCED_CMD); if (status == USB_ERROR_XFER_ERROR) { usbs->set_text(usbs->label, "#FFDD00 Error:# EP IN transfer!"); @@ -364,12 +366,12 @@ static u8 _hid_transfer_start(usb_ctxt_t *usbs, u32 len) static bool _hid_poll_jc(usb_ctxt_t *usbs) { - int res = _jc_poll((gamepad_report_t *)USB_EP_BULK_IN_BUF_ADDR); + int res = _jc_poll(rpt_buffer); if (res == INPUT_POLL_EXIT) return true; // Send HID report. - if (res == INPUT_POLL_HAS_PACKET) + if (res == INPUT_POLL_HAS_PACKET || usbs->idle) if (_hid_transfer_start(usbs, sizeof(gamepad_report_t))) return true; // EP Error. @@ -378,7 +380,7 @@ static bool _hid_poll_jc(usb_ctxt_t *usbs) static bool _hid_poll_touch(usb_ctxt_t *usbs) { - _fts_touch_read((touchpad_report_t *)USB_EP_BULK_IN_BUF_ADDR); + _fts_touch_read(rpt_buffer); // Send HID report. if (_hid_transfer_start(usbs, sizeof(touchpad_report_t))) @@ -399,6 +401,10 @@ int usb_device_gadget_hid(usb_ctxt_t *usbs) else xusb_device_get_ops(&usb_ops); + // Always push packets by default. + //! TODO: For now only per polling rate or on change is supported. + usbs->idle = 1; + if (usbs->type == USB_HID_GAMEPAD) { polling_time = 15000; @@ -426,7 +432,8 @@ int usb_device_gadget_hid(usb_ctxt_t *usbs) usbs->set_text(usbs->label, "#C7EA46 Status:# Waiting for HID report request"); - if (usb_ops.usb_device_class_send_hid_report()) + u32 rpt_size = usbs->type == USB_HID_GAMEPAD ? sizeof(gamepad_report_t) : sizeof(touchpad_report_t); + if (usb_ops.usb_device_class_send_hid_report(rpt_buffer, rpt_size)) goto error; usbs->set_text(usbs->label, "#C7EA46 Status:# Started HID emulation"); @@ -436,6 +443,13 @@ int usb_device_gadget_hid(usb_ctxt_t *usbs) { u32 timer = get_tmr_us(); + // Check for suspended USB in case the cable was pulled. + if (usb_ops.usb_device_get_suspended()) + break; // Disconnected. + + // Handle control endpoint. + usb_ops.usbd_handle_ep0_ctrl_setup(&usbs->idle); + // Parse input device. if (usbs->type == USB_HID_GAMEPAD) { @@ -448,13 +462,6 @@ int usb_device_gadget_hid(usb_ctxt_t *usbs) break; } - // Check for suspended USB in case the cable was pulled. - if (usb_ops.usb_device_get_suspended()) - break; // Disconnected. - - // Handle control endpoint. - usb_ops.usbd_handle_ep0_ctrl_setup(); - // Wait max gadget timing. timer = get_tmr_us() - timer; if (timer < polling_time) diff --git a/bdk/usb/usb_gadget_ums.c b/bdk/usb/usb_gadget_ums.c index 1655bd5d..4b06ed67 100644 --- a/bdk/usb/usb_gadget_ums.c +++ b/bdk/usb/usb_gadget_ums.c @@ -285,7 +285,7 @@ static void raise_exception(usbd_gadget_ums_t *ums, enum ums_state new_state) static void _handle_ep0_ctrl(usbd_gadget_ums_t *ums) { - if (usb_ops.usbd_handle_ep0_ctrl_setup()) + if (usb_ops.usbd_handle_ep0_ctrl_setup(NULL)) raise_exception(ums, UMS_STATE_PROTOCOL_RESET); } diff --git a/bdk/usb/usbd.c b/bdk/usb/usbd.c index aa5351bf..70a930fb 100644 --- a/bdk/usb/usbd.c +++ b/bdk/usb/usbd.c @@ -101,7 +101,11 @@ typedef struct _usbd_controller_t bool configuration_set; bool max_lun_set; bool bulk_reset_req; + u32 intr_idle_rate; + bool intr_idle_req; bool hid_report_sent; + void *hid_rpt_buffer; + u32 hid_rpt_size; u32 charger_detect; } usbd_controller_t; @@ -889,9 +893,9 @@ static void _usbd_handle_get_class_request(bool *transmit_data, u8 *desc, int *s u16 _wLength = usbd_otg->control_setup.wLength; bool valid_interface = _wIndex == usbd_otg->interface_num; - bool valid_len = (_bRequest == USB_REQUEST_BULK_GET_MAX_LUN) ? 1 : 0; + bool valid_val = (_bRequest >= USB_REQUEST_BULK_GET_MAX_LUN) ? (!_wValue) : true; - if (!valid_interface || _wValue != 0 || _wLength != valid_len) + if (!valid_interface || !valid_val) { *ep_stall = true; return; @@ -899,20 +903,48 @@ static void _usbd_handle_get_class_request(bool *transmit_data, u8 *desc, int *s switch (_bRequest) { + case USB_REQUEST_INTR_GET_REPORT: + if (usbd_otg->hid_rpt_size != _wLength) + break; + + // _wValue unused as there's only one report type and id. + *transmit_data = true; + *size = usbd_otg->hid_rpt_size; + memcpy(desc, usbd_otg->hid_rpt_buffer, usbd_otg->hid_rpt_size); + return; + + case USB_REQUEST_INTR_SET_IDLE: + if (_wLength) + break; + + usbd_otg->intr_idle_rate = (_wValue & 0xFF) * 4 * 1000; // Only one interface so upper byte ignored. + usbd_otg->intr_idle_req = true; + _usbd_ep_ack(USB_EP_CTRL_IN); + return; // DELAYED_STATUS; + case USB_REQUEST_BULK_RESET: + if (_wLength) + break; + _usbd_ep_ack(USB_EP_CTRL_IN); usbd_otg->bulk_reset_req = true; - break; // DELAYED_STATUS; + return; // DELAYED_STATUS; + case USB_REQUEST_BULK_GET_MAX_LUN: + if (_wLength != 1) + break; + *transmit_data = true; *size = 1; desc[0] = usbd_otg->max_lun; // Set 0 LUN for 1 drive supported. usbd_otg->max_lun_set = true; - break; + return; + default: - *ep_stall = true; break; } + + *ep_stall = true; } static void _usbd_handle_get_descriptor(bool *transmit_data, void **desc, int *size, bool *ep_stall) @@ -1395,7 +1427,7 @@ int usb_device_enumerate(usb_gadget_type gadget) return _usbd_ep0_initialize(); } -int usbd_handle_ep0_ctrl_setup() +int usbd_handle_ep0_ctrl_setup(u32 *data) { // Acknowledge setup request for EP0 and copy its configuration. u32 ep0_setup_req = usbd_otg->regs->endptsetupstat; @@ -1407,6 +1439,15 @@ int usbd_handle_ep0_ctrl_setup() memset(usb_ep0_ctrl_buf, 0, USB_TD_BUFFER_PAGE_SIZE); } + if (usbd_otg->intr_idle_req) + { + if (data) + *data = usbd_otg->intr_idle_rate; + + usbd_otg->intr_idle_req = false; + return USB_RES_OK; + } + // Only return error if bulk reset was requested. if (usbd_otg->bulk_reset_req) { @@ -1489,7 +1530,7 @@ int usb_device_ep1_out_reading_finish(u32 *pending_bytes, u32 sync_timeout) if ((ep_status == USB_EP_STATUS_IDLE) || (ep_status == USB_EP_STATUS_DISABLED)) break; - usbd_handle_ep0_ctrl_setup(); + usbd_handle_ep0_ctrl_setup(NULL); } while ((ep_status == USB_EP_STATUS_ACTIVE) || (ep_status == USB_EP_STATUS_STALLED)); @@ -1538,7 +1579,7 @@ int usb_device_ep1_in_writing_finish(u32 *pending_bytes, u32 sync_timeout) if ((ep_status == USB_EP_STATUS_IDLE) || (ep_status == USB_EP_STATUS_DISABLED)) break; - usbd_handle_ep0_ctrl_setup(); + usbd_handle_ep0_ctrl_setup(NULL); } while ((ep_status == USB_EP_STATUS_ACTIVE) || (ep_status == USB_EP_STATUS_STALLED)); @@ -1574,7 +1615,7 @@ int usb_device_class_send_max_lun(u8 max_lun) while (!usbd_otg->max_lun_set) { - usbd_handle_ep0_ctrl_setup(); + usbd_handle_ep0_ctrl_setup(NULL); if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN)) return USB_ERROR_USER_ABORT; } @@ -1582,15 +1623,19 @@ int usb_device_class_send_max_lun(u8 max_lun) return USB_RES_OK; } -int usb_device_class_send_hid_report() +int usb_device_class_send_hid_report(void *rpt_buffer, u32 rpt_size) { + // Set buffers. + usbd_otg->hid_rpt_buffer = rpt_buffer; + usbd_otg->hid_rpt_size = rpt_size; + // Timeout if get GET_HID_REPORT request doesn't happen in 10s. u32 timer = get_tmr_ms() + 10000; // Wait for request and transfer start. while (!usbd_otg->hid_report_sent) { - usbd_handle_ep0_ctrl_setup(); + usbd_handle_ep0_ctrl_setup(NULL); if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN)) return USB_ERROR_USER_ABORT; } diff --git a/bdk/usb/usbd.h b/bdk/usb/usbd.h index 86aa8fa3..3895a9c2 100644 --- a/bdk/usb/usbd.h +++ b/bdk/usb/usbd.h @@ -1,7 +1,7 @@ /* * Enhanced & eXtensible USB Device (EDCI & XDCI) driver for Tegra X1 * - * Copyright (c) 2019-2021 CTCaer + * Copyright (c) 2019-2025 CTCaer * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -31,11 +31,11 @@ #define USB_EP_BUFFER_ALIGN (USB_TD_BUFFER_PAGE_SIZE) #define USB_XFER_START 0 -#define USB_XFER_SYNCED_ENUM 1000000 -#define USB_XFER_SYNCED_CMD 1000000 -#define USB_XFER_SYNCED_DATA 2000000 -#define USB_XFER_SYNCED_CLASS 5000000 -#define USB_XFER_SYNCED -1 +#define USB_XFER_SYNCED_ENUM 1000000 // ~2s. +#define USB_XFER_SYNCED_CMD 1000000 // ~2s. +#define USB_XFER_SYNCED_DATA 2000000 // ~4s. +#define USB_XFER_SYNCED_CLASS 5000000 // ~10s. +#define USB_XFER_SYNCED -1 // Max. typedef enum _usb_hid_type { @@ -122,6 +122,9 @@ typedef enum { USB_REQUEST_GET_MS_DESCRIPTOR = 0x99, + USB_REQUEST_INTR_GET_REPORT = 1, + USB_REQUEST_INTR_SET_IDLE = 10, + USB_REQUEST_BULK_GET_MAX_LUN = 0xFE, USB_REQUEST_BULK_RESET = 0xFF } usb_standard_req_t; @@ -167,12 +170,13 @@ typedef struct _usb_ops_t { int (*usbd_flush_endpoint)(u32); int (*usbd_set_ep_stall)(u32, int); - int (*usbd_handle_ep0_ctrl_setup)(); + int (*usbd_handle_ep0_ctrl_setup)(u32 *); void (*usbd_end)(bool, bool); int (*usb_device_init)(); - int (*usb_device_enumerate)(usb_gadget_type gadget); + int (*usb_device_enumerate)(usb_gadget_type); + int (*usb_device_class_send_max_lun)(u8); - int (*usb_device_class_send_hid_report)(); + int (*usb_device_class_send_hid_report)(void *, u32); int (*usb_device_ep1_out_read)(u8 *, u32, u32 *, u32); int (*usb_device_ep1_out_read_big)(u8 *, u32, u32 *); @@ -186,10 +190,17 @@ typedef struct _usb_ops_t typedef struct _usb_ctxt_t { u32 type; + + // UMS. u32 partition; u32 offset; u32 sectors; u32 ro; + + // HID. + u32 idle; + + // System. void (*system_maintenance)(bool); void *label; void (*set_text)(void *, const char *); @@ -201,4 +212,4 @@ void xusb_device_get_ops(usb_ops_t *ops); int usb_device_gadget_ums(usb_ctxt_t *usbs); int usb_device_gadget_hid(usb_ctxt_t *usbs); -#endif \ No newline at end of file +#endif diff --git a/bdk/usb/xusbd.c b/bdk/usb/xusbd.c index b7bc30b0..a4a47e60 100644 --- a/bdk/usb/xusbd.c +++ b/bdk/usb/xusbd.c @@ -381,6 +381,10 @@ typedef struct _xusbd_controller_t u8 max_lun; bool max_lun_set; + void *hid_rpt_buffer; + u32 hid_rpt_size; + u32 intr_idle_rate; + bool intr_idle_req; bool bulk_reset_req; } xusbd_controller_t; @@ -1469,20 +1473,39 @@ static int _xusb_handle_get_class_request(const usb_ctrl_setup_t *ctrl_setup) u16 _wLength = ctrl_setup->wLength; bool valid_interface = _wIndex == usbd_xotg->interface_num; - bool valid_len = (_bRequest == USB_REQUEST_BULK_GET_MAX_LUN) ? 1 : 0; + bool valid_val = (_bRequest >= USB_REQUEST_BULK_GET_MAX_LUN) ? (!_wValue) : true; - if (!valid_interface || _wValue != 0 || _wLength != valid_len) + if (!valid_interface || !valid_val) goto stall; switch (_bRequest) { + case USB_REQUEST_INTR_GET_REPORT: + if (usbd_xotg->hid_rpt_size != _wLength) + goto stall; + + // _wValue unused as there's only one report type and id. + return _xusb_issue_data_trb(usbd_xotg->hid_rpt_buffer, usbd_xotg->hid_rpt_size, USB_DIR_IN); + + case USB_REQUEST_INTR_SET_IDLE: + if (_wLength) + goto stall; + + usbd_xotg->intr_idle_rate = (_wValue & 0xFF) * 4 * 1000; // Only one interface so upper byte ignored. + usbd_xotg->intr_idle_req = true; + return _xusb_issue_status_trb(USB_DIR_IN); // DELAYED_STATUS; + case USB_REQUEST_BULK_RESET: + if (_wLength) + goto stall; + usbd_xotg->bulk_reset_req = true; return _xusb_issue_status_trb(USB_DIR_IN); // DELAYED_STATUS; case USB_REQUEST_BULK_GET_MAX_LUN: - if (!usbd_xotg->max_lun_set) + if (_wLength != 1 || !usbd_xotg->max_lun_set) goto stall; + usbd_xotg->device_state = XUSB_LUN_CONFIGURED_STS_WAIT; return _xusb_issue_data_trb(&usbd_xotg->max_lun, 1, USB_DIR_IN); } @@ -2019,12 +2042,24 @@ void xusb_end(bool reset_ep, bool only_controller) _xusb_device_power_down(); } -int xusb_handle_ep0_ctrl_setup() +int xusb_handle_ep0_ctrl_setup(u32 *data) { /* * EP0 Control handling is done by normal ep operation in XUSB. - * Here we handle the bulk reset only. + * Here we handle the interface only, except if HID. */ + if (usbd_xotg->gadget >= USB_GADGET_HID_GAMEPAD) + _xusb_ep_operation(1); + + if (usbd_xotg->intr_idle_req) + { + if (data) + *data = usbd_xotg->intr_idle_rate; + + usbd_xotg->intr_idle_req = false; + return USB_RES_OK; + } + if (usbd_xotg->bulk_reset_req) { usbd_xotg->bulk_reset_req = false; @@ -2178,8 +2213,12 @@ bool xusb_device_class_send_max_lun(u8 max_lun) return false; } -bool xusb_device_class_send_hid_report() +bool xusb_device_class_send_hid_report(void *rpt_buffer, u32 rpt_size) { + // Set buffers. + usbd_xotg->hid_rpt_buffer = rpt_buffer; + usbd_xotg->hid_rpt_size = rpt_size; + // Timeout if get GET_HID_REPORT request doesn't happen in 10s. u32 timer = get_tmr_ms() + 10000;