writing to out endpoint causes the endpoint to get shut down
hi, i wrote a small usb-hid project. everything works fine with the IN endpoint, but for the last few days i've been trying to debug the OUT endpoint and i still have no idea what's going on. the device enumerates fine, received well, values are being updated. when i try to send something to the out endpoint (fake out report), the device closes (what i mean by that, i don't see updates on a gamepad tester website, it's a joystick device), i get a "cannot send after transport endpoint shutdown" urb status on wireshark. dmesg, journalctl is clear. i guess that means that it's something happening on the device? i tried doing this even without a callback on OUT (as indicated by the commented out line) but it still broke down. please note my callback would actually get called, although the endpoint read would return all 0's. the length seems to be correct, weird. i'm attaching my main file along with the handle_all function. thanks for any help, also sorry if here is not the right place for getting help
edit: forgot to mention it's an stm32f103
#include <stddef.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/hid.h>
#include <usart.h>
#include <ffb_controller.h>
#include <pid_desc.h>
#include <dev.h>
void reenumerate(void);
static usbd_device* usbd_dev;
u8 usbd_control_buffer[4096];
static enum usbd_request_return_codes hid_control_request(
usbd_device* dev,
struct usb_setup_data* req,
u8** buf,
u16* len,
void (**complete)(usbd_device*, struct usb_setup_data*)
) {
(void)complete;
(void)dev;
/* only get_descriptor is supported */
if ((req->bmRequestType != 0x81) ||
(req->bRequest != USB_REQ_GET_DESCRIPTOR) ||
(req->wValue != 0x2200))
return USBD_REQ_NOTSUPP;
/* send back the hid report desc */
*buf = (u8*)hid_report_descriptor;
*len = sizeof(hid_report_descriptor);
return USBD_REQ_HANDLED;
}
static void hid_set_config(usbd_device* dev, u16 wValue) {
(void)wValue;
usbd_ep_setup(dev, EP_ADDR, USB_ENDPOINT_ATTR_INTERRUPT, EP_SIZE, NULL);
usbd_ep_setup(dev, EP_OUT_ADDR, USB_ENDPOINT_ATTR_INTERRUPT, EP_OUT_SIZE, handle_all);
/* usbd_ep_setup(dev, 0x01, USB_ENDPOINT_ATTR_INTERRUPT, 64, NULL); */
/* for get_descriptor */
usbd_register_control_callback(
dev,
USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE,
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
hid_control_request
);
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8);
/* SysTick interrupt every N clock pulses: set reload to N-1 */
systick_set_reload(99999);
systick_interrupt_enable();
systick_counter_enable();
}
void reenumerate(void) {
rcc_periph_clock_enable(RCC_GPIOA);
/*
* This is a somewhat common cheap hack to trigger device re-enumeration
* on startup. Assuming a fixed external pullup on D+, (For USB-FS)
* setting the pin to output, and driving it explicitly low effectively
* "removes" the pullup. The subsequent USB init will "take over" the
* pin, and it will appear as a proper pullup to the host.
* The magic delay is somewhat arbitrary, no guarantees on USBIF
* compliance here, but "it works" in most places.
*/
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
gpio_clear(GPIOA, GPIO12);
for (unsigned i = 0; i < 800000; i++) {
__asm__("nop");
}
}
int main(void) {
rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]);
rcc_periph_clock_enable(RCC_GPIOC);
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
gpio_clear(GPIOC, GPIO13);
usart_init();
usart_str("main called");
reenumerate();
usbd_dev = usbd_init(
&st_usbfs_v1_usb_driver,
&dev_descr,
&config,
usb_strings,
sizeof(usb_strings),
usbd_control_buffer,
sizeof(usbd_control_buffer)
);
usbd_register_set_config_callback(usbd_dev, hid_set_config);
while (1) {
usbd_poll(usbd_dev);
}
}
uint32_t ctr = 0;
void sys_tick_handler(void) {
u8 buf[EP_SIZE] = {};
for (u8 n = 0; n < EP_SIZE; n++) {
buf[n] = 0x00;
}
/* report id */
buf[0] = 1;
if (ctr % 10) {
buf[1] = 0b10101010;
} else {
buf[1] = 0b00001111;
}
ctr++;
usbd_ep_write_packet(usbd_dev, EP_ADDR, buf, sizeof(buf));
}
void handle_all(usbd_device* dev, u8 ep) {
usart_str("handle_all called");
char buf[EP_OUT_SIZE] = {};
u8 len = usbd_ep_read_packet(dev, ep, buf, EP_OUT_SIZE);
usart_str("packet read");
if (len) {
/* lazy dummy, improve this */
usart_str_nocrlf("len: ");
char len_str = len + 48;
usart_str(&len_str);
usart_str("recv buffer:");
for (uint8_t i = 0; i < len; i++) {
char temp[200];
snprintf(temp, sizeof(temp), "%02X ", buf[i]);
usart_str_nocrlf(temp);
}
usart_str_nocrlf("\n");
/* create new effect report */
/* (report id 5) */
if (buf[0] == 5) {
usart_str("report id 5");
// effect type
switch (buf[1]) {
// et square
case 0x30: {
handle_square_effect();
break;
}
default: {
break;
}
}
} else {
usart_str("report id other than 5");
}
}
}
oops. i didn't notice i'm posting in examples. hope this reaches someone who can help