#include "usbd_vendor.h" #include "usbd_desc.h" #include "usbd_ctlreq.h" #include "usbd_vendor_desc.h" static uint8_t USBD_VENDOR_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_VENDOR_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_VENDOR_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static uint8_t USBD_VENDOR_Error (USBD_HandleTypeDef *pdev, uint8_t epnum); static uint8_t USBD_VENDOR_EP0_RxReady (USBD_HandleTypeDef *pdev, size_t * ); static uint8_t USBD_VENDOR_EP0_TxSent (USBD_HandleTypeDef *pdev, size_t * ); /* Vendor interface class callbacks structure */ const USBD_ClassTypeDef USBD_VENDOR = { .Init = USBD_VENDOR_Init, .DeInit = USBD_VENDOR_DeInit, .Error = USBD_VENDOR_Error, .Setup = USBD_VENDOR_Setup, .EP0_TxSent = USBD_VENDOR_EP0_TxSent, .EP0_RxReady = USBD_VENDOR_EP0_RxReady, .DataIn = NULL, .DataOut = NULL, .SOF = NULL, .pDesc = &USBD_VENDOR_DescriptorHandlers, }; /** * @brief USBD_VENDOR_Init * Initialize the Vendor interface * @param pdev: device instance * @param cfgidx: Configuration index * @retval status */ static uint8_t USBD_VENDOR_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) { uint8_t ret = USBD_OK; USBD_VENDOR_HandleTypeDef *hvendor; pdev->pClassData = USBD_malloc(sizeof (USBD_VENDOR_HandleTypeDef)); if(pdev->pClassData == NULL) { ret = USBD_FAIL; } else { hvendor = (USBD_VENDOR_HandleTypeDef*) pdev->pClassData; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); usb_create_transfer( &hvendor->ep0_tx_transfer, &hvendor->ep0_tx_buffer[0], sizeof(hvendor->ep0_tx_buffer), NULL, pdev ); usb_create_transfer( &hvendor->ep0_rx_transfer, &hvendor->ep0_rx_buffer[0], sizeof(hvendor->ep0_rx_buffer), NULL, pdev ); if( 0 == pUserIface->fUsbInit() ) { if( USBD_STATE_DEFAULT == pdev->dev_state ) // useless { if( NULL != pUserIface->fResetEvent ) { pUserIface->fResetEvent(); } } if( pdev->dev_state == USBD_STATE_CONFIGURED ) { if( NULL != pUserIface->fSetIface ) { //-------------------------------------------------------------------------------------- // Enumerate interface descriptors and call SetInterface callback uint16_t nConfDescLength = 0; const uint8_t * pDescBytePtr = NULL; #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_Config, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pDescBytePtr, nConfDescLength, GetConfigDescriptor ); #endif if( USBD_OK == ret ) { const sUSBConfigurationDescriptor_t * conf = (const sUSBConfigurationDescriptor_t *)pDescBytePtr; size_t ifidx = 0; while( nConfDescLength > 0 ) { sUSBDescriptorHeader_t * pDescHeader = (sUSBDescriptorHeader_t*)pDescBytePtr; if( pDescHeader->bLength == 0 ) break; if( USB_DESC_TYPE_INTERFACE == pDescHeader->bDescriptorType ) { const sUSBInterfaceDescriptor_t * ifdesc = (const sUSBInterfaceDescriptor_t *)pDescBytePtr; if( ! pUserIface->fSetIface( cfgidx, ifidx++, ifdesc ) ) { ret = USBD_FAIL; break; } } nConfDescLength -= pDescHeader->bLength; pDescBytePtr += (pDescHeader->bLength); } (void)conf; } //-------------------------------------------------------------------------------------- } } } else { ret = USBD_FAIL; } } return ret; } /* static const sUSBInterfaceDescriptor_t * USBD_VENDOR_SearchInterfaceDescriptor( USBD_HandleTypeDef *pdev, uint8_t cfgidx, uint8_t nIf ) { //-------------------------------------------------------------------------------------- // Enumerate interface descriptors and call ClearInterface callback (void)cfgidx; // to do - use cfgidx to get configuration descriptor uint16_t nConfDescLength = 0; const uint8_t * pDescBytePtr = (const uint8_t * )pdev->pClass->GetFSConfigDescriptor(&nConfDescLength); const sUSBConfigurationDescriptor_t * conf = (const sUSBConfigurationDescriptor_t *)pDescBytePtr; while( nConfDescLength > 0 ) { sUSBDescriptorHeader_t * pDescHeader = (sUSBDescriptorHeader_t*)pDescBytePtr; if( pDescHeader->bLength == 0 ) break; if( USB_DESC_TYPE_INTERFACE == pDescHeader->bDescriptorType ) { const sUSBInterfaceDescriptor_t * ifdesc = (const sUSBInterfaceDescriptor_t *)pDescBytePtr; if( ifdesc->bInterfaceNumber == nIf ) { return ifdesc; } } nConfDescLength -= pDescHeader->bLength; pDescBytePtr += (pDescHeader->bLength); } //-------------------------------------------------------------------------------------- return NULL; } */ /** * @brief USBD_VENDOR_DeInit * DeInitialize the Vendor layer * @param pdev: device instance * @param cfgidx: Configuration index * @retval status */ static uint8_t USBD_VENDOR_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) { uint8_t ret = USBD_OK; /* DeInit physical Interface components */ if(pdev->pClassData != NULL) { if( pdev->pUserData != NULL ) { USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if( NULL != pUserIface->fClrIface ) { if( ((pdev->dev_old_state == USBD_STATE_CONFIGURED) || (pdev->dev_old_state == USBD_STATE_SUSPENDED) ) && ((pdev->dev_state != USBD_STATE_CONFIGURED) || (pdev->dev_state != USBD_STATE_SUSPENDED) ) ) { //-------------------------------------------------------------------------------------- // Enumerate interface descriptors and call ClearInterface callback (void)cfgidx; // to do - use cfgidx to get configuration descriptor uint16_t nConfDescLength = 0; const uint8_t * pDescBytePtr = NULL; #if USBD_GETDESCRIPTORS_ALTMETHOD == 0 ret = USBD_CallGetDescriptorHandler( pdev, eGetDescriptorHandlerType_Config, &pbuf, &len ); #else USBD_CallGetDescriptorHandlerAlt( ret, pdev, pDescBytePtr, nConfDescLength, GetConfigDescriptor ); #endif if( USBD_OK == ret ) { const sUSBConfigurationDescriptor_t * conf = (const sUSBConfigurationDescriptor_t *)pDescBytePtr; size_t ifidx = 0; while( nConfDescLength > 0 ) { sUSBDescriptorHeader_t * pDescHeader = (sUSBDescriptorHeader_t*)pDescBytePtr; if( pDescHeader->bLength == 0 ) break; if( USB_DESC_TYPE_INTERFACE == pDescHeader->bDescriptorType ) { const sUSBInterfaceDescriptor_t * ifdesc = (const sUSBInterfaceDescriptor_t *)pDescBytePtr; pUserIface->fClrIface( cfgidx, ifidx++, ifdesc ); } nConfDescLength -= pDescHeader->bLength; pDescBytePtr += (pDescHeader->bLength); } (void)conf; } //-------------------------------------------------------------------------------------- } } if( USBD_STATE_DEFAULT == pdev->dev_state ) { if( NULL != pUserIface->fResetEvent ) { pUserIface->fResetEvent(); } } pUserIface->fUsbDeInit(); USBD_free(pdev->pClassData); pdev->pClassData = NULL; } } return ret; } static uint8_t USBD_VENDOR_Error (USBD_HandleTypeDef *pdev, uint8_t epnum) { if(pdev->pClassData != NULL) { if( pdev->pUserData != NULL ) { USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if( epnum == 0 ) { if( NULL != pUserIface->fUsbSetup ) { pUserIface->fUsbSetup( &pdev->request, false, false ); } } else { if( NULL != pUserIface->fDataErrHandler ) { pUserIface->fDataErrHandler( epnum, 0 ); } } } } return USBD_OK; } /** * @brief USBD_VENDOR_Setup * Handle the vendor specific requests * @param pdev: instance * @param req: usb requests * @retval status */ #if VENDOR_USE_COMMON_SETUP static uint8_t USBD_VENDOR_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { uint8_t ret = USBD_OK; USBD_VENDOR_HandleTypeDef *hvendor = (USBD_VENDOR_HandleTypeDef*) pdev->pClassData; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if( NULL == pUserIface->fUsbSetup ) { return USBD_FAIL; // @fUsbSetup is missing } if( NULL == hvendor) { return USBD_FAIL; } // SETUP filtering: if( ! pUserIface->fUsbSetup( req, true, false ) ) { return USBD_FAIL; // do not call @fUsbSetup second time } else { // Checking request type switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_VENDOR : // Vendor I/O { if (req->wLength > 0) { if (req->bmRequest & 0x80) { #if RECREATE_CONTROL_TRANSFER_OBJ usb_create_transfer( &hvendor->ep0_tx_transfer, &hvendor->ep0_tx_buffer[0], sizeof(hvendor->ep0_tx_buffer), NULL, pdev ); #endif ret = USBD_COMMON_Setup_DataProcess( pdev, req, &hvendor->ep0_tx_transfer ); } else { #if RECREATE_CONTROL_TRANSFER_OBJ usb_create_transfer( &hvendor->ep0_rx_transfer, &hvendor->ep0_rx_buffer[0], sizeof(hvendor->ep0_rx_buffer), NULL, pdev ); #endif ret = USBD_COMMON_Setup_DataProcess( pdev, req, &hvendor->ep0_rx_transfer ); } } else { USBD_CtlSendStatus( pdev ); // need answer for ZeroLength requests } } break; case USB_REQ_TYPE_STANDARD: { switch (req->bRequest) { case USB_REQ_GET_INTERFACE: { if( NULL != pUserIface->fGetIface ) { uint8_t nIf = req->wIndex; uint8_t ifalt = 0; if( pUserIface->fGetIface( pdev->dev_config, nIf, &ifalt ) ) { USBD_CtlSendData (pdev, &ifalt, sizeof(ifalt)); } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } break; case USB_REQ_SET_INTERFACE: break; } } break; default: break; } } if( ret != USBD_OK ) { pUserIface->fUsbSetup( req, false, false ); } return ret; } #else static uint8_t USBD_VENDOR_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { uint8_t ret = USBD_OK; USBD_VENDOR_HandleTypeDef *hvendor = (USBD_VENDOR_HandleTypeDef*) pdev->pClassData; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if( NULL == pUserIface->fUsbSetup ) { return USBD_FAIL; // @fUsbSetup is missing } if( NULL == hvendor) { return USBD_FAIL; } // SETUP filtering: if( ! pUserIface->fUsbSetup( req, true, false ) ) { return USBD_FAIL; // do not call @fUsbSetup second time } else { switch (req->bmRequest & USB_REQ_TYPE_MASK) { case USB_REQ_TYPE_VENDOR : // Vendor I/O { if (req->wLength > 0) { if (req->bmRequest & 0x80) { /*if( req->wLength > 0 ) --- extra checking fixed 22/05/19 */ { // DataIN stage preparing #if RECREATE_CONTROL_TRANSFER_OBJ usb_create_transfer( &hvendor->ep0_tx_transfer, &hvendor->ep0_tx_buffer[0], sizeof(hvendor->ep0_tx_buffer), NULL, pdev ); #endif if( usb_validate_transfer( &hvendor->ep0_tx_transfer ) ) { // First packet in the stage: reset the transfer usb_reset_transfer( &hvendor->ep0_tx_transfer ); // Single-transfer mode only: // Whole transfer is limited by the @ep0_tx_transfer size size_t nBytesProcessed = pUserIface->fUsbCtlEpTx( (const tUSBSetupPacket_t*)req, &hvendor->ep0_tx_transfer, 0, req->wLength ); // User can perform move semantic on @ep0_tx_transfer transfer // This is an error condition, let's check it: #warning Check if( usb_validate_transfer( &hvendor->ep0_tx_transfer ) ) { USBD_EndpointTypeDef * pep = &pdev->ep_in[0]; // If SETUP data stage assumes trasmitting more than one MAX_PACKET bytes, // this packet must include MAX_PACKET bytes (not to be "short packet") size_t min_limit = ((req->wLength > pep->maxpacket)?(pep->maxpacket-1):(0)); // Actually, @ep0_tx_transfer can store more data than @nBytesProcessed. // @nBytesProcessed: bytes processed by last @fUsbCtlEpTx call. // Just check it for zero: if( nBytesProcessed > 0 ) { // Check the transfer for minimal bytes limit: if( min_limit < usb_count_transfer( &hvendor->ep0_tx_transfer ) ) { // truncate the transfer in case if the transfer size is // greater than requested length. usb_truncate_transfer( &hvendor->ep0_tx_transfer, req->wLength ); USBD_CtlSendData (pdev, (const uint8_t *)hvendor->ep0_tx_transfer.pReadData, req->wLength ); } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } } else { // DataOUT stage preparing if( req->wLength > 0 ) { #if RECREATE_CONTROL_TRANSFER_OBJ usb_create_transfer( &hvendor->ep0_rx_transfer, &hvendor->ep0_rx_buffer[0], sizeof(hvendor->ep0_rx_buffer), NULL, pdev ); #endif if( usb_validate_transfer( &hvendor->ep0_rx_transfer ) ) { // Reset the RX-transfer usb_reset_transfer( &hvendor->ep0_rx_transfer ); // Begin receiving first packet USBD_CtlPrepareRx (pdev, (uint8_t *)hvendor->ep0_rx_transfer.pWriteData, req->wLength ); } else { ret = USBD_FAIL; } } } } else { USBD_CtlSendStatus( pdev ); // need answer for ZeroLength requests } } break; case USB_REQ_TYPE_STANDARD: { switch (req->bRequest) { case USB_REQ_GET_INTERFACE: { if( NULL != pUserIface->fGetIface ) { uint8_t nIf = req->wIndex; uint8_t ifalt = 0; if( pUserIface->fGetIface( pdev->dev_config, nIf, &ifalt ) ) { USBD_CtlSendData (pdev, &ifalt, sizeof(ifalt)); } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } break; case USB_REQ_SET_INTERFACE: break; } } break; default: break; } } if( ret != USBD_OK ) { pUserIface->fUsbSetup( req, false, false ); } return ret; } #endif // USBD_VENDOR_EP0_RxReady() // This function is called when a packet via CONTROL protocol had been received from host // This function continues the transmitting #if VENDOR_USE_COMMON_EP0RXREADY static uint8_t USBD_VENDOR_EP0_RxReady (USBD_HandleTypeDef *pdev, size_t * pnBytesRollback ) { USBD_VENDOR_HandleTypeDef *hvendor = (USBD_VENDOR_HandleTypeDef*) pdev->pClassData; return USBD_COMMON_EP0_RxReady( pdev, &hvendor->ep0_rx_transfer, pnBytesRollback ); } #else static uint8_t USBD_VENDOR_EP0_RxReady (USBD_HandleTypeDef *pdev, size_t * pnBytesRollback ) { USBD_VENDOR_HandleTypeDef *hvendor = (USBD_VENDOR_HandleTypeDef*) pdev->pClassData; USBD_EndpointTypeDef * pep = &pdev->ep_out[0]; USBD_StatusTypeDef ret = USBD_FAIL; sUSBTransfer_t * rx = &hvendor->ep0_rx_transfer; if( pdev->pUserData != NULL && usb_validate_transfer(rx) ) { USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if( NULL != pUserIface->fUsbCtlEpRx ) { size_t nBytesReceived = ((pep->rem_length > pep->maxpacket)?pep->maxpacket:pep->rem_length); size_t nBytesRemaining = (pep->rem_length - nBytesReceived); // Since the core uses raw @ep0_rx_transfer->pWriteData pointer, it is required to update the transfer information. // Use 'virutal write' to shift the pointer: usb_transfer_virtual_write( rx, nBytesReceived ); // Check if the trasfer have enough free space for receiving another packet? // Or maybe it is a last packet? if( (usb_space_transfer(rx) < MIN(nBytesRemaining, pep->maxpacket)) || (0 == nBytesRemaining) ) { // No, there no enough free space // Need to call user handler. // Retirieve actual amount of data in transfer size_t nBytesInTransfer = usb_count_transfer(rx); // Calculate current data offset size_t nOffset = (pep->total_length - nBytesRemaining - nBytesInTransfer); // pass the data to user size_t nBytesProcessed = pUserIface->fUsbCtlEpRx( &pdev->request, rx, nOffset, nBytesRemaining + nBytesInTransfer ); // user can perform move semantic on transfer @rx. // It is valid condition if no remaining bytes left only. // Let's check it: bool bValid = usb_validate_transfer( rx ); #warning Check if( 0 == nBytesRemaining || bValid ) { // Actually, it does not matter, what exactly the function @fUsbCtlEpRx returns. // The only that matters is the amount of data in the transfer. // Just check the return value for zero: if( nBytesProcessed > 0 ) { // By default ( usb_count_transfer() is ZERO ) ==> @nCompressed equals to the number of bytes in the transfer before user handler call. size_t nCompressed = nBytesInTransfer; // It is required that the transfer be valid only if @nBytesRemaining greater than zero if( bValid ) { // If the transfer still contains data, it means user didn't perform full read. if( usb_count_transfer(rx) > 0 ) { // it is required to compress the data in the transfer // optimize the transfer to compress stored data nCompressed = usb_transfer_compress( rx ); // @bNeedPacketSpace - true if the core deadly needs at least MAX_PACKET free bytes in transfer bool bNeedPacketSpace = false; // After compressing: check if the transfer have enough space for next packet? if( usb_space_transfer(rx) >= MIN(nBytesRemaining, pep->maxpacket) ) { // yes, ok ret = USBD_OK; } else { // nope, user didn't read enough data from the transfer to free enough space for next packet // Need to free at least MAX_PACKET free bytes in transfer bNeedPacketSpace = true; } // try to call user handler again: if( bNeedPacketSpace || (0 == nBytesRemaining) ) { // Here: two reasons: // 1) if it is required to free MAX_PACKET bytes in transfer for next packet // 2) or this is a last packet: last chance to retrieve data. It is required to feed all data to user... Even if he resists!!!! >:D // For USB-core it does not matter if user processed all bytes or not... // There no incoming packets expected. // The only problem is to send a confirmation for this transaction. // As soon user process all bytes from the transfer @rx, this function returns control to the // core to send a confirmation. // Ok, lets feed user.. while( nBytesProcessed > 0 ) { nBytesInTransfer = usb_count_transfer(rx); size_t nLeftBytes = nBytesInTransfer + ((bNeedPacketSpace)?nBytesRemaining:0); size_t nOffset = (pep->total_length - nLeftBytes); // Call user handler, again... nBytesProcessed = pUserIface->fUsbCtlEpRx( &pdev->request, rx, nOffset, nLeftBytes ); // user can perform move semantic on transfer @rx. // It is valid condition if no remaining bytes left only. // Let's check it: #warning Check if( 0 == nBytesRemaining || usb_validate_transfer( rx ) ) { // ~~~ do not do this here, extra compressing possible (reduces performance) ~~ /* usb_transfer_compress( rx ); */ // ~~~ do not do this here, extra compressing possible (reduces performance) ~~ /* nCompressed += (nBytesInTransfer - usb_count_transfer(rx)); */ // Reason 1: (only if @bNeedPacketSpace is true) // check if the transfer has enough data for next packet if( bNeedPacketSpace && (usb_space_transfer(rx) >= MIN(nBytesRemaining, pep->maxpacket)) ) { // Reason 1: exit, because transfer has enough free space for next packet // User have read data portion from the transfer, need to compress it again // Need to update @nCompressed variable to take in account new pointer to the free memory. usb_transfer_compress( rx ); nCompressed += (nBytesInTransfer - usb_count_transfer(rx)); ret = USBD_OK; break; } else { // User have read data portion from the transfer, need to compress it again // Need to update @nCompressed variable to take in account new pointer to the free memory. usb_transfer_compress( rx ); nCompressed += (nBytesInTransfer - usb_count_transfer(rx)); } // check if the transfer is still not empty if( 0 == usb_count_transfer(rx) ) { // Reason 2: exit if transfer is empty // Empty! => Success (nBytesProcessed>0) break; // yee, at last! } } else { // Failed! ret = USBD_FAIL; } } // Check the last user call status if( 0 == nBytesProcessed ) { // Failed! ret = USBD_FAIL; } } } else { // ~~~~~~~~~~~~ ADDED 18/04/19 // Check if the trasfer have enough free space for receiving another packet? // Or maybe it is a last packet? if( (usb_space_transfer(rx) < MIN(nBytesRemaining, pep->maxpacket)) ) { // it is required to compress the data in the transfer // optimize the transfer to compress stored data nCompressed = usb_transfer_compress( rx ); } // ~~~~~~~~~~~~ ret = USBD_OK; } } else { // If there no bytes left to receive // it is normal that the transfer is invalid (user may use move semantic) if( 0 == nBytesRemaining ) { ret = USBD_OK; } } // let the core know how many excaclty bytes is required to shift the pointer back. if( (USBD_OK == ret) && (NULL != pnBytesRollback) ) { // pass the exact number of bytes for rollback to the core *pnBytesRollback = nCompressed; } } else { // user must not return zero to proceed usb_reset_transfer( rx ); ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } else { // yep, there is free space for the next packet, let's receive it ret = USBD_CONTINUE; } } } return ret; } #endif // USBD_VENDOR_EP0_TxSent() // This function is called when at least one packet via CONTROL protocol had been sent to the host // This function continues the transmitting #if VENDOR_USE_COMMON_EP0TXSENT static uint8_t USBD_VENDOR_EP0_TxSent (USBD_HandleTypeDef *pdev, size_t * pnBytesRollback ) { USBD_VENDOR_HandleTypeDef *hvendor = (USBD_VENDOR_HandleTypeDef*) pdev->pClassData; return USBD_COMMON_EP0_TxSent( pdev, &hvendor->ep0_tx_transfer, pnBytesRollback ); } #else static uint8_t USBD_VENDOR_EP0_TxSent (USBD_HandleTypeDef *pdev, size_t * pnBytesRollback ) { USBD_VENDOR_HandleTypeDef *hvendor = (USBD_VENDOR_HandleTypeDef*) pdev->pClassData; #if CONFIG_USB_VENDOR_DUMMYREAD memset( hvendor->ep0_tx_transfer.pDefaultBuffer, 0xAA, hvendor->ep0_tx_transfer.size ); return USBD_OK; #endif USBD_EndpointTypeDef * pep = &pdev->ep_in[0]; sUSBTransfer_t * tx = &hvendor->ep0_tx_transfer; uint8_t ret = USBD_FAIL; if( pdev->pUserData != NULL && usb_validate_transfer(tx) ) { USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if( NULL != pUserIface->fUsbCtlEpTx ) { // Due to the low-level driver uses dry transfer->@pReadData pointer, the // tranfer object must be modified. size_t nBytesRequested = 0; if( 0 == pep->rem_length ) { // Update transfer information: // If @pep->rem_length is zero, the last packet sent was a last packet // The size of the short packet is a remainder of the division the // full transaction size and the endpoint size. // If the result of operation is non zero, the last packet was a short packet. // If the result of operation is zero, the last packet was full packet size_t last_packet_size = (pep->total_length % pep->maxpacket); if( last_packet_size == 0 ) { last_packet_size = pep->maxpacket; } usb_transfer_virtual_read( tx, last_packet_size ); } else { nBytesRequested = MIN( pep->maxpacket, pep->rem_length ); // Update transfer information: // If @pep->rem_length is non zero, the last packet send was a full packet. // The size of full packet is pep->maxpacket usb_transfer_virtual_read( tx, pep->maxpacket ); } // // Update transfer information: // usb_transfer_virtual_read( tx, // // The variable @nBytesRequested have quite confusing name, but this argument // // requires the length of already sent data. Actually the number of bytes already sent // // is numerially equals to @nBytesRequested // nBytesRequested // for pep->rem_length >= pep->maxpacket: the pep->maxpacket bytes just have been sent // ); // for pep->rem_length < pep->maxpacket: the pep->rem_length bytes just have been sent // // If the transaction is still not completed: if( pep->rem_length == 0 ) { // transfer full reset usb_reset_transfer( tx ); ret = USBD_OK; } else { size_t nBytesLeft = usb_count_transfer( tx ); // If the transfer has enougth data to send another packet: if( nBytesLeft >= nBytesRequested ) { ret = USBD_CONTINUE; // ask the core to continue sending the transfer (without rollback) } else { if( nBytesLeft > 0 ) { // optimize the transfer to compress stored data size_t nCompressed = usb_transfer_compress( tx ); if( NULL != pnBytesRollback ) { // pass the exact number of bytes for rollback to the core *pnBytesRollback = nCompressed; } } else { // // transfer full reset // usb_reset_transfer( tx ); size_t nCompressed = usb_transfer_compress( tx ); if( NULL != pnBytesRollback ) { // pass the exact number of bytes for rollback to the core *pnBytesRollback = nCompressed; } } size_t nOffset = (pep->total_length - pep->rem_length + nBytesLeft); size_t nBytesProcessed = pUserIface->fUsbCtlEpTx( &pdev->request, tx, nOffset, pep->rem_length ); // User can perform move semantic on the @tx transfer. // This is an error condition, let's check it: #warning Check if( usb_validate_transfer( tx ) ) { // Actually, does not matter what exactly the function @fUsbCtlEpTx returns, // because only transfer size really matters ( usb_count_transfer() ). // Just check the return value for zero: if( nBytesProcessed > 0 ) { // If more than one FULL packet is required to send: if( pep->rem_length >= pep->maxpacket ) { // This packet must be full: transfer must contain at least @pep->maxpacket bytes if( usb_count_transfer( tx ) >= pep->maxpacket ) { ret = USBD_OK; } } else // No, it is required to send "short packet" { // This packet must be short: transfer must contain exactly @pep->rem_length bytes if( usb_count_transfer( tx ) == pep->rem_length ) { ret = USBD_OK; } } } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } } } } return ret; } #endif