#include "include.h" #include "usb_com.h" #include "usb_hid.h" #include "usb_hid_desc.h" #if USB_HID_EN #define TRACE_RATE_EN 0 #define TRACE_RATE_PERIOD 1000 // Unit: sof #define HID_RECV_PKS_NUM 5 #define HID_RESUME_PREVENT_TIME 1000 // Unit: ms udh_t udh_0 AT(.usb_buf.usb); epcb_t ep_hid_tx AT(.usb_buf.usb); epcb_t ep_hid_rx AT(.usb_buf.usb); u8 ep_hid_buf_out[HID_EP_OUT_MAX_SIZE + 4] AT(.usb_buf.hid); // CRC u8 ep_hid_buf_in[HID_EP_IN_MAX_SIZE] AT(.usb_buf.hid); u32 tick_resume_prevent AT(.bss.usb_buf.hid); #if TRACE_RATE_EN static u32 statistic_sof_count; static u32 statistic_tx_done_count; #endif static struct { uint8_t ridx; uint8_t widx; uint16_t cnt; uint8_t data[HID_EP_IN_MAX_SIZE * HID_RECV_PKS_NUM]; } hid_recv_cb AT(.usb_buf.hid); #define usb_hid_is_valid() (udh_0.valid == true) #define usb_hid_resume_prevent_en() {tick_resume_prevent = tick_get();} #define usb_hid_resume_prevent_dis() {tick_resume_prevent = 0;} #define usb_hid_resume_prevent_chk() (!tick_resume_prevent || !tick_check_expire(tick_resume_prevent, HID_RESUME_PREVENT_TIME)) AT(.com_text.hid.mouse) static bool hid_recv_queue_push(uint8_t *data) { GLOBAL_INT_DISABLE(); bool status = false; uint8_t *wptr = &hid_recv_cb.data[hid_recv_cb.widx * HID_EP_IN_MAX_SIZE]; if (hid_recv_cb.cnt < HID_RECV_PKS_NUM) { memcpy(wptr, data, HID_EP_IN_MAX_SIZE); hid_recv_cb.widx = (hid_recv_cb.widx + 1) % HID_RECV_PKS_NUM; hid_recv_cb.cnt++; status = true; } GLOBAL_INT_RESTORE(); return (status); } AT(.com_text.hid.mouse) static bool hid_recv_queue_pop(uint8_t *data) { GLOBAL_INT_DISABLE(); bool status = false; uint8_t *rptr = &hid_recv_cb.data[hid_recv_cb.ridx * HID_EP_IN_MAX_SIZE]; if (hid_recv_cb.cnt) { memcpy(data, rptr, HID_EP_IN_MAX_SIZE); hid_recv_cb.ridx = (hid_recv_cb.ridx + 1) % HID_RECV_PKS_NUM; hid_recv_cb.cnt--; status = true; } GLOBAL_INT_RESTORE(); return (status); } AT(.com_text.hid.mouse) static void hid_recv_queue_clear(void) { GLOBAL_INT_DISABLE(); hid_recv_cb.ridx = 0; hid_recv_cb.widx = 0; hid_recv_cb.cnt = 0; GLOBAL_INT_RESTORE(); } void ude_hid_setvalid(bool valid) { udh_t *udh = &udh_0; udh->valid = valid; } AT(.usbdev.com) void ude_hid_rx_process(void) { u16 size; udh_t *udh = &udh_0; if (udh->valid) { size = usb_ep_get_rx_len(udh->int_out); usb_hid_receive_callback(udh->int_out->buf, size); usb_ep_clear_rx_fifo(udh->int_out); } } AT(.usbdev.com) void ude_hid_tx_process(void) { usb_hid_send_kick(); #if TRACE_RATE_EN if (statistic_sof_count < (TRACE_RATE_PERIOD + 1)) { statistic_tx_done_count++; } #endif } AT(.com_text.isr.suspend) void usb_hid_suspend_callback(bool suspend) { if (suspend) { hid_recv_queue_clear(); usb_hid_resume_prevent_en(); } else { usb_hid_resume_prevent_dis(); } } AT(.com_text.isr.sof) void usb_sof_hid_isr(void) { #if TRACE_RATE_EN if (statistic_sof_count < (TRACE_RATE_PERIOD + 1)) { statistic_sof_count++; } #endif } ///USB hid endpoint reset void usb_int_ep_reset(void) { udh_t *udh = &udh_0; /* Any usb class can't always use ep0, so it't un-init when index is zere */ if (udh->int_in->index) { usb_ep_reset(udh->int_in); } if (udh->int_out->index) { usb_ep_reset(udh->int_out); } } ///USB HID 初始化 void udh_init(void) { epcb_t *epcb; udh_t *udh = &udh_0; memset(&udh_0, 0, sizeof(udh_0)); udh_0.int_in = &ep_hid_tx; udh_0.int_out = &ep_hid_rx; memset(udh->int_in, 0x00, sizeof(epcb_t)); memset(udh->int_out, 0x00, sizeof(epcb_t)); usb_ep_callback_register(ude_hid_tx_process, USB_HID_EP_IN_INDEX, EP_DIR_IN); usb_ep_callback_register(ude_hid_rx_process, USB_HID_EP_OUT_INDEX, EP_DIR_OUT); memset(ep_hid_buf_in, 0, sizeof(ep_hid_buf_in)); epcb = udh->int_in; epcb->dir = EP_DIR_IN; epcb->index = USB_HID_EP_IN_INDEX; epcb->type = EP_TYPE_INTR; epcb->epsize = HID_EP_IN_MAX_SIZE; epcb->buf = ep_hid_buf_in; usb_ep_init(epcb); memset(ep_hid_buf_out, 0, sizeof(ep_hid_buf_out)); epcb = udh->int_out; epcb->dir = EP_DIR_OUT; epcb->index = USB_HID_EP_OUT_INDEX; epcb->type = EP_TYPE_INTR; epcb->epsize = HID_EP_OUT_MAX_SIZE; epcb->buf = ep_hid_buf_out; usb_ep_init(epcb); ude_hid_setvalid(false); } /* START: API for upper layer */ WEAK void usb_hid_receive_callback(uint8_t *buf, uint32_t size) { hid_rep_info_t *hid_rep_info = (hid_rep_info_t *)buf; printf("hid data out[%d]:\n", size); print_r(buf, size); if (hid_rep_info->report_id == HID_REP_ID_LED) { u8 caps_lock[1] = {(hid_rep_info->pdu.led.caps_lock)? 1: 0}; wireless_send_ctrl_cmd(WIRELESS_CTRL_CMD_CODE_CAPS_LOCK_CHANGE, 1, caps_lock); } } AT(.com_text.usb.kick) void usb_hid_send_kick(void) { udh_t *udh = &udh_0; u32 size = 0; if (!udh->valid) { return; } if (usb_device_is_suspend()) { if (false == usb_hid_resume_prevent_chk()) { usb_hid_resume_prevent_dis(); usb_device_resume(); } return; } if (usb_ep_transfer(udh->int_in)) { usb_hid_send_prepare(udh->int_in->buf, &size); if (size) { usb_ep_start_transfer(udh->int_in, size); } } } AT(.com_text.usb.hid) bool usb_hid_buffer_push(uint8_t *buf, uint32_t size) { if (!usb_hid_is_valid() || usb_device_is_suspend()) { return false; } if ((size % HID_EP_IN_MAX_SIZE) != 0) { return false; } while (size && hid_recv_queue_push(buf)) { size -= HID_EP_IN_MAX_SIZE; buf += HID_EP_IN_MAX_SIZE; } // ASSERT: all data can push in queue if (size) { } return true; } AT(.com_text.usb.hid) bool usb_hid_buffer_push_check(void) { return (hid_recv_cb.cnt < HID_RECV_PKS_NUM); } AT(.com_text.usb.hid) void usb_hid_send_prepare(uint8_t *buf, uint32_t *size) { if (sys_cb.usb_is_active && hid_recv_queue_pop(buf)) { *size = HID_EP_IN_MAX_SIZE; } else { *size = 0; } } void usb_hid_init(void) { memset(&hid_recv_cb, 0x00, sizeof(hid_recv_cb)); } void usb_hid_deinit(void) { ude_hid_setvalid(false); } #if TRACE_RATE_EN AT(.com_text.test) void usb_hid_statistic_flush(void) { GLOBAL_INT_DISABLE(); statistic_sof_count = 0; statistic_tx_done_count = 0; GLOBAL_INT_RESTORE(); } #endif void usb_hid_process(void) { #if TRACE_RATE_EN if (statistic_sof_count > TRACE_RATE_PERIOD) { printf("[USB]: %din/%dsof\n", statistic_tx_done_count, TRACE_RATE_PERIOD); usb_hid_statistic_flush(); } #endif } #endif // USB_HID_EN