add tentative example
This commit is contained in:
		| @@ -228,8 +228,17 @@ typedef struct TU_ATTR_PACKED { | ||||
| //--------------------------------------------------------------------+ | ||||
| // Requests | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| /* 4.3.1.1 */ | ||||
| typedef struct TU_ATTR_PACKED { | ||||
|   uint16_t bmHint; | ||||
|   struct TU_ATTR_PACKEDt { | ||||
|     uint16_t dwFrameInterval: 1; | ||||
|     uint16_t wKeyFrameRatel : 1; | ||||
|     uint16_t wPFrameRate    : 1; | ||||
|     uint16_t wCompQuality   : 1; | ||||
|     uint16_t wCompWindowSize: 1; | ||||
|     uint16_t                : 0; | ||||
|   } bmHint; | ||||
|   uint8_t  bFormatIndex; | ||||
|   uint8_t  bFrameIndex; | ||||
|   uint32_t dwFrameInterval; | ||||
| @@ -241,7 +250,12 @@ typedef struct TU_ATTR_PACKED { | ||||
|   uint32_t dwMaxVideoFrameSize; | ||||
|   uint32_t dwMaxPayloadTransferSize; | ||||
|   uint32_t dwClockFrequency; | ||||
|   uint8_t  bmFramingInfo; | ||||
|   struct TU_ATTR_PACKED { | ||||
|     uint8_t FrameID   : 1; | ||||
|     uint8_t EndOfFrame: 1; | ||||
|     uint8_t EndOfSlice: 1; | ||||
|     uint8_t           : 0; | ||||
|   } bmFramingInfo; | ||||
|   uint8_t  bPreferedVersion; | ||||
|   uint8_t  bMinVersion; | ||||
|   uint8_t  bMaxVersion; | ||||
|   | ||||
| @@ -82,7 +82,7 @@ typedef struct | ||||
|  | ||||
| } videod_interface_t; | ||||
|  | ||||
| #define ITF_MEM_RESET_SIZE   offsetof(cdcd_interface_t, wanted_char) | ||||
| #define ITF_MEM_RESET_SIZE   offsetof(videod_interface_t, ctl_buf) | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // INTERNAL OBJECT & FUNCTION DECLARATION | ||||
| @@ -109,6 +109,14 @@ static uint16_t* _get_desc_ofs(videod_interface_t *self, unsigned itfnum) | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| static tusb_desc_vs_itf_t const *_get_desc_vs(videod_interface_t const *self, unsigned itfnum) | ||||
| { | ||||
|   uint16_t const *ofs = _get_desc_ofs((videod_interface_t*)self, itfnum); | ||||
|   if (!ofs) return NULL; | ||||
|   return (tusb_desc_vs_itf_t const*)(self->beg + *ofs); | ||||
| } | ||||
|  | ||||
|  | ||||
| /** Find the first descriptor with the specified descriptor type. | ||||
|  * | ||||
|  * @param[in] beg     The head of descriptor byte array. | ||||
| @@ -188,6 +196,10 @@ static void const* _find_desc_entity(tusb_desc_vc_itf_t const *vc, unsigned enti | ||||
|   return end; | ||||
| } | ||||
|  | ||||
| /** Close current video control interface. | ||||
|  * | ||||
|  * @param[in,out] self     The context. | ||||
|  * @param[in]     altnum   The target alternate setting number. */ | ||||
| static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self) | ||||
| { | ||||
|   tusb_desc_vc_itf_t const *vc = _get_desc_vc(self); | ||||
| @@ -206,6 +218,10 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self) | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /** Set the specified alternate setting to own video control interface. | ||||
|  * | ||||
|  * @param[in,out] self     The context. | ||||
|  * @param[in]     altnum   The target alternate setting number. */ | ||||
| static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, unsigned altnum) | ||||
| { | ||||
|   void const *beg = self->beg; | ||||
| @@ -238,48 +254,6 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, unsigned altn | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /** Set the specified alternate setting to own video control interface. | ||||
|  * | ||||
|  * @param[in,out] self     The context. | ||||
|  * @param[in]     altnum   The target alternate setting number. */ | ||||
| static bool _set_vc_itf(uint8_t rhport, videod_interface_t *self, unsigned altnum) | ||||
| { | ||||
|   void const *beg = self->beg; | ||||
|   void const *end = beg + self->len; | ||||
|   /* The head descriptor is a video control interface descriptor. */ | ||||
|   unsigned itfnum = ((tusb_desc_interface_t const *)beg)->bInterfaceNumber; | ||||
|   void const *cur = _find_desc_itf(beg, end, itfnum, altnum); | ||||
|   TU_VERIFY(cur < end); | ||||
|  | ||||
|   tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const *)cur; | ||||
|   /* Support for up to 2 streaming interfaces only. */ | ||||
|   TU_VERIFY(vc->ctl.bInCollection < 3); | ||||
|  | ||||
|   /* Close the previous notification endpoint if it is opened */ | ||||
|   if (self->ep_notif) { | ||||
|     usbd_edpt_close(rhport, self->ep_notif); | ||||
|     self->ep_notif = 0; | ||||
|   } | ||||
|   /* Advance to the next descriptor after the class-specific VC interface header descriptor. */ | ||||
|   cur += vc->std.bLength + vc->ctl.bLength; | ||||
|   /* Update to point the end of the video control interface descriptor. */ | ||||
|   end  = cur + vc->ctl.wTotalLength; | ||||
|   /* Open the notification endpoint if it exist. */ | ||||
|   if (vc->std.bNumEndpoints) { | ||||
|     /* Support for 1 endpoint only. */ | ||||
|     TU_VERIFY(1 == vc->std.bNumEndpoints); | ||||
|     /* Find the notification endpoint descriptor. */ | ||||
|     cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); | ||||
|     TU_VERIFY(cur < end); | ||||
|     tusb_desc_endpoint_t const *notif = (tusb_desc_endpoint_t const *)cur; | ||||
|     /* Open the notification endpoint */ | ||||
|     TU_ASSERT(usbd_edpt_open(rhport, notif)); | ||||
|     self->ep_notif = notif->bEndpointAddress; | ||||
|   } | ||||
|   self->ofs[0] = (void const*)vc - beg; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /** Set the specified alternate setting to own video control interface. | ||||
|  * | ||||
|  * @param[in,out] self     The context. | ||||
| @@ -293,7 +267,7 @@ static bool _close_vs_itf(uint8_t rhport, videod_interface_t *self, unsigned itf | ||||
|   void const *cur = (void const*)vs + vs->std.bLength + vs->stm.bLength; | ||||
|   /* The end of the video streaming interface descriptor. */ | ||||
|   void const *end = cur + vs->stm.wTotalLength; | ||||
|   if (unsigned i = 0; i < vs->std.bNumEndpoints; ++i) { | ||||
|   for (unsigned i = 0; i < vs->std.bNumEndpoints; ++i) { | ||||
|     cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); | ||||
|     TU_ASSERT(cur < end); | ||||
|     tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const *)cur; | ||||
| @@ -345,123 +319,28 @@ static bool _open_vs_itf(uint8_t rhport, videod_interface_t *self, unsigned itfn | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /** Set the specified alternate setting to own video control interface. | ||||
|  * | ||||
|  * @param[in,out] self     The context. | ||||
|  * @param[in]     itfnum   The target interface number. | ||||
|  * @param[in]     altnum   The target alternate setting number. */ | ||||
| static bool _set_vs_itf(uint8_t rhport, videod_interface_t *self, unsigned itfnum, unsigned altnum) | ||||
| { | ||||
|   unsigned i; | ||||
|   tusb_desc_vc_itf_t const *vc = _get_desc_vc(self); | ||||
|   void const *end = self->beg + self->len; | ||||
|   /* Set the end of the video control interface descriptor. */ | ||||
|   void const *cur = (void const*)vc + vc->std.bLength + vc->ctl.bLength + vc->ctl.wTotalLength; | ||||
|  | ||||
|   /* Check itfnum is valid */ | ||||
|   unsigned bInCollection = vc->ctl.bInCollection; | ||||
|   for (i = 0; (i < bInCollection) && (vc->ctl.baInterfaceNr[i] != itfnum); ++i) ; | ||||
|   TU_VERIFY(i < bInCollection); | ||||
|  | ||||
|   cur = _find_desc_itf(cur, end, itfnum, altnum); | ||||
|   TU_VERIFY(cur < end); | ||||
|   tusb_desc_vs_itf_t const *vs = (tusb_desc_vs_itf_t const*)cur; | ||||
|   /* Advance to the next descriptor after the class-specific VS interface header descriptor. */ | ||||
|   cur += vs->std.bLength + vs->stm.bLength; | ||||
|   /* Update to point the end of the video control interface descriptor. */ | ||||
|   end  = cur + vs->stm.wTotalLength; | ||||
|  | ||||
|   switch (vs->stm.bDescriptorSubType) { | ||||
|   default: return false; | ||||
|   case VIDEO_CS_VS_INTERFACE_INPUT_HEADER: | ||||
|     /* Support for up to 2 endpoint only. */ | ||||
|     TU_VERIFY(vc->std.bNumEndpoints < 3); | ||||
|     if (self->ep_sti) { | ||||
|       usbd_edpt_close(rhport, self->ep_sti); | ||||
|       self->ep_sti = 0; | ||||
|     } | ||||
|     if (self->ep_in) { | ||||
|       usbd_edpt_close(rhport, self->ep_in); | ||||
|       self->ep_in  = 0; | ||||
|     } | ||||
|     if (i = 0; i < vs->std.bNumEndpoints; ++i) { | ||||
|       cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); | ||||
|       TU_VERIFY(cur < end); | ||||
|       tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const *)cur; | ||||
|       if (vs->stm.bEndpointAddress == ep->bEndpointAddress) { | ||||
|         /* video input endpoint */ | ||||
|         TU_ASSERT(!self->ep_in); | ||||
|         TU_ASSERT(usbd_edpt_open(rhport, ep)); | ||||
|         self->ep_in  = ep->bEndpointAddress; | ||||
|       } else { | ||||
|         /* still image input endpoint */ | ||||
|         TU_ASSERT(!self->ep_sti); | ||||
|         TU_ASSERT(usbd_edpt_open(rhport, ep)); | ||||
|         self->ep_sti = ep->bEndpointAddress; | ||||
|       } | ||||
|       cur += tu_desc_len(cur); | ||||
|     } | ||||
|     break; | ||||
|   case VIDEO_CS_VS_INTERFACE_OUTPUT_HEADER: | ||||
|     /* Support for up to 1 endpoint only. */ | ||||
|     TU_VERIFY(vc->std.bNumEndpoints < 2); | ||||
|     if (self->ep_out) { | ||||
|       usbd_edpt_close(rhport, self->ep_out); | ||||
|       self->ep_out = 0; | ||||
|     } | ||||
|     if (vs->std.bNumEndpoints) { | ||||
|       cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); | ||||
|       TU_VERIFY(cur < end); | ||||
|       tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const *)cur; | ||||
|       if (vs->stm.bEndpointAddress == ep->bEndpointAddress) { | ||||
|         /* video output endpoint */ | ||||
|         TU_ASSERT(usbd_edpt_open(rhport, ep)); | ||||
|         self->ep_out = ep->bEndpointAddress; | ||||
|       } | ||||
|     } | ||||
|     break; | ||||
|   } | ||||
|   for (unsigned i = 1; i < sizeof(self->ofs)/sizeof(ofs[0]); ++i) { | ||||
|     if (!self->ofs[i]) { | ||||
|       return true; | ||||
|     } | ||||
|     tusb_desc_interface_t const* itf = (tusb_desc_interface_t const*)(beg + ofs[i]); | ||||
|     if (itfnum == itf->bInterfaceNumber) return itf; | ||||
|   } | ||||
|   return NULL; | ||||
|   for (i = 0; i < sizeof(self->vs)/sizeof(self->vs[0]); ++i) { | ||||
|     if (!self->vs[i] || self->vs[i].stm.bInterfaceNumber == vs->stm.bInterfaceNumber) { | ||||
|       self->ofs[i] = (void const*)vs - self->beg; | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| static int handle_video_ctl_std_req_get_itf(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, unsigned itf) | ||||
| { | ||||
|   if (stage != CONTROL_STAGE_SETUP) | ||||
|     return VIDEO_NO_ERROR; | ||||
|   videod_interface_t *self = &_videod_itf[itf]; | ||||
|   TU_VERIFY(1 == request->wLength, VIDEO_UNKNOWN); | ||||
|   tusb_desc_interface_t const *p = _get_desc_cur_itf(self, itfnum); | ||||
|   if (!p) return VIDEO_UNKNOWN; | ||||
|   if (tud_control_xfer(rhport, request, &p->bAlternateSettings, sizeof(p->bAlternateSettings))) | ||||
|     return VIDEO_NO_ERROR; | ||||
|   return VIDEO_UNKNOWN; | ||||
| } | ||||
|  | ||||
| /** Handle a standard request to the video control interface. */ | ||||
| static int handle_video_ctl_std_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, unsigned itf) | ||||
| { | ||||
|   switch (p_request->bRequest) { | ||||
|   switch (request->bRequest) { | ||||
|   case TUSB_REQ_GET_INTERFACE: | ||||
|     handle_video_ctl_std_req_get_itf(rhport, stage, request, itf); | ||||
|   case TUSB_REQ_SET_INTERFACE: | ||||
|     if (stage != CONTROL_STAGE_SETUP) return VIDEO_NO_ERROR; | ||||
|     if (_set_vc_itf(rhport, &_videod_itf[itf], request->wValue)) | ||||
|     TU_VERIFY(1 == request->wLength, VIDEO_UNKNOWN); | ||||
|     tusb_desc_vc_itf_t const *vc = _get_desc_vc(&_videod_itf[itf]); | ||||
|     if (!vc) return VIDEO_UNKNOWN; | ||||
|     if (tud_control_xfer(rhport, request, | ||||
| 			 (void*)&vc->std.bAlternateSetting, | ||||
| 			 sizeof(vc->std.bAlternateSetting))) | ||||
|       return VIDEO_NO_ERROR; | ||||
|     return VIDEO_UNKNOWN; | ||||
|   case TUSB_REQ_SET_INTERFACE: | ||||
|     if (stage != CONTROL_STAGE_SETUP) return VIDEO_NO_ERROR; | ||||
|     TU_VERIFY(0 == request->wLength, VIDEO_UNKNOWN); | ||||
|     if (!_close_vc_itf(rhport, &_videod_itf[itf])) | ||||
|       return VIDEO_UNKNOWN; | ||||
|     if (!_open_vc_itf(rhport, &_videod_itf[itf], request->wValue)) | ||||
|       return VIDEO_UNKNOWN; | ||||
|     return VIDEO_NO_ERROR; | ||||
|   default: /* Unknown/Unsupported request */ | ||||
|     TU_BREAKPOINT(); | ||||
|     return VIDEO_INVALID_REQUEST; | ||||
| @@ -470,11 +349,11 @@ static int handle_video_ctl_std_req(uint8_t rhport, uint8_t stage, tusb_control_ | ||||
|  | ||||
| static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, unsigned itf) | ||||
| { | ||||
|   videod_interface_t *self = &_videod_itf[i]; | ||||
|   videod_interface_t *self = &_videod_itf[itf]; | ||||
|   /* 4.2.1 Interface Control Request */ | ||||
|   switch (TU_U16_HIGH(request->wValue)) { | ||||
|   case VIDEO_VC_CTL_VIDEO_POWER_MODE: | ||||
|     switch (p_request->bRequest) { | ||||
|     switch (request->bRequest) { | ||||
|     case VIDEO_REQUEST_SET_CUR: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Set Power Mode\r\n"); | ||||
| @@ -482,7 +361,7 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r | ||||
|         if (!tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } else if (stage == CONTROL_STAGE_ACK) { | ||||
|         if (tud_video_power_mode_cb) return tud_video_power_mode_cb(itf, &self->power_mode); | ||||
|         if (tud_video_power_mode_cb) return tud_video_power_mode_cb(itf, self->power_mode); | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
|     case VIDEO_REQUEST_GET_CUR: | ||||
| @@ -497,7 +376,7 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Get Info Power Mode\r\n"); | ||||
|         TU_VERIFY(1 == request->wLength, VIDEO_UNKNOWN); | ||||
|         if (!tud_control_xfer(rhport, request, &_cap_get_set, sizeof(_cap_get_set))) | ||||
|         if (!tud_control_xfer(rhport, request, (uint8_t*)&_cap_get_set, sizeof(_cap_get_set))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
| @@ -505,7 +384,7 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r | ||||
|     } | ||||
|     break; | ||||
|   case VIDEO_VC_CTL_REQUEST_ERROR_CODE: | ||||
|     switch (p_request->bRequest) { | ||||
|     switch (request->bRequest) { | ||||
|     case VIDEO_REQUEST_GET_CUR: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Get Error Code\r\n"); | ||||
| @@ -516,7 +395,7 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r | ||||
|     case VIDEO_REQUEST_GET_INFO: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Get Info Error Code\r\n"); | ||||
|         if (tud_control_xfer(rhport, request, &_cap_get, sizeof(_cap_get))) | ||||
|         if (tud_control_xfer(rhport, request, (uint8_t*)&_cap_get, sizeof(_cap_get))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
| @@ -532,14 +411,17 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r | ||||
|  | ||||
| static int handle_video_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, unsigned itf) | ||||
| { | ||||
|   switch (p_request->bmRequestType_bit.type) { | ||||
|   unsigned entity_id; | ||||
|   switch (request->bmRequestType_bit.type) { | ||||
|   case TUSB_REQ_TYPE_STANDARD: | ||||
|     return handle_video_ctl_std_req(rhport, stage, request, itf); | ||||
|   case TUSB_REQ_TYPE_CLASS: | ||||
|     if (!TU_U16_HIGH(request->wIndex)) { | ||||
|     entity_id = TU_U16_HIGH(request->wIndex); | ||||
|     if (!entity_id) { | ||||
|       return handle_video_ctl_cs_req(rhport, stage, request, itf); | ||||
|     } else { | ||||
|       /* TODO: */ | ||||
|       if (!_find_desc_entity(_get_desc_vc(&_videod_itf[itf]), entity_id)) | ||||
| 	return VIDEO_INVALID_REQUEST; | ||||
|       return VIDEO_INVALID_REQUEST; | ||||
|     } | ||||
|   default: | ||||
| @@ -549,16 +431,27 @@ static int handle_video_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_requ | ||||
|  | ||||
| static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, unsigned itf) | ||||
| { | ||||
|   switch (p_request->bRequest) { | ||||
|   unsigned itfnum; | ||||
|   switch (request->bRequest) { | ||||
|   case TUSB_REQ_GET_INTERFACE: | ||||
|     handle_video_ctl_std_req_get_itf(rhport, stage, request, itf); | ||||
|   case TUSB_REQ_SET_INTERFACE: | ||||
|     videod_interface_t *self   = &_videod_itf[itf]; | ||||
|     unsigned            itfnum = tu_u16_low(p_request->wIndex); | ||||
|     if (stage != CONTROL_STAGE_SETUP) return VIDEO_NO_ERROR; | ||||
|     if (_set_vs_itf(rhport, self, itfnum, request->wValue)) | ||||
|     TU_VERIFY(1 == request->wLength, VIDEO_UNKNOWN); | ||||
|     itfnum = tu_u16_low(request->wIndex); | ||||
|     tusb_desc_vs_itf_t const *vs = _get_desc_vs(&_videod_itf[itf], itfnum); | ||||
|     if (!vs) return VIDEO_UNKNOWN; | ||||
|     if (tud_control_xfer(rhport, request, | ||||
| 			 (void*)&vs->std.bAlternateSetting, | ||||
| 			 sizeof(vs->std.bAlternateSetting))) | ||||
|       return VIDEO_NO_ERROR; | ||||
|     return VIDEO_UNKNOWN; | ||||
|   case TUSB_REQ_SET_INTERFACE: | ||||
|     if (stage != CONTROL_STAGE_SETUP) return VIDEO_NO_ERROR; | ||||
|     itfnum = tu_u16_low(request->wIndex); | ||||
|     if (!_close_vs_itf(rhport, &_videod_itf[itf], itfnum)) | ||||
|       return VIDEO_UNKNOWN; | ||||
|     if (!_open_vs_itf(rhport, &_videod_itf[itf], itfnum, request->wValue)) | ||||
|       return VIDEO_UNKNOWN; | ||||
|     return VIDEO_NO_ERROR; | ||||
|   default: /* Unknown/Unsupported request */ | ||||
|     TU_BREAKPOINT(); | ||||
|     return VIDEO_INVALID_REQUEST; | ||||
| @@ -567,56 +460,52 @@ static int handle_video_stm_std_req(uint8_t rhport, uint8_t stage, tusb_control_ | ||||
|  | ||||
| static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, unsigned itf) | ||||
| { | ||||
|   videod_interface_t *self = &_videod_itf[i]; | ||||
|   //  videod_interface_t *self = &_videod_itf[itf]; | ||||
|   (void)rhport; (void)stage; (void)itf; | ||||
|   /* 4.2.1 Interface Control Request */ | ||||
|   switch (TU_U16_HIGH(request->wValue)) { | ||||
|   case VIDEO_VS_CTL_PROBE: | ||||
|     switch (p_request->bRequest) { | ||||
|     TU_LOG2("  Probe "); | ||||
|     switch (request->bRequest) { | ||||
|     case VIDEO_REQUEST_SET_CUR: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Set Power Mode\r\n"); | ||||
|         TU_VERIFY(1 == request->wLength, VIDEO_UNKNOWN); | ||||
|         if (!tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } else if (stage == CONTROL_STAGE_ACK) { | ||||
|         if (tud_video_power_mode_cb) return tud_video_power_mode_cb(itf, &self->power_mode); | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
|       TU_LOG2("Set\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_CUR: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Get Power Mode\r\n"); | ||||
|         TU_VERIFY(1 == request->wLength, VIDEO_UNKNOWN); | ||||
|         if (!tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
|       TU_LOG2("Get\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_MIN: | ||||
|       TU_LOG2("Get Min\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_MAX: | ||||
|       TU_LOG2("Get Man\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_RES: | ||||
|       TU_LOG2("Get Res\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_DEF: | ||||
|       TU_LOG2("Get Def\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_LEN: | ||||
|       TU_LOG2("Get Len\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_INFO: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Get Info Power Mode\r\n"); | ||||
|         TU_VERIFY(1 == request->wLength, VIDEO_UNKNOWN); | ||||
|         if (!tud_control_xfer(rhport, request, &_cap_get_set, sizeof(_cap_get_set))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
|       TU_LOG2("Get Info\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     default: break; | ||||
|     } | ||||
|     break; | ||||
|   case VIDEO_VS_CTL_COMMIT: | ||||
|     switch (p_request->bRequest) { | ||||
|     TU_LOG2("  Commit "); | ||||
|     switch (request->bRequest) { | ||||
|     case VIDEO_REQUEST_SET_CUR: | ||||
|       TU_LOG2("Set\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_CUR: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Get Error Code\r\n"); | ||||
|         if (!tud_control_xfer(rhport, request, &self->error_code, sizeof(self->error_code))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
|       TU_LOG2("Get\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     case VIDEO_REQUEST_GET_INFO: | ||||
|       if (stage == CONTROL_STAGE_SETUP) { | ||||
|         TU_LOG2("  Get Info Error Code\r\n"); | ||||
|         if (tud_control_xfer(rhport, request, &_cap_get, sizeof(_cap_get))) | ||||
|           return VIDEO_UNKNOWN; | ||||
|       } | ||||
|       return VIDEO_NO_ERROR; | ||||
|       TU_LOG2("Get Info\r\n"); | ||||
|       return VIDEO_UNKNOWN; | ||||
|     default: break; | ||||
|     } | ||||
|     break; | ||||
| @@ -629,7 +518,7 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r | ||||
|  | ||||
| static int handle_video_stm_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request, unsigned itf) | ||||
| { | ||||
|   switch (p_request->bmRequestType_bit.type) { | ||||
|   switch (request->bmRequestType_bit.type) { | ||||
|   case TUSB_REQ_TYPE_STANDARD: | ||||
|     return handle_video_stm_std_req(rhport, stage, request, itf); | ||||
|   case TUSB_REQ_TYPE_CLASS: | ||||
| @@ -642,40 +531,14 @@ static int handle_video_stm_req(uint8_t rhport, uint8_t stage, tusb_control_requ | ||||
|   return VIDEO_UNKNOWN; | ||||
| } | ||||
|  | ||||
| static void _prep_out_transaction(cdcd_interface_t* p_cdc) | ||||
| { | ||||
|   uint8_t const rhport = TUD_OPT_RHPORT; | ||||
|   uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff); | ||||
|  | ||||
|   // Prepare for incoming data but only allow what we can store in the ring buffer. | ||||
|   // TODO Actually we can still carry out the transfer, keeping count of received bytes | ||||
|   // and slowly move it to the FIFO when read(). | ||||
|   // This pre-check reduces endpoint claiming | ||||
|   TU_VERIFY(available >= sizeof(p_cdc->epout_buf), ); | ||||
|  | ||||
|   // claim endpoint | ||||
|   TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out), ); | ||||
|  | ||||
|   // fifo can be changed before endpoint is claimed | ||||
|   available = tu_fifo_remaining(&p_cdc->rx_ff); | ||||
|  | ||||
|   if ( available >= sizeof(p_cdc->epout_buf) ) | ||||
|   { | ||||
|     usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); | ||||
|   }else | ||||
|   { | ||||
|     // Release endpoint since we don't make any transfer | ||||
|     usbd_edpt_release(rhport, p_cdc->ep_out); | ||||
|   } | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // APPLICATION API | ||||
| //--------------------------------------------------------------------+ | ||||
| bool tud_video_n_connected(uint8_t itf) | ||||
| { | ||||
|   (void)itf; | ||||
|   // DTR (bit 0) active  is considered as connected | ||||
|   return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0); | ||||
|   return tud_ready(); | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| @@ -695,7 +558,7 @@ void videod_init(void) | ||||
|  | ||||
|   for (unsigned i = 0; i < CFG_TUD_VIDEO; ++i) | ||||
|   { | ||||
|     videod_interface_t* p_video = &_videod_itf[i]; | ||||
|     // videod_interface_t* p_video = &_videod_itf[i]; | ||||
|  | ||||
|     // TODO | ||||
|   } | ||||
| @@ -716,14 +579,14 @@ void videod_reset(uint8_t rhport) | ||||
|  | ||||
| uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) | ||||
| { | ||||
|   TU_VERIFY(TUSB_CLASS_VIDEO           == itf_desc->bInterfaceClass && | ||||
|             VIDEO_SUBCLASS_CONTROL     == itf_desc->bInterfaceSubClass && | ||||
|             VIDEO_INT_PROTOCOL_CODE_15 == itf_desc->bFunctionProtool, 0); | ||||
|   TU_VERIFY((TUSB_CLASS_VIDEO           == itf_desc->bInterfaceClass) && | ||||
| 	    (VIDEO_SUBCLASS_CONTROL     == itf_desc->bInterfaceSubClass) && | ||||
| 	    (VIDEO_INT_PROTOCOL_CODE_15 == itf_desc->bInterfaceProtocol), 0); | ||||
|  | ||||
|   /* Find available interface */ | ||||
|   videod_interface_t *self = NULL; | ||||
|   for (unsigned i = 0; i < CFG_TUD_VIDEO; ++i) { | ||||
|     if (!_videod_itf[i].vc) { | ||||
|     if (!_videod_itf[i].beg) { | ||||
|       self = &_videod_itf[i]; | ||||
|       break; | ||||
|     } | ||||
| @@ -758,24 +621,23 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin | ||||
| bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) | ||||
| { | ||||
|   int err; | ||||
|   if (p_request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_INTERFACE) { | ||||
|   if (request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_INTERFACE) { | ||||
|     return false; | ||||
|   } | ||||
|   unsigned itfnum = tu_u16_low(p_request->wIndex); | ||||
|   unsigned itfnum = tu_u16_low(request->wIndex); | ||||
|   /* Identify which interface to use */ | ||||
|   int itf; | ||||
|   tusb_desc_vc_itf_t const *vc = NULL; | ||||
|   for (itf = 0; itf < CFG_TUD_VIDEO; ++itf) { | ||||
|     vc = _videod_itf[itf].vc; | ||||
|     if (!vc) continue; | ||||
|     unsigned beg_itfnum = vc->bInterfaceNumber; | ||||
|     vc = _get_desc_vc(&_videod_itf[itf]); | ||||
|     unsigned beg_itfnum = vc->std.bInterfaceNumber; | ||||
|     unsigned end_itfnum = vc->ctl.bInCollection; | ||||
|     if (beg_itfnum <= itfnum && itfnum < end_itfnum) | ||||
|       break; | ||||
|   } | ||||
|   if (itf == CFG_TUD_VIDEO) return false; | ||||
|  | ||||
|   if (itfnum == vc->bInterfaceNumber) { | ||||
|   if (itfnum == vc->std.bInterfaceNumber) { | ||||
|     /* To video control interface */ | ||||
|     err = handle_video_ctl_req(rhport, stage, request, itf); | ||||
|   } else { | ||||
| @@ -789,68 +651,8 @@ bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_ | ||||
|  | ||||
| bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) | ||||
| { | ||||
|   (void) result; | ||||
|  | ||||
|   uint8_t itf; | ||||
|   videod_interface_t* p_video; | ||||
|  | ||||
|   // Identify which interface to use | ||||
|   for (itf = 0; itf < CFG_TUD_CDC; itf++) | ||||
|   { | ||||
|     p_video = &_videod_itf[itf]; | ||||
|     if ( ( ep_addr == p_video->ep_out ) || ( ep_addr == p_video->ep_in ) ) break; | ||||
|   } | ||||
|   TU_ASSERT(itf < CFG_TUD_CDC); | ||||
|  | ||||
|   // Received new data | ||||
|   if ( ep_addr == p_video->ep_out ) | ||||
|   { | ||||
|     tu_fifo_write_n(&p_video->rx_ff, &p_video->epout_buf, xferred_bytes); | ||||
|      | ||||
|     // Check for wanted char and invoke callback if needed | ||||
|     if ( tud_cdc_rx_wanted_cb && (((signed char) p_video->wanted_char) != -1) ) | ||||
|     { | ||||
|       for ( uint32_t i = 0; i < xferred_bytes; i++ ) | ||||
|       { | ||||
|         if ( (p_video->wanted_char == p_video->epout_buf[i]) && !tu_fifo_empty(&p_video->rx_ff) ) | ||||
|         { | ||||
|           tud_cdc_rx_wanted_cb(itf, p_video->wanted_char); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     // invoke receive callback (if there is still data) | ||||
|     if (tud_cdc_rx_cb && !tu_fifo_empty(&p_video->rx_ff) ) tud_cdc_rx_cb(itf); | ||||
|      | ||||
|     // prepare for OUT transaction | ||||
|     _prep_out_transaction(p_video); | ||||
|   } | ||||
|    | ||||
|   // Data sent to host, we continue to fetch from tx fifo to send. | ||||
|   // Note: This will cause incorrect baudrate set in line coding. | ||||
|   //       Though maybe the baudrate is not really important !!! | ||||
|   if ( ep_addr == p_video->ep_in ) | ||||
|   { | ||||
|     // invoke transmit callback to possibly refill tx fifo | ||||
|     if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf); | ||||
|  | ||||
|     if ( 0 == tud_cdc_n_write_flush(itf) ) | ||||
|     { | ||||
|       // If there is no data left, a ZLP should be sent if | ||||
|       // xferred_bytes is multiple of EP Packet size and not zero | ||||
|       if ( !tu_fifo_count(&p_video->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) ) | ||||
|       { | ||||
|         if ( usbd_edpt_claim(rhport, p_video->ep_in) ) | ||||
|         { | ||||
|           usbd_edpt_xfer(rhport, p_video->ep_in, NULL, 0); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // nothing to do with notif endpoint for now | ||||
|  | ||||
|   return true; | ||||
|   (void)rhport; (void)ep_addr; (void)result; (void)xferred_bytes; | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -46,6 +46,8 @@ TU_ATTR_WEAK bool tud_video_set_cur_cb(uint8_t rhport, tusb_control_request_t co | ||||
| // Invoked when GET_CUR request received | ||||
| TU_ATTR_WEAK bool tud_video_get_cur_cb(uint8_t rhport, tusb_control_request_t const *request); | ||||
|  | ||||
| /* @return video_error_code_t */ | ||||
| TU_ATTR_WEAK int tud_video_power_mode_cb(unsigned itf, uint8_t power_mod); | ||||
| //--------------------------------------------------------------------+ | ||||
| // INTERNAL USBD-CLASS DRIVER API | ||||
| //--------------------------------------------------------------------+ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 kkitayam
					kkitayam