/* * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright 2016 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "usb_device_config.h" #include "usb.h" #include "usb_device.h" #include "usb_device_class.h" #if ((defined(USB_DEVICE_CONFIG_PRINTER)) && (USB_DEVICE_CONFIG_PRINTER > 0U)) #include "usb_device_printer.h" /******************************************************************************* * Definitions ******************************************************************************/ /******************************************************************************* * Prototypes ******************************************************************************/ /******************************************************************************* * Variables ******************************************************************************/ USB_GLOBAL USB_RAM_ADDRESS_ALIGNMENT(USB_DATA_ALIGN_SIZE) static usb_device_printer_struct_t s_PrinterHandle[USB_DEVICE_CONFIG_PRINTER]; /******************************************************************************* * Code ******************************************************************************/ static usb_status_t USB_DevicePrinterAllocateHandle(usb_device_printer_struct_t **printerHandle) { uint8_t index; for (index = 0; index < USB_DEVICE_CONFIG_PRINTER; ++index) { if (s_PrinterHandle[index].deviceHandle == NULL) { *printerHandle = &(s_PrinterHandle[index]); return kStatus_USB_Success; } } return kStatus_USB_Busy; } static usb_status_t USB_DevicePrinterFreeHandle(usb_device_printer_struct_t *printerHandle) { /* ensure that printerHandle is not NULL before calling this function */ printerHandle->deviceHandle = NULL; printerHandle->classConfig = NULL; printerHandle->alternate = 0xFFu; printerHandle->configuration = 0; printerHandle->interfaceNumber = 0; return kStatus_USB_Success; } /*! * @brief bulk IN endpoint callback function. * * This callback function is used to notify upper layer the transfer result of a transfer. * This callback pointer is passed when the bulk IN pipe initialized. * * @param handle The device handle. It equals the value returned from USB_DeviceInit. * @param message The result of the bulk IN pipe transfer. * @param callbackParam The parameter for this callback. It is same with * usb_device_endpoint_callback_struct_t::callbackParam. * In the class, the value is the printer class handle. * * @retval kStatus_USB_Success The transfer is successful. * @retval kStatus_USB_InvalidHandle The device handle not be found. */ static usb_status_t USB_DevicePrinterBulkInCallback(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message, void *callbackParam) { usb_device_printer_struct_t *printerHandle = (usb_device_printer_struct_t *)callbackParam; usb_status_t status = kStatus_USB_Error; if (callbackParam == NULL) { return kStatus_USB_InvalidHandle; } printerHandle->bulkInBusy = 0U; if ((NULL != printerHandle->classConfig) && (NULL != printerHandle->classConfig->classCallback)) { /* Notify the application data received by calling the printer class callback. ClassCallback is initialized in classInit of s_UsbDeviceClassInterfaceMap, it is from the second parameter of classInit */ status = printerHandle->classConfig->classCallback((class_handle_t)printerHandle, kUSB_DevicePrinterEventSendResponse, message); } return status; } /*! * @brief bulk OUT endpoint callback function. * * This callback function is used to notify upper layer the transfer result of a transfer. * This callback pointer is passed when the bulk OUT pipe initialized. * * @param handle The device handle. It equals the value returned from USB_DeviceInit. * @param message The result of the bulk OUT pipe transfer. * @param callbackParam The parameter for this callback. It is same with * usb_device_endpoint_callback_struct_t::callbackParam. * In the class, the value is the printer class handle. * * @retval kStatus_USB_Success The transfer is successful. * @retval kStatus_USB_InvalidHandle The device handle not be found. */ static usb_status_t USB_DevicePrinterBulkOutCallback(usb_device_handle handle, usb_device_endpoint_callback_message_struct_t *message, void *callbackParam) { usb_device_printer_struct_t *printerHandle = (usb_device_printer_struct_t *)callbackParam; usb_status_t status = kStatus_USB_Error; if (printerHandle == NULL) { return kStatus_USB_InvalidHandle; } printerHandle->bulkOutBusy = 0U; if ((NULL != printerHandle->classConfig) && (NULL != printerHandle->classConfig->classCallback)) { /* ClassCallback is initialized in classInit of s_UsbDeviceClassInterfaceMap, it is from the second parameter of classInit */ status = printerHandle->classConfig->classCallback((class_handle_t)printerHandle, kUSB_DevicePrinterEventRecvResponse, message); } return status; } /*! * @brief Initialize the endpoints of the printer class. * * This callback function is used to initialize the endpoints of the printer class. * * @param printerHandle The device printer class handle. It equals the value returned from * usb_device_class_config_struct_t::classHandle. * * @return A USB error code or kStatus_USB_Success. */ static usb_status_t USB_DevicePrinterEndpointsInit(usb_device_printer_struct_t *printerHandle) { /* ensure that printerHandle is not NULL before calling this function */ usb_device_interface_list_t *configInterfaceList; usb_device_interface_struct_t *interface = NULL; usb_status_t status = kStatus_USB_Error; uint8_t interfaceIndex; uint8_t index; /* return error when configuration is invalid (0 or more than the configuration number) */ if ((printerHandle->configuration == 0U) || (printerHandle->configuration > printerHandle->classConfig->classInfomation->configurations)) { return status; } configInterfaceList = &(printerHandle->classConfig->classInfomation->interfaceList[printerHandle->configuration - 1U]); for (interfaceIndex = 0; interfaceIndex < configInterfaceList->count; ++interfaceIndex) { if (USB_DEVICE_CONFIG_PRINTER_CLASS_CODE == configInterfaceList->interfaces[interfaceIndex].classCode) { /* index means the alternate interface's index here */ for (index = 0; index < configInterfaceList->interfaces[interfaceIndex].count; ++index) { if (configInterfaceList->interfaces[interfaceIndex].interface[index].alternateSetting == printerHandle->alternate) { interface = &(configInterfaceList->interfaces[interfaceIndex].interface[index]); break; } } printerHandle->interfaceNumber = configInterfaceList->interfaces[interfaceIndex].interfaceNumber; break; } } if (interface == NULL) { return status; } /* Keep new interface handle. */ printerHandle->interfaceHandle = interface; /* Initialize the endpoints of the new interface. */ /* index means the endpoint's index here */ for (index = 0; index < interface->endpointList.count; ++index) { usb_device_endpoint_init_struct_t epInitStruct; usb_device_endpoint_callback_struct_t epCallback; epInitStruct.zlt = 0U; epInitStruct.interval = interface->endpointList.endpoint[index].interval; epInitStruct.endpointAddress = interface->endpointList.endpoint[index].endpointAddress; epInitStruct.maxPacketSize = interface->endpointList.endpoint[index].maxPacketSize; epInitStruct.transferType = interface->endpointList.endpoint[index].transferType; if (((epInitStruct.endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) >> USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT) == USB_IN) { epCallback.callbackFn = USB_DevicePrinterBulkInCallback; printerHandle->bulkInPipeDataBuffer = (uint8_t *)USB_INVALID_TRANSFER_BUFFER; printerHandle->bulkInPipeStall = 0U; printerHandle->bulkInPipeDataLen = 0U; } else { epCallback.callbackFn = USB_DevicePrinterBulkOutCallback; printerHandle->bulkOutPipeDataBuffer = (uint8_t *)USB_INVALID_TRANSFER_BUFFER; printerHandle->bulkOutPipeStall = 0U; printerHandle->bulkOutPipeDataLen = 0U; } epCallback.callbackParam = printerHandle; status = USB_DeviceInitEndpoint(printerHandle->deviceHandle, &epInitStruct, &epCallback); } return status; } /*! * @brief De-initialize the endpoints of the printer class. * * This callback function is used to de-initialize the endpoints of the printer class. * * @param printerHandle The device printer class handle. It equals the value returned from * usb_device_class_config_struct_t::classHandle. * * @return A USB error code or kStatus_USB_Success. */ static usb_status_t USB_DevicePrinterEndpointsDeinit(usb_device_printer_struct_t *printerHandle) { /* ensure that printerHandle is not NULL before calling this function */ usb_status_t status = kStatus_USB_Error; uint8_t index; /* return directly when the interfaceHandle is NULL, it means USB_DevicePrinterEndpointsInit is not called */ if (printerHandle->interfaceHandle == NULL) { return kStatus_USB_Success; } for (index = 0; index < printerHandle->interfaceHandle->endpointList.count; ++index) { status = USB_DeviceDeinitEndpoint(printerHandle->deviceHandle, printerHandle->interfaceHandle->endpointList.endpoint[index].endpointAddress); } printerHandle->interfaceHandle = NULL; return status; } usb_status_t USB_DevicePrinterInit(uint8_t controllerId, usb_device_class_config_struct_t *config, class_handle_t *handle) { usb_device_handle deviceHandle; usb_device_printer_struct_t *printerHandle = NULL; usb_status_t status; OSA_SR_ALLOC(); /* get the controller's device handle */ status = USB_DeviceClassGetDeviceHandle(controllerId, &deviceHandle); if (status != kStatus_USB_Success) { return status; } if (deviceHandle == NULL) { return kStatus_USB_InvalidHandle; } /* Allocate a printer class handle. */ OSA_ENTER_CRITICAL(); status = USB_DevicePrinterAllocateHandle(&printerHandle); printerHandle->deviceHandle = deviceHandle; /* this printer instance is used */ OSA_EXIT_CRITICAL(); if (status != kStatus_USB_Success) { return status; } printerHandle->classConfig = config; printerHandle->alternate = 0xFFU; printerHandle->configuration = 0U; printerHandle->interfaceNumber = 0U; printerHandle->interfaceHandle = NULL; *handle = (class_handle_t)printerHandle; return status; } usb_status_t USB_DevicePrinterDeinit(class_handle_t handle) { usb_status_t status; usb_device_printer_struct_t *printerHandle = (usb_device_printer_struct_t *)handle; if (NULL == handle) { return kStatus_USB_InvalidHandle; } status = USB_DevicePrinterEndpointsDeinit(printerHandle); #if (defined(USB_DEVICE_CONFIG_RETURN_VALUE_CHECK) && (USB_DEVICE_CONFIG_RETURN_VALUE_CHECK > 0U)) if (kStatus_USB_Success != USB_DevicePrinterFreeHandle(printerHandle)) { return kStatus_USB_Error; } #else (void)USB_DevicePrinterFreeHandle(printerHandle); #endif return status; } usb_status_t USB_DevicePrinterEvent(void *handle, uint32_t event, void *param) { usb_status_t status = kStatus_USB_Error; usb_device_printer_struct_t *printerHandle = (usb_device_printer_struct_t *)handle; uint16_t temp16; uint8_t temp8; usb_device_class_event_t eventCode = (usb_device_class_event_t)event; if ((handle == NULL) || (param == NULL)) { return kStatus_USB_InvalidHandle; } switch (eventCode) { case kUSB_DeviceClassEventDeviceReset: /* de-initialize printer instance */ printerHandle->interfaceHandle = NULL; printerHandle->alternate = 0xFFU; printerHandle->configuration = 0U; printerHandle->interfaceNumber = 0U; printerHandle->bulkInBusy = 0U; printerHandle->bulkOutBusy = 0U; status = kStatus_USB_Success; break; case kUSB_DeviceClassEventSetConfiguration: temp8 = *((uint8_t *)param); if (printerHandle->classConfig == NULL) { break; } if (temp8 == printerHandle->configuration) { status = kStatus_USB_Success; break; } /* De-initialize the endpoints when current configuration is none zero. */ if (printerHandle->configuration != 0U) { status = USB_DevicePrinterEndpointsDeinit(printerHandle); } /* Save new configuration. */ printerHandle->configuration = temp8; printerHandle->alternate = 0U; printerHandle->bulkInBusy = 0U; printerHandle->bulkOutBusy = 0U; /* Initialize the endpoints of the new current configuration */ status = USB_DevicePrinterEndpointsInit(printerHandle); break; case kUSB_DeviceClassEventSetInterface: if (printerHandle->classConfig == NULL) { break; } /* Get the new alternate setting of the interface */ temp16 = *((uint16_t *)param); /* Get the alternate setting value */ temp8 = (uint8_t)(temp16 & 0xFFU); /* Whether the interface belongs to the class. */ if (printerHandle->interfaceNumber != ((uint8_t)(temp16 >> 8U))) { break; } /* Only handle new alternate setting. */ if (temp8 == printerHandle->alternate) { status = kStatus_USB_Success; break; } /* De-initialize old endpoints */ status = USB_DevicePrinterEndpointsDeinit(printerHandle); printerHandle->alternate = temp8; printerHandle->bulkInBusy = 0U; printerHandle->bulkOutBusy = 0U; /* Initialize new endpoints */ status = USB_DevicePrinterEndpointsInit(printerHandle); break; case kUSB_DeviceClassEventSetEndpointHalt: if ((printerHandle->classConfig == NULL) || (printerHandle->interfaceHandle == NULL)) { break; } /* Get the endpoint address */ temp8 = *((uint8_t *)param); for (temp16 = 0; temp16 < printerHandle->interfaceHandle->endpointList.count; ++temp16) { if (temp8 == printerHandle->interfaceHandle->endpointList.endpoint[temp16].endpointAddress) { /* Only stall the endpoint belongs to the class */ if (USB_IN == ((printerHandle->interfaceHandle->endpointList.endpoint[temp16].endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) >> USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT)) { printerHandle->bulkInPipeStall = 1U; } else { printerHandle->bulkOutPipeStall = 1U; } status = USB_DeviceStallEndpoint(printerHandle->deviceHandle, temp8); break; } } break; case kUSB_DeviceClassEventClearEndpointHalt: if ((printerHandle->classConfig == NULL) || (printerHandle->interfaceHandle == NULL)) { break; } /* Get the endpoint address */ temp8 = *((uint8_t *)param); for (temp16 = 0; temp16 < printerHandle->interfaceHandle->endpointList.count; ++temp16) { if (temp8 == printerHandle->interfaceHandle->endpointList.endpoint[temp16].endpointAddress) { /* Only un-stall the endpoint belongs to the class */ status = USB_DeviceUnstallEndpoint(printerHandle->deviceHandle, temp8); if (USB_IN == (((temp8)&USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_MASK) >> USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT)) { if (0U != printerHandle->bulkInPipeStall) { printerHandle->bulkInPipeStall = 0U; if ((uint8_t *)USB_INVALID_TRANSFER_BUFFER != printerHandle->bulkInPipeDataBuffer) { status = USB_DeviceSendRequest( printerHandle->deviceHandle, (printerHandle->interfaceHandle->endpointList.endpoint[temp16].endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK), printerHandle->bulkInPipeDataBuffer, printerHandle->bulkInPipeDataLen); if (kStatus_USB_Success != status) { usb_device_endpoint_callback_message_struct_t endpointCallbackMessage; endpointCallbackMessage.buffer = printerHandle->bulkInPipeDataBuffer; endpointCallbackMessage.length = printerHandle->bulkInPipeDataLen; endpointCallbackMessage.isSetup = 0U; #if (defined(USB_DEVICE_CONFIG_RETURN_VALUE_CHECK) && (USB_DEVICE_CONFIG_RETURN_VALUE_CHECK > 0U)) if (kStatus_USB_Success != USB_DevicePrinterBulkInCallback(printerHandle->deviceHandle, (void *)&endpointCallbackMessage, handle)) { return kStatus_USB_Error; } #else (void)USB_DevicePrinterBulkInCallback(printerHandle->deviceHandle, (void *)&endpointCallbackMessage, handle); #endif } printerHandle->bulkInPipeDataBuffer = (uint8_t *)USB_INVALID_TRANSFER_BUFFER; printerHandle->bulkInPipeDataLen = 0U; } } } else { if (printerHandle->bulkOutPipeStall == 1U) { printerHandle->bulkOutPipeStall = 0U; if ((uint8_t *)USB_INVALID_TRANSFER_BUFFER != printerHandle->bulkOutPipeDataBuffer) { status = USB_DeviceRecvRequest( printerHandle->deviceHandle, (printerHandle->interfaceHandle->endpointList.endpoint[temp16].endpointAddress & USB_DESCRIPTOR_ENDPOINT_ADDRESS_NUMBER_MASK), printerHandle->bulkOutPipeDataBuffer, printerHandle->bulkOutPipeDataLen); if (kStatus_USB_Success != status) { usb_device_endpoint_callback_message_struct_t endpointCallbackMessage; endpointCallbackMessage.buffer = printerHandle->bulkOutPipeDataBuffer; endpointCallbackMessage.length = printerHandle->bulkOutPipeDataLen; endpointCallbackMessage.isSetup = 0U; #if (defined(USB_DEVICE_CONFIG_RETURN_VALUE_CHECK) && (USB_DEVICE_CONFIG_RETURN_VALUE_CHECK > 0U)) if (kStatus_USB_Success != USB_DevicePrinterBulkInCallback(printerHandle->deviceHandle, (void *)&endpointCallbackMessage, handle)) { return kStatus_USB_Error; } #else (void)USB_DevicePrinterBulkInCallback(printerHandle->deviceHandle, (void *)&endpointCallbackMessage, handle); #endif } printerHandle->bulkOutPipeDataBuffer = (uint8_t *)USB_INVALID_TRANSFER_BUFFER; printerHandle->bulkOutPipeDataLen = 0U; } } } break; } } break; case kUSB_DeviceClassEventClassRequest: { /* Handle the printer class specific request. */ usb_device_control_request_struct_t *controlRequest = (usb_device_control_request_struct_t *)param; usb_device_printer_class_request_t classRequest; if ((controlRequest->setup->bmRequestType & USB_REQUEST_TYPE_RECIPIENT_MASK) != USB_REQUEST_TYPE_RECIPIENT_INTERFACE) { break; } classRequest.interface = (uint8_t)(controlRequest->setup->wIndex >> 8); if (classRequest.interface != printerHandle->interfaceNumber) { break; } classRequest.alternateSetting = (uint8_t)(controlRequest->setup->wIndex); status = kStatus_USB_InvalidRequest; switch (controlRequest->setup->bRequest) { case USB_DEVICE_PRINTER_GET_DEVICE_ID: /* GET_DEVICE_ID */ if (((controlRequest->setup->bmRequestType & USB_REQUEST_TYPE_DIR_MASK) == USB_REQUEST_TYPE_DIR_IN) && (controlRequest->setup->wLength != 0U)) { classRequest.configIndex = (uint8_t)controlRequest->setup->wValue; /* ClassCallback is initialized in classInit of s_UsbDeviceClassInterfaceMap, it is from the second parameter of classInit */ status = printerHandle->classConfig->classCallback( (class_handle_t)printerHandle, kUSB_DevicePrinterEventGetDeviceId, &classRequest); controlRequest->buffer = classRequest.buffer; controlRequest->length = classRequest.length; } break; case USB_DEVICE_PRINTER_GET_PORT_STATUS: /* GET_PORT_STATUS */ if (((controlRequest->setup->bmRequestType & USB_REQUEST_TYPE_DIR_MASK) == USB_REQUEST_TYPE_DIR_IN) && (controlRequest->setup->wLength == 1U)) { /* ClassCallback is initialized in classInit of s_UsbDeviceClassInterfaceMap, it is from the second parameter of classInit */ status = printerHandle->classConfig->classCallback( (class_handle_t)printerHandle, kUSB_DevicePrinterEventGetPortStatus, &classRequest); controlRequest->buffer = classRequest.buffer; controlRequest->length = classRequest.length; } break; case USB_DEVICE_PRINTER_SOFT_RESET: if (((controlRequest->setup->bmRequestType & USB_REQUEST_TYPE_DIR_MASK) == USB_REQUEST_TYPE_DIR_OUT) && (controlRequest->setup->wLength == 0U)) { /* reset BULK_IN/OUT endpoint and inform application */ #if (defined(USB_DEVICE_CONFIG_RETURN_VALUE_CHECK) && (USB_DEVICE_CONFIG_RETURN_VALUE_CHECK > 0U)) if ((kStatus_USB_Success != USB_DevicePrinterEndpointsDeinit(printerHandle)) || (kStatus_USB_Success != USB_DevicePrinterEndpointsInit(printerHandle))) { return kStatus_USB_Error; } #else (void)USB_DevicePrinterEndpointsDeinit(printerHandle); (void)USB_DevicePrinterEndpointsInit(printerHandle); #endif /* ClassCallback is initialized in classInit of s_UsbDeviceClassInterfaceMap, it is from the second parameter of classInit */ status = printerHandle->classConfig->classCallback( (class_handle_t)printerHandle, kUSB_DevicePrinterEventSoftReset, &classRequest); } break; default: /*no action*/ break; } break; } default: /*no action*/ break; } return status; } usb_status_t USB_DevicePrinterSend(class_handle_t handle, uint8_t ep, uint8_t *buffer, uint32_t length) { usb_device_printer_struct_t *printerHandle = (usb_device_printer_struct_t *)handle; usb_status_t status; if (NULL == handle) { return kStatus_USB_InvalidHandle; } if (1U == printerHandle->bulkInBusy) { return kStatus_USB_Busy; } printerHandle->bulkInBusy = 1U; if (0U != printerHandle->bulkInPipeStall) { printerHandle->bulkInPipeDataBuffer = buffer; printerHandle->bulkInPipeDataLen = length; return kStatus_USB_Success; } status = USB_DeviceSendRequest(printerHandle->deviceHandle, ep, buffer, length); if (kStatus_USB_Success != status) { printerHandle->bulkInBusy = 0U; } return status; } usb_status_t USB_DevicePrinterRecv(class_handle_t handle, uint8_t ep, uint8_t *buffer, uint32_t length) { usb_device_printer_struct_t *printerHandle = (usb_device_printer_struct_t *)handle; usb_status_t status = kStatus_USB_Error; if (NULL == handle) { return kStatus_USB_InvalidHandle; } if (0U != printerHandle->bulkOutBusy) { return kStatus_USB_Busy; } printerHandle->bulkOutBusy = 1U; if (0U != printerHandle->bulkOutPipeStall) { printerHandle->bulkOutPipeDataBuffer = buffer; printerHandle->bulkOutPipeDataLen = length; return kStatus_USB_Success; } status = USB_DeviceRecvRequest(printerHandle->deviceHandle, ep, buffer, length); if (kStatus_USB_Success != status) { printerHandle->bulkOutBusy = 0U; } return status; } #endif