/** * @brief Starts a transmit transaction on an IN endpoint. * @note This function is meant to be called from ISR context outside * critical zones because there is a potentially slow operation * inside. * * @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 * * @iclass */ void usbStartTransmitI(USBDriver *usbp, usbep_t ep, const uint8_t *buf, size_t n) { USBInEndpointState *isp; osalDbgCheckClassI(); osalDbgCheck((usbp != NULL) && (ep <= (usbep_t)USB_MAX_ENDPOINTS)); osalDbgAssert(!usbGetTransmitStatusI(usbp, ep), "already transmitting"); /* Marking the endpoint as active.*/ usbp->transmitting |= (uint16_t)((unsigned)1U << (unsigned)ep); /* Setting up the transfer.*/ /*lint -save -e661 [18.1] pclint is confused by the check on ep.*/ isp = usbp->epc[ep]->in_state; /*lint -restore*/ isp->txbuf = buf; isp->txsize = n; isp->txcnt = 0; #if USB_USE_WAIT == TRUE isp->thread = NULL; #endif /* Starting transfer.*/ usb_lld_start_in(usbp, ep); }
/** * @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); } } }
/** * @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); } } }
/** * @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 Stalls an IN endpoint. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @return The operation status. * @retval false Endpoint stalled. * @retval true Endpoint busy, not stalled. * * @iclass */ bool usbStallTransmitI(USBDriver *usbp, usbep_t ep) { osalDbgCheckClassI(); osalDbgCheck(usbp != NULL); if (usbGetTransmitStatusI(usbp, ep)) return true; usb_lld_stall_in(usbp, ep); return false; }
/** * @brief Stalls an IN endpoint. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @return The operation status. * @retval FALSE Endpoint stalled. * @retval TRUE Endpoint busy, not stalled. * * @iclass */ bool_t usbStallTransmitI(USBDriver *usbp, usbep_t ep) { chDbgCheckClassI(); chDbgCheck(usbp != NULL, "usbStallTransmitI"); if (usbGetTransmitStatusI(usbp, ep)) return TRUE; usb_lld_stall_in(usbp, ep); return FALSE; }
/** * @brief Starts a transmit transaction on an IN endpoint. * @post The endpoint callback is invoked when the transfer has been * completed. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @return The operation status. * @retval FALSE Operation started successfully. * @retval TRUE Endpoint busy, operation not started. * * @iclass */ bool usbStartTransmitI(USBDriver *usbp, usbep_t ep) { osalDbgCheckClassI(); osalDbgCheck(usbp != NULL); if (usbGetTransmitStatusI(usbp, ep)) return TRUE; usbp->transmitting |= (1 << ep); usb_lld_start_in(usbp, ep); return FALSE; }
/** * @brief Starts a transmit transaction on an IN endpoint. * @post The endpoint callback is invoked when the transfer has been * completed. * * @param[in] usbp pointer to the @p USBDriver object * @param[in] ep endpoint number * * @return The operation status. * @retval false Operation started successfully. * @retval true Endpoint busy, operation not started. * * @iclass */ bool usbStartTransmitI(USBDriver *usbp, usbep_t ep) { osalDbgCheckClassI(); osalDbgCheck(usbp != NULL); if (usbGetTransmitStatusI(usbp, ep)) { return true; } usbp->transmitting |= (uint16_t)((unsigned)1U << (unsigned)ep); usb_lld_start_in(usbp, ep); return false; }
/** * @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); } } }
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(); }
/** * @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); } }
/* 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; }