usbx/test/regression/usbx_msrc_81108_pictbridge_object_parse_test.c
CQ Xiao 6ed7092b77
Add regression tests. (#126)
Add regression tests (auto triggered on PR, manually triggered in forked branch).
2023-11-28 15:50:39 +08:00

909 lines
39 KiB
C

/* This test is designed to test the simple dpump host/device class operation. */
#include <stdio.h>
#include "tx_api.h"
#include "fx_api.h"
#include "ux_api.h"
#include "ux_system.h"
#include "ux_utility.h"
#include "ux_device_class_pima.h"
#include "ux_host_class_pima.h"
#include "ux_pictbridge.h"
#include "ux_hcd_sim_host.h"
#include "ux_device_stack.h"
#include "ux_test_dcd_sim_slave.h"
#include "ux_test_hcd_sim_host.h"
#include "ux_test_utility_sim.h"
#include "ux_test_jpeg_image.h"
/* Define constants. */
#define UX_TEST_STACK_SIZE (2*1024)
#define UX_TEST_MEMORY_SIZE (512*1024)
/* Define local/extern function prototypes. */
static void test_thread_entry(ULONG);
static TX_THREAD test_thread_host_simulation;
static TX_THREAD test_thread_device_simulation;
static void test_thread_host_simulation_entry(ULONG);
static void test_thread_device_simulation_entry(ULONG);
static VOID test_pima_instance_activate(VOID *pima_instance);
static VOID test_pima_instance_deactivate(VOID *pima_instance);
/* Define global data structures. */
static UCHAR usbx_memory[UX_TEST_MEMORY_SIZE + (UX_TEST_STACK_SIZE * 2)];
static UX_SLAVE_CLASS_PIMA *pima_device;
static UX_PICTBRIDGE pictbridge_device;
static UX_PICTBRIDGE_PRINTINFO printinfo;
static UX_PICTBRIDGE_JOBINFO *jobinfo;
static UX_SLAVE_CLASS_PIMA_OBJECT *object;
static TX_SEMAPHORE print_semaphore;
static ULONG pictbridge_device_copy_count = 0;
static UX_HOST_CLASS_PIMA *pima_host;
static UX_PICTBRIDGE pictbridge_host;
static ULONG pictbridge_host_copy_count = 0;
static TX_SEMAPHORE wait_semaphore;
static ULONG error_counter;
static ULONG set_cfg_counter;
static ULONG rsc_mem_free_on_set_cfg;
static ULONG rsc_sem_on_set_cfg;
static ULONG rsc_sem_get_on_set_cfg;
static ULONG rsc_mutex_on_set_cfg;
static ULONG rsc_enum_sem_usage;
static ULONG rsc_enum_sem_get_count;
static ULONG rsc_enum_mutex_usage;
static ULONG rsc_enum_mem_usage;
static ULONG rsc_cdc_sem_usage;
static ULONG rsc_cdc_sem_get_count;
static ULONG rsc_cdc_mutex_usage;
static ULONG rsc_cdc_mem_usage;
static ULONG interaction_count;
static UCHAR error_callback_ignore = UX_TRUE;
static ULONG error_callback_counter;
/* Define device framework. */
UCHAR device_framework_full_speed[] = {
/* Device descriptor */
0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08,
0xE8, 0x04, 0xC5, 0x68, 0x00, 0x00, 0x01, 0x02,
0x03, 0x01,
/* Configuration descriptor */
0x09, 0x02, 0x27, 0x00, 0x01, 0x01, 0x00, 0xc0,
0x32,
/* Interface descriptor */
0x09, 0x04, 0x00, 0x00, 0x03, 0x06, 0x01, 0x01,
0x00,
/* Endpoint descriptor (Bulk In) */
0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00,
/* Endpoint descriptor (Bulk Out) */
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
/* Endpoint descriptor (Interrupt In) */
0x07, 0x05, 0x83, 0x03, 0x40, 0x00, 0x04
};
#define DEVICE_FRAMEWORK_LENGTH_FULL_SPEED sizeof(device_framework_full_speed)
UCHAR device_framework_high_speed[] = {
/* Device descriptor */
0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40,
0xE8, 0x04, 0xC5, 0x68, 0x01, 0x00, 0x01, 0x02,
0x03, 0x01,
/* Device qualifier descriptor */
0x0a, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40,
0x01, 0x00,
/* Configuration descriptor */
0x09, 0x02, 0x27, 0x00, 0x01, 0x01, 0x00, 0xc0,
0x32,
/* Interface descriptor */
0x09, 0x04, 0x00, 0x00, 0x03, 0x06, 0x01, 0x01,
0x00,
/* Endpoint descriptor (Bulk In) */
0x07, 0x05, 0x81, 0x02, 0x00, 0x02, 0x00,
/* Endpoint descriptor (Bulk Out) */
0x07, 0x05, 0x02, 0x02, 0x00, 0x02, 0x00,
/* Endpoint descriptor (Interrupt In) */
0x07, 0x05, 0x83, 0x03, 0x40, 0x00, 0x04
};
#define DEVICE_FRAMEWORK_LENGTH_HIGH_SPEED sizeof(device_framework_high_speed)
/* String Device Framework :
Byte 0 and 1 : Word containing the language ID : 0x0904 for US or 0x0000 for none.
Byte 2 : Byte containing the index of the descriptor
Byte 3 : Byte containing the length of the descriptor string
The last string entry can be the optional Microsoft String descriptor.
*/
UCHAR string_framework[] = {
/* Manufacturer string descriptor : Index 1 */
0x09, 0x04, 0x01, 0x0c,
0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x20, 0x4c,
0x6f, 0x67, 0x69, 0x63,
/* Product string descriptor : Index 2 */
0x09, 0x04, 0x02, 0x0a,
0x4d, 0x54, 0x50, 0x20, 0x70, 0x6c, 0x61, 0x79,
0x65, 0x72,
/* Serial Number string descriptor : Index 3 */
0x09, 0x04, 0x03, 0x04,
0x30, 0x30, 0x30, 0x31,
};
#define STRING_FRAMEWORK_LENGTH sizeof(string_framework)
/* Multiple languages are supported on the device, to add
a language besides english, the unicode language code must
be appended to the language_id_framework array and the length
adjusted accordingly. */
UCHAR language_id_framework[] = {
/* English. */
0x09, 0x04
};
#define LANGUAGE_ID_FRAMEWORK_LENGTH sizeof(language_id_framework)
/* Pima device info manufacture string (Unicode). */
UCHAR string_pima_manufacturer[] =
{
0x0C,
0x45, 0x00, 0x78, 0x00, 0x70, 0x00, 0x72, 0x00,
0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x4c, 0x00,
0x6f, 0x00, 0x67, 0x00, 0x69, 0x00, 0x63, 0x00
};
/* Pima device info Model string (Unicode). */
UCHAR string_pima_model[] =
{
0x0C,
0x50, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x61, 0x00,
0x20, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6D, 0x00,
0x65, 0x00, 0x72, 0x00, 0x61, 0x00, 0x20, 0x00
};
/* Pima device info Device version (Unicode). */
UCHAR string_pima_device_version[] =
{
0x04,
0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00
};
/* Pima device info Device serial number (Unicode). */
UCHAR string_pima_serial_number[] =
{
0x04,
0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00
};
UCHAR string_pima_storage_description[] =
{
0x0b,
0x56, 0x00, 0x69, 0x00, 0x72, 0x00, 0x74, 0x00,
0x75, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00,
0x44, 0x00, 0x69, 0x00, 0x73, 0x00, 0x6b, 0x00
};
UCHAR string_pima_storage_volume_label[] =
{
0x09,
0x4d, 0x00, 0x79, 0x00, 0x20, 0x00, 0x56, 0x00,
0x6f, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x6d, 0x00,
0x65, 0x00
};
/* Prototype for test control return. */
void test_control_return(UINT status);
static VOID error_callback(UINT system_level, UINT system_context, UINT error_code)
{
error_callback_counter ++;
if (!error_callback_ignore)
{
{
/* Failed test. */
printf("Error #%d, system_level: %d, system_context: %d, error_code: 0x%x\n", __LINE__, system_level, system_context, error_code);
test_control_return(1);
}
}
}
static UINT sleep_break_on_error(VOID)
{
if (error_callback_counter >= 3)
return error_callback_counter;
return UX_SUCCESS;
}
/* Define the ISR dispatch routine. */
static void test_isr(void)
{
/* For further expansion of interrupt-level testing. */
}
static UINT test_system_host_change_function(ULONG event, UX_HOST_CLASS *cls, VOID *inst)
{
// printf("hChg:%lx, %p, %p\n", event, cls, inst);
switch(event)
{
case UX_DEVICE_INSERTION:
if (cls->ux_host_class_entry_function == ux_host_class_pima_entry)
{
pima_host = (UX_HOST_CLASS_PIMA *)inst;
}
break;
case UX_DEVICE_REMOVAL:
if (cls->ux_host_class_entry_function == ux_host_class_pima_entry)
{
if ((VOID*)pima_host == inst)
pima_host = UX_NULL;
}
break;
default:
break;
}
return 0;
}
static VOID test_pima_instance_activate(VOID *instance)
{
pima_device = (UX_SLAVE_CLASS_PIMA *)instance;
}
static VOID test_pima_instance_deactivate(VOID *instance)
{
if ((VOID *)pima_device == instance)
pima_device = UX_NULL;
}
/* Define what the initial system looks like. */
#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void usbx_msrc_81108_msrc_parse_test_application_define(void *first_unused_memory)
#endif
{
UINT status;
CHAR * stack_pointer;
CHAR * memory_pointer;
ULONG test_n;
/* Inform user. */
printf("Running MSRC 81108 - Pictbridge Object Parse Test................... ");
/* Reset testing counts. */
ux_test_utility_sim_mutex_create_count_reset();
ux_test_utility_sim_sem_create_count_reset();
ux_test_utility_sim_sem_get_count_reset();
/* Reset error generations */
ux_test_utility_sim_sem_error_generation_stop();
ux_test_utility_sim_mutex_error_generation_stop();
ux_test_utility_sim_sem_get_error_generation_stop();
/* Initialize the free memory pointer */
stack_pointer = (CHAR *) usbx_memory;
memory_pointer = stack_pointer + (UX_TEST_STACK_SIZE * 2);
/* Initialize USBX Memory */
status = ux_system_initialize(memory_pointer, UX_TEST_MEMORY_SIZE, UX_NULL,0);
UX_TEST_CHECK_SUCCESS(status);
/* Register the error callback. */
_ux_utility_error_callback_register(error_callback);
/* The code below is required for installing the host portion of USBX */
status = ux_host_stack_initialize(test_system_host_change_function);
UX_TEST_CHECK_SUCCESS(status);
/* Register PIMA class. */
status = ux_host_stack_class_register(_ux_system_host_class_pima_name, ux_host_class_pima_entry);
UX_TEST_CHECK_SUCCESS(status);
/* The code below is required for installing the device portion of USBX. No call back for
device status change in this example. */
status = ux_device_stack_initialize(device_framework_high_speed, DEVICE_FRAMEWORK_LENGTH_HIGH_SPEED,
device_framework_full_speed, DEVICE_FRAMEWORK_LENGTH_FULL_SPEED,
string_framework, STRING_FRAMEWORK_LENGTH,
language_id_framework, LANGUAGE_ID_FRAMEWORK_LENGTH,UX_NULL);
UX_TEST_CHECK_SUCCESS(status);
/* Initialize the Pictbridge string components. */
ux_utility_memory_copy(pictbridge_device.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_name, "ExpressLogic",13);
ux_utility_memory_copy(pictbridge_device.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_product_name, "EL_Pictbridge_Camera",21);
ux_utility_memory_copy(pictbridge_device.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_serial_no, "ABC_123",7);
ux_utility_memory_copy(pictbridge_device.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dpsversions, "1.0 1.1",7);
pictbridge_device.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_specific_version = 0x0100;
pictbridge_device.ux_pictbridge_pima_parameter.ux_device_class_pima_instance_activate = test_pima_instance_activate;
pictbridge_device.ux_pictbridge_pima_parameter.ux_device_class_pima_instance_deactivate = test_pima_instance_deactivate;
pictbridge_device.ux_pictbridge_pima_parameter.ux_device_class_pima_parameter_device_version = "0.0";
/* Start the Pictbridge client. */
status = ux_pictbridge_dpsclient_start(&pictbridge_device);
UX_TEST_CHECK_SUCCESS(status);
/* Initialize the simulated device controller. */
status = _ux_dcd_sim_slave_initialize();
if (status != UX_SUCCESS)
{
printf("ERROR #%d\n", __LINE__);
test_control_return(1);
}
/* Register all the USB host controllers available in this system */
status = ux_host_stack_hcd_register(_ux_system_host_hcd_simulator_name, _ux_test_hcd_sim_host_initialize,0,0);
UX_TEST_CHECK_SUCCESS(status);
/* Create a semaphore for the demo. */
status = tx_semaphore_create(&wait_semaphore,"Wait Semaphore", 0);
UX_TEST_CHECK_SUCCESS(status);
/* Create the main host simulation thread. */
status = tx_thread_create(&test_thread_host_simulation, "tx demo host simulation", test_thread_host_simulation_entry, 0,
stack_pointer, UX_TEST_STACK_SIZE,
20, 20, 1, TX_AUTO_START);
UX_TEST_CHECK_SUCCESS(status);
/* Create the main slave simulation thread. */
status = tx_thread_create(&test_thread_device_simulation, "tx demo device simulation", test_thread_device_simulation_entry, 0,
stack_pointer + UX_TEST_STACK_SIZE, UX_TEST_STACK_SIZE,
20, 20, 1, TX_AUTO_START);
UX_TEST_CHECK_SUCCESS(status);
}
/* Copy the object data. */
static UINT test_pictbridge_host_object_data_write(UX_PICTBRIDGE *pictbridge,UCHAR *object_buffer, ULONG offset, ULONG total_length, ULONG length)
{
pictbridge_host_copy_count ++;
/* We have copied the requested data. Return OK. */
return(UX_SUCCESS);
}
static ULONG ux_test_command_count = 0;
static TX_THREAD replace_pima_cmd_thread;
static UCHAR replace_pima_cmd_thread_stack[UX_TEST_STACK_SIZE];
static TX_SEMAPHORE replace_pima_cmd_semaphore;
static ULONG replace_pima_cmd_n_obj = 96;
void replace_pima_cmd_thread_entry(ULONG arg)
{
UINT status;
ULONG actual_flags;
ULONG temp;
while(1)
{
tx_semaphore_get(&replace_pima_cmd_semaphore, TX_WAIT_FOREVER);
switch(ux_test_command_count)
{
case 4:
/* GetNumObjects. */
// printf("GetNumObjects\n");
pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_actual_length = pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_requested_length;
pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_completion_code = UX_SUCCESS;
tx_semaphore_put(&pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_semaphore);
status = _ux_device_class_pima_response_send(pima_device, UX_DEVICE_CLASS_PIMA_RC_OK, 1, replace_pima_cmd_n_obj, 0, 0);
UX_TEST_CHECK_SUCCESS(status);
ux_test_command_count ++;
break;
case 5:
/* GetObjectHandles. */
// printf("GetNumObjectHandles\n");
pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_actual_length = pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_requested_length;
pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_completion_code = UX_SUCCESS;
tx_semaphore_put(&pima_host -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_semaphore);
status = _ux_device_class_pima_object_handles_send(pima_device, pima_device->ux_device_class_pima_storage_id, UX_PICTBRIDGE_OBJECT_SCRIPT, 0);
UX_TEST_CHECK_SUCCESS(status);
ux_test_command_count ++;
break;
}
}
}
static VOID ux_test_pima_command(UX_TEST_ACTION *action, VOID *params)
{
UINT status;
// printf("PIMA command #%ld\n", ux_test_command_count);
switch(ux_test_command_count)
{
case 4:
tx_semaphore_put(&replace_pima_cmd_semaphore);
return;
}
ux_test_command_count ++;
}
static UX_TEST_HCD_SIM_ACTION replace_bulk_out_transfer[] = {
/* function, request to match,
port action, port status,
request action, request EP, request data, request actual length, request status,
status, additional callback,
no_return */
{ UX_HCD_TRANSFER_REQUEST, UX_NULL,
UX_FALSE, 0,
UX_TEST_MATCH_EP, 0x02, UX_NULL, 0, 0,
UX_SUCCESS, ux_test_pima_command,
UX_TRUE}, /* 1. _device_info_get */
{ UX_HCD_TRANSFER_REQUEST, UX_NULL,
UX_FALSE, 0,
UX_TEST_MATCH_EP, 0x02, UX_NULL, 0, 0,
UX_SUCCESS, ux_test_pima_command,
UX_TRUE}, /* 2. _session_open */
{ UX_HCD_TRANSFER_REQUEST, UX_NULL,
UX_FALSE, 0,
UX_TEST_MATCH_EP, 0x02, UX_NULL, 0, 0,
UX_SUCCESS, ux_test_pima_command,
UX_TRUE}, /* 3. _storage_ids_get */
{ UX_HCD_TRANSFER_REQUEST, UX_NULL,
UX_FALSE, 0,
UX_TEST_MATCH_EP, 0x02, UX_NULL, 0, 0,
UX_SUCCESS, ux_test_pima_command,
UX_TRUE}, /* 4. _storage_info_get */
{ UX_HCD_TRANSFER_REQUEST, UX_NULL,
UX_FALSE, 0,
UX_TEST_MATCH_EP, 0x02, UX_NULL, 0, 0,
UX_SUCCESS, ux_test_pima_command,
UX_FALSE}, /* 5. _num_objects_get */
{ UX_HCD_TRANSFER_REQUEST, UX_NULL,
UX_FALSE, 0,
UX_TEST_MATCH_EP, 0x02, UX_NULL, 0, 0,
UX_SUCCESS, ux_test_pima_command,
UX_TRUE}, /* 6. _num_objects_get */
{ 0 }
};
void test_thread_host_simulation_entry(ULONG arg)
{
UINT status;
INT i;
for (i = 0; i < 100; i ++)
{
if (pima_host && pima_device)
break;
ux_utility_delay_ms(1);
}
if (pima_host == UX_NULL || pima_device == UX_NULL)
{
printf("ERROR #%d\n", __LINE__);
test_control_return(1);
}
/* Initialize the dpshost structure with the printer vendor info. */
ux_utility_memory_copy(pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_name, "ExpressLogic",13);
ux_utility_memory_copy(pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_product_name, "EL_Pictbridge_Printer",21);
ux_utility_memory_copy(pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_serial_no, "ABC_123",7);
/* Set supported versions. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dpsversions[0] = 0x00010000;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dpsversions[1] = 0x00010001;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dpsversions[2] = 0;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_vendor_specific_version = 0x00010000;
/* Set print services to TRUE. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_print_service_available = 0x30010000;
/* Set Qualities. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_qualities[0] = UX_PICTBRIDGE_QUALITIES_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_qualities[1] = UX_PICTBRIDGE_QUALITIES_NORMAL;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_qualities[2] = UX_PICTBRIDGE_QUALITIES_DRAFT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_qualities[3] = UX_PICTBRIDGE_QUALITIES_FINE;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_qualities[4] = 0;
/* Set Paper Sizes. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papersizes[0] = UX_PICTBRIDGE_PAPER_SIZES_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papersizes[1] = UX_PICTBRIDGE_PAPER_SIZES_4IX6I;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papersizes[2] = UX_PICTBRIDGE_PAPER_SIZES_L;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papersizes[3] = UX_PICTBRIDGE_PAPER_SIZES_2L;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papersizes[4] = UX_PICTBRIDGE_PAPER_SIZES_LETTER;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papersizes[5] = 0;
/* Set Paper Types. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papertypes[0] = UX_PICTBRIDGE_PAPER_TYPES_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papertypes[1] = UX_PICTBRIDGE_PAPER_TYPES_PLAIN;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papertypes[2] = UX_PICTBRIDGE_PAPER_TYPES_PHOTO;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_papertypes[3] = 0;
/* Set File Types. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filetypes[0] = UX_PICTBRIDGE_FILE_TYPES_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filetypes[1] = UX_PICTBRIDGE_FILE_TYPES_EXIF_JPEG;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filetypes[2] = UX_PICTBRIDGE_FILE_TYPES_JFIF;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filetypes[3] = UX_PICTBRIDGE_FILE_TYPES_DPOF;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filetypes[4] = 0;
/* Set Date Prints. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dateprints[0] = UX_PICTBRIDGE_DATE_PRINTS_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dateprints[1] = UX_PICTBRIDGE_DATE_PRINTS_OFF;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dateprints[2] = UX_PICTBRIDGE_DATE_PRINTS_ON;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dateprints[3] = 0;
/* Set File Name Prints. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filenameprints[0] = UX_PICTBRIDGE_FILE_NAME_PRINTS_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filenameprints[1] = UX_PICTBRIDGE_FILE_NAME_PRINTS_OFF;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filenameprints[2] = UX_PICTBRIDGE_FILE_NAME_PRINTS_ON;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_filenameprints[3] = 0;
/* Set Image optimizes. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_imageoptimizes[0] = UX_PICTBRIDGE_IMAGE_OPTIMIZES_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_imageoptimizes[1] = UX_PICTBRIDGE_IMAGE_OPTIMIZES_OFF;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_imageoptimizes[2] = UX_PICTBRIDGE_IMAGE_OPTIMIZES_ON;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_imageoptimizes[3] = 0;
/* Set Layouts. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_layouts[0] = UX_PICTBRIDGE_LAYOUTS_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_layouts[1] = UX_PICTBRIDGE_LAYOUTS_1_UP_BORDER;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_layouts[2] = UX_PICTBRIDGE_LAYOUTS_INDEX_PRINT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_layouts[3] = UX_PICTBRIDGE_LAYOUTS_1_UP_BORDERLESS;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_layouts[4] = 0;
/* Set Fixed Sizes. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[0] = UX_PICTBRIDGE_FIXED_SIZE_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[1] = UX_PICTBRIDGE_FIXED_SIZE_35IX5I;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[2] = UX_PICTBRIDGE_FIXED_SIZE_4IX6I;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[3] = UX_PICTBRIDGE_FIXED_SIZE_5IX7I;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[4] = UX_PICTBRIDGE_FIXED_SIZE_7CMX10CM;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[5] = UX_PICTBRIDGE_FIXED_SIZE_LETTER;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[6] = UX_PICTBRIDGE_FIXED_SIZE_A4;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_fixedsizes[7] = 0;
/* Set croppings. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_croppings[0] = UX_PICTBRIDGE_CROPPINGS_DEFAULT;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_croppings[1] = UX_PICTBRIDGE_CROPPINGS_OFF;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_croppings[2] = UX_PICTBRIDGE_CROPPINGS_ON;
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_croppings[3] = 0;
/* Set Print Service Status to idle. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_dpsprintservicestatus = UX_PICTBRIDGE_DPS_PRINTSERVICE_STATUS_IDLE;
/* Set Job End Reason. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_jobendreason = UX_PICTBRIDGE_JOB_END_REASON_NOT_ENDED;
/* Set Error Status. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_errorstatus = UX_PICTBRIDGE_ERROR_STATUS_NO_ERROR;
/* Set Error Reason. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_errorreason = UX_PICTBRIDGE_ERROR_REASON_NO_REASON;
/* Set Disconnection Enable. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_disconnectenable = UX_PICTBRIDGE_DISCONNECT_ENABLE_TRUE;
/* Set Capability Changed. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_capabilitychanged = UX_PICTBRIDGE_CAPABILITY_CHANGED_FALSE;
/* Set New Job OK. */
pictbridge_host.ux_pictbridge_dpslocal.ux_pictbridge_devinfo_newjobok = UX_PICTBRIDGE_NEW_JOB_TRUE;
/* Set a callback when an object is being received. */
pictbridge_host.ux_pictbridge_application_object_data_write = test_pictbridge_host_object_data_write;
/*
During initialization the host pictbridge application queries the device for
number of objects and later retrieves the object handles. One may observe that
the pictbridge -> ux_pictbridge_object_handles_array array is hardcoded to be 64
elements ULONG large. In case a malicious pima device provides a number of
object larger than 64 a buffer overflow during retrieval of object handles will
occur as the buffer size is set to 4 * ux_host_class_pima_session_nb_objects,
where ux_host_class_pima_session_nb_objects is attacker controlled.
In example if a malicious device provides object count of 256,
pima_session -> ux_host_class_pima_session_nb_objects will be set to 256 and
256 * 4 bytes of payload will be retrieved from the device and stored in the
64 * 4 byte large pictbridge -> ux_pictbridge_object_handles_array array
resulting in an overflow of 768 bytes.
Since ux_host_class_pima_session_nb_objects is controlled by the attacker the
actual size of the overflow may be quite larger. One may attempt to achieve
execution of arbitrary code e.g., by overwriting one of the void pointers
present in the UX_PICTBRIDGE_STRUCT struct (the
ux_pictbridge_object_handles_array array is a member of this struct).
```
status = _ux_host_class_pima_num_objects_get(pima, pima_session,
UX_PICTBRIDGE_ALL_CONTAINERS, UX_PICTBRIDGE_OBJECT_SCRIPT);
if (status != UX_SUCCESS)
{
_ux_host_class_pima_session_close(pima, pima_session);
return(UX_PICTBRIDGE_ERROR_STORE_NOT_AVAILABLE);
}
status = _ux_host_class_pima_object_handles_get(pima, pima_session,
pictbridge -> ux_pictbridge_object_handles_array,
4 * pima_session -> ux_host_class_pima_session_nb_objects,
UX_PICTBRIDGE_ALL_CONTAINERS, UX_PICTBRIDGE_OBJECT_SCRIPT, 0);
```
The implementation does not assure that the number of provided objects can
actually fit the pictbridge -> ux_pictbridge_object_handles_array array.
*Impact*
The vulnerability may be exploited to execute arbitrary code or crash the
pictbridge host application.
*Reproduction steps*
- Connect a malicious device to the host running pictbridge application
- Provide a response with a number of objects larger than 64 (size of
pictbridge -> ux_pictbridge_object_handles_array)
- Observe a buffer overflow during retrieval of object handles
*/
/* Hook requests to replace answers. */
tx_semaphore_create(&replace_pima_cmd_semaphore, "cmd_sem", 0);
tx_thread_create(&replace_pima_cmd_thread, "cmd_thr, ", replace_pima_cmd_thread_entry, 0,
replace_pima_cmd_thread_stack, UX_TEST_STACK_SIZE,
20, 20, 1, TX_AUTO_START);
replace_pima_cmd_n_obj = 256;
ux_test_hcd_sim_host_set_actions(replace_bulk_out_transfer);
/* Activate the pictbridge dpshost. */
status = _ux_pictbridge_dpshost_start(&pictbridge_host, pima_host);
UX_TEST_CHECK_NOT_SUCCESS(status);
tx_thread_sleep(5);
/* Test object parse.
* Tag format: <xml version></xml> */
UCHAR ux_test_xml[4096] = {0};
UCHAR *tag = "<xml version=1.0>";
UCHAR *tag_end = "</xml>";
for (i = 0; i <= UX_PICTBRIDGE_MAX_TAG_DEPTH; i ++)
strcat(ux_test_xml, tag);
for (i = 0; i <= UX_PICTBRIDGE_MAX_TAG_DEPTH; i ++)
strcat(ux_test_xml, tag_end);
ULONG ux_test_xml_length = ux_utility_string_length_get(ux_test_xml);
status = _ux_pictbridge_object_parse(&pictbridge_host, ux_test_xml, ux_test_xml_length);
UX_TEST_CHECK_CODE(UX_BUFFER_OVERFLOW, status);
stepinfo(">>>>>>>>>>>> All Done\n");
/* Finally disconnect the device. */
ux_device_stack_disconnect();
/* And deinitialize the class. */
status = ux_device_stack_class_unregister(_ux_system_slave_class_pima_name, ux_device_class_pima_entry);
/* Deinitialize the device side of usbx. */
_ux_device_stack_uninitialize();
/* And finally the usbx system resources. */
_ux_system_uninitialize();
/* Successful test. */
printf("SUCCESS!\n");
test_control_return(0);
}
static UINT test_pictbridge_device_object_data_copy(UX_PICTBRIDGE *pictbridge, ULONG object_handle, UCHAR *object_buffer, ULONG object_offset, ULONG object_length, ULONG *actual_length)
{
pictbridge_device_copy_count ++;
/* Copy the demanded object data portion. */
ux_utility_memory_copy(object_buffer, ux_test_jpeg_image + object_offset, object_length);
/* Update the actual length. */
*actual_length = object_length;
/* We have copied the requested data. Return OK. */
return(UX_SUCCESS);
}
UINT test_pictbridge_device_event_callback(struct UX_PICTBRIDGE_STRUCT *pictbridge, UINT event_flag)
{
/* Check if we received NotifyDeviceStatus event. */
if (event_flag & UX_PICTBRIDGE_EVENT_FLAG_NOTIFY_DEVICE_STATUS)
{
/* Check if the printer can accept new job. */
if (pictbridge -> ux_pictbridge_dpsclient.ux_pictbridge_devinfo_newjobok == UX_PICTBRIDGE_NEW_JOB_TRUE)
{
/* Let the demo thread to send a new job. */
tx_semaphore_put(&print_semaphore);
}
}
return UX_SUCCESS;
}
void test_thread_device_simulation_entry(ULONG arg)
{
UINT status;
ULONG actual_flags;
/* Create a semaphore for the demo. */
status = tx_semaphore_create(&print_semaphore,"Print Semaphore", 0);
UX_TEST_CHECK_SUCCESS(status);
while(1)
{
/* We should wait for the host and the client to discover one another. */
status = ux_utility_event_flags_get(&pictbridge_device.ux_pictbridge_event_flags_group, UX_PICTBRIDGE_EVENT_FLAG_DISCOVERY,
TX_AND_CLEAR, &actual_flags, UX_PICTBRIDGE_EVENT_TIMEOUT);
/* Check status. */
if (status == UX_SUCCESS)
{
/* Check if the pictbridge state machine has changed to discovery complete. */
if (pictbridge_device.ux_pictbridge_discovery_state == UX_PICTBRIDGE_DPSCLIENT_DISCOVERY_COMPLETE)
{
/* We can now communicate using XML scripts with the printer. First get information on capabilities. */
status = ux_pictbridge_dpsclient_api_configure_print_service(&pictbridge_device);
UX_TEST_CHECK_SUCCESS(status);
/* Check status. */
if (status == UX_SUCCESS)
{
/* Get the printer capabilities : Qualities. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_QUALITIES, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : PaperSizes. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_PAPER_SIZES, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : FileTypes. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_FILE_TYPES, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : DatePrints. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_DATE_PRINTS, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : FileNamePrints. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_FILE_NAME_PRINTS, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : ImageOptimizes. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_IMAGE_OPTIMIZES, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : Layouts. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_LAYOUTS, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : FixedSizes. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_FIXED_SIZES, 0);
UX_TEST_CHECK_SUCCESS(status);
/* Get the printer capabilities : Croppings. */
status = ux_pictbridge_dpsclient_api_capability(&pictbridge_device, UX_PICTBRIDGE_API_CROPPINGS, 0);
UX_TEST_CHECK_SUCCESS(status);
/* We have all the printer capabilities, get the device status. */
status = ux_pictbridge_dpsclient_api_device_status(&pictbridge_device);
UX_TEST_CHECK_SUCCESS(status);
/* Check status. */
if (status == UX_SUCCESS)
{
/* Check if the printer is ready for a pring job. */
if (pictbridge_device.ux_pictbridge_dpsclient.ux_pictbridge_devinfo_newjobok == UX_PICTBRIDGE_NEW_JOB_TRUE)
{
/* We can start a new job. Fill in the JobConfig and PrintInfo structures. */
jobinfo = &pictbridge_device.ux_pictbridge_jobinfo;
/* Attach a printinfo structure to the job. */
jobinfo -> ux_pictbridge_jobinfo_printinfo_start = &printinfo;
/* Set the default values for print job. */
jobinfo -> ux_pictbridge_jobinfo_quality = UX_PICTBRIDGE_QUALITIES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_papersize = UX_PICTBRIDGE_PAPER_SIZES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_papertype = UX_PICTBRIDGE_PAPER_TYPES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_filetype = UX_PICTBRIDGE_FILE_TYPES_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_dateprint = UX_PICTBRIDGE_DATE_PRINTS_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_filenameprint = UX_PICTBRIDGE_FILE_NAME_PRINTS_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_imageoptimize = UX_PICTBRIDGE_IMAGE_OPTIMIZES_OFF;
jobinfo -> ux_pictbridge_jobinfo_layout = UX_PICTBRIDGE_LAYOUTS_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_fixedsize = UX_PICTBRIDGE_FIXED_SIZE_DEFAULT;
jobinfo -> ux_pictbridge_jobinfo_cropping = UX_PICTBRIDGE_CROPPINGS_DEFAULT;
/* Program the callback function for reading the object data. */
jobinfo -> ux_pictbridge_jobinfo_object_data_read = test_pictbridge_device_object_data_copy;
/* This is a demo, the fileID is hardwired (1 and 2 for scripts, 3 for photo to be printed. */
printinfo.ux_pictbridge_printinfo_fileid = UX_PICTBRIDGE_OBJECT_HANDLE_PRINT;
ux_utility_memory_copy(printinfo.ux_pictbridge_printinfo_filename, "Pictbridge demo file", 20);
ux_utility_memory_copy(printinfo.ux_pictbridge_printinfo_date, "01/01/2008", 10);
/* Fill in the object info to be printed. First get the pointer to the object container in the job info structure. */
object = (UX_SLAVE_CLASS_PIMA_OBJECT *) jobinfo -> ux_pictbridge_jobinfo_object;
/* Store the object format : JPEG picture. */
object -> ux_device_class_pima_object_format = UX_DEVICE_CLASS_PIMA_OFC_EXIF_JPEG;
object -> ux_device_class_pima_object_compressed_size = UX_TEST_JPEG_IMAGE_LENGTH;
object -> ux_device_class_pima_object_offset = 0;
object -> ux_device_class_pima_object_handle_id = UX_PICTBRIDGE_OBJECT_HANDLE_PRINT;
object -> ux_device_class_pima_object_length = UX_TEST_JPEG_IMAGE_LENGTH;
/* File name is in Unicode. */
ux_utility_string_to_unicode("JPEG Image", object -> ux_device_class_pima_object_filename);
/* And start the job. */
status = ux_pictbridge_dpsclient_api_start_job(&pictbridge_device);
UX_TEST_CHECK_SUCCESS(status);
/* Register the callback function to receive events from the printer. */
ux_pictbridge_dpsclient_register_event_callback_function(&pictbridge_device, test_pictbridge_device_event_callback);
/* Wait for the job to complete. */
status = tx_semaphore_get(&print_semaphore, 30000);
UX_TEST_CHECK_SUCCESS(status);
if (status == TX_SUCCESS)
{
/* Print the job again to demo the use of callback function. */
status = ux_pictbridge_dpsclient_api_start_job(&pictbridge_device);
UX_TEST_CHECK_SUCCESS(status);
/* Let host thread run to end. */
tx_semaphore_put(&wait_semaphore);
}
/* Unregister the callback function by passing a Null pointer. */
ux_pictbridge_dpsclient_register_event_callback_function(&pictbridge_device, UX_NULL);
}
}
}
}
}
}
}