/* * Copyright (c) 2015, Freescale Semiconductor, Inc. * Copyright 2016 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include "usb_device_config.h" #include "usb.h" #include "usb_device.h" #include "usb_device_phdc.h" #include "usb_device_ch9.h" #include "usb_device_descriptor.h" #include "ieee11073_types.h" #include "ieee11073_timer.h" #include "ieee11073_agent.h" #include "usb_shim_agent.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ #if META_DATA_MESSAGE_PREAMBLE_IMPLEMENTED /*! @brief the string used to give preamble verifiability */ static char metaDataMsgPreambleSignature[16U] = "PhdcQoSSignature"; #endif extern usb_shim_agent_struct_t g_shimAgent; /******************************************************************************* * Code ******************************************************************************/ /*! * @brief USB shim agent receive data callback function. * * This function implements a queue to support receiving the PHDC data with length is more than * maximum of endpoint packet size. Then notify to upper layer when receiving is all completed. * * @param handle The device handle. * @param param The param of receive callback function. * * @return A USB error code or kStatus_USB_Success. */ usb_status_t USB_ShimAgentRecvComplete(void *handle, void *param) { usb_device_endpoint_callback_message_struct_t *message = (usb_device_endpoint_callback_message_struct_t *)param; if ((!message->length) || (USB_CANCELLED_TRANSFER_LENGTH == message->length)) { /* Prepare for the next receiving */ USB_DeviceRecvRequest((void *)g_shimAgent.deviceHandle, g_shimAgent.bulkOutData.epNumber, g_shimAgent.recvDataBuffer, g_shimAgent.bulkOutData.epMaxPacketSize); return kStatus_USB_Success; } #if META_DATA_MESSAGE_PREAMBLE_IMPLEMENTED if (1U == g_shimAgent.isMetaDataMessagePreambleEnabled) { /* The meta-data message preamble feature is enabled, then all data transfers or sets of data transfers shall be preceded by a meta-data message preamble transfer. The numberTransferBulkOut is initialized as zero for receiving this message preamble data, then it is updated to the value of bNumTransfers field of message preamble data */ if (g_shimAgent.numberTransferBulkOut) { /* When numberTransferBulkOut reduces to 0, a new meta-data message preamble shall be transferred */ g_shimAgent.numberTransferBulkOut--; AGENT_Callback(handle, SHIM_AGENT_EVENT_RECV_OPAQUE_DATA, (uint8_t *)(message->buffer), message->length); return kStatus_USB_Success; } else { uint8_t preambleSignatureChecking = 1U; /* The received packet is meta-data message preamble */ usb_shim_metadata_preamble_t *metaDataMsgPreamble = (usb_shim_metadata_preamble_t *)message->buffer; /* Meta-data message preamble signature checking */ for (uint8_t i = 0U; i < METADATA_PREAMBLE_SIGNATURE; i++) { if ((metaDataMsgPreamble->aSignature[i]) != metaDataMsgPreambleSignature[i]) { preambleSignatureChecking = 0U; break; } } if (preambleSignatureChecking) { /* Checks if the meta-data message preamble contains an invalid bmLatencyReliability value or bNumTransfers value */ if ((!(metaDataMsgPreamble->bNumberTransfers)) || /* bNumTransfers shall never equal zero */ (metaDataMsgPreamble->bQosEncodingVersion != 0x01U) || /* Encoding version should be 0x01 */ ((metaDataMsgPreamble->bmLatencyReliability != 0x02U) && /* Medium.Good latency, reliability bin */ (metaDataMsgPreamble->bmLatencyReliability != 0x04U) && /* Medium.Better latency, reliability bin */ (metaDataMsgPreamble->bmLatencyReliability != 0x08U) && /* Medium.Best latency, reliability bin */ (metaDataMsgPreamble->bmLatencyReliability != 0x10U) && /* High.Best latency, reliability bin */ (metaDataMsgPreamble->bmLatencyReliability != 0x20U) /* VeryHigh.Best latency, reliability bin */)) { /* The device shall stall subsequent transaction to the receiving endpoint */ return kStatus_USB_InvalidRequest; } else { /* The meta-data message preamble data is correct, update the phdc status and numberTransferBulkOut * value */ g_shimAgent.numberTransferBulkOut = metaDataMsgPreamble->bNumberTransfers; AGENT_Callback(handle, SHIM_AGENT_EVENT_RECV_MESSAGE_PREAMBLE, (uint8_t *)(message->buffer), message->length); return kStatus_USB_Success; } } else { /* The device shall stall subsequent transaction to the receiving endpoint */ return kStatus_USB_InvalidRequest; } } } #endif if (NULL == g_shimAgent.bulkOutData.recvData.buffer) { /* Save the length of the first received data block */ g_shimAgent.bulkOutData.transferCount = message->length; g_shimAgent.bulkOutData.recvData.transferSize = (uint32_t)(USB_SHORT_FROM_BIG_ENDIAN(*((uint16_t *)message->buffer + 1U)) + 4U /* APDU header */); g_shimAgent.bulkOutData.recvData.buffer = &g_shimAgent.recvDataBuffer[0]; /* Save the received data */ memcpy(g_shimAgent.bulkOutData.recvData.buffer, message->buffer, message->length); } else { /* update the transferred data length */ g_shimAgent.bulkOutData.transferCount += message->length; } if (g_shimAgent.bulkOutData.transferCount == g_shimAgent.bulkOutData.recvData.transferSize) { /* Receive all the data */ AGENT_Callback(handle, SHIM_AGENT_EVENT_RECV_COMPLETE, g_shimAgent.bulkOutData.recvData.buffer, g_shimAgent.bulkOutData.recvData.transferSize); g_shimAgent.bulkOutData.transferCount = 0; g_shimAgent.bulkOutData.recvData.transferSize = 0; g_shimAgent.bulkOutData.recvData.buffer = NULL; /* Prepare for the next receiving */ USB_DeviceRecvRequest((void *)handle, g_shimAgent.bulkOutData.epNumber, g_shimAgent.recvDataBuffer, g_shimAgent.bulkOutData.epMaxPacketSize); } else { /* The data is still pending for receiving */ #if (defined(USB_DEVICE_CONFIG_RETURN_VALUE_CHECK) && (USB_DEVICE_CONFIG_RETURN_VALUE_CHECK > 0U)) if (kStatus_USB_Success != USB_DeviceRecvRequest((void *)handle, g_shimAgent.bulkOutData.epNumber, (uint8_t *)(g_shimAgent.bulkOutData.recvData.buffer + g_shimAgent.bulkOutData.transferCount), g_shimAgent.bulkOutData.epMaxPacketSize)) { return kStatus_USB_Error; } #else (void)USB_DeviceRecvRequest( (void *)handle, g_shimAgent.bulkOutData.epNumber, (uint8_t *)(g_shimAgent.bulkOutData.recvData.buffer + g_shimAgent.bulkOutData.transferCount), g_shimAgent.bulkOutData.epMaxPacketSize); #endif } return kStatus_USB_Success; } /*! * @brief USB shim agent send data callback function. * * This function is the callback function of USB shim agent send function, it is implemented * to continue sending the data in sending queue if the queue is not empty. * * @param handle he device handle. * @param param The param of receive callback function. * * @return A USB error code or kStatus_USB_Success. */ usb_status_t USB_ShimAgentSendComplete(void *handle, uint32_t event, void *param) { usb_device_endpoint_callback_message_struct_t *message = (usb_device_endpoint_callback_message_struct_t *)param; usb_shim_tx_data_struct_t *sentData = NULL; if (USB_PHDC_EVENT_INTERRUPT_IN_SEND_COMPLETE == event) { sentData = &g_shimAgent.interruptInData; } else { sentData = &g_shimAgent.bulkInData; } g_shimAgent.endpointsHaveData &= (uint16_t)(~(uint16_t)(1U << sentData->epNumber)); /* notify the application of the send complete */ AGENT_Callback(handle, SHIM_AGENT_EVENT_SENT_COMPLETE, (uint8_t *)message->buffer, message->length); /* de-queue the queue */ sentData->buyer += 1U; if (sentData->buyer != sentData->seller) { /* Set bit map for the corresponding endpoint*/ g_shimAgent.endpointsHaveData |= (uint16_t)(1U << sentData->epNumber); USB_DeviceSendRequest((void *)handle, sentData->epNumber, sentData->sendData[sentData->buyer % SHIM_AGENT_MAX_QUEUE_NUMBER].buffer, sentData->sendData[sentData->buyer % SHIM_AGENT_MAX_QUEUE_NUMBER].transferSize); } return kStatus_USB_Success; } /*! * @brief USB shim agent send data function. * * This function implements a queue to support sending multiple transfer request. * * @param handle The device handle. * @param metaData The flag to check the data to send is meta data or not. * @param numberTransfer The number of transfer following the meta data message preamble * when sending data is meta data. * @param qos The current QoS of the transfer. * @param appBuffer The data buffer. * @param size The length of the transfer. * * @return A USB error code or kStatus_USB_Success. */ usb_status_t USB_ShimAgentSendData(void *handle, uint8_t qos, uint8_t *appBuffer, uint32_t size) { usb_status_t status = kStatus_USB_Success; usb_shim_tx_data_struct_t *dataToSend = NULL; if (qos != 0x01U /* low latency/good reliability */) { #if META_DATA_MESSAGE_PREAMBLE_IMPLEMENTED if (1U == g_shimAgent.isMetaDataMessagePreambleEnabled) { /* The meta-data message preamble feature is enabled, then all data transfers or sets of data transfers shall be preceded by a meta-data message preamble transfer. The numberTransferBulkIn is initialized as zero for sending this message preamble data, then it is updated to the value of bNumTransfers field of message preamble data */ if (g_shimAgent.numberTransferBulkOut) { /* When numberTransferBulkIn reduces to 0, a new meta-data message preamble shall be transferred */ g_shimAgent.numberTransferBulkIn--; } { uint8_t latencyReliability = ((usb_shim_metadata_preamble_t *)appBuffer)->bmLatencyReliability; /* Latency reliability validity checking */ if ((latencyReliability != 0x02U) && /* Medium.Good latency, reliability bin */ (latencyReliability != 0x04U) && /* Medium.Better latency, reliability bin */ (latencyReliability != 0x08U) && /* Medium.Best latency, reliability bin */ (latencyReliability != 0x10U) && /* High.Best latency, reliability bin */ (latencyReliability != 0x20U) /* VeryHigh.Best latency, reliability bin */) { status = kStatus_USB_InvalidRequest; usb_echo("USB_ShimAgentSendData: Error invalid LatencyReliability"); } /* LatencyReliablity checking */ if (0U == (USB_PHDC_WEIGHT_SCALE_BULK_IN_QOS & ~latencyReliability)) { status = kStatus_USB_Error; usb_echo( "USB_ShimAgentSendData: Error the latency reliability is not supported by Bulk IN endpoint"); } if (0U == ((usb_shim_metadata_preamble_t *)appBuffer)->bNumberTransfers) { status = kStatus_USB_Error; usb_echo("USB_ShimAgentSendData: Error the numTransfer should never zero"); } /* Update the number of bulk in transfer */ g_shimAgent.numberTransferBulkIn = ((usb_shim_metadata_preamble_t *)appBuffer)->bNumberTransfers; } } #endif /* Initialize the data to send */ dataToSend = &g_shimAgent.bulkInData; } else { /* Initialize the data to send */ dataToSend = &g_shimAgent.interruptInData; } if (kStatus_USB_Success == status) { /* Set bit map for the corresponding endpoint*/ g_shimAgent.endpointsHaveData |= (uint16_t)(1U << dataToSend->epNumber); /* Add data to send to sending queue */ if ((uint8_t)(dataToSend->seller - dataToSend->buyer) < (uint8_t)(SHIM_AGENT_MAX_QUEUE_NUMBER)) { dataToSend->sendData[dataToSend->seller % SHIM_AGENT_MAX_QUEUE_NUMBER].transferSize = size; dataToSend->sendData[dataToSend->seller % SHIM_AGENT_MAX_QUEUE_NUMBER].buffer = appBuffer; /* increase queue number by 1 */ dataToSend->seller += 1U; /* send the first entry of queue */ if (1U == (uint32_t)(dataToSend->seller - dataToSend->buyer)) { status = USB_DeviceSendRequest((void *)handle, dataToSend->epNumber, appBuffer, size); } } else { /* sending queue is full */ status = kStatus_USB_Busy; } } return status; }