/**
 * @brief   Default requests hook.
 * @details Applications wanting to use the teensy HID debug driver can use
 *          this function as requests hook in the USB configuration.
 *
 * @param[in] usbp      pointer to the @p USBDriver object
 * @return              The hook status.
 * @retval true         Message handled internally.
 * @retval false        Message not handled.
 */
bool hidDebugRequestsHook(USBDriver *usbp) {
  const USBDescriptor *dp;

  /* Handle HID class specific requests */
  /* Only GetReport is mandatory for HID devices */
  if((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) {
    if(usbp->setup[1] == HID_GET_REPORT) {
      /* setup[3] (MSB of wValue) = Report ID (must be 0 as we
       * have declared only one IN report)
       * setup[2] (LSB of wValue) = Report Type (1 = Input, 3 = Feature)
       */
      if((usbp->setup[3] == 0) && (usbp->setup[2] == 1)) {
        /* When do we get requests like this anyway?
         * (Doing it over ENDPOINT0)
         * just send some empty packet
         */
        usbSetupTransfer(usbp, NULL, 0, NULL);
      }
    }
    if(usbp->setup[1] == HID_SET_REPORT) {
      /* Not implemented (yet) */
    }
  }

  /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */
  if((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) {
    dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4]));
    if(dp == NULL)
      return false;
    usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
    return true;
  }

  return false;
}
Esempio n. 2
0
/**
 * @brief   Default EP0 IN callback.
 * @details This function is used by the low level driver as default handler
 *          for EP0 IN events.
 *
 * @param[in] usbp      pointer to the @p USBDriver object
 * @param[in] ep        endpoint number, always zero
 *
 * @notapi
 */
void _usb_ep0in(USBDriver *usbp, usbep_t ep) {
  size_t max;

  (void)ep;
  switch (usbp->ep0state) {
  case USB_EP0_TX:
    max = (size_t)get_hword(&usbp->setup[6]);
    /* If the transmitted size is less than the requested size and it is a
       multiple of the maximum packet size then a zero size packet must be
       transmitted.*/
    if ((usbp->ep0n < max) &&
        ((usbp->ep0n % usbp->epc[0]->in_maxsize) == 0U)) {
      usbPrepareTransmit(usbp, 0, NULL, 0);
      osalSysLockFromISR();
      (void) usbStartTransmitI(usbp, 0);
      osalSysUnlockFromISR();
      usbp->ep0state = USB_EP0_WAITING_TX0;
      return;
    }
    /* Falls into, it is intentional.*/
  case USB_EP0_WAITING_TX0:
    /* Transmit phase over, receiving the zero sized status packet.*/
    usbp->ep0state = USB_EP0_WAITING_STS;
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
    usbPrepareReceive(usbp, 0, NULL, 0);
    osalSysLockFromISR();
    (void) usbStartReceiveI(usbp, 0);
    osalSysUnlockFromISR();
#else
    usb_lld_end_setup(usbp, ep);
#endif
    return;
  case USB_EP0_SENDING_STS:
    /* Status packet sent, invoking the callback if defined.*/
    if (usbp->ep0endcb != NULL) {
      usbp->ep0endcb(usbp);
    }
    usbp->ep0state = USB_EP0_WAITING_SETUP;
    return;
  case USB_EP0_WAITING_SETUP:
  case USB_EP0_WAITING_STS:
  case USB_EP0_RX:
    /* All the above are invalid states in the IN phase.*/
    osalDbgAssert(false, "EP0 state machine error");
    /* Falling through is intentional.*/
  case USB_EP0_ERROR:
    /* Error response, the state machine goes into an error state, the low
       level layer will have to reset it to USB_EP0_WAITING_SETUP after
       receiving a SETUP packet.*/
    usb_lld_stall_in(usbp, 0);
    usb_lld_stall_out(usbp, 0);
    _usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
    usbp->ep0state = USB_EP0_ERROR;
    return;
  default:
    osalDbgAssert(false, "EP0 state machine invalid state");
  }
}
Esempio n. 3
0
/**
 * @brief   Default requests hook.
 * @details Applications wanting to use the USB HID driver can use
 *          this function at the end of the application specific
 *          requests hook. The HID_* requests handled here do not
 *          transfer any data to the application.
 *          The following requests are handled:
 *          - HID_GET_IDLE.
 *          - HID_GET_PROTOCOL.
 *          - HID_SET_REPORT.
 *          - HID_SET_IDLE.
 *          - HID_SET_PROTOCOL.
 *          - USB_REQ_GET_DESCRIPTOR.
 *          .
 *
 * @param[in] usbp      pointer to the @p USBDriver object
 * @return              The hook status.
 * @retval true         Message handled internally.
 * @retval false        Message not handled.
 */
bool hidRequestsHook(USBDriver *usbp) {
  const USBDescriptor *dp;

  if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) {
    switch (usbp->setup[1]) {
    case HID_GET_IDLE:
      usbSetupTransfer(usbp, NULL, 0, NULL);
      return true;
    case HID_GET_PROTOCOL:
      return true;
    case HID_SET_REPORT:
      usbSetupTransfer(usbp, NULL, 0, NULL);
      return true;
    case HID_SET_IDLE:
      usbSetupTransfer(usbp, NULL, 0, NULL);
      return true;
    case HID_SET_PROTOCOL:
      return true;
    default:
      return false;
    }
  }

  /* GET_DESCRIPTOR from interface not handled by default so handle it here */
  if (((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_DEV2HOST) &&
      ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) {
    switch (usbp->setup[1]) {
    case USB_REQ_GET_DESCRIPTOR:
      dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2],
                                           get_hword(&usbp->setup[4]));
      if (dp == NULL)
        return false;

      usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
      return true;
    default:
      return false;
    }
  }
  return false;
}
Esempio n. 4
0
/**
 * @brief   Standard requests handler.
 * @details This is the standard requests default handler, most standard
 *          requests are handled here, the user can override the standard
 *          handling using the @p requests_hook_cb hook in the
 *          @p USBConfig structure.
 *
 * @param[in] usbp      pointer to the @p USBDriver object
 * @return              The request handling exit code.
 * @retval false        Request not recognized by the handler or error.
 * @retval true         Request handled.
 */
static bool default_handler(USBDriver *usbp) {
  const USBDescriptor *dp;

  /* Decoding the request.*/
  switch ((((uint32_t)usbp->setup[0] & (USB_RTYPE_RECIPIENT_MASK |
                                        USB_RTYPE_TYPE_MASK)) |
           ((uint32_t)usbp->setup[1] << 8U))) {
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_GET_STATUS << 8):
    /* Just returns the current status word.*/
    usbSetupTransfer(usbp, (uint8_t *)&usbp->status, 2, NULL);
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_CLEAR_FEATURE << 8):
    /* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
       number is handled as an error.*/
    if (usbp->setup[2] == USB_FEATURE_DEVICE_REMOTE_WAKEUP) {
      usbp->status &= ~2U;
      usbSetupTransfer(usbp, NULL, 0, NULL);
      return true;
    }
    return false;
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_FEATURE << 8):
    /* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
       number is handled as an error.*/
    if (usbp->setup[2] == USB_FEATURE_DEVICE_REMOTE_WAKEUP) {
      usbp->status |= 2U;
      usbSetupTransfer(usbp, NULL, 0, NULL);
      return true;
    }
    return false;
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_ADDRESS << 8):
    /* The SET_ADDRESS handling can be performed here or postponed after
       the status packed depending on the USB_SET_ADDRESS_MODE low
       driver setting.*/
#if USB_SET_ADDRESS_MODE == USB_EARLY_SET_ADDRESS
    if ((usbp->setup[0] == USB_RTYPE_RECIPIENT_DEVICE) &&
        (usbp->setup[1] == USB_REQ_SET_ADDRESS)) {
      set_address(usbp);
    }
    usbSetupTransfer(usbp, NULL, 0, NULL);
#else
    usbSetupTransfer(usbp, NULL, 0, set_address);
#endif
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_GET_DESCRIPTOR << 8):
    /* Handling descriptor requests from the host.*/
    dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3],
                                         usbp->setup[2],
                                         get_hword(&usbp->setup[4]));
    if (dp == NULL) {
      return false;
    }
    /*lint -save -e9005 [11.8] Removing const is fine.*/
    usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
    /*lint -restore*/
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_GET_CONFIGURATION << 8):
    /* Returning the last selected configuration.*/
    usbSetupTransfer(usbp, &usbp->configuration, 1, NULL);
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_CONFIGURATION << 8):
    /* Handling configuration selection from the host only if it is different
       from the current configuration.*/
    if (usbp->configuration != usbp->setup[2]) {
      /* If the USB device is already active then we have to perform the clear
         procedure on the current configuration.*/
      if (usbp->state == USB_ACTIVE) {
        /* Current configuration cleared.*/
        osalSysLockFromISR ();
        usbDisableEndpointsI(usbp);
        osalSysUnlockFromISR ();
        usbp->configuration = 0U;
        usbp->state = USB_SELECTED;
        _usb_isr_invoke_event_cb(usbp, USB_EVENT_UNCONFIGURED);
      }
      if (usbp->setup[2] != 0U) {
        /* New configuration.*/
        usbp->configuration = usbp->setup[2];
        usbp->state = USB_ACTIVE;
        _usb_isr_invoke_event_cb(usbp, USB_EVENT_CONFIGURED);
      }
    }
    usbSetupTransfer(usbp, NULL, 0, NULL);
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_GET_STATUS << 8):
  case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_SYNCH_FRAME << 8):
    /* Just sending two zero bytes, the application can change the behavior
       using a hook..*/
    /*lint -save -e9005 [11.8] Removing const is fine.*/
    usbSetupTransfer(usbp, (uint8_t *)zero_status, 2, NULL);
    /*lint -restore*/
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_GET_STATUS << 8):
    /* Sending the EP status.*/
    if ((usbp->setup[4] & 0x80U) != 0U) {
      switch (usb_lld_get_status_in(usbp, usbp->setup[4] & 0x0FU)) {
      case EP_STATUS_STALLED:
        /*lint -save -e9005 [11.8] Removing const is fine.*/
        usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
        /*lint -restore*/
        return true;
      case EP_STATUS_ACTIVE:
        /*lint -save -e9005 [11.8] Removing const is fine.*/
        usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
        /*lint -restore*/
        return true;
      case EP_STATUS_DISABLED:
      default:
        return false;
      }
    }
    else {
      switch (usb_lld_get_status_out(usbp, usbp->setup[4] & 0x0FU)) {
      case EP_STATUS_STALLED:
        /*lint -save -e9005 [11.8] Removing const is fine.*/
        usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
        /*lint -restore*/
        return true;
      case EP_STATUS_ACTIVE:
        /*lint -save -e9005 [11.8] Removing const is fine.*/
        usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
        /*lint -restore*/
        return true;
      case EP_STATUS_DISABLED:
      default:
        return false;
      }
    }
  case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_CLEAR_FEATURE << 8):
    /* Only ENDPOINT_HALT is handled as feature.*/
    if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT) {
      return false;
    }
    /* Clearing the EP status, not valid for EP0, it is ignored in that case.*/
    if ((usbp->setup[4] & 0x0FU) != 0U) {
      if ((usbp->setup[4] & 0x80U) != 0U) {
        usb_lld_clear_in(usbp, usbp->setup[4] & 0x0FU);
      }
      else {
        usb_lld_clear_out(usbp, usbp->setup[4] & 0x0FU);
      }
    }
    usbSetupTransfer(usbp, NULL, 0, NULL);
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_ENDPOINT | ((uint32_t)USB_REQ_SET_FEATURE << 8):
    /* Only ENDPOINT_HALT is handled as feature.*/
    if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT) {
      return false;
    }
    /* Stalling the EP, not valid for EP0, it is ignored in that case.*/
    if ((usbp->setup[4] & 0x0FU) != 0U) {
      if ((usbp->setup[4] & 0x80U) != 0U) {
        usb_lld_stall_in(usbp, usbp->setup[4] & 0x0FU);
      }
      else {
        usb_lld_stall_out(usbp, usbp->setup[4] & 0x0FU);
      }
    }
    usbSetupTransfer(usbp, NULL, 0, NULL);
    return true;
  case (uint32_t)USB_RTYPE_RECIPIENT_DEVICE | ((uint32_t)USB_REQ_SET_DESCRIPTOR << 8):
  case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_CLEAR_FEATURE << 8):
  case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_SET_FEATURE << 8):
  case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_GET_INTERFACE << 8):
  case (uint32_t)USB_RTYPE_RECIPIENT_INTERFACE | ((uint32_t)USB_REQ_SET_INTERFACE << 8):
    /* All the above requests are not handled here, if you need them then
       use the hook mechanism and provide handling.*/
  default:
    return false;
  }
}
Esempio n. 5
0
/**
 * @brief   Default EP0 SETUP callback.
 * @details This function is used by the low level driver as default handler
 *          for EP0 SETUP events.
 *
 * @param[in] usbp      pointer to the @p USBDriver object
 * @param[in] ep        endpoint number, always zero
 *
 * @notapi
 */
void _usb_ep0setup(USBDriver *usbp, usbep_t ep) {
  size_t max;

  usbp->ep0state = USB_EP0_WAITING_SETUP;
  usbReadSetup(usbp, ep, usbp->setup);

  /* First verify if the application has an handler installed for this
     request.*/
  /*lint -save -e9007 [13.5] No side effects, it is intentional.*/
  if ((usbp->config->requests_hook_cb == NULL) ||
      !(usbp->config->requests_hook_cb(usbp))) {
  /*lint -restore*/
    /* Invoking the default handler, if this fails then stalls the
       endpoint zero as error.*/
    /*lint -save -e9007 [13.5] No side effects, it is intentional.*/
    if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) != USB_RTYPE_TYPE_STD) ||
        !default_handler(usbp)) {
    /*lint -restore*/
      /* Error response, the state machine goes into an error state, the low
         level layer will have to reset it to USB_EP0_WAITING_SETUP after
         receiving a SETUP packet.*/
      usb_lld_stall_in(usbp, 0);
      usb_lld_stall_out(usbp, 0);
      _usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
      usbp->ep0state = USB_EP0_ERROR;
      return;
    }
  }
#if (USB_SET_ADDRESS_ACK_HANDLING == USB_SET_ADDRESS_ACK_HW)
  if (usbp->setup[1] == USB_REQ_SET_ADDRESS) {
    /* Zero-length packet sent by hardware */
    return;
  }
#endif
  /* Transfer preparation. The request handler must have populated
     correctly the fields ep0next, ep0n and ep0endcb using the macro
     usbSetupTransfer().*/
  max = (size_t)get_hword(&usbp->setup[6]);
  /* The transfer size cannot exceed the specified amount.*/
  if (usbp->ep0n > max) {
    usbp->ep0n = max;
  }
  if ((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_DEV2HOST) {
    /* IN phase.*/
    if (usbp->ep0n != 0U) {
      /* Starts the transmit phase.*/
      usbp->ep0state = USB_EP0_TX;
      osalSysLockFromISR();
      usbStartTransmitI(usbp, 0, usbp->ep0next, usbp->ep0n);
      osalSysUnlockFromISR();
    }
    else {
      /* No transmission phase, directly receiving the zero sized status
         packet.*/
      usbp->ep0state = USB_EP0_WAITING_STS;
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
      osalSysLockFromISR();
      usbStartReceiveI(usbp, 0, NULL, 0);
      osalSysUnlockFromISR();
#else
      usb_lld_end_setup(usbp, ep);
#endif
    }
  }
  else {
    /* OUT phase.*/
    if (usbp->ep0n != 0U) {
      /* Starts the receive phase.*/
      usbp->ep0state = USB_EP0_RX;
      osalSysLockFromISR();
      usbStartReceiveI(usbp, 0, usbp->ep0next, usbp->ep0n);
      osalSysUnlockFromISR();
    }
    else {
      /* No receive phase, directly sending the zero sized status
         packet.*/
      usbp->ep0state = USB_EP0_SENDING_STS;
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
      osalSysLockFromISR();
      usbStartTransmitI(usbp, 0, NULL, 0);
      osalSysUnlockFromISR();
#else
      usb_lld_end_setup(usbp, ep);
#endif
    }
  }
}
Esempio n. 6
0
/* Callback for SETUP request on the endpoint 0 (control) */
static bool usb_request_hook_cb(USBDriver *usbp) {
  const USBDescriptor *dp;

  /* usbp->setup fields:
   *  0:   bmRequestType (bitmask)
   *  1:   bRequest
   *  2,3: (LSB,MSB) wValue
   *  4,5: (LSB,MSB) wIndex
   *  6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */

  /* Handle HID class specific requests */
  if(((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) &&
     ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) {
    switch(usbp->setup[0] & USB_RTYPE_DIR_MASK) {
    case USB_RTYPE_DIR_DEV2HOST:
      switch(usbp->setup[1]) {   /* bRequest */
      case HID_GET_REPORT:
        switch(usbp->setup[4]) {     /* LSB(wIndex) (check MSB==0?) */
        case KEYBOARD_INTERFACE:
#ifdef NKRO_ENABLE
        case NKRO_INTERFACE:
#endif /* NKRO_ENABLE */
          usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL);
          return TRUE;
          break;

#ifdef MOUSE_ENABLE
        case MOUSE_INTERFACE:
          usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL);
          return TRUE;
          break;
#endif /* MOUSE_ENABLE */

#ifdef EXTRAKEY_ENABLE
        case EXTRAKEY_INTERFACE:
          if(usbp->setup[3] == 1) { /* MSB(wValue) [Report Type] == 1 [Input Report] */
            switch(usbp->setup[2]) { /* LSB(wValue) [Report ID] */
              case REPORT_ID_SYSTEM:
                extra_report_blank[0] = REPORT_ID_SYSTEM;
                usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL);
                return TRUE;
                break;
              case REPORT_ID_CONSUMER:
                extra_report_blank[0] = REPORT_ID_CONSUMER;
                usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL);
                return TRUE;
                break;
              default:
                return FALSE;
            }
          } else {
            return FALSE;
          }
          break;
#endif /* EXTRAKEY_ENABLE */

        default:
          usbSetupTransfer(usbp, NULL, 0, NULL);
          return TRUE;
          break;
        }
        break;

      case HID_GET_PROTOCOL:
        if((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) {   /* wIndex */
          usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL);
          return TRUE;
        }
        break;

      case HID_GET_IDLE:
        usbSetupTransfer(usbp, &keyboard_idle, 1, NULL);
        return TRUE;
        break;
      }
      break;

    case USB_RTYPE_DIR_HOST2DEV:
      switch(usbp->setup[1]) {   /* bRequest */
      case HID_SET_REPORT:
        switch(usbp->setup[4]) {       /* LSB(wIndex) (check MSB==0 and wLength==1?) */
        case KEYBOARD_INTERFACE:
#ifdef NKRO_ENABLE
        case NKRO_INTERFACE:
#endif  /* NKRO_ENABLE */
        /* keyboard_led_stats = <read byte from next OUT report>
         * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */
          usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL);
          return TRUE;
          break;
        }
        break;

      case HID_SET_PROTOCOL:
        if((usbp->setup[4] == KEYBOARD_INTERFACE) && (usbp->setup[5] == 0)) {   /* wIndex */
          keyboard_protocol = ((usbp->setup[2]) != 0x00);   /* LSB(wValue) */
#ifdef NKRO_ENABLE
          keymap_config.nkro = !!keyboard_protocol;
          if(!keymap_config.nkro && keyboard_idle) {
#else /* NKRO_ENABLE */
          if(keyboard_idle) {
#endif /* NKRO_ENABLE */
          /* arm the idle timer if boot protocol & idle */
            osalSysLockFromISR();
            chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
            osalSysUnlockFromISR();
          }
        }
        usbSetupTransfer(usbp, NULL, 0, NULL);
        return TRUE;
        break;

      case HID_SET_IDLE:
        keyboard_idle = usbp->setup[3];     /* MSB(wValue) */
        /* arm the timer */
#ifdef NKRO_ENABLE
        if(!keymap_config.nkro && keyboard_idle) {
#else /* NKRO_ENABLE */
        if(keyboard_idle) {
#endif /* NKRO_ENABLE */
          osalSysLockFromISR();
          chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp);
          osalSysUnlockFromISR();
        }
        usbSetupTransfer(usbp, NULL, 0, NULL);
        return TRUE;
        break;
      }
      break;
    }
  }

  /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */
  if((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) {
    dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4]));
    if(dp == NULL)
      return FALSE;
    usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
    return TRUE;
  }

  for (int i=0;i<NUM_USB_DRIVERS;i++) {
    if (drivers.array[i].config.int_in) {
      // NOTE: Assumes that we only have one serial driver
      return qmkusbRequestsHook(usbp);
    }
  }

  return FALSE;
}

/* Start-of-frame callback */
static void usb_sof_cb(USBDriver *usbp) {
  kbd_sof_cb(usbp);
  osalSysLockFromISR();
  for (int i=0; i<NUM_USB_DRIVERS;i++) {
    qmkusbSOFHookI(&drivers.array[i].driver);
  }
  osalSysUnlockFromISR();
}


/* USB driver configuration */
static const USBConfig usbcfg = {
  usb_event_cb,                 /* USB events callback */
  usb_get_descriptor_cb,        /* Device GET_DESCRIPTOR request callback */
  usb_request_hook_cb,          /* Requests hook callback */
  usb_sof_cb                    /* Start Of Frame callback */
};

/*
 * Initialize the USB driver
 */
void init_usb_driver(USBDriver *usbp) {
  for (int i=0; i<NUM_USB_DRIVERS;i++) {
    QMKUSBDriver* driver = &drivers.array[i].driver;
    drivers.array[i].in_ep_config.in_state = &drivers.array[i].in_ep_state;
    drivers.array[i].out_ep_config.out_state = &drivers.array[i].out_ep_state;
    drivers.array[i].int_ep_config.in_state = &drivers.array[i].int_ep_state;
    qmkusbObjectInit(driver, &drivers.array[i].config);
    qmkusbStart(driver, &drivers.array[i].config);
  }

  /*
   * Activates the USB driver and then the USB bus pull-up on D+.
   * Note, a delay is inserted in order to not have to disconnect the cable
   * after a reset.
   */
  usbDisconnectBus(usbp);
  wait_ms(1500);
  usbStart(usbp, &usbcfg);
  usbConnectBus(usbp);

  chVTObjectInit(&keyboard_idle_timer);
}

/* ---------------------------------------------------------
 *                  Keyboard functions
 * ---------------------------------------------------------
 */
/* keyboard IN callback hander (a kbd report has made it IN) */
void kbd_in_cb(USBDriver *usbp, usbep_t ep) {
  /* STUB */
  (void)usbp;
  (void)ep;
}