#include "drivers/usb/class/common/class_common.h" #include "app/usb/usb_transfer_flags.h" // USB_TRANSFER_FLAG_SHORTPACKET #include "usbd_desc.h" #include "usbd_ctlreq.h" #if DEBUG_USB > 0 // SCPI debug volatile uint32_t gDebugUSBEPTxBytes = 0; volatile uint32_t gDebugUSBEPTxLastPacket = 0; #endif // USBD_COMMON_EP0_RxReady // This function is called when a packet via CONTROL protocol had been received from host // This function continues the transmitting uint8_t USBD_COMMON_EP0_RxReady (USBD_HandleTypeDef *pdev, sUSBTransfer_t * rx, size_t * pnBytesRollback ) { USBD_EndpointTypeDef * pep = &pdev->ep_out[0]; USBD_StatusTypeDef ret = USBD_FAIL; 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 ); 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: 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; } // USBD_COMMON_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 uint8_t USBD_COMMON_EP0_TxSent (USBD_HandleTypeDef *pdev, sUSBTransfer_t * tx, size_t * pnBytesRollback ) { USBD_EndpointTypeDef * pep = &pdev->ep_in[0]; 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: 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; } // USBD_COMMON_Setup_DataProcess() // Handler for specific vendor or class requests // @pdev, @pUserIface, @req and @transf MUST BE NON-NULL! // Do not call if @req->wLength is zero uint8_t USBD_COMMON_Setup_DataProcess( USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req, sUSBTransfer_t * transf ) { uint8_t ret = USBD_OK; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if (req->wLength > 0) { if (req->bmRequest & 0x80) { // DataIN stage preparing if( usb_validate_transfer( transf ) ) // TX-transfer { // First packet in the stage: reset the transfer usb_reset_transfer( transf ); // Single-transfer mode only: // Whole transfer is limited by the @ep0_tx_transfer size size_t nBytesProcessed = pUserIface->fUsbCtlEpTx( (const tUSBSetupPacket_t*)req, transf, 0, req->wLength ); // User can perform move semantic on @ep0_tx_transfer transfer // This is an error condition, let's check it: if( usb_validate_transfer( transf ) ) { 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: size_t nBytesCount = usb_count_transfer( transf ); if( nBytesProcessed > 0 ) { // Check the transfer for minimal bytes limit: if( min_limit < nBytesCount ) { // truncate the transfer in case if the transfer size is // greater than requested length. usb_truncate_transfer( transf, req->wLength ); USBD_CtlSendData (pdev, (const uint8_t *)transf->pReadData, req->wLength ); } else { USBD_CtlSendData (pdev, (const uint8_t *)transf->pReadData, nBytesCount ); //ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } else { ret = USBD_FAIL; } } else { // DataOUT stage preparing if( usb_validate_transfer( transf ) ) // RX-transfer { // Reset the RX-transfer usb_reset_transfer( transf ); // Begin receiving first packet USBD_CtlPrepareRx (pdev, (uint8_t *)transf->pWriteData, req->wLength ); } else { ret = USBD_FAIL; } } } else { ret = USBD_FAIL; // must be processed by caller } return ret; } #if DATAIN_INITIATESEND_REQUIRED // USBD_COMMON_DataIn_BeginSend // Initiates the first packet sending using virtual DataIN event // @bTxAlreadyPrepared - if the TX-transfer is already prepared to transmission, do not reset transfer, do not call TX-handler // // Note: e.g., is called from @USBD_USBTMC_DataIn_BeginSend // uint8_t USBD_COMMON_DataIn_BeginSend( USBD_HandleTypeDef *pdev, uint8_t epnum, sUSBTransfer_t * transf, bool bTxAlreadyPrepared ) { USBD_EndpointTypeDef * pep = &pdev->ep_in[ epnum & 0x7F ]; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); if( !bTxAlreadyPrepared ) { if( NULL == pUserIface->fDataTxHandler ) { // error: no user handler specified return USBD_FAIL; } // restore transfer usb_reset_transfer( transf ); #warning SCPI: note, that setting 'short-packet' indicator is useless here // forward set short packet indicator: // (just to be compatible to @USBD_COMMON_DataInProcess function) usb_transfer_set_shortpacket( transf ); // call user handler: generate virtual DataIN event if( ! pUserIface->fDataTxHandler( epnum, transf ) ) { // error: user handler error return USBD_FAIL; } } uint16_t bytes_to_send = usb_count_transfer( transf ); if( bytes_to_send > pep->maxpacket ) { bytes_to_send = pep->maxpacket; } if( bytes_to_send == pep->maxpacket ) { // this is the last packet with size equal EP size: the next packet must be zero packet usb_transfer_set_shortpacket( transf ); } // initiate the data sending // Note: @total_length and @rem_length will be modified: @bytes_to_send will be stored to these fields. if( USBD_OK != USBD_EpSendData( pdev, (uint8_t*)transf->pReadData, bytes_to_send, epnum ) ) { #if DEBUG_USB gDebugUSBEPTxLastPacket = bytes_to_send; #endif // error: can not send data return USBD_FAIL; } // perform virtual reading due to the USB Low Level have sent the data from the transfer usb_transfer_virtual_read( transf, bytes_to_send ); #if DEBUG_USB > 0 // SCPI debug gDebugUSBEPTxBytes += bytes_to_send; #endif return USBD_OK; } #endif // USBD_USBTMC_DataIn_ZeroSend // Queue zero packet in EP IN // uint8_t USBD_COMMON_DataIn_ZeroSend( USBD_HandleTypeDef *pdev, uint8_t epnum ) { USBD_EndpointTypeDef * pep = &pdev->ep_in[ epnum & 0x7F ]; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); uint8_t dummy = 0; // initiate the data sending // Note: @total_length and @rem_length will be modified: @bytes_to_send will be stored to these fields. if( USBD_OK != USBD_EpSendData( pdev, &dummy, 0, epnum ) ) { // error: can not send data return USBD_FAIL; } (void)pep, (void)pUserIface; return USBD_OK; } // USBD_COMMON_DataInProcess // Process DataIN events for Bulk and Interrupt endpoints // // Note: e.g., is called from @USBD_USBTMC_DataIn() // uint8_t USBD_COMMON_DataInProcess( USBD_HandleTypeDef *pdev, uint8_t epnum, sUSBTransfer_t * transf ) { #if DEBUG_USBTMC_REQUESTS > 0 void debug_log( uint8_t value, uint16_t value2 ); #define DEBUG_LOG_STATE_DATAIN 0x04 debug_log( DEBUG_LOG_STATE_DATAIN, usb_count_transfer( transf ) ); #endif USBD_EndpointTypeDef * pep = &pdev->ep_in[ epnum & 0x7F ]; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); #if !DATAIN_INITIATESEND_REQUIRED // Initiating the first packet sending is disabled, // the flow is being controlled by IRQ only, // so the fDataTxHandler is required. if( NULL == pUserIface->fDataTxHandler ) { return USBD_FAIL; } #endif // Note: @total_length and @rem_length had been modified: these fields stores the length of the FIRST packet sent (USBD_EpSendData) // Check if is there enough data for next packet if( usb_count_transfer( transf ) < pep->maxpacket ) { // No, the transfer does not have enough data for the full next packet // Check if the transfer have enough free space to store next packet? if( usb_space_transfer( transf ) < pep->maxpacket ) { // No, the transfer must be compressed usb_transfer_compress( transf ); } // Call user handler: // User shall reset Short-Packet indicator using @usb_transfer_modify_flags // ... routine to avoid sending short packet in case no data queued into // ... the transfer after successful call. if( ! pUserIface->fDataTxHandler( epnum, transf ) ) { // restore transfer usb_reset_transfer( transf ); // error return USBD_FAIL; } } // retrieve amount of bytes to send uint16_t bytes_to_send = usb_count_transfer( transf ); if( bytes_to_send > pep->maxpacket ) { // limit the packet size to EP buffer size bytes_to_send = pep->maxpacket; // this is the last packet with size equal EP size: the next packet must be zero packet usb_transfer_set_shortpacket( transf ); #if DEBUG_USB > 0 gDebugUSBEPTxLastPacket = bytes_to_send; #endif } else { // detect if a zero packet must be send if( bytes_to_send == pep->maxpacket ) { // this is the last packet with size equal EP size: the next packet must be zero packet usb_transfer_set_shortpacket( transf ); } else if( bytes_to_send > 0 ) // bytes_to_send < pep->maxpacket { usb_transfer_clear_shortpacket( transf ); } else // Check if there is no data to send: if( 0 == bytes_to_send ) { // probably, the short packet shall be send, check it: if( ! usb_transfer_check_shortpacket( transf ) ) { // no, there no Short Packet to be send // Thus, there no more data, exiting... return USBD_OK; } // yes, the short packet will be send now, // but it is requied to clear the indicator: usb_transfer_clear_shortpacket( transf ); } } // send next data part: if( USBD_OK == USBD_EpContinueSendData( pdev, (uint8_t*)transf->pReadData, bytes_to_send, epnum ) ) { #if DEBUG_USB > 0 // SCPI debug gDebugUSBEPTxBytes += bytes_to_send; #endif #if DEBUG_USB gDebugUSBEPTxLastPacket = bytes_to_send; #endif // perform virtual reading due to the USB Low Level have sent the data from the transfer usb_transfer_virtual_read( transf, bytes_to_send ); return USBD_OK; } return USBD_FAIL; } // USBD_COMMON_DataOutProcess // Process DataOUT events for Bulk and Interrupt endpoints uint8_t USBD_COMMON_DataOutProcess( USBD_HandleTypeDef *pdev, uint8_t epnum, sUSBTransfer_t * transf, bool ShortPacketReceived ) { USBD_EndpointTypeDef * pep = &pdev->ep_out[ epnum & 0x7F ]; USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); // Retrieve amount of received bytes: uint32_t nBytesReceived = pep->total_length - pep->rem_length; if( ShortPacketReceived ) usb_transfer_set_shortpacket( transf ); // Data-out: short packet received else usb_transfer_clear_shortpacket( transf ); // Data-out: Not-short packet received if( NULL != pUserIface->fDataRxHandler ) { // If zero packet received: if( 0 == nBytesReceived ) { // restore transfer usb_reset_transfer( transf ); if( pUserIface->fDataRxHandler( epnum, transf ) ) { return USBD_OK; } goto DataOut_failed; } // Perform virtual writing to the transfer: the transfer had been modified in unusual way // check if the transfer able to receive such packet with length = @nBytesReceived if( ! usb_transfer_virtual_write( transf, nBytesReceived ) ) { // No, error goto DataOut_failed; } #if 0 // Check if transfer is not empty while( 0 < usb_count_transfer( transf ) ) { #error User-defined handler @fDataRxHandler can defer the data processing til enough data is received for correct processing // Call user handler if( ! pUserIface->fDataRxHandler( epnum, transf ) ) { // error: user handler call failed break; } } #else // Call user handler if( ! pUserIface->fDataRxHandler( epnum, transf ) ) { goto DataOut_failed; } #endif #if 1 // If a short packet received - it is a last transfer transaction if( ShortPacketReceived ) { // this case no bytes can be deferred in the transfer due to no user handler // call is expected for this transfer after this transaction. if( 0 < usb_count_transfer( transf ) ) { // if the transfer is not empty - this is error condition goto DataOut_failed; } // it is a last transfer transaction - prepare transfer for the first transaction (next transfer) usb_reset_transfer( transf ); } #endif // retrieve amount of free bytes in the transfer uint16_t free_space = usb_space_transfer( transf ); // Check if the transfer have enough free space for next transaction? if( usb_space_transfer( transf ) < pep->maxpacket ) { // No, not enough free space goto DataOut_failed; } else { // yes, let's receive // Optimize the transfer buffer and compress the data usb_transfer_compress( transf ); // To prepare the receiving next transfer, the transfer size must be a multiply of max_packet of endpoint free_space -= free_space % pep->maxpacket; } // prepare the transfer for the next OUT transaction return USBD_EpContinueRx( pdev, (uint8_t*)transf->pWriteData, free_space, epnum ); } DataOut_failed: // restore transfer usb_reset_transfer( transf ); return USBD_FAIL; } // USBD_COMMON_DataOutBegin // Prepare the EP for next OUT (receive) transaction uint8_t USBD_COMMON_DataOutBegin( USBD_HandleTypeDef *pdev, uint8_t epnum, sUSBTransfer_t * transf ) { USBD_COMMON_ItfTypeDef * pUserIface = ((USBD_COMMON_ItfTypeDef *)pdev->pUserData); // if( NULL != pUserIface->fDataRxHandler ) // { // // If the transfer contains a data // // Notify user about the data stored in the transfer // while( (0 < usb_count_transfer( transf )) && pUserIface->fDataRxHandler( epnum, transf ) ) // { // // Optimize the transfer buffer and compress the data // usb_transfer_compress( transf ); // } // // // Notify user about starting new transfer: transf=NULL // pUserIface->fDataRxHandler( epnum, NULL ); // } // recover transfer usb_reset_transfer( transf ); // retrieve amount of free bytes in the transfer uint16_t free_space = usb_space_transfer( transf ); USBD_EndpointTypeDef * pep = &pdev->ep_out[ epnum & 0x7F ]; // the transfer must be able to contain at least one full packet if( free_space >= pep->maxpacket ) { // To prepare the receiving next transfer, the transfer size must be a multiply of max_packet of endpoint free_space -= free_space % pep->maxpacket; // prepare the transfer for the next OUT transaction return USBD_EpPrepareRx( pdev, (uint8_t *)transf->pWriteData, free_space, epnum ); } (void)pUserIface; return USBD_FAIL; } /* // USBD_COMMON_DataOutBegin // Prepare the EP for next OUT (receive) transaction uint8_t USBD_COMMON_DataOutBegin( USBD_HandleTypeDef *pdev, uint8_t epnum, sUSBTransfer_t * transf ) { // Optimize the transfer buffer and compress the data usb_transfer_compress( transf ); // retrieve amount of free bytes in the transfer uint16_t free_space = usb_space_transfer( transf ); // prepare the transfer for the next OUT transaction return USBD_EpPrepareRx( pdev, (uint8_t *)transf->pWriteData, free_space, epnum ); } */ /** * @brief USBD_COMMON_RegisterInterface * @param pdev: device instance * @param fops: CD Interface callback * @retval status */ #if USBD_CONSTSUBCLASS_IFACE uint8_t USBD_COMMON_RegisterInterface (USBD_HandleTypeDef *pdev, const USBD_COMMON_ItfTypeDef *fops) #else uint8_t USBD_COMMON_RegisterInterface (USBD_HandleTypeDef *pdev, USBD_COMMON_ItfTypeDef *fops) #endif { uint8_t ret = USBD_FAIL; if(fops != NULL) { pdev->pUserData= fops; ret = USBD_OK; } return ret; }