/* Function: USBD_Stall Causes the given endpoint to acknowledge the next packet it receives with a STALL handshake. Parameters: eptnum - Endpoint number. Returns: USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED. */ unsigned char USBD_Stall(unsigned char eptnum) { Endpoint *endpoint = &(endpoints[eptnum]); // Check that endpoint is in Idle state if (endpoint->state != UDP_ENDPOINT_IDLE) { TRACE_WARNING( "USBD_Stall: Endpoint%d locked\n\r", eptnum); return USBD_STATUS_LOCKED; } TRACE_INFO( "iStall%d ", eptnum); SET_CSR(eptnum, AT91C_UDP_FORCESTALL); return USBD_STATUS_SUCCESS; }
/* Function: USBD_ConfigureEndpoint Configures an endpoint according to its Endpoint Descriptor. Parameters: descriptor - Pointer to an Endpoint descriptor. */ void USBD_ConfigureEndpoint(const USBEndpointDescriptor *descriptor) { Endpoint *endpoint; unsigned char eptnum; unsigned char type; unsigned char direction; // NULL descriptor -> Control endpoint 0 if (descriptor == 0) { eptnum = 0; endpoint = &(endpoints[eptnum]); type = USBEndpointDescriptor_CONTROL; direction = 0; endpoint->size = BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0); } else { eptnum = USBEndpointDescriptor_GetNumber(descriptor); endpoint = &(endpoints[eptnum]); type = USBEndpointDescriptor_GetType(descriptor); direction = USBEndpointDescriptor_GetDirection(descriptor); endpoint->size = USBEndpointDescriptor_GetMaxPacketSize(descriptor); } // Abort the current transfer is the endpoint was configured and in // Write or Read state if ((endpoint->state == UDP_ENDPOINT_RECEIVING) || (endpoint->state == UDP_ENDPOINT_SENDING)) { UDP_EndOfTransfer(eptnum, USBD_STATUS_RESET); } endpoint->state = UDP_ENDPOINT_IDLE; // Reset Endpoint Fifos AT91C_BASE_UDP->UDP_RSTEP |= (1 << eptnum); AT91C_BASE_UDP->UDP_RSTEP &= ~(1 << eptnum); // Configure endpoint SET_CSR(eptnum, AT91C_UDP_EPEDS | (type << 8) | (direction << 10)); if (type == USBEndpointDescriptor_CONTROL) { AT91C_BASE_UDP->UDP_IER = (1 << eptnum); } TRACE_INFO( "\r\n# CfgEpt%d ", eptnum); }
/* Function: USBD_Write Sends data through a USB endpoint. Sets up the transfer descriptor, writes one or two data payloads (depending on the number of FIFO bank for the endpoint) and then starts the actual transfer. The operation is complete when all the data has been sent. *If the size of the buffer is greater than the size of the endpoint (or twice the size if the endpoint has two FIFO banks), then the buffer must be kept allocated until the transfer is finished*. This means that it is not possible to declare it on the stack (i.e. as a local variable of a function which returns after starting a transfer). Parameters: eptnum - Endpoint number. data - Pointer to a buffer with the data to send. size - Size of the data buffer. callback - Optional callback function to invoke when the transfer is complete. argument - Optional argument to the callback function. Returns: USBD_STATUS_SUCCESS if the transfer has been started; otherwise, the corresponding error status code. */ char USBD_Write(unsigned char eptnum, const void *data, unsigned int size, TransferCallback callback, void *argument) { Endpoint *endpoint = &(endpoints[eptnum]); Transfer *transfer = &(endpoint->transfer); // Check that the endpoint is in Idle state if (endpoint->state != UDP_ENDPOINT_IDLE) { return USBD_STATUS_LOCKED; } //TRACE_INFO( "Write%d(%u) ", eptnum, size); // Setup the transfer descriptor transfer->data = (void *) data; transfer->remaining = size; transfer->buffered = 0; transfer->transferred = 0; transfer->callback = callback; transfer->argument = argument; // Send the first packet endpoint->state = UDP_ENDPOINT_SENDING; while((AT91C_BASE_UDP->UDP_CSR[eptnum]&AT91C_UDP_TXPKTRDY)==AT91C_UDP_TXPKTRDY); UDP_WritePayload(eptnum); SET_CSR(eptnum, AT91C_UDP_TXPKTRDY); // If double buffering is enabled and there is data remaining, // prepare another packet if ((BOARD_USB_ENDPOINTS_BANKS(eptnum) > 1) && (transfer->remaining > 0)) { UDP_WritePayload(eptnum); } // Enable interrupt on endpoint AT91C_BASE_UDP->UDP_IER = 1 << eptnum; return USBD_STATUS_SUCCESS; }
/* Function: USBD_Halt Sets the HALT feature on the given endpoint (if not already in this state). Parameters: eptnum - Endpoint number. */ void USBD_Halt(unsigned char eptnum) { Endpoint *endpoint = &(endpoints[eptnum]); // Check that endpoint is enabled and not already in Halt state if ((endpoint->state != UDP_ENDPOINT_DISABLED) && (endpoint->state != UDP_ENDPOINT_HALTED)) { TRACE_INFO( "Halt%d ", eptnum); // Abort the current transfer if necessary UDP_EndOfTransfer(eptnum, USBD_STATUS_ABORTED); // Put endpoint into Halt state SET_CSR(eptnum, AT91C_UDP_FORCESTALL); endpoint->state = UDP_ENDPOINT_HALTED; // Enable the endpoint interrupt AT91C_BASE_UDP->UDP_IER = 1 << eptnum; } }
//----------------------------------------------------------------------------- void usb_svc_halt_hook(USB_DRV_INFO drv_info, HANDLE hnd) { USB_DRIVER_DATA* drv_data = drv_info->drv_data; Udp* pUDP = drv_info->hw_base; unsigned int eptnum = hnd->src.as_int; Endpoint *endpoint = &drv_data->endpoints[eptnum]; TRACE_USB(" Halt%d", eptnum); // Check that endpoint is enabled and not already in Halt state if (endpoint->state >= ENDPOINT_STATE_IDLE) { // Abort the current transfer if necessary usb_drv_end_transfers(endpoint, USBD_STATUS_ABORTED); // Put endpoint into Halt state SET_CSR(&pUDP->UDP_CSR[eptnum], UDP_CSR_FORCESTALL); endpoint->state = ENDPOINT_STATE_HALTED; // Enable the endpoint interrupt pUDP->UDP_IER |= 1 << eptnum; } }
/* Function: UDP_EndpointHandler Endpoint interrupt handler. Manages IN, OUT & SETUP transaction, as well as the STALL condition. Parameters: eptnum - Number of the endpoint to handle interrupt for. */ static void UDP_EndpointHandler(unsigned char eptnum) { Endpoint *endpoint = &(endpoints[eptnum]); Transfer *transfer = &(endpoint->transfer); unsigned int status = AT91C_BASE_UDP->UDP_CSR[eptnum]; //TRACE_INFO( "Ept%d ", eptnum); //TRACE_INFO( "["); // Handle interrupts // IN packet sent if ((status & AT91C_UDP_TXCOMP) != 0) { //TRACE_INFO( "Wr "); // Check that endpoint was in Sending state if (endpoint->state == UDP_ENDPOINT_SENDING) { // End of transfer ? if (UDP_IsTransferFinished(eptnum)) { // TRACE_INFO( "%d ", transfer->buffered); transfer->transferred += transfer->buffered; transfer->buffered = 0; // Disable interrupt if this is not a control endpoint if ((status & AT91C_UDP_EPTYPE) != AT91C_UDP_EPTYPE_CTRL) { AT91C_BASE_UDP->UDP_IDR = 1 << eptnum; } UDP_EndOfTransfer(eptnum, USBD_STATUS_SUCCESS); CLEAR_CSR(eptnum, AT91C_UDP_TXCOMP); } else { // Transfer remaining data //TRACE_INFO( "PQ:%d ", endpoint->size); transfer->transferred += endpoint->size; transfer->buffered -= endpoint->size; // Send next packet if (BOARD_USB_ENDPOINTS_BANKS(eptnum) == 1) { // No double buffering UDP_WritePayload(eptnum); SET_CSR(eptnum, AT91C_UDP_TXPKTRDY); CLEAR_CSR(eptnum, AT91C_UDP_TXCOMP); } else { // Double buffering SET_CSR(eptnum, AT91C_UDP_TXPKTRDY); CLEAR_CSR(eptnum, AT91C_UDP_TXCOMP); UDP_WritePayload(eptnum); } } } else { // Acknowledge interrupt CLEAR_CSR(eptnum, AT91C_UDP_TXCOMP); } } // OUT packet received if ((status & UDP_RXDATA) != 0) { //TRACE_INFO( "Rd "); // Check that the endpoint is in Receiving state if (endpoint->state != UDP_ENDPOINT_RECEIVING) { // Check if an ACK has been received on a Control endpoint if (((status & AT91C_UDP_EPTYPE) == AT91C_UDP_EPTYPE_CTRL) && ((status & AT91C_UDP_RXBYTECNT) == 0)) { // Acknowledge the data and finish the current transfer ///TRACE_INFO( "[%d]Ack ",eptnum); UDP_ClearRxFlag(eptnum); UDP_EndOfTransfer(eptnum, USBD_STATUS_SUCCESS); } // Check if the data has been STALLed else if ((status & AT91C_UDP_FORCESTALL) != 0) { // Discard STALLed data TRACE_INFO( "[%d]Disc ",eptnum); UDP_ClearRxFlag(eptnum); } // NAK the data else { TRACE_INFO( "[%d]Nak ",eptnum); AT91C_BASE_UDP->UDP_IDR = 1 << eptnum; } } // Endpoint is in Read state else { // Retrieve data and store it into the current transfer buffer unsigned short size = (unsigned short) (status >> 16); //TRACE_INFO( "-[%d]-", size); //TRACE_INFO( "Rec "); UDP_ReadPayload(eptnum, size); UDP_ClearRxFlag(eptnum); // Check if the transfer is finished if ((transfer->remaining == 0) || (size < endpoint->size)) { // Disable interrupt if this is not a control endpoint if ((status & AT91C_UDP_EPTYPE) != AT91C_UDP_EPTYPE_CTRL) { AT91C_BASE_UDP->UDP_IDR = 1 << eptnum; } UDP_EndOfTransfer(eptnum, USBD_STATUS_SUCCESS); } } } // SETUP packet received if ((status & AT91C_UDP_RXSETUP) != 0) { //TRACE_INFO( "Stp "); // If a transfer was pending, complete it // Handles the case where during the status phase of a control write // transfer, the host receives the device ZLP and ack it, but the ack // is not received by the device if ((endpoint->state == UDP_ENDPOINT_RECEIVING) || (endpoint->state == UDP_ENDPOINT_SENDING)) { UDP_EndOfTransfer(eptnum, USBD_STATUS_SUCCESS); } USBGenericRequest request; UDP_ReadRequest(&request); // Set the DIR bit before clearing RXSETUP in Control IN sequence if (USBGenericRequest_GetDirection(&request) == USBGenericRequest_IN) { SET_CSR(eptnum, AT91C_UDP_DIR); } CLEAR_CSR(eptnum, AT91C_UDP_RXSETUP); // Forward the request to the upper layer USBDCallbacks_RequestReceived(&request);//------------------------------------ } // STALL sent if ((status & AT91C_UDP_STALLSENT) != 0) { TRACE_INFO( "STA "); // If the endpoint is not halted, clear the STALL condition CLEAR_CSR(eptnum, AT91C_UDP_STALLSENT); if (endpoint->state != UDP_ENDPOINT_HALTED) { CLEAR_CSR(eptnum, AT91C_UDP_FORCESTALL); } } //TRACE_INFO( "]"); }
/** * Sends data frames through a USB endpoint. Sets up the transfer descriptor * list, writes one or two data payloads (depending on the number of FIFO bank * for the endpoint) and then starts the actual transfer. The operation is * complete when all the data has been sent. * * *If the size of the frame is greater than the size of the endpoint * (or twice the size if the endpoint has two FIFO banks), then the buffer * must be kept allocated until the frame is finished*. This means that * it is not possible to declare it on the stack (i.e. as a local variable * of a function which returns after starting a transfer). * * \param bEndpoint Endpoint number. * \param pMbl Pointer to a frame (USBDTransferBuffer) list that describes * the buffer list to send. * \param wListSize Size of the frame list. * \param bCircList Circle the list. * \param wStartNdx For circled list only, the first buffer index to transfer. * \param fCallback Optional callback function to invoke when the transfer is * complete. * \param pArgument Optional argument to the callback function. * \return USBD_STATUS_SUCCESS if the transfer has been started; * otherwise, the corresponding error status code. * \see USBDTransferBuffer, MblTransferCallback, USBD_MblReuse */ uint8_t USBD_MblWrite( uint8_t bEndpoint, void *pMbl, uint16_t wListSize, uint8_t bCircList, uint16_t wStartNdx, MblTransferCallback fCallback, void *pArgument ) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); uint16_t i; /* EP0 is not suitable for Mbl */ if (bEndpoint == 0) { return USBD_STATUS_INVALID_PARAMETER; } /* Check that the endpoint is in Idle state */ if (pEndpoint->state != UDP_ENDPOINT_IDLE) { return USBD_STATUS_LOCKED; } pEndpoint->state = UDP_ENDPOINT_SENDINGM; TRACE_DEBUG_WP("WriteM%d(0x%x,%d) ", bEndpoint, pMbl, wListSize); /* Start from first if not circled list */ if (!bCircList) wStartNdx = 0; /* Setup the transfer descriptor */ pTransfer->pMbl = (USBDTransferBuffer*)pMbl; pTransfer->listSize = wListSize; pTransfer->fCallback = fCallback; pTransfer->pArgument = pArgument; pTransfer->currBuffer = wStartNdx; pTransfer->freedBuffer = 0; pTransfer->pLastLoaded = &(((USBDTransferBuffer*)pMbl)[wStartNdx]); pTransfer->circList = bCircList; pTransfer->allUsed = 0; /* Clear all buffer */ for (i = 0; i < wListSize; i ++) { pTransfer->pMbl[i].transferred = 0; pTransfer->pMbl[i].buffered = 0; pTransfer->pMbl[i].remaining = pTransfer->pMbl[i].size; } /* Send the first packet */ while((UDP->UDP_CSR[bEndpoint]&UDP_CSR_TXPKTRDY)==UDP_CSR_TXPKTRDY); UDP_MblWriteFifo(bEndpoint); SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY); /* If double buffering is enabled and there is data remaining, */ /* prepare another packet */ if ((CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) && (pTransfer->pMbl[pTransfer->currBuffer].remaining > 0)) { UDP_MblWriteFifo(bEndpoint); } /* Enable interrupt on endpoint */ UDP->UDP_IER = 1 << bEndpoint; return USBD_STATUS_SUCCESS; }