/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * Copyright (c) 2026-present Eclipse ThreadX contributors * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT **************************************************************************/ /**************************************************************************/ /** */ /** USBX Component */ /** */ /** Device Video Class */ /** */ /**************************************************************************/ /**************************************************************************/ #define UX_SOURCE_CODE /* Include necessary system files. */ #include "ux_api.h" #include "ux_device_class_video.h" #include "ux_device_stack.h" /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_device_class_video_initialize PORTABLE C */ /* 6.3.0 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function initializes the USB Video device. */ /* */ /* INPUT */ /* */ /* command Pointer to video command */ /* */ /* OUTPUT */ /* */ /* Completion Status */ /* */ /* CALLS */ /* */ /* _ux_utility_memory_allocate Allocate memory */ /* _ux_utility_memory_copy Copy memory */ /* _ux_utility_memory_free Free memory */ /* _ux_utility_thread_create Create thread to use */ /* _ux_utility_thread_delete Delete thread */ /* */ /* CALLED BY */ /* */ /* Device Video Class */ /* */ /**************************************************************************/ UINT _ux_device_class_video_initialize(UX_SLAVE_CLASS_COMMAND *command) { UINT status = UX_SUCCESS; UX_DEVICE_CLASS_VIDEO *video; UX_DEVICE_CLASS_VIDEO_PARAMETER *video_parameter; UX_DEVICE_CLASS_VIDEO_STREAM *stream; UX_DEVICE_CLASS_VIDEO_STREAM_PARAMETER *stream_parameter; UX_SLAVE_CLASS *class_inst; ULONG memory_size; ULONG streams_size; ULONG i; /* Get the class container. */ class_inst = command -> ux_slave_class_command_class_ptr; /* Get the pointer to the application parameters for the video class. */ video_parameter = (UX_DEVICE_CLASS_VIDEO_PARAMETER *)command -> ux_slave_class_command_parameter; /* Create an instance of the device video class, with additional streams and controls instances. */ memory_size = sizeof(UX_DEVICE_CLASS_VIDEO); /* Put 0 to default result. */ streams_size = 0; /* Confirm there is no overflow on multiply. */ UX_UTILITY_MULC_SAFE(video_parameter -> ux_device_class_video_parameter_streams_nb, (ULONG)sizeof(UX_DEVICE_CLASS_VIDEO_STREAM), streams_size, status); if (status != UX_SUCCESS) return(status); /* Confirm there is no overflow on add. */ UX_UTILITY_ADD_SAFE(memory_size, streams_size, memory_size, status); if (status != UX_SUCCESS) return(status); /* Create buffer for video and controls. */ video = (UX_DEVICE_CLASS_VIDEO *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size); /* Check for successful allocation. */ if (video == UX_NULL) return(UX_MEMORY_INSUFFICIENT); /* Save streams. */ if (streams_size) { video -> ux_device_class_video_streams = (UX_DEVICE_CLASS_VIDEO_STREAM *)((UCHAR *)video + sizeof(UX_DEVICE_CLASS_VIDEO)); video -> ux_device_class_video_streams_nb = video_parameter -> ux_device_class_video_parameter_streams_nb; } /* Allocate resources for streams. */ stream = video -> ux_device_class_video_streams; stream_parameter = video_parameter -> ux_device_class_video_parameter_streams; for (i = 0; i < video -> ux_device_class_video_streams_nb; i ++) { /* Create memory block based on max payload buffer size and max number of payloads buffered. Each payload require some additional header memory (8 bytes). */ stream -> ux_device_class_video_stream_payload_buffer_size = stream_parameter -> ux_device_class_video_stream_parameter_max_payload_buffer_size; if (UX_OVERFLOW_CHECK_ADD_USHORT(stream -> ux_device_class_video_stream_payload_buffer_size, 4)) { status = UX_ERROR; break; } stream -> ux_device_class_video_stream_payload_buffer_size += 4; if (UX_OVERFLOW_CHECK_MULV_ULONG(stream -> ux_device_class_video_stream_payload_buffer_size, stream_parameter -> ux_device_class_video_stream_parameter_max_payload_buffer_nb)) { status = UX_ERROR; break; } memory_size = stream -> ux_device_class_video_stream_payload_buffer_size * stream_parameter -> ux_device_class_video_stream_parameter_max_payload_buffer_nb; /* Create block of buffer buffer is cache safe for USB transfer. */ #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1 stream -> ux_device_class_video_stream_buffer = (UCHAR *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, memory_size); #else stream -> ux_device_class_video_stream_buffer = (UCHAR *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size); #endif /* Check for successful allocation. */ if (stream -> ux_device_class_video_stream_buffer == UX_NULL) { status = UX_MEMORY_INSUFFICIENT; break; } stream -> ux_device_class_video_stream_buffer_size = memory_size; stream -> ux_device_class_video_stream_transfer_pos = (UX_DEVICE_CLASS_VIDEO_PAYLOAD *)stream -> ux_device_class_video_stream_buffer; stream -> ux_device_class_video_stream_access_pos = stream -> ux_device_class_video_stream_transfer_pos; #if !defined(UX_DEVICE_STANDALONE) /* Create memory block for streaming thread stack in addition. */ if (stream_parameter -> ux_device_class_video_stream_parameter_thread_stack_size == 0) memory_size = UX_DEVICE_CLASS_VIDEO_THREAD_STACK_SIZE; else memory_size = stream_parameter -> ux_device_class_video_stream_parameter_thread_stack_size; stream -> ux_device_class_video_stream_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size); /* Check for successful allocation. */ if (stream -> ux_device_class_video_stream_thread_stack == UX_NULL) { status = UX_MEMORY_INSUFFICIENT; break; } /* Create streaming thread. */ status = _ux_utility_thread_create(&stream -> ux_device_class_video_stream_thread , "ux_device_class_video_stream_thread", stream_parameter -> ux_device_class_video_stream_parameter_thread_entry, (ULONG)(ALIGN_TYPE)stream, (VOID *) stream -> ux_device_class_video_stream_thread_stack, memory_size, UX_THREAD_PRIORITY_CLASS, UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START); /* Check for successful allocation. */ if (status != UX_SUCCESS) break; UX_THREAD_EXTENSION_PTR_SET(&(stream -> ux_device_class_video_stream_thread), stream) #else /* Save task function for streaming. */ stream -> ux_device_class_video_stream_task_function = stream_parameter -> ux_device_class_video_stream_parameter_task_function; #endif /* Save callbacks. */ _ux_utility_memory_copy(&stream -> ux_device_class_video_stream_callbacks, &stream_parameter -> ux_device_class_video_stream_parameter_callbacks, sizeof(UX_DEVICE_CLASS_VIDEO_STREAM_CALLBACKS)); /* Use case of memcpy is verified. */ /* Save video instance. */ stream -> ux_device_class_video_stream_video = video; stream ++; stream_parameter ++; } /* Check for successful creation. */ if (status == UX_SUCCESS) { /* Save the address of the Video instance inside the Video container. */ class_inst -> ux_slave_class_instance = (VOID *) video; /* Link to class instance. */ video -> ux_device_class_video_class = class_inst; /* Save callbacks. */ _ux_utility_memory_copy(&video -> ux_device_class_video_callbacks, &video_parameter -> ux_device_class_video_parameter_callbacks, sizeof(UX_DEVICE_CLASS_VIDEO_CALLBACKS)); /* Use case of memcpy is verified. */ #if defined(UX_DEVICE_STANDALONE) /* Link task function. */ class_inst -> ux_slave_class_task_function = _ux_device_class_video_tasks_run; #endif /* Return completion status. */ return(UX_SUCCESS); } /* Error trap! */ _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); /* Free allocated resources. */ stream = video -> ux_device_class_video_streams; for (i = 0; i < video -> ux_device_class_video_streams_nb; i ++) { #if !defined(UX_DEVICE_STANDALONE) if (_ux_device_thread_created(&stream -> ux_device_class_video_stream_thread)) _ux_utility_thread_delete(&stream -> ux_device_class_video_stream_thread); if (stream -> ux_device_class_video_stream_thread_stack) _ux_utility_memory_free(stream -> ux_device_class_video_stream_thread_stack); #endif if (stream -> ux_device_class_video_stream_buffer) _ux_utility_memory_free(stream -> ux_device_class_video_stream_buffer); stream ++; } _ux_utility_memory_free(video); return(status); } /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _uxe_device_class_video_initialize PORTABLE C */ /* 6.3.0 */ /* AUTHOR */ /* */ /* Yajun Xia, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function checks errors in video initialization function call. */ /* */ /* INPUT */ /* */ /* command Pointer to video command */ /* */ /* OUTPUT */ /* */ /* Completion Status */ /* */ /* CALLS */ /* */ /* _ux_device_class_video_initialize Initialize video instance */ /* */ /* CALLED BY */ /* */ /* Device Video Class */ /* */ /**************************************************************************/ UINT _uxe_device_class_video_initialize(UX_SLAVE_CLASS_COMMAND *command) { UX_DEVICE_CLASS_VIDEO_PARAMETER *video_parameter; ULONG i; /* Get the pointer to the application parameters for the video class. */ video_parameter = (UX_DEVICE_CLASS_VIDEO_PARAMETER *)command -> ux_slave_class_command_parameter; /* Sanity checks. */ if (video_parameter == UX_NULL) return(UX_INVALID_PARAMETER); /* There must be at least one stream. */ if ((video_parameter -> ux_device_class_video_parameter_streams_nb == 0) || (video_parameter -> ux_device_class_video_parameter_streams == UX_NULL)) return(UX_INVALID_PARAMETER); for (i = 0; i < video_parameter -> ux_device_class_video_parameter_streams_nb; i ++) { if ((video_parameter -> ux_device_class_video_parameter_streams[i].ux_device_class_video_stream_parameter_max_payload_buffer_size == 0) || (video_parameter -> ux_device_class_video_parameter_streams[i].ux_device_class_video_stream_parameter_max_payload_buffer_nb == 0)) return(UX_INVALID_PARAMETER); } /* Do initialize. */ return(_ux_device_class_video_initialize(command)); }