/** * @brief Notification of data inserted into the output queue. * * @param[in] qp the queue pointer. */ static void onotify(io_queue_t *qp) { size_t n; SerialUSBDriver *sdup = qGetLink(qp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) || (sdup->state != SDU_READY)) { return; } /* If there is not an ongoing transaction and the output queue contains data then a new transaction is started.*/ if (!usbGetTransmitStatusI(sdup->config->usbp, sdup->config->bulk_in)) { if ((n = oqGetFullI(&sdup->oqueue)) > 0U) { osalSysUnlock(); usbPrepareQueuedTransmit(sdup->config->usbp, sdup->config->bulk_in, &sdup->oqueue, n); osalSysLock(); (void) usbStartTransmitI(sdup->config->usbp, sdup->config->bulk_in); } } }
static msg_t put(void *ip, uint8_t b) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) { return MSG_RESET; } return obqPutTimeout(&((SerialUSBDriver *)ip)->obqueue, b, TIME_INFINITE); }
/** * @brief Notification of data removed from the input queue. */ static void inotify(GenericQueue *qp) { size_t n, maxsize; SerialUSBDriver *sdup = chQGetLink(qp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if (usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) return; /* If there is in the queue enough space to hold at least one packet and a transaction is not yet started then a new transaction is started for the available space.*/ maxsize = sdup->config->usbp->epc[USB_CDC_DATA_AVAILABLE_EP]->out_maxsize; if (!usbGetReceiveStatusI(sdup->config->usbp, USB_CDC_DATA_AVAILABLE_EP) && ((n = chIQGetEmptyI(&sdup->iqueue)) >= maxsize)) { chSysUnlock(); n = (n / maxsize) * maxsize; usbPrepareQueuedReceive(sdup->config->usbp, USB_CDC_DATA_AVAILABLE_EP, &sdup->iqueue, n); chSysLock(); usbStartReceiveI(sdup->config->usbp, USB_CDC_DATA_AVAILABLE_EP); } }
/** * @brief Notification of data removed from the input queue. * * @param[in] qp the queue pointer. */ static void inotify(io_queue_t *qp) { size_t n, maxsize; SerialUSBDriver *sdup = qGetLink(qp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) || (sdup->state != SDU_READY)) { return; } /* If there is in the queue enough space to hold at least one packet and a transaction is not yet started then a new transaction is started for the available space.*/ maxsize = sdup->config->usbp->epc[sdup->config->bulk_out]->out_maxsize; if (!usbGetReceiveStatusI(sdup->config->usbp, sdup->config->bulk_out)) { if ((n = iqGetEmptyI(&sdup->iqueue)) >= maxsize) { osalSysUnlock(); n = (n / maxsize) * maxsize; usbPrepareQueuedReceive(sdup->config->usbp, sdup->config->bulk_out, &sdup->iqueue, n); osalSysLock(); (void) usbStartReceiveI(sdup->config->usbp, sdup->config->bulk_out); } } }
/** * @brief SOF handler. * @details The SOF interrupt is used for automatic flushing of incomplete * buffers pending in the output queue. * * @param[in] sdup pointer to a @p SerialUSBDriver object * * @iclass */ void sduSOFHookI(SerialUSBDriver *sdup) { /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) || (sdup->state != SDU_READY)) { return; } /* If there is already a transaction ongoing then another one cannot be started.*/ if (usbGetTransmitStatusI(sdup->config->usbp, sdup->config->bulk_in)) { return; } /* Checking if there only a buffer partially filled, if so then it is enforced in the queue and transmitted.*/ if (obqTryFlushI(&sdup->obqueue)) { size_t n; uint8_t *buf = obqGetFullBufferI(&sdup->obqueue, &n); osalDbgAssert(buf != NULL, "queue is empty"); usbStartTransmitI(sdup->config->usbp, sdup->config->bulk_in, buf, n); } }
/** * @brief Notification of data removed from the input queue. */ static void inotify(GenericQueue *qp) { size_t n, maxsize; BulkUSBDriver *bdup = chQGetLink(qp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if ((usbGetDriverStateI(bdup->config->usbp) != USB_ACTIVE) || (bdup->state != BDU_READY)) return; /* If there is in the queue enough space to hold at least one packet and a transaction is not yet started then a new transaction is started for the available space.*/ maxsize = bdup->config->usbp->epc[bdup->config->bulk_out]->out_maxsize; if (!usbGetReceiveStatusI(bdup->config->usbp, bdup->config->bulk_out) && ((n = chIQGetEmptyI(&bdup->iqueue)) >= maxsize)) { chSysUnlock(); n = (n / maxsize) * maxsize; usbPrepareQueuedReceive(bdup->config->usbp, bdup->config->bulk_out, &bdup->iqueue, n); chSysLock(); usbStartReceiveI(bdup->config->usbp, bdup->config->bulk_out); } }
/** * @brief Notification of data inserted into the output queue. * * @param[in] qp the queue pointer. */ static void onotify(io_queue_t *qp) { size_t n; HIDDebugDriver *hiddp = qGetLink(qp); /* If the USB driver is not in the appropriate state then transactions * must not be started.*/ if((usbGetDriverStateI(hiddp->config->usbp) != USB_ACTIVE) || (hiddp->state != HIDDEBUG_READY)) { return; } /* If there is not an ongoing transaction and the output queue contains * enough data then a new transaction is started.*/ if(!usbGetTransmitStatusI(hiddp->config->usbp, hiddp->config->ep_in)) { if((n = oqGetFullI(&hiddp->oqueue)) >= DEBUG_TX_SIZE) { osalSysUnlock(); usbPrepareQueuedTransmit(hiddp->config->usbp, hiddp->config->ep_in, &hiddp->oqueue, DEBUG_TX_SIZE); osalSysLock(); (void)usbStartTransmitI(hiddp->config->usbp, hiddp->config->ep_in); } } }
void usb_debug_flush_output(HIDDebugDriver *hiddp) { size_t n; /* we'll sleep for a moment to finish any transfers that may be pending already */ /* there's a race condition somewhere, maybe because we have 2x buffer */ chThdSleepMilliseconds(2); osalSysLock(); /* check that the states of things are as they're supposed to */ if((usbGetDriverStateI(hiddp->config->usbp) != USB_ACTIVE) || (hiddp->state != HIDDEBUG_READY)) { osalSysUnlock(); return; } /* rearm the timer */ chVTSetI(&hid_debug_flush_timer, MS2ST(DEBUG_TX_FLUSH_MS), hid_debug_flush_cb, hiddp); /* don't do anything if the queue is empty */ if((n = oqGetFullI(&hiddp->oqueue)) == 0) { osalSysUnlock(); return; } osalSysUnlock(); /* if we don't have enough bytes in the queue, fill with zeroes */ while(n++ < DEBUG_TX_SIZE) { oqPut(&hiddp->oqueue, 0); } /* will transmit automatically because of the onotify callback */ /* which transmits as soon as the queue has enough */ }
static msg_t get(void *ip) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) { return MSG_RESET; } return ibqGetTimeout(&((SerialUSBDriver *)ip)->ibqueue, TIME_INFINITE); }
static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t timeout) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) { return 0; } return ibqReadTimeout(&((SerialUSBDriver *)ip)->ibqueue, bp, n, timeout); }
static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t timeout) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) { return 0; } return obqWriteTimeout(&((SerialUSBDriver *)ip)->obqueue, bp, n, timeout); }
static msg_t gett(void *ip, systime_t timeout) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) { return MSG_RESET; } return ibqGetTimeout(&((SerialUSBDriver *)ip)->ibqueue, timeout); }
static msg_t putt(void *ip, uint8_t b, systime_t timeout) { if (usbGetDriverStateI(((USBHIDDriver *)ip)->config->usbp) != USB_ACTIVE) { return MSG_RESET; } return obqPutTimeout(&((USBHIDDriver *)ip)->obqueue, b, timeout); }
static size_t read(void *ip, uint8_t *bp, size_t n) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) { return 0; } return ibqReadTimeout(&((SerialUSBDriver *)ip)->ibqueue, bp, n, TIME_INFINITE); }
static size_t write(void *ip, const uint8_t *bp, size_t n) { if (usbGetDriverStateI(((SerialUSBDriver *)ip)->config->usbp) != USB_ACTIVE) { return 0; } return obqWriteTimeout(&((SerialUSBDriver *)ip)->obqueue, bp, n, TIME_INFINITE); }
void LogTextMessage(const char* format, ...) { if ((usbGetDriverStateI(BDU1.config->usbp) == USB_ACTIVE) && (connected)) { int h = 0x546F7841; // "AxoT" chSequentialStreamWrite((BaseSequentialStream * )&BDU1, (const unsigned char* )&h, 4); va_list ap; va_start(ap, format); chvprintf((BaseSequentialStream *)&BDU1, format, ap); va_end(ap); chSequentialStreamPut((BaseSequentialStream * )&BDU1, 0); } }
static void send_extra_report(uint8_t report_id, uint16_t data) { osalSysLock(); if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { osalSysUnlock(); return; } report_extra_t report = { .report_id = report_id, .usage = data }; usbStartTransmitI(&USB_DRIVER, EXTRAKEY_IN_EPNUM, (uint8_t *)&report, sizeof(report_extra_t)); osalSysUnlock(); }
/** * @brief Performs a transmit transaction on an IN endpoint. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * @param[in] buf buffer where to fetch the data to be transmitted * @param[in] n transaction size * * @return The operation status. * @retval MSG_OK operation performed successfully. * @retval MSG_RESET driver not in @p USB_ACTIVE state or the operation * has been aborted by an USB reset or a transition to * the @p USB_SUSPENDED state. * * @api */ msg_t usbTransmit(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n) { msg_t msg; osalSysLock(); if (usbGetDriverStateI(usbp) != USB_ACTIVE) { osalSysUnlock(); return MSG_RESET; } usbStartTransmitI(usbp, ep, buf, n); msg = osalThreadSuspendS(&usbp->epc[ep]->in_state->thread); osalSysUnlock(); return msg; }
/** * @brief Notification of filled buffer inserted into the output buffers queue. * * @param[in] bqp the buffers queue pointer. */ static void obnotify(io_buffers_queue_t *bqp) { size_t n; SerialUSBDriver *sdup = bqGetLinkX(bqp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) || (sdup->state != SDU_READY)) { return; } /* Checking if there is already a transaction ongoing on the endpoint.*/ if (!usbGetTransmitStatusI(sdup->config->usbp, sdup->config->bulk_in)) { /* Trying to get a full buffer.*/ uint8_t *buf = obqGetFullBufferI(&sdup->obqueue, &n); if (buf != NULL) { /* Buffer found, starting a new transaction.*/ usbStartTransmitI(sdup->config->usbp, sdup->config->bulk_in, buf, n); } } }
static THD_FUNCTION(serialThread, arg) { (void)arg; event_listener_t new_data_listener; event_listener_t sd1_listener; event_listener_t sd2_listener; chEvtRegister(&new_data_event, &new_data_listener, 0); eventflags_t events = CHN_INPUT_AVAILABLE | SD_PARITY_ERROR | SD_FRAMING_ERROR | SD_OVERRUN_ERROR | SD_NOISE_ERROR | SD_BREAK_DETECTED; chEvtRegisterMaskWithFlags(chnGetEventSource(&SD1), &sd1_listener, EVENT_MASK(1), events); chEvtRegisterMaskWithFlags(chnGetEventSource(&SD2), &sd2_listener, EVENT_MASK(2), events); bool need_wait = false; while(true) { eventflags_t flags1 = 0; eventflags_t flags2 = 0; if (need_wait) { eventmask_t mask = chEvtWaitAnyTimeout(ALL_EVENTS, MS2ST(1000)); if (mask & EVENT_MASK(1)) { flags1 = chEvtGetAndClearFlags(&sd1_listener); print_error("DOWNLINK", flags1, &SD1); } if (mask & EVENT_MASK(2)) { flags2 = chEvtGetAndClearFlags(&sd2_listener); print_error("UPLINK", flags2, &SD2); } } // Always stay as master, even if the USB goes into sleep mode is_master |= usbGetDriverStateI(&USBD1) == USB_ACTIVE; router_set_master(is_master); need_wait = true; need_wait &= read_from_serial(&SD2, UP_LINK) == 0; need_wait &= read_from_serial(&SD1, DOWN_LINK) == 0; update_transport(); } }
void send_mouse(report_mouse_t *report) { osalSysLock(); if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { osalSysUnlock(); return; } if(usbGetTransmitStatusI(&USB_DRIVER, MOUSE_IN_EPNUM)) { /* Need to either suspend, or loop and call unlock/lock during * every iteration - otherwise the system will remain locked, * no interrupts served, so USB not going through as well. * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[MOUSE_IN_EPNUM]->in_state->thread, MS2ST(10)==MSG_TIMEOUT)) { osalSysUnlock(); return; } } usbStartTransmitI(&USB_DRIVER, MOUSE_IN_EPNUM, (uint8_t *)report, sizeof(report_mouse_t)); osalSysUnlock(); }
static THD_FUNCTION(buttonThread, arg) { (void)arg; wkup_old_state = 0; while(1) { wkup_cur_state = palReadPad(GPIOA, GPIOA_BUTTON); if(wkup_cur_state != wkup_old_state) { chSysLock(); if(usbGetDriverStateI(&USBD1) == USB_ACTIVE) { chSysUnlock(); chnWrite((BaseChannel *)&HIDD, (uint8_t *)"Hello, world!\n", 14); } else chSysUnlock(); wkup_old_state = wkup_cur_state; } chThdSleepMilliseconds(50); } }
/** * @brief Notification of empty buffer released into the input buffers queue. * * @param[in] bqp the buffers queue pointer. */ static void ibnotify(io_buffers_queue_t *bqp) { SerialUSBDriver *sdup = bqGetLinkX(bqp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) || (sdup->state != SDU_READY)) { return; } /* Checking if there is already a transaction ongoing on the endpoint.*/ if (!usbGetReceiveStatusI(sdup->config->usbp, sdup->config->bulk_out)) { /* Trying to get a free buffer.*/ uint8_t *buf = ibqGetEmptyBufferI(&sdup->ibqueue); if (buf != NULL) { /* Buffer found, starting a new transaction.*/ usbStartReceiveI(sdup->config->usbp, sdup->config->bulk_out, buf, SERIAL_USB_BUFFERS_SIZE); } } }
/* callback */ static void hid_debug_flush_cb(void *arg) { HIDDebugDriver *hiddp = (HIDDebugDriver *)arg; size_t i, n; uint8_t buf[DEBUG_TX_SIZE]; osalSysLockFromISR(); /* check that the states of things are as they're supposed to */ if((usbGetDriverStateI(hiddp->config->usbp) != USB_ACTIVE) || (hiddp->state != HIDDEBUG_READY)) { /* rearm the timer */ chVTSetI(&hid_debug_flush_timer, MS2ST(DEBUG_TX_FLUSH_MS), hid_debug_flush_cb, hiddp); osalSysUnlockFromISR(); return; } /* don't do anything if the queue or has enough stuff in it */ if(((n = oqGetFullI(&hiddp->oqueue)) == 0) || (n >= DEBUG_TX_SIZE)) { /* rearm the timer */ chVTSetI(&hid_debug_flush_timer, MS2ST(DEBUG_TX_FLUSH_MS), hid_debug_flush_cb, hiddp); osalSysUnlockFromISR(); return; } /* there's stuff hanging in the queue - so dequeue and send */ for(i = 0; i < n; i++) buf[i] = (uint8_t)oqGetI(&hiddp->oqueue); for(i = n; i < DEBUG_TX_SIZE; i++) buf[i] = 0; osalSysUnlockFromISR(); usbPrepareTransmit(hiddp->config->usbp, hiddp->config->ep_in, buf, DEBUG_TX_SIZE); osalSysLockFromISR(); (void)usbStartTransmitI(hiddp->config->usbp, hiddp->config->ep_in); /* rearm the timer */ chVTSetI(&hid_debug_flush_timer, MS2ST(DEBUG_TX_FLUSH_MS), hid_debug_flush_cb, hiddp); osalSysUnlockFromISR(); }
/** * @brief Notification of data inserted into the output queue. */ static void onotify(GenericQueue *qp) { size_t n; SerialUSBDriver *sdup = chQGetLink(qp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if (usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) return; /* If there is not an ongoing transaction and the output queue contains data then a new transaction is started.*/ if (!usbGetTransmitStatusI(sdup->config->usbp, USB_CDC_DATA_REQUEST_EP) && ((n = chOQGetFullI(&sdup->oqueue)) > 0)) { chSysUnlock(); usbPrepareQueuedTransmit(sdup->config->usbp, USB_CDC_DATA_REQUEST_EP, &sdup->oqueue, n); chSysLock(); usbStartTransmitI(sdup->config->usbp, USB_CDC_DATA_REQUEST_EP); } }
/** * @brief Notification of data inserted into the output queue. */ static void onotify(GenericQueue *qp) { size_t n; BulkUSBDriver *bdup = chQGetLink(qp); /* If the USB driver is not in the appropriate state then transactions must not be started.*/ if ((usbGetDriverStateI(bdup->config->usbp) != USB_ACTIVE) || (bdup->state != BDU_READY)) return; /* If there is not an ongoing transaction and the output queue contains data then a new transaction is started.*/ if (!usbGetTransmitStatusI(bdup->config->usbp, bdup->config->bulk_in) && ((n = chOQGetFullI(&bdup->oqueue)) > 0)) { chSysUnlock(); usbPrepareQueuedTransmit(bdup->config->usbp, bdup->config->bulk_in, &bdup->oqueue, n); chSysLock(); usbStartTransmitI(bdup->config->usbp, bdup->config->bulk_in); } }
static THD_FUNCTION(buttonThread, arg) { (void)arg; wkup_old_state = 0; while(1) { wkup_cur_state = palReadPad(BUTTON_GPIO, BUTTON_PIN); if(wkup_cur_state != wkup_old_state) { chSysLock(); if(usbGetDriverStateI(&USB_DRIVER) == USB_ACTIVE) { chSysUnlock(); /* just some test code for various reports * choose one and comment the others */ /* keyboard test, sends 'n' in nkro mode, 'm' in normal mode */ #ifdef NKRO_ENABLE if(wkup_cur_state == BUTTON_ACTIVE) { report.nkro.bits[2] |= 2; // 'n' } else { report.nkro.bits[2] &= 0b11111101; } #else report.keys[0] = ((wkup_cur_state == BUTTON_ACTIVE) ? 0x10 : 0); // 'm' #endif send_keyboard(&report); /* mouse test, moves the mouse pointer diagonally right and down */ // send_mouse(&mouse_report); /* consumer keys test, sends 'mute audio' */ // if(wkup_cur_state == BUTTON_ACTIVE) { // send_consumer(AUDIO_MUTE); // } else { // send_consumer(0); // } /* system keys test, sends 'sleep key' * on macs it takes a second or two for the system to react * I suppose it's to prevent from accidental hits of the sleep key */ // if(wkup_cur_state == BUTTON_ACTIVE) { // send_system(SYSTEM_SLEEP); // } else { // send_system(0); // } /* debug console test, sends the button state and the alphabet * - also blink, to see that the code above is not blocking */ // sendchar(((wkup_cur_state == BUTTON_ACTIVE) ? '1' : '0')); // uint8_t n; // for(n = 0; n < 26; n++) { // sendchar('A' + n); // sendchar('a' + n); // } // sendchar('\n'); // palSetPad(LED2_GPIO, LED2_PIN); // chThdSleepMilliseconds(50); // palClearPad(LED2_GPIO, LED2_PIN); } else chSysUnlock(); wkup_old_state = wkup_cur_state; } chThdSleepMilliseconds(50); } }
/* Idle requests timer code * callback (called from ISR, unlocked state) */ static void keyboard_idle_timer_cb(void *arg) { USBDriver *usbp = (USBDriver *)arg; osalSysLockFromISR(); /* check that the states of things are as they're supposed to */ if(usbGetDriverStateI(usbp) != USB_ACTIVE) { /* do not rearm the timer, should be enabled on IDLE request */ osalSysUnlockFromISR(); return; } #ifdef NKRO_ENABLE if(!keymap_config.nkro && keyboard_idle) { #else /* NKRO_ENABLE */ if(keyboard_idle) { #endif /* NKRO_ENABLE */ /* TODO: are we sure we want the KBD_ENDPOINT? */ if(!usbGetTransmitStatusI(usbp, KEYBOARD_IN_EPNUM)) { usbStartTransmitI(usbp, KEYBOARD_IN_EPNUM, (uint8_t *)&keyboard_report_sent, KEYBOARD_EPSIZE); } /* rearm the timer */ chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); } /* do not rearm the timer if the condition above fails * it should be enabled again on either IDLE or SET_PROTOCOL requests */ osalSysUnlockFromISR(); } /* LED status */ uint8_t keyboard_leds(void) { return (uint8_t)(keyboard_led_stats & 0xFF); } /* prepare and start sending a report IN * not callable from ISR or locked state */ void send_keyboard(report_keyboard_t *report) { osalSysLock(); if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { osalSysUnlock(); return; } osalSysUnlock(); #ifdef NKRO_ENABLE if(keymap_config.nkro) { /* NKRO protocol */ /* need to wait until the previous packet has made it through */ /* can rewrite this using the synchronous API, then would wait * until *after* the packet has been transmitted. I think * this is more efficient */ /* busy wait, should be short and not very common */ osalSysLock(); if(usbGetTransmitStatusI(&USB_DRIVER, NKRO_IN_EPNUM)) { /* Need to either suspend, or loop and call unlock/lock during * every iteration - otherwise the system will remain locked, * no interrupts served, so USB not going through as well. * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ osalThreadSuspendS(&(&USB_DRIVER)->epc[NKRO_IN_EPNUM]->in_state->thread); } usbStartTransmitI(&USB_DRIVER, NKRO_IN_EPNUM, (uint8_t *)report, sizeof(report_keyboard_t)); osalSysUnlock(); } else #endif /* NKRO_ENABLE */ { /* boot protocol */ /* need to wait until the previous packet has made it through */ /* busy wait, should be short and not very common */ osalSysLock(); if(usbGetTransmitStatusI(&USB_DRIVER, KEYBOARD_IN_EPNUM)) { /* Need to either suspend, or loop and call unlock/lock during * every iteration - otherwise the system will remain locked, * no interrupts served, so USB not going through as well. * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ osalThreadSuspendS(&(&USB_DRIVER)->epc[KEYBOARD_IN_EPNUM]->in_state->thread); } usbStartTransmitI(&USB_DRIVER, KEYBOARD_IN_EPNUM, (uint8_t *)report, KEYBOARD_EPSIZE); osalSysUnlock(); } keyboard_report_sent = *report; }