From 15044435fb627392d1b361e10ae9b2d54d76dec6 Mon Sep 17 00:00:00 2001 From: PProvost Date: Mon, 11 May 2020 09:03:23 -0600 Subject: [PATCH] Initial commit --- .gitattributes | 41 + .gitignore | 14 + CMakeLists.txt | 51 + LICENSE.txt | 246 ++ LICENSED-HARDWARE.txt | 16 + README.md | 1 + TODO | 0 common/CMakeLists.txt | 1804 +++++++++++++++ common/core/CMakeLists.txt | 208 ++ common/core/inc/ux_api.h | 1983 +++++++++++++++++ common/core/inc/ux_dcd_sim_slave.h | 137 ++ common/core/inc/ux_device_class_dpump.h | 101 + common/core/inc/ux_device_stack.h | 87 + common/core/inc/ux_hcd_sim_host.h | 236 ++ common/core/inc/ux_host_class_dpump.h | 89 + common/core/inc/ux_host_stack.h | 116 + common/core/inc/ux_system.h | 136 ++ common/core/inc/ux_user.h | 314 +++ common/core/inc/ux_utility.h | 234 ++ .../core/src/ux_dcd_sim_slave_address_set.c | 80 + .../src/ux_dcd_sim_slave_endpoint_create.c | 114 + .../src/ux_dcd_sim_slave_endpoint_destroy.c | 86 + .../src/ux_dcd_sim_slave_endpoint_reset.c | 86 + .../src/ux_dcd_sim_slave_endpoint_stall.c | 86 + .../src/ux_dcd_sim_slave_endpoint_status.c | 89 + .../src/ux_dcd_sim_slave_frame_number_get.c | 82 + common/core/src/ux_dcd_sim_slave_function.c | 172 ++ common/core/src/ux_dcd_sim_slave_initialize.c | 104 + .../ux_dcd_sim_slave_initialize_complete.c | 141 ++ .../core/src/ux_dcd_sim_slave_state_change.c | 80 + .../src/ux_dcd_sim_slave_transfer_abort.c | 90 + .../src/ux_dcd_sim_slave_transfer_request.c | 123 + .../core/src/ux_device_class_dpump_activate.c | 141 ++ .../core/src/ux_device_class_dpump_change.c | 168 ++ .../src/ux_device_class_dpump_deactivate.c | 129 ++ common/core/src/ux_device_class_dpump_entry.c | 147 ++ .../src/ux_device_class_dpump_initialize.c | 100 + common/core/src/ux_device_class_dpump_read.c | 182 ++ .../core/src/ux_device_class_dpump_thread.c | 165 ++ common/core/src/ux_device_class_dpump_write.c | 186 ++ .../ux_device_stack_alternate_setting_get.c | 133 ++ .../ux_device_stack_alternate_setting_set.c | 408 ++++ .../core/src/ux_device_stack_class_register.c | 151 ++ .../src/ux_device_stack_class_unregister.c | 137 ++ .../core/src/ux_device_stack_clear_feature.c | 175 ++ .../src/ux_device_stack_configuration_get.c | 103 + .../src/ux_device_stack_configuration_set.c | 374 ++++ .../ux_device_stack_control_request_process.c | 312 +++ .../src/ux_device_stack_descriptor_send.c | 544 +++++ common/core/src/ux_device_stack_disconnect.c | 162 ++ .../core/src/ux_device_stack_endpoint_stall.c | 110 + common/core/src/ux_device_stack_get_status.c | 184 ++ common/core/src/ux_device_stack_host_wakeup.c | 91 + common/core/src/ux_device_stack_initialize.c | 414 ++++ .../src/ux_device_stack_interface_delete.c | 132 ++ .../core/src/ux_device_stack_interface_get.c | 141 ++ .../core/src/ux_device_stack_interface_set.c | 277 +++ .../src/ux_device_stack_interface_start.c | 130 ++ ...evice_stack_microsoft_extension_register.c | 86 + common/core/src/ux_device_stack_set_feature.c | 195 ++ .../core/src/ux_device_stack_transfer_abort.c | 126 ++ ..._device_stack_transfer_all_request_abort.c | 89 + .../src/ux_device_stack_transfer_request.c | 183 ++ .../core/src/ux_device_stack_uninitialize.c | 122 + .../ux_hcd_sim_host_asynch_queue_process.c | 79 + .../src/ux_hcd_sim_host_asynch_schedule.c | 126 ++ ...cd_sim_host_asynchronous_endpoint_create.c | 122 + ...d_sim_host_asynchronous_endpoint_destroy.c | 121 + common/core/src/ux_hcd_sim_host_ed_obtain.c | 102 + common/core/src/ux_hcd_sim_host_ed_td_clean.c | 96 + .../core/src/ux_hcd_sim_host_endpoint_reset.c | 86 + common/core/src/ux_hcd_sim_host_entry.c | 273 +++ .../src/ux_hcd_sim_host_frame_number_get.c | 78 + .../src/ux_hcd_sim_host_frame_number_set.c | 80 + common/core/src/ux_hcd_sim_host_initialize.c | 191 ++ ...x_hcd_sim_host_interrupt_endpoint_create.c | 180 ++ .../src/ux_hcd_sim_host_iso_queue_process.c | 78 + .../core/src/ux_hcd_sim_host_iso_schedule.c | 77 + ...hcd_sim_host_isochronous_endpoint_create.c | 114 + .../ux_hcd_sim_host_isochronous_td_obtain.c | 102 + .../ux_hcd_sim_host_least_traffic_list_get.c | 136 ++ ...x_hcd_sim_host_periodic_endpoint_destroy.c | 110 + .../src/ux_hcd_sim_host_periodic_schedule.c | 109 + .../ux_hcd_sim_host_periodic_tree_create.c | 138 ++ common/core/src/ux_hcd_sim_host_port_reset.c | 108 + .../src/ux_hcd_sim_host_port_status_get.c | 125 ++ .../src/ux_hcd_sim_host_regular_td_obtain.c | 116 + .../ux_hcd_sim_host_request_bulk_transfer.c | 219 ++ ...ux_hcd_sim_host_request_control_transfer.c | 321 +++ ...x_hcd_sim_host_request_interupt_transfer.c | 135 ++ ...cd_sim_host_request_isochronous_transfer.c | 230 ++ .../src/ux_hcd_sim_host_request_transfer.c | 124 ++ .../core/src/ux_hcd_sim_host_timer_function.c | 97 + .../ux_hcd_sim_host_transaction_schedule.c | 495 ++++ .../core/src/ux_hcd_sim_host_transfer_abort.c | 131 ++ .../core/src/ux_host_class_dpump_activate.c | 146 ++ .../core/src/ux_host_class_dpump_configure.c | 143 ++ .../core/src/ux_host_class_dpump_deactivate.c | 136 ++ .../src/ux_host_class_dpump_endpoints_get.c | 163 ++ common/core/src/ux_host_class_dpump_entry.c | 121 + common/core/src/ux_host_class_dpump_ioctl.c | 164 ++ common/core/src/ux_host_class_dpump_read.c | 205 ++ common/core/src/ux_host_class_dpump_write.c | 205 ++ .../core/src/ux_host_stack_bandwidth_check.c | 270 +++ .../core/src/ux_host_stack_bandwidth_claim.c | 244 ++ .../src/ux_host_stack_bandwidth_release.c | 240 ++ common/core/src/ux_host_stack_class_call.c | 109 + .../src/ux_host_stack_class_device_scan.c | 132 ++ common/core/src/ux_host_stack_class_get.c | 124 ++ .../src/ux_host_stack_class_instance_create.c | 114 + .../ux_host_stack_class_instance_destroy.c | 143 ++ .../src/ux_host_stack_class_instance_get.c | 113 + .../src/ux_host_stack_class_instance_verify.c | 132 ++ .../src/ux_host_stack_class_interface_scan.c | 196 ++ .../core/src/ux_host_stack_class_register.c | 153 ++ ...ost_stack_configuration_descriptor_parse.c | 137 ++ .../ux_host_stack_configuration_enumerate.c | 189 ++ ...host_stack_configuration_instance_create.c | 108 + ...host_stack_configuration_instance_delete.c | 107 + ...x_host_stack_configuration_interface_get.c | 183 ++ .../src/ux_host_stack_configuration_set.c | 155 ++ common/core/src/ux_host_stack_delay_ms.c | 74 + .../src/ux_host_stack_device_address_set.c | 158 ++ .../ux_host_stack_device_configuration_get.c | 134 ++ ...ux_host_stack_device_configuration_reset.c | 123 + ...x_host_stack_device_configuration_select.c | 160 ++ .../ux_host_stack_device_descriptor_read.c | 164 ++ common/core/src/ux_host_stack_device_get.c | 133 ++ common/core/src/ux_host_stack_device_remove.c | 192 ++ .../src/ux_host_stack_device_resources_free.c | 216 ++ .../ux_host_stack_endpoint_instance_create.c | 152 ++ .../ux_host_stack_endpoint_instance_delete.c | 119 + .../core/src/ux_host_stack_endpoint_reset.c | 118 + .../ux_host_stack_endpoint_transfer_abort.c | 92 + .../src/ux_host_stack_enum_thread_entry.c | 108 + common/core/src/ux_host_stack_hcd_register.c | 135 ++ .../core/src/ux_host_stack_hcd_thread_entry.c | 112 + .../src/ux_host_stack_hcd_transfer_request.c | 91 + .../ux_host_stack_hnp_polling_thread_entry.c | 232 ++ common/core/src/ux_host_stack_initialize.c | 345 +++ .../ux_host_stack_interface_endpoint_get.c | 132 ++ .../ux_host_stack_interface_instance_create.c | 105 + .../ux_host_stack_interface_instance_delete.c | 99 + common/core/src/ux_host_stack_interface_set.c | 103 + .../ux_host_stack_interface_setting_select.c | 203 ++ .../core/src/ux_host_stack_interfaces_scan.c | 201 ++ .../ux_host_stack_new_configuration_create.c | 116 + .../src/ux_host_stack_new_device_create.c | 244 ++ .../core/src/ux_host_stack_new_device_get.c | 107 + .../src/ux_host_stack_new_endpoint_create.c | 139 ++ .../src/ux_host_stack_new_interface_create.c | 207 ++ .../src/ux_host_stack_rh_change_process.c | 173 ++ .../src/ux_host_stack_rh_device_extraction.c | 86 + .../src/ux_host_stack_rh_device_insertion.c | 170 ++ common/core/src/ux_host_stack_role_swap.c | 115 + .../core/src/ux_host_stack_transfer_request.c | 177 ++ .../ux_host_stack_transfer_request_abort.c | 137 ++ common/core/src/ux_system_error_handler.c | 87 + common/core/src/ux_system_initialize.c | 241 ++ common/core/src/ux_system_uninitialize.c | 77 + common/core/src/ux_trace_event_insert.c | 140 ++ common/core/src/ux_trace_event_update.c | 128 ++ common/core/src/ux_trace_object_register.c | 91 + common/core/src/ux_trace_object_unregister.c | 86 + .../src/ux_utility_debug_callback_register.c | 83 + common/core/src/ux_utility_debug_log.c | 310 +++ common/core/src/ux_utility_delay_ms.c | 87 + common/core/src/ux_utility_descriptor_pack.c | 114 + common/core/src/ux_utility_descriptor_parse.c | 114 + .../src/ux_utility_error_callback_register.c | 78 + .../core/src/ux_utility_event_flags_create.c | 91 + .../core/src/ux_utility_event_flags_delete.c | 79 + common/core/src/ux_utility_event_flags_get.c | 89 + common/core/src/ux_utility_event_flags_set.c | 83 + common/core/src/ux_utility_long_get.c | 84 + .../core/src/ux_utility_long_get_big_endian.c | 83 + common/core/src/ux_utility_long_put.c | 82 + .../core/src/ux_utility_long_put_big_endian.c | 90 + common/core/src/ux_utility_memory_allocate.c | 354 +++ .../src/ux_utility_memory_allocate_add_safe.c | 76 + .../ux_utility_memory_allocate_mulc_safe.c | 80 + .../ux_utility_memory_allocate_mulv_safe.c | 80 + common/core/src/ux_utility_memory_compare.c | 98 + common/core/src/ux_utility_memory_copy.c | 92 + common/core/src/ux_utility_memory_free.c | 182 ++ .../ux_utility_memory_free_block_best_get.c | 141 ++ common/core/src/ux_utility_memory_set.c | 90 + common/core/src/ux_utility_mutex_create.c | 92 + common/core/src/ux_utility_mutex_delete.c | 80 + common/core/src/ux_utility_mutex_off.c | 77 + common/core/src/ux_utility_mutex_on.c | 87 + common/core/src/ux_utility_pci_class_scan.c | 130 ++ common/core/src/ux_utility_pci_read.c | 123 + common/core/src/ux_utility_pci_write.c | 127 ++ common/core/src/ux_utility_physical_address.c | 82 + common/core/src/ux_utility_semaphore_create.c | 93 + common/core/src/ux_utility_semaphore_delete.c | 79 + common/core/src/ux_utility_semaphore_get.c | 107 + common/core/src/ux_utility_semaphore_put.c | 79 + .../src/ux_utility_set_interrupt_handler.c | 77 + common/core/src/ux_utility_short_get.c | 82 + .../src/ux_utility_short_get_big_endian.c | 80 + common/core/src/ux_utility_short_put.c | 80 + .../src/ux_utility_short_put_big_endian.c | 89 + .../core/src/ux_utility_string_length_check.c | 110 + .../core/src/ux_utility_string_length_get.c | 91 + .../core/src/ux_utility_string_to_unicode.c | 109 + common/core/src/ux_utility_thread_create.c | 105 + common/core/src/ux_utility_thread_delete.c | 84 + common/core/src/ux_utility_thread_identify.c | 77 + .../core/src/ux_utility_thread_relinquish.c | 73 + common/core/src/ux_utility_thread_resume.c | 80 + .../src/ux_utility_thread_schedule_other.c | 100 + common/core/src/ux_utility_thread_sleep.c | 79 + common/core/src/ux_utility_thread_suspend.c | 80 + common/core/src/ux_utility_timer_create.c | 90 + .../core/src/ux_utility_unicode_to_string.c | 94 + common/core/src/ux_utility_virtual_address.c | 81 + common/usbx_device_classes/CMakeLists.txt | 151 ++ .../inc/ux_device_class_audio.h | 421 ++++ .../inc/ux_device_class_audio10.h | 360 +++ .../inc/ux_device_class_audio20.h | 418 ++++ .../inc/ux_device_class_cdc_acm.h | 259 +++ .../inc/ux_device_class_cdc_ecm.h | 337 +++ .../inc/ux_device_class_dfu.h | 183 ++ .../inc/ux_device_class_hid.h | 180 ++ .../inc/ux_device_class_pima.h | 996 +++++++++ .../inc/ux_device_class_rndis.h | 570 +++++ .../inc/ux_device_class_storage.h | 527 +++++ .../ux_device_class_audio10_control_process.c | 237 ++ .../ux_device_class_audio20_control_process.c | 295 +++ .../src/ux_device_class_audio_activate.c | 166 ++ .../src/ux_device_class_audio_change.c | 192 ++ .../ux_device_class_audio_control_request.c | 101 + .../src/ux_device_class_audio_deactivate.c | 116 + .../src/ux_device_class_audio_entry.c | 158 ++ .../src/ux_device_class_audio_frame_write.c | 127 ++ .../src/ux_device_class_audio_initialize.c | 238 ++ .../src/ux_device_class_audio_ioctl.c | 106 + .../ux_device_class_audio_read_frame_free.c | 124 ++ .../ux_device_class_audio_read_frame_get.c | 112 + .../ux_device_class_audio_read_thread_entry.c | 170 ++ .../ux_device_class_audio_reception_start.c | 104 + .../src/ux_device_class_audio_sample_read16.c | 141 ++ .../src/ux_device_class_audio_sample_read24.c | 141 ++ .../src/ux_device_class_audio_sample_read32.c | 143 ++ .../src/ux_device_class_audio_sample_read8.c | 141 ++ .../src/ux_device_class_audio_stream_get.c | 93 + ...ux_device_class_audio_transmission_start.c | 104 + .../src/ux_device_class_audio_unitialize.c | 104 + ...ux_device_class_audio_write_frame_commit.c | 126 ++ .../ux_device_class_audio_write_frame_get.c | 113 + ...ux_device_class_audio_write_thread_entry.c | 172 ++ .../src/ux_device_class_cdc_acm_activate.c | 106 + .../ux_device_class_cdc_acm_bulkin_thread.c | 210 ++ .../ux_device_class_cdc_acm_bulkout_thread.c | 149 ++ .../ux_device_class_cdc_acm_control_request.c | 179 ++ .../src/ux_device_class_cdc_acm_deactivate.c | 136 ++ .../src/ux_device_class_cdc_acm_entry.c | 147 ++ .../src/ux_device_class_cdc_acm_initialize.c | 140 ++ .../src/ux_device_class_cdc_acm_ioctl.c | 428 ++++ .../src/ux_device_class_cdc_acm_read.c | 220 ++ .../src/ux_device_class_cdc_acm_unitialize.c | 100 + .../src/ux_device_class_cdc_acm_write.c | 227 ++ ...device_class_cdc_acm_write_with_callback.c | 132 ++ .../src/ux_device_class_cdc_ecm_activate.c | 244 ++ .../ux_device_class_cdc_ecm_bulkin_thread.c | 229 ++ .../ux_device_class_cdc_ecm_bulkout_thread.c | 178 ++ .../src/ux_device_class_cdc_ecm_change.c | 200 ++ .../ux_device_class_cdc_ecm_control_request.c | 127 ++ .../src/ux_device_class_cdc_ecm_deactivate.c | 158 ++ .../src/ux_device_class_cdc_ecm_entry.c | 161 ++ .../src/ux_device_class_cdc_ecm_initialize.c | 266 +++ ...ux_device_class_cdc_ecm_interrupt_thread.c | 152 ++ .../ux_device_class_cdc_ecm_uninitialize.c | 132 ++ .../src/ux_device_class_cdc_ecm_write.c | 130 ++ .../src/ux_device_class_dfu_activate.c | 150 ++ .../src/ux_device_class_dfu_control_request.c | 635 ++++++ .../src/ux_device_class_dfu_deactivate.c | 127 ++ .../src/ux_device_class_dfu_entry.c | 139 ++ .../src/ux_device_class_dfu_initialize.c | 213 ++ .../src/ux_device_class_dfu_thread.c | 134 ++ .../src/ux_device_class_hid_activate.c | 137 ++ .../src/ux_device_class_hid_control_request.c | 190 ++ .../src/ux_device_class_hid_deactivate.c | 99 + .../src/ux_device_class_hid_descriptor_send.c | 212 ++ .../src/ux_device_class_hid_entry.c | 147 ++ .../src/ux_device_class_hid_event_get.c | 115 + .../src/ux_device_class_hid_event_set.c | 157 ++ .../src/ux_device_class_hid_initialize.c | 184 ++ .../ux_device_class_hid_interrupt_thread.c | 178 ++ .../src/ux_device_class_hid_report_get.c | 172 ++ .../src/ux_device_class_hid_report_set.c | 146 ++ .../src/ux_device_class_hid_uninitialize.c | 102 + .../src/ux_device_class_pima_activate.c | 146 ++ .../ux_device_class_pima_control_request.c | 159 ++ .../src/ux_device_class_pima_data.c | 274 +++ .../src/ux_device_class_pima_deactivate.c | 105 + .../ux_device_class_pima_device_info_send.c | 309 +++ ...x_device_class_pima_device_prop_desc_get.c | 156 ++ ..._device_class_pima_device_prop_value_get.c | 142 ++ ..._device_class_pima_device_prop_value_set.c | 139 ++ .../src/ux_device_class_pima_device_reset.c | 103 + .../src/ux_device_class_pima_entry.c | 138 ++ .../src/ux_device_class_pima_event_get.c | 112 + .../src/ux_device_class_pima_event_set.c | 135 ++ .../src/ux_device_class_pima_initialize.c | 215 ++ .../ux_device_class_pima_interrupt_thread.c | 175 ++ .../src/ux_device_class_pima_object_add.c | 98 + .../ux_device_class_pima_object_data_get.c | 287 +++ .../ux_device_class_pima_object_data_send.c | 254 +++ .../src/ux_device_class_pima_object_delete.c | 98 + ...ux_device_class_pima_object_handles_send.c | 147 ++ .../ux_device_class_pima_object_info_get.c | 188 ++ .../ux_device_class_pima_object_info_send.c | 244 ++ ...x_device_class_pima_object_prop_desc_get.c | 169 ++ ..._device_class_pima_object_prop_value_get.c | 169 ++ ..._device_class_pima_object_prop_value_set.c | 152 ++ ...ce_class_pima_object_props_supported_get.c | 190 ++ ..._device_class_pima_object_references_get.c | 163 ++ ..._device_class_pima_object_references_set.c | 145 ++ ...ux_device_class_pima_objects_number_send.c | 102 + ...evice_class_pima_partial_object_data_get.c | 307 +++ .../src/ux_device_class_pima_response_send.c | 141 ++ .../src/ux_device_class_pima_storage_format.c | 98 + .../ux_device_class_pima_storage_id_send.c | 125 ++ .../ux_device_class_pima_storage_info_get.c | 181 ++ .../src/ux_device_class_pima_thread.c | 465 ++++ .../src/ux_device_class_rndis_activate.c | 208 ++ .../src/ux_device_class_rndis_bulkin_thread.c | 223 ++ .../ux_device_class_rndis_bulkout_thread.c | 210 ++ .../ux_device_class_rndis_control_request.c | 200 ++ .../src/ux_device_class_rndis_deactivate.c | 129 ++ .../src/ux_device_class_rndis_entry.c | 142 ++ .../src/ux_device_class_rndis_initialize.c | 367 +++ .../ux_device_class_rndis_interrupt_thread.c | 132 ++ .../ux_device_class_rndis_msg_initialize.c | 142 ++ .../ux_device_class_rndis_msg_keep_alive.c | 107 + .../src/ux_device_class_rndis_msg_query.c | 357 +++ .../src/ux_device_class_rndis_msg_reset.c | 101 + .../src/ux_device_class_rndis_msg_set.c | 135 ++ .../src/ux_device_class_rndis_write.c | 129 ++ .../src/ux_device_class_storage_activate.c | 112 + .../ux_device_class_storage_control_request.c | 163 ++ .../src/ux_device_class_storage_csw_send.c | 113 + .../src/ux_device_class_storage_deactivate.c | 129 ++ .../src/ux_device_class_storage_entry.c | 153 ++ .../src/ux_device_class_storage_format.c | 102 + ...x_device_class_storage_get_configuration.c | 508 +++++ .../ux_device_class_storage_get_performance.c | 152 ++ ...ce_class_storage_get_status_notification.c | 156 ++ .../src/ux_device_class_storage_initialize.c | 205 ++ .../src/ux_device_class_storage_inquiry.c | 207 ++ .../src/ux_device_class_storage_mode_select.c | 105 + .../src/ux_device_class_storage_mode_sense.c | 261 +++ ...lass_storage_prevent_allow_media_removal.c | 96 + .../src/ux_device_class_storage_read.c | 226 ++ .../ux_device_class_storage_read_capacity.c | 146 ++ ...vice_class_storage_read_disk_information.c | 159 ++ ..._device_class_storage_read_dvd_structure.c | 276 +++ ...evice_class_storage_read_format_capacity.c | 127 ++ .../src/ux_device_class_storage_read_toc.c | 188 ++ .../src/ux_device_class_storage_report_key.c | 153 ++ .../ux_device_class_storage_request_sense.c | 132 ++ .../src/ux_device_class_storage_start_stop.c | 91 + ...x_device_class_storage_synchronize_cache.c | 174 ++ .../src/ux_device_class_storage_test_ready.c | 116 + .../src/ux_device_class_storage_thread.c | 417 ++++ .../ux_device_class_storage_uninitialize.c | 100 + .../src/ux_device_class_storage_verify.c | 91 + .../src/ux_device_class_storage_write.c | 238 ++ common/usbx_host_classes/CMakeLists.txt | 267 +++ .../inc/ux_host_class_asix.h | 413 ++++ .../inc/ux_host_class_audio.h | 445 ++++ .../inc/ux_host_class_cdc_acm.h | 315 +++ .../inc/ux_host_class_cdc_ecm.h | 289 +++ .../inc/ux_host_class_gser.h | 261 +++ .../usbx_host_classes/inc/ux_host_class_hid.h | 1030 +++++++++ .../inc/ux_host_class_hid_keyboard.h | 219 ++ .../inc/ux_host_class_hid_mouse.h | 107 + .../inc/ux_host_class_hid_remote_control.h | 87 + .../usbx_host_classes/inc/ux_host_class_hub.h | 198 ++ .../inc/ux_host_class_pima.h | 570 +++++ .../inc/ux_host_class_printer.h | 117 + .../inc/ux_host_class_prolific.h | 293 +++ .../inc/ux_host_class_storage.h | 451 ++++ .../inc/ux_host_class_swar.h | 144 ++ .../inc/ux_host_class_video.h | 635 ++++++ .../src/ux_host_class_asix_activate.c | 281 +++ .../src/ux_host_class_asix_configure.c | 153 ++ .../src/ux_host_class_asix_deactivate.c | 216 ++ .../src/ux_host_class_asix_endpoints_get.c | 267 +++ .../src/ux_host_class_asix_entry.c | 132 ++ ...x_host_class_asix_interrupt_notification.c | 189 ++ .../src/ux_host_class_asix_read.c | 181 ++ .../ux_host_class_asix_reception_callback.c | 158 ++ .../src/ux_host_class_asix_setup.c | 642 ++++++ .../src/ux_host_class_asix_thread.c | 420 ++++ ...ux_host_class_asix_transmission_callback.c | 146 ++ .../src/ux_host_class_asix_write.c | 195 ++ .../src/ux_host_class_audio_activate.c | 209 ++ ...ost_class_audio_alternate_setting_locate.c | 244 ++ .../src/ux_host_class_audio_configure.c | 174 ++ .../src/ux_host_class_audio_control_get.c | 212 ++ .../ux_host_class_audio_control_value_get.c | 149 ++ .../ux_host_class_audio_control_value_set.c | 154 ++ .../src/ux_host_class_audio_deactivate.c | 129 ++ .../src/ux_host_class_audio_descriptor_get.c | 158 ++ ...ost_class_audio_device_controls_list_get.c | 253 +++ .../src/ux_host_class_audio_device_type_get.c | 220 ++ .../src/ux_host_class_audio_endpoints_get.c | 118 + .../src/ux_host_class_audio_entry.c | 128 ++ .../src/ux_host_class_audio_read.c | 141 ++ ..._host_class_audio_streaming_sampling_get.c | 395 ++++ ..._host_class_audio_streaming_sampling_set.c | 187 ++ ..._host_class_audio_streaming_terminal_get.c | 191 ++ .../ux_host_class_audio_transfer_request.c | 103 + ...t_class_audio_transfer_request_completed.c | 96 + .../src/ux_host_class_audio_write.c | 137 ++ .../src/ux_host_class_cdc_acm_activate.c | 242 ++ .../ux_host_class_cdc_acm_capabilities_get.c | 260 +++ .../src/ux_host_class_cdc_acm_command.c | 163 ++ .../src/ux_host_class_cdc_acm_configure.c | 146 ++ .../src/ux_host_class_cdc_acm_deactivate.c | 170 ++ .../src/ux_host_class_cdc_acm_endpoints_get.c | 212 ++ .../src/ux_host_class_cdc_acm_entry.c | 152 ++ .../src/ux_host_class_cdc_acm_ioctl.c | 294 +++ .../src/ux_host_class_cdc_acm_read.c | 200 ++ ...ux_host_class_cdc_acm_reception_callback.c | 142 ++ .../ux_host_class_cdc_acm_reception_start.c | 142 ++ .../ux_host_class_cdc_acm_reception_stop.c | 134 ++ ...class_cdc_acm_transfer_request_completed.c | 111 + .../src/ux_host_class_cdc_acm_write.c | 200 ++ .../src/ux_host_class_cdc_ecm_activate.c | 373 ++++ .../src/ux_host_class_cdc_ecm_deactivate.c | 203 ++ .../src/ux_host_class_cdc_ecm_endpoints_get.c | 266 +++ .../src/ux_host_class_cdc_ecm_entry.c | 154 ++ ...ost_class_cdc_ecm_interrupt_notification.c | 174 ++ .../ux_host_class_cdc_ecm_mac_address_get.c | 346 +++ .../src/ux_host_class_cdc_ecm_thread.c | 244 ++ ...host_class_cdc_ecm_transmission_callback.c | 162 ++ ..._host_class_cdc_ecm_transmit_queue_clean.c | 140 ++ .../src/ux_host_class_cdc_ecm_write.c | 216 ++ .../src/ux_host_class_gser_activate.c | 149 ++ .../src/ux_host_class_gser_command.c | 163 ++ .../src/ux_host_class_gser_configure.c | 164 ++ .../src/ux_host_class_gser_deactivate.c | 130 ++ .../src/ux_host_class_gser_endpoints_get.c | 164 ++ .../src/ux_host_class_gser_entry.c | 120 + .../src/ux_host_class_gser_ioctl.c | 287 +++ .../src/ux_host_class_gser_read.c | 210 ++ .../ux_host_class_gser_reception_callback.c | 147 ++ .../src/ux_host_class_gser_reception_start.c | 135 ++ .../src/ux_host_class_gser_reception_stop.c | 111 + .../src/ux_host_class_gser_write.c | 204 ++ .../src/ux_host_class_hid_activate.c | 190 ++ .../src/ux_host_class_hid_client_register.c | 183 ++ .../src/ux_host_class_hid_client_search.c | 149 ++ .../src/ux_host_class_hid_configure.c | 139 ++ .../src/ux_host_class_hid_deactivate.c | 155 ++ .../src/ux_host_class_hid_descriptor_parse.c | 254 +++ .../src/ux_host_class_hid_entry.c | 125 ++ .../src/ux_host_class_hid_field_decompress.c | 158 ++ .../src/ux_host_class_hid_global_item_parse.c | 249 +++ .../src/ux_host_class_hid_idle_get.c | 143 ++ .../src/ux_host_class_hid_idle_set.c | 121 + .../src/ux_host_class_hid_instance_clean.c | 206 ++ ...host_class_hid_interrupt_endpoint_search.c | 135 ++ .../src/ux_host_class_hid_item_data_get.c | 108 + .../src/ux_host_class_hid_keyboard_activate.c | 414 ++++ .../src/ux_host_class_hid_keyboard_callback.c | 570 +++++ .../ux_host_class_hid_keyboard_deactivate.c | 129 ++ .../src/ux_host_class_hid_keyboard_entry.c | 121 + .../src/ux_host_class_hid_keyboard_ioctl.c | 124 ++ .../src/ux_host_class_hid_keyboard_key_get.c | 123 + .../src/ux_host_class_hid_keyboard_thread.c | 137 ++ .../src/ux_host_class_hid_local_item_parse.c | 250 +++ .../src/ux_host_class_hid_main_item_parse.c | 183 ++ .../src/ux_host_class_hid_mouse_activate.c | 185 ++ .../src/ux_host_class_hid_mouse_buttons_get.c | 100 + .../src/ux_host_class_hid_mouse_callback.c | 149 ++ .../src/ux_host_class_hid_mouse_deactivate.c | 108 + .../src/ux_host_class_hid_mouse_entry.c | 119 + .../ux_host_class_hid_mouse_position_get.c | 106 + .../src/ux_host_class_hid_mouse_wheel_get.c | 100 + .../ux_host_class_hid_periodic_report_start.c | 135 ++ .../ux_host_class_hid_periodic_report_stop.c | 113 + ...x_host_class_hid_remote_control_activate.c | 180 ++ ...x_host_class_hid_remote_control_callback.c | 131 ++ ...host_class_hid_remote_control_deactivate.c | 115 + .../ux_host_class_hid_remote_control_entry.c | 121 + ..._host_class_hid_remote_control_usage_get.c | 125 ++ .../src/ux_host_class_hid_report_add.c | 291 +++ ..._host_class_hid_report_callback_register.c | 146 ++ .../src/ux_host_class_hid_report_compress.c | 207 ++ .../src/ux_host_class_hid_report_decompress.c | 113 + .../ux_host_class_hid_report_descriptor_get.c | 204 ++ .../src/ux_host_class_hid_report_get.c | 252 +++ .../src/ux_host_class_hid_report_id_get.c | 178 ++ .../ux_host_class_hid_report_item_analyse.c | 128 ++ .../src/ux_host_class_hid_report_set.c | 248 +++ .../src/ux_host_class_hid_resources_free.c | 78 + ...ost_class_hid_transfer_request_completed.c | 269 +++ .../src/ux_host_class_hub_activate.c | 172 ++ .../src/ux_host_class_hub_change_detect.c | 118 + .../src/ux_host_class_hub_change_process.c | 115 + .../src/ux_host_class_hub_configure.c | 183 ++ .../src/ux_host_class_hub_deactivate.c | 151 ++ .../src/ux_host_class_hub_descriptor_get.c | 183 ++ .../src/ux_host_class_hub_entry.c | 125 ++ .../src/ux_host_class_hub_feature.c | 106 + .../ux_host_class_hub_hub_change_process.c | 79 + ..._host_class_hub_interrupt_endpoint_start.c | 139 ++ ...class_hub_port_change_connection_process.c | 220 ++ ...ost_class_hub_port_change_enable_process.c | 86 + ...ass_hub_port_change_over_current_process.c | 89 + .../ux_host_class_hub_port_change_process.c | 114 + ...host_class_hub_port_change_reset_process.c | 86 + ...st_class_hub_port_change_suspend_process.c | 86 + .../src/ux_host_class_hub_port_reset.c | 128 ++ .../src/ux_host_class_hub_ports_power.c | 130 ++ .../src/ux_host_class_hub_status_get.c | 139 ++ ...ost_class_hub_transfer_request_completed.c | 125 ++ .../src/ux_host_class_pima_activate.c | 176 ++ .../src/ux_host_class_pima_command.c | 298 +++ .../src/ux_host_class_pima_configure.c | 146 ++ .../src/ux_host_class_pima_deactivate.c | 186 ++ .../src/ux_host_class_pima_device_info_get.c | 351 +++ .../src/ux_host_class_pima_device_reset.c | 101 + .../src/ux_host_class_pima_endpoints_get.c | 233 ++ .../src/ux_host_class_pima_entry.c | 122 + .../src/ux_host_class_pima_notification.c | 217 ++ .../src/ux_host_class_pima_num_objects_get.c | 120 + .../src/ux_host_class_pima_object_close.c | 219 ++ .../src/ux_host_class_pima_object_copy.c | 121 + .../src/ux_host_class_pima_object_delete.c | 116 + .../src/ux_host_class_pima_object_get.c | 412 ++++ .../ux_host_class_pima_object_handles_get.c | 176 ++ .../src/ux_host_class_pima_object_info_get.c | 234 ++ .../src/ux_host_class_pima_object_info_send.c | 252 +++ .../src/ux_host_class_pima_object_move.c | 121 + .../src/ux_host_class_pima_object_open.c | 104 + .../src/ux_host_class_pima_object_send.c | 376 ++++ ...ux_host_class_pima_object_transfer_abort.c | 108 + .../src/ux_host_class_pima_read.c | 287 +++ .../src/ux_host_class_pima_request_cancel.c | 188 ++ .../src/ux_host_class_pima_session_close.c | 123 + .../src/ux_host_class_pima_session_open.c | 128 ++ .../src/ux_host_class_pima_storage_ids_get.c | 158 ++ .../src/ux_host_class_pima_storage_info_get.c | 157 ++ .../src/ux_host_class_pima_thumb_get.c | 372 ++++ .../src/ux_host_class_pima_write.c | 297 +++ .../src/ux_host_class_printer_activate.c | 162 ++ .../src/ux_host_class_printer_configure.c | 145 ++ .../src/ux_host_class_printer_deactivate.c | 136 ++ .../src/ux_host_class_printer_endpoints_get.c | 163 ++ .../src/ux_host_class_printer_entry.c | 116 + .../src/ux_host_class_printer_name_get.c | 197 ++ .../src/ux_host_class_printer_read.c | 227 ++ .../src/ux_host_class_printer_soft_reset.c | 125 ++ .../src/ux_host_class_printer_status_get.c | 162 ++ .../src/ux_host_class_printer_write.c | 206 ++ .../src/ux_host_class_prolific_activate.c | 192 ++ .../src/ux_host_class_prolific_command.c | 119 + .../src/ux_host_class_prolific_configure.c | 153 ++ .../src/ux_host_class_prolific_deactivate.c | 163 ++ .../ux_host_class_prolific_endpoints_get.c | 248 +++ .../src/ux_host_class_prolific_entry.c | 118 + .../src/ux_host_class_prolific_ioctl.c | 332 +++ .../src/ux_host_class_prolific_read.c | 191 ++ ...x_host_class_prolific_reception_callback.c | 155 ++ .../ux_host_class_prolific_reception_start.c | 128 ++ .../ux_host_class_prolific_reception_stop.c | 109 + .../src/ux_host_class_prolific_setup.c | 416 ++++ ...lass_prolific_transfer_request_completed.c | 127 ++ .../src/ux_host_class_prolific_write.c | 190 ++ .../src/ux_host_class_storage_activate.c | 168 ++ .../ux_host_class_storage_cbw_initialize.c | 111 + .../src/ux_host_class_storage_configure.c | 148 ++ .../src/ux_host_class_storage_deactivate.c | 208 ++ .../ux_host_class_storage_device_initialize.c | 169 ++ .../src/ux_host_class_storage_device_reset.c | 117 + ..._host_class_storage_device_support_check.c | 137 ++ .../src/ux_host_class_storage_driver_entry.c | 214 ++ .../src/ux_host_class_storage_endpoints_get.c | 210 ++ .../src/ux_host_class_storage_entry.c | 185 ++ .../src/ux_host_class_storage_max_lun_get.c | 147 ++ ...ux_host_class_storage_media_capacity_get.c | 180 ++ ..._class_storage_media_characteristics_get.c | 134 ++ ..._class_storage_media_format_capacity_get.c | 122 + .../src/ux_host_class_storage_media_mount.c | 190 ++ .../src/ux_host_class_storage_media_open.c | 162 ++ ...ost_class_storage_media_protection_check.c | 160 ++ .../src/ux_host_class_storage_media_read.c | 155 ++ ...t_class_storage_media_recovery_sense_get.c | 136 ++ .../src/ux_host_class_storage_media_write.c | 134 ++ .../ux_host_class_storage_partition_read.c | 127 ++ .../src/ux_host_class_storage_request_sense.c | 152 ++ ..._host_class_storage_sense_code_translate.c | 80 + .../src/ux_host_class_storage_start_stop.c | 127 ++ .../src/ux_host_class_storage_thread_entry.c | 306 +++ .../src/ux_host_class_storage_transport.c | 154 ++ .../src/ux_host_class_storage_transport_bo.c | 387 ++++ .../src/ux_host_class_storage_transport_cb.c | 176 ++ .../src/ux_host_class_storage_transport_cbi.c | 199 ++ .../ux_host_class_storage_unit_ready_test.c | 108 + .../src/ux_host_class_swar_activate.c | 155 ++ .../src/ux_host_class_swar_configure.c | 148 ++ .../src/ux_host_class_swar_deactivate.c | 141 ++ .../src/ux_host_class_swar_endpoints_get.c | 164 ++ .../src/ux_host_class_swar_entry.c | 120 + .../src/ux_host_class_swar_ioctl.c | 127 ++ .../src/ux_host_class_swar_read.c | 205 ++ .../ux_host_class_swar_reception_callback.c | 143 ++ .../src/ux_host_class_swar_reception_start.c | 129 ++ .../src/ux_host_class_swar_reception_stop.c | 110 + .../src/ux_host_class_swar_write.c | 207 ++ .../src/ux_host_class_video_activate.c | 200 ++ ...ost_class_video_alternate_setting_locate.c | 155 ++ .../src/ux_host_class_video_channel_start.c | 268 +++ .../src/ux_host_class_video_configure.c | 179 ++ .../src/ux_host_class_video_control_get.c | 212 ++ .../ux_host_class_video_control_list_get.c | 190 ++ .../ux_host_class_video_control_value_get.c | 172 ++ .../ux_host_class_video_control_value_set.c | 168 ++ .../src/ux_host_class_video_deactivate.c | 131 ++ .../src/ux_host_class_video_descriptor_get.c | 158 ++ .../src/ux_host_class_video_endpoints_get.c | 119 + .../src/ux_host_class_video_entry.c | 129 ++ .../src/ux_host_class_video_format_data_get.c | 176 ++ .../src/ux_host_class_video_frame_data_get.c | 176 ++ .../ux_host_class_video_frame_interval_get.c | 199 ++ ...ux_host_class_video_frame_parameters_set.c | 290 +++ .../ux_host_class_video_input_format_get.c | 201 ++ .../ux_host_class_video_input_terminal_get.c | 195 ++ .../src/ux_host_class_video_ioctl.c | 216 ++ .../src/ux_host_class_video_max_payload_get.c | 77 + .../src/ux_host_class_video_read.c | 151 ++ .../src/ux_host_class_video_start.c | 93 + .../src/ux_host_class_video_stop.c | 144 ++ .../ux_host_class_video_transfer_buffer_add.c | 161 ++ ...ux_host_class_video_transfer_buffers_add.c | 208 ++ ...x_host_class_video_transfer_callback_set.c | 77 + .../ux_host_class_video_transfer_request.c | 130 ++ ...st_class_video_transfer_request_callback.c | 102 + ...t_class_video_transfer_request_completed.c | 96 + common/usbx_host_controllers/CMakeLists.txt | 93 + .../usbx_host_controllers/inc/ux_hcd_ehci.h | 822 +++++++ .../usbx_host_controllers/inc/ux_hcd_ohci.h | 393 ++++ .../src/ux_hcd_ehci_asynch_td_process.c | 209 ++ ...ux_hcd_ehci_asynchronous_endpoint_create.c | 172 ++ ...x_hcd_ehci_asynchronous_endpoint_destroy.c | 115 + .../src/ux_hcd_ehci_controller_disable.c | 106 + .../src/ux_hcd_ehci_done_queue_process.c | 167 ++ .../src/ux_hcd_ehci_door_bell_wait.c | 101 + .../src/ux_hcd_ehci_ed_clean.c | 110 + .../src/ux_hcd_ehci_ed_obtain.c | 107 + .../src/ux_hcd_ehci_endpoint_reset.c | 91 + .../src/ux_hcd_ehci_entry.c | 268 +++ .../src/ux_hcd_ehci_frame_number_get.c | 88 + .../src/ux_hcd_ehci_frame_number_set.c | 95 + .../src/ux_hcd_ehci_fsisochronous_td_obtain.c | 116 + .../ux_hcd_ehci_fsisochronous_tds_process.c | 88 + .../src/ux_hcd_ehci_hsisochronous_td_obtain.c | 116 + .../ux_hcd_ehci_hsisochronous_tds_process.c | 443 ++++ .../src/ux_hcd_ehci_initialize.c | 416 ++++ .../ux_hcd_ehci_interrupt_endpoint_create.c | 400 ++++ .../ux_hcd_ehci_interrupt_endpoint_destroy.c | 195 ++ .../src/ux_hcd_ehci_interrupt_handler.c | 189 ++ .../ux_hcd_ehci_isochronous_endpoint_create.c | 546 +++++ ...ux_hcd_ehci_isochronous_endpoint_destroy.c | 275 +++ .../src/ux_hcd_ehci_least_traffic_list_get.c | 165 ++ .../src/ux_hcd_ehci_next_td_clean.c | 79 + .../ux_hcd_ehci_periodic_descriptor_link.c | 167 ++ .../src/ux_hcd_ehci_periodic_tree_create.c | 185 ++ .../src/ux_hcd_ehci_poll_rate_entry_get.c | 103 + .../src/ux_hcd_ehci_port_disable.c | 104 + .../src/ux_hcd_ehci_port_reset.c | 240 ++ .../src/ux_hcd_ehci_port_resume.c | 80 + .../src/ux_hcd_ehci_port_status_get.c | 252 +++ .../src/ux_hcd_ehci_port_suspend.c | 107 + .../src/ux_hcd_ehci_power_down_port.c | 81 + .../src/ux_hcd_ehci_power_on_port.c | 80 + .../src/ux_hcd_ehci_power_root_hubs.c | 105 + .../src/ux_hcd_ehci_register_read.c | 78 + .../src/ux_hcd_ehci_register_write.c | 81 + .../src/ux_hcd_ehci_regular_td_obtain.c | 122 + .../src/ux_hcd_ehci_request_bulk_transfer.c | 168 ++ .../ux_hcd_ehci_request_control_transfer.c | 234 ++ .../ux_hcd_ehci_request_interrupt_transfer.c | 134 ++ ...ux_hcd_ehci_request_isochronous_transfer.c | 178 ++ .../src/ux_hcd_ehci_request_transfer.c | 126 ++ .../src/ux_hcd_ehci_request_transfer_add.c | 156 ++ .../src/ux_hcd_ehci_transfer_abort.c | 181 ++ .../ux_hcd_ehci_transfer_request_process.c | 92 + ...ux_hcd_ohci_asynchronous_endpoint_create.c | 167 ++ ...x_hcd_ohci_asynchronous_endpoint_destroy.c | 201 ++ .../src/ux_hcd_ohci_controller_disable.c | 93 + .../src/ux_hcd_ohci_done_queue_process.c | 340 +++ .../src/ux_hcd_ohci_ed_obtain.c | 103 + .../src/ux_hcd_ohci_endpoint_error_clear.c | 90 + .../src/ux_hcd_ohci_endpoint_reset.c | 89 + .../src/ux_hcd_ohci_entry.c | 274 +++ .../src/ux_hcd_ohci_frame_number_get.c | 83 + .../src/ux_hcd_ohci_frame_number_set.c | 81 + .../src/ux_hcd_ohci_initialize.c | 224 ++ .../ux_hcd_ohci_interrupt_endpoint_create.c | 210 ++ .../src/ux_hcd_ohci_interrupt_handler.c | 179 ++ .../ux_hcd_ohci_isochronous_endpoint_create.c | 133 ++ .../src/ux_hcd_ohci_isochronous_td_obtain.c | 103 + .../src/ux_hcd_ohci_least_traffic_list_get.c | 133 ++ .../src/ux_hcd_ohci_next_td_clean.c | 118 + .../ux_hcd_ohci_periodic_endpoint_destroy.c | 155 ++ .../src/ux_hcd_ohci_periodic_tree_create.c | 147 ++ .../src/ux_hcd_ohci_port_disable.c | 104 + .../src/ux_hcd_ohci_port_enable.c | 119 + .../src/ux_hcd_ohci_port_reset.c | 144 ++ .../src/ux_hcd_ohci_port_resume.c | 77 + .../src/ux_hcd_ohci_port_status_get.c | 160 ++ .../src/ux_hcd_ohci_port_suspend.c | 77 + .../src/ux_hcd_ohci_power_down_port.c | 77 + .../src/ux_hcd_ohci_power_on_port.c | 76 + .../src/ux_hcd_ohci_power_root_hubs.c | 148 ++ .../src/ux_hcd_ohci_register_read.c | 77 + .../src/ux_hcd_ohci_register_write.c | 81 + .../src/ux_hcd_ohci_regular_td_obtain.c | 117 + .../src/ux_hcd_ohci_request_bulk_transfer.c | 242 ++ .../ux_hcd_ohci_request_control_transfer.c | 273 +++ .../ux_hcd_ohci_request_interupt_transfer.c | 135 ++ ...ux_hcd_ohci_request_isochronous_transfer.c | 246 ++ .../src/ux_hcd_ohci_request_transfer.c | 129 ++ .../src/ux_hcd_ohci_transfer_abort.c | 147 ++ .../ux_hcd_ohci_transfer_request_process.c | 91 + common/usbx_network/CMakeLists.txt | 10 + common/usbx_network/inc/ux_network_driver.h | 111 + common/usbx_network/src/ux_network_driver.c | 937 ++++++++ docs/USBX_Device_Stack_User_Guide.docx | Bin 0 -> 568349 bytes ...X_Device_Stack_User_Guide_Supplemental.pdf | Bin 0 -> 678046 bytes docs/USBX_Express_Startup.pdf | Bin 0 -> 226820 bytes docs/USBX_Host_Stack_UVC_User_Guide.docx | Bin 0 -> 68907 bytes docs/USBX_Host_Stack_User_Guide.docx | Bin 0 -> 512866 bytes ...SBX_Host_Stack_User_Guide_Supplemental.pdf | Bin 0 -> 546527 bytes ports/cortex_m0/gnu/CMakeLists.txt | 9 + ports/cortex_m0/gnu/inc/ux_port.h | 215 ++ ports/cortex_m3/gnu/CMakeLists.txt | 9 + ports/cortex_m3/gnu/inc/ux_port.h | 215 ++ ports/cortex_m4/gnu/CMakeLists.txt | 9 + ports/cortex_m4/gnu/inc/ux_port.h | 215 ++ ports/cortex_m7/gnu/CMakeLists.txt | 9 + ports/cortex_m7/gnu/inc/ux_port.h | 215 ++ rebuild.sh | 20 + samples/demo_usbx.c | 395 ++++ .../CDC_ACM_Template.inf | 65 + .../CDC_ACM_Template_Win7_64bit.inf | 66 + .../CDC_Composite_Template.inf | 68 + .../CDC_ECM_Template.inf | 182 ++ .../RNDIS_Template.inf | 124 ++ usbx_windows_host_files/CDC_ACM_Template.inf | 65 + .../CDC_ACM_Template_Win7_64bit.inf | 66 + .../CDC_Composite_Template.inf | 68 + usbx_windows_host_files/CDC_ECM_Template.inf | 182 ++ usbx_windows_host_files/RNDIS_Template.inf | 124 ++ 762 files changed, 131832 insertions(+) create mode 100644 .gitattributes create mode 100755 .gitignore create mode 100755 CMakeLists.txt create mode 100644 LICENSE.txt create mode 100644 LICENSED-HARDWARE.txt create mode 100755 README.md create mode 100644 TODO create mode 100644 common/CMakeLists.txt create mode 100644 common/core/CMakeLists.txt create mode 100644 common/core/inc/ux_api.h create mode 100644 common/core/inc/ux_dcd_sim_slave.h create mode 100644 common/core/inc/ux_device_class_dpump.h create mode 100644 common/core/inc/ux_device_stack.h create mode 100644 common/core/inc/ux_hcd_sim_host.h create mode 100644 common/core/inc/ux_host_class_dpump.h create mode 100644 common/core/inc/ux_host_stack.h create mode 100644 common/core/inc/ux_system.h create mode 100644 common/core/inc/ux_user.h create mode 100644 common/core/inc/ux_utility.h create mode 100644 common/core/src/ux_dcd_sim_slave_address_set.c create mode 100644 common/core/src/ux_dcd_sim_slave_endpoint_create.c create mode 100644 common/core/src/ux_dcd_sim_slave_endpoint_destroy.c create mode 100644 common/core/src/ux_dcd_sim_slave_endpoint_reset.c create mode 100644 common/core/src/ux_dcd_sim_slave_endpoint_stall.c create mode 100644 common/core/src/ux_dcd_sim_slave_endpoint_status.c create mode 100644 common/core/src/ux_dcd_sim_slave_frame_number_get.c create mode 100644 common/core/src/ux_dcd_sim_slave_function.c create mode 100644 common/core/src/ux_dcd_sim_slave_initialize.c create mode 100644 common/core/src/ux_dcd_sim_slave_initialize_complete.c create mode 100644 common/core/src/ux_dcd_sim_slave_state_change.c create mode 100644 common/core/src/ux_dcd_sim_slave_transfer_abort.c create mode 100644 common/core/src/ux_dcd_sim_slave_transfer_request.c create mode 100644 common/core/src/ux_device_class_dpump_activate.c create mode 100644 common/core/src/ux_device_class_dpump_change.c create mode 100644 common/core/src/ux_device_class_dpump_deactivate.c create mode 100644 common/core/src/ux_device_class_dpump_entry.c create mode 100644 common/core/src/ux_device_class_dpump_initialize.c create mode 100644 common/core/src/ux_device_class_dpump_read.c create mode 100644 common/core/src/ux_device_class_dpump_thread.c create mode 100644 common/core/src/ux_device_class_dpump_write.c create mode 100644 common/core/src/ux_device_stack_alternate_setting_get.c create mode 100644 common/core/src/ux_device_stack_alternate_setting_set.c create mode 100644 common/core/src/ux_device_stack_class_register.c create mode 100644 common/core/src/ux_device_stack_class_unregister.c create mode 100644 common/core/src/ux_device_stack_clear_feature.c create mode 100644 common/core/src/ux_device_stack_configuration_get.c create mode 100644 common/core/src/ux_device_stack_configuration_set.c create mode 100644 common/core/src/ux_device_stack_control_request_process.c create mode 100644 common/core/src/ux_device_stack_descriptor_send.c create mode 100644 common/core/src/ux_device_stack_disconnect.c create mode 100644 common/core/src/ux_device_stack_endpoint_stall.c create mode 100644 common/core/src/ux_device_stack_get_status.c create mode 100644 common/core/src/ux_device_stack_host_wakeup.c create mode 100644 common/core/src/ux_device_stack_initialize.c create mode 100644 common/core/src/ux_device_stack_interface_delete.c create mode 100644 common/core/src/ux_device_stack_interface_get.c create mode 100644 common/core/src/ux_device_stack_interface_set.c create mode 100644 common/core/src/ux_device_stack_interface_start.c create mode 100644 common/core/src/ux_device_stack_microsoft_extension_register.c create mode 100644 common/core/src/ux_device_stack_set_feature.c create mode 100644 common/core/src/ux_device_stack_transfer_abort.c create mode 100644 common/core/src/ux_device_stack_transfer_all_request_abort.c create mode 100644 common/core/src/ux_device_stack_transfer_request.c create mode 100644 common/core/src/ux_device_stack_uninitialize.c create mode 100644 common/core/src/ux_hcd_sim_host_asynch_queue_process.c create mode 100644 common/core/src/ux_hcd_sim_host_asynch_schedule.c create mode 100644 common/core/src/ux_hcd_sim_host_asynchronous_endpoint_create.c create mode 100644 common/core/src/ux_hcd_sim_host_asynchronous_endpoint_destroy.c create mode 100644 common/core/src/ux_hcd_sim_host_ed_obtain.c create mode 100644 common/core/src/ux_hcd_sim_host_ed_td_clean.c create mode 100644 common/core/src/ux_hcd_sim_host_endpoint_reset.c create mode 100644 common/core/src/ux_hcd_sim_host_entry.c create mode 100644 common/core/src/ux_hcd_sim_host_frame_number_get.c create mode 100644 common/core/src/ux_hcd_sim_host_frame_number_set.c create mode 100644 common/core/src/ux_hcd_sim_host_initialize.c create mode 100644 common/core/src/ux_hcd_sim_host_interrupt_endpoint_create.c create mode 100644 common/core/src/ux_hcd_sim_host_iso_queue_process.c create mode 100644 common/core/src/ux_hcd_sim_host_iso_schedule.c create mode 100644 common/core/src/ux_hcd_sim_host_isochronous_endpoint_create.c create mode 100644 common/core/src/ux_hcd_sim_host_isochronous_td_obtain.c create mode 100644 common/core/src/ux_hcd_sim_host_least_traffic_list_get.c create mode 100644 common/core/src/ux_hcd_sim_host_periodic_endpoint_destroy.c create mode 100644 common/core/src/ux_hcd_sim_host_periodic_schedule.c create mode 100644 common/core/src/ux_hcd_sim_host_periodic_tree_create.c create mode 100644 common/core/src/ux_hcd_sim_host_port_reset.c create mode 100644 common/core/src/ux_hcd_sim_host_port_status_get.c create mode 100644 common/core/src/ux_hcd_sim_host_regular_td_obtain.c create mode 100644 common/core/src/ux_hcd_sim_host_request_bulk_transfer.c create mode 100644 common/core/src/ux_hcd_sim_host_request_control_transfer.c create mode 100644 common/core/src/ux_hcd_sim_host_request_interupt_transfer.c create mode 100644 common/core/src/ux_hcd_sim_host_request_isochronous_transfer.c create mode 100644 common/core/src/ux_hcd_sim_host_request_transfer.c create mode 100644 common/core/src/ux_hcd_sim_host_timer_function.c create mode 100644 common/core/src/ux_hcd_sim_host_transaction_schedule.c create mode 100644 common/core/src/ux_hcd_sim_host_transfer_abort.c create mode 100644 common/core/src/ux_host_class_dpump_activate.c create mode 100644 common/core/src/ux_host_class_dpump_configure.c create mode 100644 common/core/src/ux_host_class_dpump_deactivate.c create mode 100644 common/core/src/ux_host_class_dpump_endpoints_get.c create mode 100644 common/core/src/ux_host_class_dpump_entry.c create mode 100644 common/core/src/ux_host_class_dpump_ioctl.c create mode 100644 common/core/src/ux_host_class_dpump_read.c create mode 100644 common/core/src/ux_host_class_dpump_write.c create mode 100644 common/core/src/ux_host_stack_bandwidth_check.c create mode 100644 common/core/src/ux_host_stack_bandwidth_claim.c create mode 100644 common/core/src/ux_host_stack_bandwidth_release.c create mode 100644 common/core/src/ux_host_stack_class_call.c create mode 100644 common/core/src/ux_host_stack_class_device_scan.c create mode 100644 common/core/src/ux_host_stack_class_get.c create mode 100644 common/core/src/ux_host_stack_class_instance_create.c create mode 100644 common/core/src/ux_host_stack_class_instance_destroy.c create mode 100644 common/core/src/ux_host_stack_class_instance_get.c create mode 100644 common/core/src/ux_host_stack_class_instance_verify.c create mode 100644 common/core/src/ux_host_stack_class_interface_scan.c create mode 100644 common/core/src/ux_host_stack_class_register.c create mode 100644 common/core/src/ux_host_stack_configuration_descriptor_parse.c create mode 100644 common/core/src/ux_host_stack_configuration_enumerate.c create mode 100644 common/core/src/ux_host_stack_configuration_instance_create.c create mode 100644 common/core/src/ux_host_stack_configuration_instance_delete.c create mode 100644 common/core/src/ux_host_stack_configuration_interface_get.c create mode 100644 common/core/src/ux_host_stack_configuration_set.c create mode 100644 common/core/src/ux_host_stack_delay_ms.c create mode 100644 common/core/src/ux_host_stack_device_address_set.c create mode 100644 common/core/src/ux_host_stack_device_configuration_get.c create mode 100644 common/core/src/ux_host_stack_device_configuration_reset.c create mode 100644 common/core/src/ux_host_stack_device_configuration_select.c create mode 100644 common/core/src/ux_host_stack_device_descriptor_read.c create mode 100644 common/core/src/ux_host_stack_device_get.c create mode 100644 common/core/src/ux_host_stack_device_remove.c create mode 100644 common/core/src/ux_host_stack_device_resources_free.c create mode 100644 common/core/src/ux_host_stack_endpoint_instance_create.c create mode 100644 common/core/src/ux_host_stack_endpoint_instance_delete.c create mode 100644 common/core/src/ux_host_stack_endpoint_reset.c create mode 100644 common/core/src/ux_host_stack_endpoint_transfer_abort.c create mode 100644 common/core/src/ux_host_stack_enum_thread_entry.c create mode 100644 common/core/src/ux_host_stack_hcd_register.c create mode 100644 common/core/src/ux_host_stack_hcd_thread_entry.c create mode 100644 common/core/src/ux_host_stack_hcd_transfer_request.c create mode 100644 common/core/src/ux_host_stack_hnp_polling_thread_entry.c create mode 100644 common/core/src/ux_host_stack_initialize.c create mode 100644 common/core/src/ux_host_stack_interface_endpoint_get.c create mode 100644 common/core/src/ux_host_stack_interface_instance_create.c create mode 100644 common/core/src/ux_host_stack_interface_instance_delete.c create mode 100644 common/core/src/ux_host_stack_interface_set.c create mode 100644 common/core/src/ux_host_stack_interface_setting_select.c create mode 100644 common/core/src/ux_host_stack_interfaces_scan.c create mode 100644 common/core/src/ux_host_stack_new_configuration_create.c create mode 100644 common/core/src/ux_host_stack_new_device_create.c create mode 100644 common/core/src/ux_host_stack_new_device_get.c create mode 100644 common/core/src/ux_host_stack_new_endpoint_create.c create mode 100644 common/core/src/ux_host_stack_new_interface_create.c create mode 100644 common/core/src/ux_host_stack_rh_change_process.c create mode 100644 common/core/src/ux_host_stack_rh_device_extraction.c create mode 100644 common/core/src/ux_host_stack_rh_device_insertion.c create mode 100644 common/core/src/ux_host_stack_role_swap.c create mode 100644 common/core/src/ux_host_stack_transfer_request.c create mode 100644 common/core/src/ux_host_stack_transfer_request_abort.c create mode 100644 common/core/src/ux_system_error_handler.c create mode 100644 common/core/src/ux_system_initialize.c create mode 100644 common/core/src/ux_system_uninitialize.c create mode 100644 common/core/src/ux_trace_event_insert.c create mode 100644 common/core/src/ux_trace_event_update.c create mode 100644 common/core/src/ux_trace_object_register.c create mode 100644 common/core/src/ux_trace_object_unregister.c create mode 100644 common/core/src/ux_utility_debug_callback_register.c create mode 100644 common/core/src/ux_utility_debug_log.c create mode 100644 common/core/src/ux_utility_delay_ms.c create mode 100644 common/core/src/ux_utility_descriptor_pack.c create mode 100644 common/core/src/ux_utility_descriptor_parse.c create mode 100644 common/core/src/ux_utility_error_callback_register.c create mode 100644 common/core/src/ux_utility_event_flags_create.c create mode 100644 common/core/src/ux_utility_event_flags_delete.c create mode 100644 common/core/src/ux_utility_event_flags_get.c create mode 100644 common/core/src/ux_utility_event_flags_set.c create mode 100644 common/core/src/ux_utility_long_get.c create mode 100644 common/core/src/ux_utility_long_get_big_endian.c create mode 100644 common/core/src/ux_utility_long_put.c create mode 100644 common/core/src/ux_utility_long_put_big_endian.c create mode 100644 common/core/src/ux_utility_memory_allocate.c create mode 100644 common/core/src/ux_utility_memory_allocate_add_safe.c create mode 100644 common/core/src/ux_utility_memory_allocate_mulc_safe.c create mode 100644 common/core/src/ux_utility_memory_allocate_mulv_safe.c create mode 100644 common/core/src/ux_utility_memory_compare.c create mode 100644 common/core/src/ux_utility_memory_copy.c create mode 100644 common/core/src/ux_utility_memory_free.c create mode 100644 common/core/src/ux_utility_memory_free_block_best_get.c create mode 100644 common/core/src/ux_utility_memory_set.c create mode 100644 common/core/src/ux_utility_mutex_create.c create mode 100644 common/core/src/ux_utility_mutex_delete.c create mode 100644 common/core/src/ux_utility_mutex_off.c create mode 100644 common/core/src/ux_utility_mutex_on.c create mode 100644 common/core/src/ux_utility_pci_class_scan.c create mode 100644 common/core/src/ux_utility_pci_read.c create mode 100644 common/core/src/ux_utility_pci_write.c create mode 100644 common/core/src/ux_utility_physical_address.c create mode 100644 common/core/src/ux_utility_semaphore_create.c create mode 100644 common/core/src/ux_utility_semaphore_delete.c create mode 100644 common/core/src/ux_utility_semaphore_get.c create mode 100644 common/core/src/ux_utility_semaphore_put.c create mode 100644 common/core/src/ux_utility_set_interrupt_handler.c create mode 100644 common/core/src/ux_utility_short_get.c create mode 100644 common/core/src/ux_utility_short_get_big_endian.c create mode 100644 common/core/src/ux_utility_short_put.c create mode 100644 common/core/src/ux_utility_short_put_big_endian.c create mode 100644 common/core/src/ux_utility_string_length_check.c create mode 100644 common/core/src/ux_utility_string_length_get.c create mode 100644 common/core/src/ux_utility_string_to_unicode.c create mode 100644 common/core/src/ux_utility_thread_create.c create mode 100644 common/core/src/ux_utility_thread_delete.c create mode 100644 common/core/src/ux_utility_thread_identify.c create mode 100644 common/core/src/ux_utility_thread_relinquish.c create mode 100644 common/core/src/ux_utility_thread_resume.c create mode 100644 common/core/src/ux_utility_thread_schedule_other.c create mode 100644 common/core/src/ux_utility_thread_sleep.c create mode 100644 common/core/src/ux_utility_thread_suspend.c create mode 100644 common/core/src/ux_utility_timer_create.c create mode 100644 common/core/src/ux_utility_unicode_to_string.c create mode 100644 common/core/src/ux_utility_virtual_address.c create mode 100644 common/usbx_device_classes/CMakeLists.txt create mode 100644 common/usbx_device_classes/inc/ux_device_class_audio.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_audio10.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_audio20.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_cdc_acm.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_cdc_ecm.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_dfu.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_hid.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_pima.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_rndis.h create mode 100644 common/usbx_device_classes/inc/ux_device_class_storage.h create mode 100644 common/usbx_device_classes/src/ux_device_class_audio10_control_process.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio20_control_process.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_change.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_frame_write.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_ioctl.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_read_frame_free.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_read_frame_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_read_thread_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_reception_start.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_sample_read16.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_sample_read24.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_sample_read32.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_sample_read8.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_stream_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_transmission_start.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_unitialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_write_frame_commit.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_write_frame_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_audio_write_thread_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkin_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkout_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_ioctl.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_read.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_unitialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_write.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_acm_write_with_callback.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkin_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkout_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_change.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_interrupt_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_uninitialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_cdc_ecm_write.c create mode 100644 common/usbx_device_classes/src/ux_device_class_dfu_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_dfu_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_dfu_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_dfu_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_dfu_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_dfu_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_descriptor_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_event_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_event_set.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_interrupt_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_report_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_report_set.c create mode 100644 common/usbx_device_classes/src/ux_device_class_hid_uninitialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_data.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_device_info_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_device_prop_desc_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_set.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_device_reset.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_event_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_event_set.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_interrupt_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_add.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_data_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_data_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_delete.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_handles_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_info_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_info_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_prop_desc_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_set.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_props_supported_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_references_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_object_references_set.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_objects_number_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_partial_object_data_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_response_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_storage_format.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_storage_id_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_storage_info_get.c create mode 100644 common/usbx_device_classes/src/ux_device_class_pima_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_bulkin_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_bulkout_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_interrupt_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_msg_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_msg_keep_alive.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_msg_query.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_msg_reset.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_msg_set.c create mode 100644 common/usbx_device_classes/src/ux_device_class_rndis_write.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_activate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_control_request.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_csw_send.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_deactivate.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_entry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_format.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_get_configuration.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_get_performance.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_get_status_notification.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_initialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_inquiry.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_mode_select.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_mode_sense.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_prevent_allow_media_removal.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_read.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_read_capacity.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_read_disk_information.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_read_dvd_structure.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_read_format_capacity.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_read_toc.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_report_key.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_request_sense.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_start_stop.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_synchronize_cache.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_test_ready.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_thread.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_uninitialize.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_verify.c create mode 100644 common/usbx_device_classes/src/ux_device_class_storage_write.c create mode 100644 common/usbx_host_classes/CMakeLists.txt create mode 100644 common/usbx_host_classes/inc/ux_host_class_asix.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_audio.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_cdc_acm.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_cdc_ecm.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_gser.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_hid.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_hid_keyboard.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_hid_mouse.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_hid_remote_control.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_hub.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_pima.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_printer.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_prolific.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_storage.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_swar.h create mode 100644 common/usbx_host_classes/inc/ux_host_class_video.h create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_interrupt_notification.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_reception_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_setup.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_thread.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_transmission_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_asix_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_alternate_setting_locate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_control_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_control_value_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_control_value_set.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_descriptor_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_device_controls_list_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_device_type_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_set.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_streaming_terminal_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_transfer_request.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_transfer_request_completed.c create mode 100644 common/usbx_host_classes/src/ux_host_class_audio_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_capabilities_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_command.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_ioctl.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_stop.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_transfer_request_completed.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_acm_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_interrupt_notification.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_mac_address_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_thread.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmission_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmit_queue_clean.c create mode 100644 common/usbx_host_classes/src/ux_host_class_cdc_ecm_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_command.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_ioctl.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_reception_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_reception_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_reception_stop.c create mode 100644 common/usbx_host_classes/src/ux_host_class_gser_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_client_register.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_client_search.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_descriptor_parse.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_field_decompress.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_global_item_parse.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_idle_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_idle_set.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_instance_clean.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_interrupt_endpoint_search.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_item_data_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_keyboard_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_keyboard_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_keyboard_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_keyboard_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_keyboard_ioctl.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_keyboard_key_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_keyboard_thread.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_local_item_parse.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_main_item_parse.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_mouse_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_mouse_buttons_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_mouse_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_mouse_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_mouse_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_mouse_position_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_mouse_wheel_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_periodic_report_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_periodic_report_stop.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_remote_control_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_remote_control_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_remote_control_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_remote_control_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_remote_control_usage_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_add.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_callback_register.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_compress.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_decompress.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_descriptor_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_id_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_item_analyse.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_report_set.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_resources_free.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hid_transfer_request_completed.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_change_detect.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_change_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_descriptor_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_feature.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_hub_change_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_interrupt_endpoint_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_port_change_connection_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_port_change_enable_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_port_change_over_current_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_port_change_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_port_change_reset_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_port_change_suspend_process.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_port_reset.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_ports_power.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_status_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_hub_transfer_request_completed.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_command.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_device_info_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_device_reset.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_notification.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_num_objects_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_close.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_copy.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_delete.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_handles_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_info_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_info_send.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_move.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_open.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_send.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_object_transfer_abort.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_request_cancel.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_session_close.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_session_open.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_storage_ids_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_storage_info_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_thumb_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_pima_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_name_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_soft_reset.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_status_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_printer_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_command.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_ioctl.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_reception_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_reception_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_reception_stop.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_setup.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_transfer_request_completed.c create mode 100644 common/usbx_host_classes/src/ux_host_class_prolific_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_cbw_initialize.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_device_initialize.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_device_reset.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_device_support_check.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_driver_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_max_lun_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_capacity_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_characteristics_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_format_capacity_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_mount.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_open.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_protection_check.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_recovery_sense_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_media_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_partition_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_request_sense.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_sense_code_translate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_start_stop.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_thread_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_transport.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_transport_bo.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_transport_cb.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_transport_cbi.c create mode 100644 common/usbx_host_classes/src/ux_host_class_storage_unit_ready_test.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_ioctl.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_reception_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_reception_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_reception_stop.c create mode 100644 common/usbx_host_classes/src/ux_host_class_swar_write.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_activate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_alternate_setting_locate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_channel_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_configure.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_control_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_control_list_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_control_value_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_control_value_set.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_deactivate.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_descriptor_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_endpoints_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_entry.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_format_data_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_frame_data_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_frame_interval_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_frame_parameters_set.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_input_format_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_input_terminal_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_ioctl.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_max_payload_get.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_read.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_start.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_stop.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_transfer_buffer_add.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_transfer_buffers_add.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_transfer_callback_set.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_transfer_request.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_transfer_request_callback.c create mode 100644 common/usbx_host_classes/src/ux_host_class_video_transfer_request_completed.c create mode 100644 common/usbx_host_controllers/CMakeLists.txt create mode 100644 common/usbx_host_controllers/inc/ux_hcd_ehci.h create mode 100644 common/usbx_host_controllers/inc/ux_hcd_ohci.h create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_asynch_td_process.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_destroy.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_controller_disable.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_done_queue_process.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_door_bell_wait.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_ed_clean.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_ed_obtain.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_endpoint_reset.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_entry.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_get.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_set.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_td_obtain.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_tds_process.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_td_obtain.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_tds_process.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_initialize.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_destroy.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_handler.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_destroy.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_least_traffic_list_get.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_next_td_clean.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_periodic_descriptor_link.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_periodic_tree_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_poll_rate_entry_get.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_port_disable.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_port_reset.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_port_resume.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_port_status_get.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_port_suspend.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_power_down_port.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_power_on_port.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_power_root_hubs.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_register_read.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_register_write.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_regular_td_obtain.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_request_bulk_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_request_control_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_request_interrupt_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_request_isochronous_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer_add.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_transfer_abort.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ehci_transfer_request_process.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_destroy.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_controller_disable.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_done_queue_process.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_ed_obtain.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_error_clear.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_reset.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_entry.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_get.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_set.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_initialize.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_endpoint_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_handler.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_endpoint_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_td_obtain.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_least_traffic_list_get.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_next_td_clean.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_periodic_endpoint_destroy.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_periodic_tree_create.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_port_disable.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_port_enable.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_port_reset.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_port_resume.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_port_status_get.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_port_suspend.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_power_down_port.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_power_on_port.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_power_root_hubs.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_register_read.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_register_write.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_regular_td_obtain.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_request_bulk_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_request_control_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_request_interupt_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_request_isochronous_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_request_transfer.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_transfer_abort.c create mode 100644 common/usbx_host_controllers/src/ux_hcd_ohci_transfer_request_process.c create mode 100644 common/usbx_network/CMakeLists.txt create mode 100644 common/usbx_network/inc/ux_network_driver.h create mode 100644 common/usbx_network/src/ux_network_driver.c create mode 100644 docs/USBX_Device_Stack_User_Guide.docx create mode 100644 docs/USBX_Device_Stack_User_Guide_Supplemental.pdf create mode 100755 docs/USBX_Express_Startup.pdf create mode 100644 docs/USBX_Host_Stack_UVC_User_Guide.docx create mode 100644 docs/USBX_Host_Stack_User_Guide.docx create mode 100644 docs/USBX_Host_Stack_User_Guide_Supplemental.pdf create mode 100644 ports/cortex_m0/gnu/CMakeLists.txt create mode 100644 ports/cortex_m0/gnu/inc/ux_port.h create mode 100644 ports/cortex_m3/gnu/CMakeLists.txt create mode 100644 ports/cortex_m3/gnu/inc/ux_port.h create mode 100644 ports/cortex_m4/gnu/CMakeLists.txt create mode 100644 ports/cortex_m4/gnu/inc/ux_port.h create mode 100644 ports/cortex_m7/gnu/CMakeLists.txt create mode 100644 ports/cortex_m7/gnu/inc/ux_port.h create mode 100755 rebuild.sh create mode 100644 samples/demo_usbx.c create mode 100755 support/usbx_windows_host_files/CDC_ACM_Template.inf create mode 100755 support/usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf create mode 100755 support/usbx_windows_host_files/CDC_Composite_Template.inf create mode 100755 support/usbx_windows_host_files/CDC_ECM_Template.inf create mode 100755 support/usbx_windows_host_files/RNDIS_Template.inf create mode 100644 usbx_windows_host_files/CDC_ACM_Template.inf create mode 100644 usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf create mode 100644 usbx_windows_host_files/CDC_Composite_Template.inf create mode 100644 usbx_windows_host_files/CDC_ECM_Template.inf create mode 100644 usbx_windows_host_files/RNDIS_Template.inf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..cbdd44a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,41 @@ +.git* export-ignore +.hooks* export-ignore + +# Custom attribute to mark sources as using our C code style. +[attr]our-c-style whitespace=tab-in-indent eol=lf format.clang-format-6.0 + +# Custom attribute to mark sources as generated. +# Do not perform whitespace checks. Do not format. +[attr]generated whitespace=-tab-in-indent,-indent-with-non-tab -format.clang-format-6.0 + +bootstrap eol=lf +configure eol=lf +*.[1-9] eol=lf +*.bash eol=lf +*.sh eol=lf +*.sh.in eol=lf + +*.bat eol=crlf +*.bat.in eol=crlf +*.sln eol=crlf +*.vcproj eol=crlf +*.inf eol=crlf + +*.pfx -text +*.png -text +*.png.in -text + +*.c our-c-style +*.cc our-c-style +*.cpp our-c-style +*.cu our-c-style +*.cxx our-c-style +*.h our-c-style +*.hh our-c-style +*.hpp our-c-style +*.hxx our-c-style +*.notcu our-c-style + +*.cmake whitespace=tab-in-indent +*.rst whitespace=tab-in-indent conflict-marker-size=79 +*.txt whitespace=tab-in-indent diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..9c33dc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.vscode/ +_deps/ +build/ +CMakeFiles/ +CMakeScripts/ +CMakeLists.txt.user +CMakeCache.txt +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..3a6d0e5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) + +# Set up the project +project(usbx + VERSION 6.0.0 + LANGUAGES C ASM +) + +if(NOT DEFINED THREADX_ARCH) + message(FATAL_ERROR "Error: THREADX_ARCH not defined") +endif() +if(NOT DEFINED THREADX_TOOLCHAIN) + message(FATAL_ERROR "Error: THREADX_TOOLCHAIN not defined") +endif() + +# Define our target library and an alias for consumers +add_library(${PROJECT_NAME}) +add_library("azrtos::${PROJECT_NAME}" ALIAS ${PROJECT_NAME}) + +# Define any required dependencies between this library and others +target_link_libraries(${PROJECT_NAME} PUBLIC + "azrtos::threadx" + "azrtos::filex" + "azrtos::netxduo" +) + +# A place for generated/copied include files +set(CUSTOM_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/custom_inc) + +# Pick up the port specific stuff first +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/ports/${THREADX_ARCH}/${THREADX_TOOLCHAIN}) + +# Then the common files +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/common) + + + + +# If the user provided an override, copy it to the custom directory +if (NOT UX_USER_FILE) + message(STATUS "Using default ux_user.h file") + set(UX_USER_FILE ${CMAKE_CURRENT_LIST_DIR}/common/inc/ux_user_sample.h) +else() + message(STATUS "Using custom ux_user.h file from ${UX_USER_FILE}") +endif() +configure_file(${UX_USER_FILE} ${CUSTOM_INC_DIR}/ux_user.h COPYONLY) +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CUSTOM_INC_DIR} +) +target_compile_definitions(${PROJECT_NAME} PUBLIC "UX_INCLUDE_USER_DEFINE_FILE" ) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..76974a3 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,246 @@ +MICROSOFT SOFTWARE LICENSE TERMS + +MICROSOFT AZURE RTOS + +Shape + +These license terms are an agreement between you and Microsoft Corporation (or +one of its affiliates). They apply to the software named above and any Microsoft +services or software updates (except to the extent such services or updates are +accompanied by new or additional terms, in which case those different terms +apply prospectively and do not alter your or Microsoft’s rights relating to +pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU +HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. + +INSTALLATION AND USE RIGHTS. + +General. You may install and use the software and the included Microsoft +applications solely for internal development, testing and evaluation purposes. +Any distribution or production use requires a separate license as set forth in +Section 2. + +Contributions. Microsoft welcomes contributions to this software. In the event +that you make a contribution to this software you will be required to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and +actually do, grant Microsoft the rights to use your contribution. For details, +visit https://cla.microsoft.com. + +Included Microsoft Applications. The software includes other Microsoft +applications which are governed by the licenses embedded in or made available +with those applications. + +Third Party Components. The software may include third party components with +separate legal notices or governed by other agreements, as may be described +within the software or in the ThirdPartyNotices file(s) accompanying the +software. + +Competitive Benchmarking. If you are a direct competitor, and you access or use +the software for purposes of competitive benchmarking, analysis, or intelligence +gathering, you waive as against Microsoft, its subsidiaries, and its affiliated +companies (including prospectively) any competitive use, access, and +benchmarking test restrictions in the terms governing your software to the +extent your terms of use are, or purport to be, more restrictive than +Microsoft’s terms. If you do not waive any such purported restrictions in the +terms governing your software, you are not allowed to access or use this +software, and will not do so. + +DISTRIBUTION AND PRODUCTION USE. If you have obtained and/or are developing on +microprocessor(s) and/or microcontroller(s) (“hardware”) listed in the file +named “LICENSED-HARDWARE.txt” included in the repository and/or distributed with +the software you have the following rights in and to the software solely when +used in combination with the hardware. In the event hardware is not listed in +the LICENSED-HARDWARE.txt file, you do not have the rights in this Section 2. + +Distribution and Production Use Rights. + +You may use the software in production (e.g. program the modified or unmodified +software to devices you own or control) and distribute (i.e. make available to +third parties) the modified or unmodified binary image produced from this code. + + +You may permit your device distributors or developers to copy and distribute the +binary image as programmed or to be programmed to your devices. + +You may redistribute the unmodified or modified source to your device +distributors or developers. Modifications must be clearly marked. Any +redistribution in source code form must contain this license and any other +licenses that accompany the software. + +Requirements. For any code you distribute, you must: + +when distributed in binary form, except as embedded in a device, include with +such distribution the terms of this agreement; + +when distributed in source code form to distributors or developers of your +devices, include with such distribution the terms of this agreement; and + +indemnify, defend and hold harmless Microsoft from any claims, including +attorneys’ fees, related to the distribution or use of your devices, except to +the extent that any claim is based solely on the unmodified software. + +Restrictions. You may not: + +use or modify the software to create a competing real time operating system +software; + +remove any copyright notices or licenses contained in the software; + +use Microsoft’s trademarks or trade dress in your application in any way that +suggests your device or application comes from or is endorsed by Microsoft; + +transfer individual components, specific libraries, classes, functions or code +fragments of the software separately for purposes unrelated to the software; or + +use or distribute the software in any way that would subject the software or +Microsoft’s intellectual property or technology to any other license terms. + +SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all +other rights. Unless applicable law gives you more rights despite this +limitation, you will not (and have no right to): + +remove, minimize, block, or modify any notices of Microsoft or its suppliers in +the software; + +use the software in any way that is against the law or to create or propagate +malware; or + +share, publish, distribute, or lease the software (except as permitted in +Section 2 above), or provide the software as a stand-alone offering for others +to use. + +DATA. This software may interact with other Microsoft products that collect data +that is transmitted to Microsoft. To learn more about how Microsoft processes +personal data we collect, please see the Microsoft Privacy Statement at +https://go.microsoft.com/fwlink/?LinkId=248681. + +EXPORT RESTRICTIONS. You must comply with all domestic and international export +laws and regulations that apply to the software, which include restrictions on +destinations, end users, and end use. For further information on export +restrictions, visit https://aka.ms/exporting. + +SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any +support services for the software. Any support provided is “as is”, “with all +faults”, and without warranty of any kind. + +UPDATES. Microsoft may periodically update the software. You may obtain updates +only from Microsoft or Microsoft-authorized sources. Updates may not include or +support all existing software features, services, or peripheral devices. + +TERMINATION. Without prejudice to any other rights, Microsoft may terminate this +agreement if you fail to comply with any of its terms or conditions. In such +event, you must destroy all copies of the software and all of its component +parts. + +ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for +supplements, updates, or third-party applications, is the entire agreement for +the software. To the extent you have entered into a separate agreement with +Microsoft relating specifically to the software, the terms in such agreement +shall control. + +APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in +the United States or Canada, the laws of the state or province where you live +(or, if a business, where your principal place of business is located) govern +the interpretation of this agreement, claims for its breach, and all other +claims (including consumer protection, unfair competition, and tort claims), +regardless of conflict of laws principles. If you acquired the software in any +other country, its laws apply. If U.S. federal jurisdiction exists, you and +Microsoft consent to exclusive jurisdiction and venue in the federal court in +King County, Washington for all disputes heard in court. If not, you and +Microsoft consent to exclusive jurisdiction and venue in the Superior Court of +King County, Washington for all disputes heard in court. + +CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal +rights. You may have other rights, including consumer rights, under the laws of +your state or country. Separate and apart from your relationship with Microsoft, +you may also have rights with respect to the party from which you acquired the +software. This agreement does not change those other rights if the laws of your +state or country do not permit it to do so. For example, if you acquired the +software in one of the below regions, or mandatory country law applies, then the +following provisions apply to you: + +Australia. You have statutory guarantees under the Australian Consumer Law and +nothing in this agreement is intended to affect those rights. + +Germany and Austria. + +i.Warranty. The properly licensed software will perform substantially as +described in any Microsoft materials that accompany the software. However, +Microsoft gives no contractual guarantee in relation to the licensed software. + +ii.Limitation of Liability. In case of intentional conduct, gross negligence, +claims based on the Product Liability Act, as well as, in case of death or +personal or physical injury, Microsoft is liable according to the statutory law. + + +Subject to the foregoing clause ii., Microsoft will only be liable for slight +negligence if Microsoft is in breach of such material contractual obligations, +the fulfillment of which facilitate the due performance of this agreement, the +breach of which would endanger the purpose of this agreement and the compliance +with which a party may constantly trust in (so-called "cardinal obligations"). +In other cases of slight negligence, Microsoft will not be liable for slight +negligence. + +DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF +USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO +THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED +WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT. + +LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING +DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM +MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT +RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, +INDIRECT, OR INCIDENTAL DAMAGES. + +This limitation applies to (a) anything related to the software, services, +content (including code) on third party Internet sites, or third party +applications; and (b) claims for breach of contract, warranty, guarantee, or +condition; strict liability, negligence, or other tort; or any other claim; in +each case to the extent permitted by applicable law. + +It also applies even if Microsoft knew or should have known about the +possibility of the damages. The above limitation or exclusion may not apply to +you because your state, province, or country may not allow the exclusion or +limitation of incidental, consequential, or other damages. + + + +Please note: As this software is distributed in Canada, some of the clauses in +this agreement are provided below in French. + +Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce +contrat sont fournies ci-dessous en français. + +EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel +». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft +n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits +additionnels en vertu du droit local sur la protection des consommateurs, que ce +contrat ne peut modifier. La ou elles sont permises par le droit locale, les +garanties implicites de qualité marchande, d’adéquation à un usage particulier +et d’absence de contrefaçon sont exclues. + +LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES +DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une +indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous +ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris +les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. + +Cette limitation concerne: + +•tout ce qui est relié au logiciel, aux services ou au contenu (y compris le +code) figurant sur des sites Internet tiers ou dans des programmes tiers; et + +•les réclamations au titre de violation de contrat ou de garantie, ou au titre +de responsabilité stricte, de négligence ou d’une autre faute dans la limite +autorisée par la loi en vigueur. + +Elle s’applique également, même si Microsoft connaissait ou devrait connaître +l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la +limitation de responsabilité pour les dommages indirects, accessoires ou de +quelque nature que ce soit, il se peut que la limitation ou l’exclusion +ci-dessus ne s’appliquera pas à votre égard. + +EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous +pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent +contrat ne modifie pas les droits que vous confèrent les lois de votre pays si +celles-ci ne le permettent pas. \ No newline at end of file diff --git a/LICENSED-HARDWARE.txt b/LICENSED-HARDWARE.txt new file mode 100644 index 0000000..77dd1ab --- /dev/null +++ b/LICENSED-HARDWARE.txt @@ -0,0 +1,16 @@ +LICENSED HARDWARE LIST + +Last Updated: 2020-05-08 + +Microsoft has entered into OEM Agreements with manufacturers of the following +microprocessors and microcontrollers (the “hardware”) to enable those +manufacturers to include and distribute Azure RTOS in certain hardware. If you +have obtained and/or are developing on microprocessor(s) and/or +microcontroller(s) (“hardware”) listed below you inherit the “Distribution and +Production Use” rights in Section 2 of the Microsoft Software License Terms for +Microsoft Azure RTOS. If hardware is not listed below, you do not have those +rights. + +-------------------------------------------------------------------------------- + +More coming soon. Please check back frequently for updates. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..379da1b --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# USBX \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..e69de29 diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..4b30487 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,1804 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_api.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_altera.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_at91.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_at91hs.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_atm7.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_bf52.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_isp1181.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_kxxf.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_lpc3131.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_lpc3180.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_mcf5329.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_mcimx6.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_ml6965.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_musb.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_pic32.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_ppc440.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_rx.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_rz.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_sh2a.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_sh7705.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_sim_slave.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_spear.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_stm32.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_ti18x.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_dcd_tilm3.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_audio.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_audio10.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_audio20.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_cdc_acm.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_cdc_ecm.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_dfu.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_dpump.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_hid.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_pima.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_rndis.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_class_storage.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_device_stack.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_atm7.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_ehci.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_isp1161.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_isp1362.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_kxxf.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_musb.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_ohci.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_pic32.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_rx.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_rz.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_sh2a.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_sim_host.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_hcd_stm32.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_asix.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_audio.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_cdc_acm.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_cdc_ecm.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_dpump.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_gser.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_hid.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_hid_keyboard.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_hid_mouse.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_hid_remote_control.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_hub.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_pima.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_printer.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_prolific.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_storage.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_swar.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_class_video.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_host_stack.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_network_driver.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_system.h + ${CMAKE_CURRENT_LIST_DIR}/inc/ux_utility.h + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_delay.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_endpoint_register_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_fifo_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_altera_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_at91hs_transfer_request.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_address_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_endpoint_destroy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_endpoint_reset.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_endpoint_stall.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_endpoint_status.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_fifo_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_fifo_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_frame_number_get.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_function.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_initialize.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_initialize_complete.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_register_clear.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_register_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_register_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_register_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_state_change.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_transfer_callback.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_atm7_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_endpoint_register_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_bf52_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_bandwidth_update.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_read_data16.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_read_data32.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_write_data16.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_isp1181_write_data32.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_endpoint_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_endpoint_stall_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_get_update_toggle.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_kxxf_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_stall_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_qtd_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_status_phase_hook.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3131_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_command_data_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_command_data_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_command_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_endpoint_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_endpoint_stall_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_lpc3180_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_stall_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_qtd_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_status_phase_hook.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcf5329_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_stall_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_qtd_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_status_phase_hook.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_mcimx6_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ml6965_vbus_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_fifo_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_musb_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_bdt_fill.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_endpoint_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_endpoint_stall_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_pic32_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_endpoint_index_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ppc440_vbus_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_buffer_empty_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_buffer_notready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_buffer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_buffer_ready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_buffer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_current_endpoint_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_data_buffersize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_endpoint_nak_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_fifo_port_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_fifoc_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_fifod_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rx_transfer_request.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_address_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_buffer_empty_interrupt.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_buffer_notready_interrupt.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_buffer_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_buffer_ready_interrupt.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_buffer_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_current_endpoint_change.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_data_buffersize.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_dma_register_clear.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_dma_register_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_dma_register_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_dma_register_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_dma_rx_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_dma_tx_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_endpoint_destroy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_endpoint_nak_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_endpoint_reset.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_endpoint_stall.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_endpoint_status.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_fifo_port_change.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_fifo_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_fifoc_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_fifod_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_frame_number_get.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_function.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_initialize.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_initialize_complete.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_low_level_setup.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_pipe_select.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_register_clear.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_register_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_register_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_register_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_state_change.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_transfer_abort.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_transfer_callback.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_rz_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_buffer_empty_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_buffer_notready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_buffer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_buffer_ready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_buffer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_current_endpoint_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_data_buffer_size.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_endpoint_nak_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_fifo_port_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_fifoc_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_fifod_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh2a_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_endpoint_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sh7705_vbus_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_fifo_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_spear_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_delay.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_endpoint_register_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_fifo_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_stm32_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_fifo_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_ti18x_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_fifo_flush.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_fifo_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_transfer_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_tilm3_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio10_control_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio20_control_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_frame_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_read_frame_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_read_frame_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_read_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read16.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read24.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read32.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read8.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_stream_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_transmission_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_unitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_write_frame_commit.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_write_frame_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_write_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_bulkin_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_bulkout_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_control_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_unitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_write_with_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_bulkin_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_bulkout_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_descriptor_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_event_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_event_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_report_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_report_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_control_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_data.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_info_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_prop_desc_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_prop_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_prop_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_event_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_event_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_data_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_handles_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_info_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_prop_desc_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_prop_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_prop_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_props_supported_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_references_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_references_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_objects_number_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_partial_object_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_response_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_storage_format.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_storage_id_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_storage_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_bulkin_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_bulkout_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_keep_alive.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_query.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_csw_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_format.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_get_configuration.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_get_performance.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_get_status_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_inquiry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_mode_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_mode_sense.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_prevent_allow_media_removal.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_capacity.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_disk_information.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_dvd_structure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_format_capacity.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_toc.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_report_key.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_request_sense.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_start_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_synchronize_cache.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_test_ready.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_verify.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_alternate_setting_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_alternate_setting_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_class_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_class_unregister.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_clear_feature.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_configuration_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_configuration_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_control_request_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_descriptor_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_disconnect.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_get_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_host_wakeup.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_microsoft_extension_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_set_feature.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_transfer_all_request_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_uninitialize.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_asynch_queue_process.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_asynch_schedule.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_asynchronous_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_asynchronous_endpoint_destroy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_controller_disable.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_delay.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_ed_obtain.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_ed_td_clean.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_endpoint_reset.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_entry.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_frame_number_get.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_frame_number_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_initialize.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_interrupt_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_iso_queue_process.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_iso_schedule.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_isochronous_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_isochronous_td_obtain.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_periodic_endpoint_destroy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_periodic_schedule.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_port_disable.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_port_enable.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_port_reset.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_port_resume.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_port_status_get.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_port_suspend.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_power_down_port.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_power_on_port.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_register_clear.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_register_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_register_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_register_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_regular_td_obtain.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_request_bulk_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_request_control_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_request_interupt_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_request_isochronous_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_request_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_td_schedule.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_atm7_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_asynch_td_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_done_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_door_bell_wait.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_ed_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_fsisochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_hsisochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_interrupt_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_isochronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_next_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_interrupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_transfer_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_transfer_request_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_atl_buffer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_atl_buffer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_hcer_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_hcer_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_hcor_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_hcor_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_ptd_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1161_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_aitl_buffer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_aitl_buffer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_atl_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_hcer_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_hcer_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_hcor_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_hcor_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_interrupt_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_isochronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_itl_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_ptd_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_isp1362_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_delay.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_td_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_kxxf_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_delay.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_td_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_musb_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_done_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_endpoint_error_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_next_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_transfer_request_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_bus_error_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_td_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_pic32_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynch_queue_process_bemp.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynch_queue_process_brdy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynch_queue_process_nrdy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynch_queue_process_sign.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_buffer_empty_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_buffer_notready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_buffer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_buffer_ready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_buffer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_bulk_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_bulk_int_td_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_control_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_control_td_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_current_endpoint_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_data_buffer_size.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_endpoint_nak_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_fifo_port_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_fifoc_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_fifod_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_td_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rx_transfer_abort.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynch_queue_process.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynch_queue_process_bemp.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynch_queue_process_brdy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynch_queue_process_nrdy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynch_queue_process_sign.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynch_schedule.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynchronous_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_asynchronous_endpoint_destroy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_buffer_empty_interrupt.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_buffer_notready_interrupt.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_buffer_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_buffer_ready_interrupt.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_buffer_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_bulk_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_bulk_int_td_add.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_control_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_control_td_add.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_controller_disable.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_current_endpoint_change.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_data_buffer_size.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_dma_register_clear.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_dma_register_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_dma_register_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_dma_register_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_dma_rx_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_dma_tx_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_ed_obtain.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_ed_td_clean.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_endpoint_nak_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_endpoint_reset.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_entry.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_fifo_port_change.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_fifo_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_fifoc_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_fifod_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_frame_number_get.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_frame_number_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_initialize.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_interrupt_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_interrupt_handler.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_iso_queue_process.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_iso_schedule.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_isochronous_endpoint_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_isochronous_td_obtain.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_least_traffic_list_get.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_low_level_setup.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_periodic_endpoint_destroy.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_periodic_schedule.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_periodic_tree_create.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_port_disable.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_port_enable.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_port_reset.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_port_resume.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_port_status_get.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_port_suspend.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_power_down_port.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_power_on_port.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_power_root_hubs.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_register_clear.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_register_read.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_register_set.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_register_write.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_regular_td_obtain.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_request_bulk_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_request_control_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_request_interupt_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_request_isochronous_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_request_transfer.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_td_add.c + # ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_rz_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynch_queue_process_bemp.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynch_queue_process_brdy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynch_queue_process_nrdy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynch_queue_process_sign.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_buffer_empty_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_buffer_notready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_buffer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_buffer_ready_interrupt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_buffer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_bulk_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_bulk_int_td_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_control_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_control_td_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_current_endpoint_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_data_buffer_size.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_endpoint_nak_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_fifo_port_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_fifo_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_fifoc_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_fifod_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_td_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sh2a_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_timer_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_transaction_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_channel_halt.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_delay.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_initialize_fscore.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_initialize_hscore.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_register_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_register_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_td_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_stm32_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_interrupt_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_setup.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_transmission_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_alternate_setting_locate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_control_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_control_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_control_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_device_controls_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_device_type_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_streaming_sampling_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_streaming_sampling_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_streaming_terminal_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_capabilities_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_interrupt_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_mac_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_transmission_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_transmit_queue_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_client_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_client_search.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_descriptor_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_field_decompress.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_global_item_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_idle_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_idle_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_instance_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_interrupt_endpoint_search.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_item_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_key_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_local_item_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_main_item_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_buttons_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_position_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_wheel_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_periodic_report_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_periodic_report_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_usage_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_callback_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_compress.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_decompress.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_id_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_item_analyse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_resources_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_change_detect.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_feature.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_hub_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_interrupt_endpoint_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_connection_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_enable_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_over_current_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_reset_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_suspend_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_ports_power.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_device_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_device_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_num_objects_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_handles_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_info_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_move.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_request_cancel.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_session_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_session_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_storage_ids_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_storage_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_thumb_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_soft_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_setup.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_cbw_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_device_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_device_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_device_support_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_driver_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_max_lun_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_capacity_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_characteristics_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_format_capacity_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_mount.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_protection_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_recovery_sense_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_partition_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_request_sense.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_sense_code_translate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_start_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport_bo.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport_cb.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport_cbi.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_unit_ready_test.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_alternate_setting_locate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_channel_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_format_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_frame_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_frame_interval_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_frame_parameters_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_input_format_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_input_terminal_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_max_payload_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_buffer_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_callback_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_request_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_bandwidth_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_bandwidth_claim.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_bandwidth_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_call.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_device_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_verify.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_interface_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_descriptor_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_enumerate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_instance_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_interface_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_delay_ms.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_configuration_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_configuration_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_configuration_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_descriptor_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_remove.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_resources_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_instance_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_enum_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hcd_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hcd_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hcd_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hnp_polling_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_endpoint_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_instance_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_setting_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interfaces_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_configuration_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_device_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_device_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_interface_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_rh_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_rh_device_extraction.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_rh_device_insertion.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_role_swap.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_transfer_request_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_network_driver.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_system_error_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_system_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_system_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_event_insert.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_event_update.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_object_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_object_unregister.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_debug_callback_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_debug_log.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_delay_ms.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_descriptor_pack.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_descriptor_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_error_callback_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_get_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_put.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_put_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_compare.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_free_block_best_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_off.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_on.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_pci_class_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_pci_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_pci_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_physical_address.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_put.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_set_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_get_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_put.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_put_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_string_length_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_string_length_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_string_to_unicode.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_identify.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_relinquish.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_schedule_other.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_sleep.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_timer_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_unicode_to_string.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_virtual_address.c + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/common/core/CMakeLists.txt b/common/core/CMakeLists.txt new file mode 100644 index 0000000..ce38377 --- /dev/null +++ b/common/core/CMakeLists.txt @@ -0,0 +1,208 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_endpoint_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_initialize_complete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_state_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_dcd_sim_slave_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dpump_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_alternate_setting_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_alternate_setting_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_class_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_class_unregister.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_clear_feature.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_configuration_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_configuration_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_control_request_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_descriptor_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_disconnect.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_endpoint_stall.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_get_status.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_host_wakeup.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_interface_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_microsoft_extension_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_set_feature.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_transfer_all_request_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_stack_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynch_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynch_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_ed_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_iso_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_iso_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_periodic_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_timer_function.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_transaction_schedule.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_sim_host_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_dpump_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_bandwidth_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_bandwidth_claim.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_bandwidth_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_call.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_device_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_instance_verify.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_interface_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_class_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_descriptor_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_enumerate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_instance_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_interface_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_configuration_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_delay_ms.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_address_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_configuration_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_configuration_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_configuration_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_descriptor_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_remove.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_device_resources_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_instance_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_endpoint_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_enum_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hcd_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hcd_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hcd_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_hnp_polling_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_endpoint_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_instance_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_instance_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interface_setting_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_interfaces_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_configuration_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_device_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_device_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_new_interface_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_rh_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_rh_device_extraction.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_rh_device_insertion.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_role_swap.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_stack_transfer_request_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_system_error_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_system_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_system_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_event_insert.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_event_update.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_object_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_trace_object_unregister.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_debug_callback_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_debug_log.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_delay_ms.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_descriptor_pack.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_descriptor_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_error_callback_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_event_flags_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_get_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_put.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_long_put_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_allocate_add_safe.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_allocate_mulc_safe.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_allocate_mulv_safe.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_compare.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_free_block_best_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_memory_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_off.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_mutex_on.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_pci_class_scan.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_pci_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_pci_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_physical_address.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_semaphore_put.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_set_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_get_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_put.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_short_put_big_endian.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_string_length_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_string_length_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_string_to_unicode.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_identify.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_relinquish.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_schedule_other.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_sleep.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_thread_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_timer_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_unicode_to_string.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_utility_virtual_address.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/common/core/inc/ux_api.h b/common/core/inc/ux_api.h new file mode 100644 index 0000000..d528e9a --- /dev/null +++ b/common/core/inc/ux_api.h @@ -0,0 +1,1983 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Application Interface (API) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* APPLICATION INTERFACE DEFINITION RELEASE */ +/* */ +/* ux_api.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the basic Application Interface (API) to the */ +/* high-performance USBX real-time USB stack. All service prototypes */ +/* and data structure definitions are defined in this file. */ +/* Please note that basic data type definitions and other architecture-*/ +/* specific information is contained in the file ux_port.h. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_API_H +#define UX_API_H + +/* Determine if a C++ compiler is being used. If so, ensure that standard + C is used to process the API information. */ + +#ifdef __cplusplus + +/* Yes, C++ compiler is present. Use standard C. */ +extern "C" { + +#endif + + +/* Include ThreadX API include file. */ + +#include "tx_api.h" + + +/* Include USBX port specific file. */ + +#include "ux_port.h" + +/* Define the maximum length for class names (exclude string null-terminator). */ +#define UX_MAX_CLASS_NAME_LENGTH 63 + +/* Define the maximum length for HCD names (exclude string null-terminator). */ +#define UX_MAX_HCD_NAME_LENGTH 63 + +/* Disable warning of parameter not used. */ +#ifndef UX_PARAMETER_NOT_USED +#define UX_PARAMETER_NOT_USED(p) ((VOID)(p)) +#endif + +/* Define additional generic USBX types. */ + +#ifndef SCHAR +typedef signed char SCHAR; +#endif + +/* If the port file did not define the memory barrier instruction, define it + to nothing since this is platform-specific. */ +#ifndef UX_DATA_MEMORY_BARRIER +#define UX_DATA_MEMORY_BARRIER +#endif + + +/* This defines the ASSERT and process on ASSERT fail. */ +#ifdef UX_ENABLE_ASSERT +#ifndef UX_ASSERT_FAIL +#define UX_ASSERT_FAIL for (;;) {tx_thread_sleep(UX_WAIT_FOREVER); } +#endif +#define UX_ASSERT(s) if (!(s)) {UX_ASSERT_FAIL} +#else +#define UX_ASSERT(s) +#endif /* UX_ENABLE_ASSERT */ + + +#ifndef UX_MAX_SLAVE_INTERFACES +#define UX_MAX_SLAVE_INTERFACES 16 +#endif + +/* Convert from millisecond to ThreadX Tick value. */ +#define MS_TO_TICK(ms) ((ms) * (UX_PERIODIC_RATE) / 1000) + + +/* Define USBX Host Enum Thread Stack Size. */ +#ifndef UX_HOST_ENUM_THREAD_STACK_SIZE +#define UX_HOST_ENUM_THREAD_STACK_SIZE UX_THREAD_STACK_SIZE +#endif + +/* Define USBX Host Thread Stack Size. */ +#ifndef UX_HOST_HCD_THREAD_STACK_SIZE +#define UX_HOST_HCD_THREAD_STACK_SIZE UX_THREAD_STACK_SIZE +#endif + +/* Define USBX Host HNP Polling Thread Stack Size */ +#ifndef UX_HOST_HNP_POLLING_THREAD_STACK +#define UX_HOST_HNP_POLLING_THREAD_STACK UX_THREAD_STACK_SIZE +#endif + +/* Macros for concatenating tokens, where UX_CONCATn concatenates n tokens. */ + +#define UX_CONCAT_BASE(x,y) x ## y +#define UX_CONCAT2(s0,s1) UX_CONCAT_BASE(s0,s1) + +/* Static assert that can report an error at compile time. */ +#define UX_COMPILE_TIME_ASSERT(exp, meaningful_name_as_variable) \ + typedef char UX_CONCAT2(meaningful_name_as_variable, __LINE__)[!(exp) ? -1 : 1]; + +/* Arithmetics. */ + +#define UX_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define UX_MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* Safe arithmetic check macros. */ + +#ifndef UX_DISABLE_ARITHMETIC_CHECK + +/* Calculate and check if result overflow/underflow. */ + +#define UX_OVERFLOW_CHECK_ADD_ULONG(a, b) ((a) > 0xFFFFFFFFul - (b)) +#define UX_OVERFLOW_CHECK_ADD_USHORT(a, b) ((a) > 0xFFFFul - (b)) +#define UX_OVERFLOW_CHECK_ADD_UCHAR(a, b) ((a) > 0xFFul - (b)) + +#define UX_UNDERFLOW_CHECK_MINUS(a, b) ((a) < (b)) + +/* Overflow check optimized in case multiplying a 2nd factor of const. */ +#define UX_OVERFLOW_CHECK_MULC_ULONG(v, c) ((v) > 0xFFFFFFFFul / (c)) +#define UX_OVERFLOW_CHECK_MULC_USHORT(v, c) ((v) > 0xFFFFul / (c)) +#define UX_OVERFLOW_CHECK_MULC_UCHAR(v, c) ((v) > 0xFFul / (c)) + +/* Overflow check optimized in case multiplying factors of variables and division instruction unavailable. */ +#define UX_OVERFLOW_CHECK_MULV_ULONG(v, v1) ((v) * (v1) < UX_MIN(v, v1)) +#define UX_OVERFLOW_CHECK_MULV_USHORT(v, v1) ((USHORT)((v) * (v1)) < UX_MIN(v, v1)) +#define UX_OVERFLOW_CHECK_MULV_UCHAR(v, v1) ((UCHAR)((v) * (v1)) < UX_MIN(v, v1)) + +#else + +/* There is no overflow/underflow, always 0 (false). */ + +#define UX_OVERFLOW_CHECK_ADD_ULONG(a, b) (0) +#define UX_OVERFLOW_CHECK_ADD_USHORT(a, b) (0) +#define UX_OVERFLOW_CHECK_ADD_UCHAR(a, b) (0) + +#define UX_OVERFLOW_CHECK_MULC_ULONG(v, c) (0) +#define UX_OVERFLOW_CHECK_MULC_USHORT(v, c) (0) +#define UX_OVERFLOW_CHECK_MULC_UCHAR(v, c) (0) + +#define UX_OVERFLOW_CHECK_MULV_ULONG(v, v1) (0) +#define UX_OVERFLOW_CHECK_MULV_USHORT(v, v1) (0) +#define UX_OVERFLOW_CHECK_MULV_UCHAR(v, v1) (0) +#endif + +/* Define the default extension to hold the control block for 64-bit mode. */ + +#ifndef UX_THREAD_EXTENSION_PTR_SET +#define UX_THREAD_EXTENSION_PTR_SET(a, b) +#endif + +#ifndef UX_THREAD_EXTENSION_PTR_GET +#define UX_THREAD_EXTENSION_PTR_GET(a, b, c) (a) = (b *)(c); +#endif + +#ifndef UX_TIMER_EXTENSION_PTR_SET +#define UX_TIMER_EXTENSION_PTR_SET(a, b) +#endif + +#ifndef UX_TIMER_EXTENSION_PTR_GET +#define UX_TIMER_EXTENSION_PTR_GET(a, b, c) (a) = (b *)(c); +#endif + +/* Determine if error log is enabled. */ + +#ifdef UX_ENABLE_DEBUG_LOG + +#ifndef UX_DEBUG_LOG_SIZE +#define UX_DEBUG_LOG_SIZE (1024 * 32) +#endif + +/* Map the error log macros to internal USBX function. */ + +#define UX_DEBUG_LOG(debug_location, debug_message, debug_code, debug_parameter_1, debug_parameter_2) _ux_utility_debug_log((UCHAR *) debug_location, (UCHAR *) debug_message, (ULONG) debug_code, (ULONG) debug_parameter_1, (ULONG) debug_parameter_2); + +VOID _ux_utility_debug_log(UCHAR *debug_location, UCHAR *debug_message, ULONG debug_code, ULONG debug_parameter_1, ULONG debug_parameter_2); + +/* DEBUG LOG MESSAGES SHOULD BE WRITEN LIKE THIS IN THE CODE : */ +/* If error log is enabled, insert this error message into the log buffer. */ +/* UX_DEBUG_LOG("_ux_host_stack_rh_device_insertion", "Device insertion", port_index, port_index, 0) */ + + +#else + +/* If Log is not defined, map it to nothing so that debug messages can stay in the code. */ +#define UX_DEBUG_LOG(debug_location, debug_message, debug_code, debug_parameter_1, debug_parameter_2) +#endif + +/* Determine if tracing is enabled. */ + +#ifdef TX_ENABLE_EVENT_TRACE + +/* Trace is enabled. Remap calls so that interrupts can be disabled around the actual event logging. */ + +#include "tx_trace.h" + + +/* Map the trace macros to internal USBX versions so we can get interrupt protection. */ + +#define UX_TRACE_OBJECT_REGISTER(t,p,n,a,b) _ux_trace_object_register(t, (VOID *) p, (CHAR *) n, (ULONG) a, (ULONG) b); +#define UX_TRACE_OBJECT_UNREGISTER(o) _ux_trace_object_unregister((VOID *) o); +#define UX_TRACE_IN_LINE_INSERT(i,a,b,c,d,f,g,h) _ux_trace_event_insert((ULONG) i, (ULONG) a, (ULONG) b, (ULONG) c, (ULONG) d, (ULONG) f, g, h); +#define UX_TRACE_EVENT_UPDATE(e,t,i,a,b,c,d) _ux_trace_event_update((TX_TRACE_BUFFER_ENTRY *) e, (ULONG) t, (ULONG) i, (ULONG) a, (ULONG) b, (ULONG) c, (ULONG) d); + + +/* Define USBX trace prototypes. */ + +VOID _ux_trace_object_register(UCHAR object_type, VOID *object_ptr, CHAR *object_name, ULONG parameter_1, ULONG parameter_2); +VOID _ux_trace_object_unregister(VOID *object_ptr); +VOID _ux_trace_event_insert(ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4, ULONG filter, TX_TRACE_BUFFER_ENTRY **current_event, ULONG *current_timestamp); +VOID _ux_trace_event_update(TX_TRACE_BUFFER_ENTRY *event, ULONG timestamp, ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4); + + +/* Define USBX event trace constants. */ + +#define UX_TRACE_OBJECT_TYPE_BASE 20 +#define UX_TRACE_HOST_OBJECT_TYPE_DEVICE (UX_TRACE_OBJECT_TYPE_BASE + 1) +#define UX_TRACE_HOST_OBJECT_TYPE_INTERFACE (UX_TRACE_OBJECT_TYPE_BASE + 2) +#define UX_TRACE_HOST_OBJECT_TYPE_ENDPOINT (UX_TRACE_OBJECT_TYPE_BASE + 3) +#define UX_TRACE_HOST_OBJECT_TYPE_CLASS_INSTANCE (UX_TRACE_OBJECT_TYPE_BASE + 4) + +#define UX_TRACE_DEVICE_OBJECT_TYPE_DEVICE (UX_TRACE_OBJECT_TYPE_BASE + 5) +#define UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE (UX_TRACE_OBJECT_TYPE_BASE + 6) +#define UX_TRACE_DEVICE_OBJECT_TYPE_ENDPOINT (UX_TRACE_OBJECT_TYPE_BASE + 7) +#define UX_TRACE_DEVICE_OBJECT_TYPE_CLASS_INSTANCE (UX_TRACE_OBJECT_TYPE_BASE + 8) + +/* Define event filters that can be used to selectively disable certain events or groups of events. */ + +#define UX_TRACE_ALL_EVENTS 0x7F000000 /* All USBX events */ +#define UX_TRACE_ERRORS 0x01000000 /* USBX Errors events */ +#define UX_TRACE_HOST_STACK_EVENTS 0x02000000 /* USBX Host Class Events */ +#define UX_TRACE_DEVICE_STACK_EVENTS 0x04000000 /* USBX Device Class Events */ +#define UX_TRACE_HOST_CONTROLLER_EVENTS 0x08000000 /* USBX Host Controller Events */ +#define UX_TRACE_DEVICE_CONTROLLER_EVENTS 0x10000000 /* USBX Device Controllers Events */ +#define UX_TRACE_HOST_CLASS_EVENTS 0x20000000 /* USBX Host Class Events */ +#define UX_TRACE_DEVICE_CLASS_EVENTS 0x40000000 /* USBX Device Class Events */ + + +/* Define the trace events in USBX, if not defined. */ + +/* Define the USBX host stack events. */ + +#define UX_TRACE_HOST_STACK_EVENTS_BASE 600 +#define UX_TRACE_HOST_STACK_CLASS_INSTANCE_CREATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 1) /* I1 = class , I2 = class instance */ +#define UX_TRACE_HOST_STACK_CLASS_INSTANCE_DESTROY (UX_TRACE_HOST_STACK_EVENTS_BASE + 2) /* I1 = class , I2 = class instance */ +#define UX_TRACE_HOST_STACK_CONFIGURATION_DELETE (UX_TRACE_HOST_STACK_EVENTS_BASE + 3) /* I1 = configuration */ +#define UX_TRACE_HOST_STACK_CONFIGURATION_ENUMERATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 4) /* I1 = device */ +#define UX_TRACE_HOST_STACK_CONFIGURATION_INSTANCE_CREATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 5) /* I1 = configuration */ +#define UX_TRACE_HOST_STACK_CONFIGURATION_INSTANCE_DELETE (UX_TRACE_HOST_STACK_EVENTS_BASE + 6) /* I1 = configuration */ +#define UX_TRACE_HOST_STACK_CONFIGURATION_SET (UX_TRACE_HOST_STACK_EVENTS_BASE + 7) /* I1 = configuration */ +#define UX_TRACE_HOST_STACK_DEVICE_ADDRESS_SET (UX_TRACE_HOST_STACK_EVENTS_BASE + 8) /* I1 = device , I2 = device address */ +#define UX_TRACE_HOST_STACK_DEVICE_CONFIGURATION_GET (UX_TRACE_HOST_STACK_EVENTS_BASE + 9) /* I1 = device , I2 = configuration */ +#define UX_TRACE_HOST_STACK_DEVICE_CONFIGURATION_SELECT (UX_TRACE_HOST_STACK_EVENTS_BASE + 10) /* I1 = device , I2 = configuration */ +#define UX_TRACE_HOST_STACK_DEVICE_DESCRIPTOR_READ (UX_TRACE_HOST_STACK_EVENTS_BASE + 11) /* I1 = device */ +#define UX_TRACE_HOST_STACK_DEVICE_GET (UX_TRACE_HOST_STACK_EVENTS_BASE + 12) /* I1 = device index */ +#define UX_TRACE_HOST_STACK_DEVICE_REMOVE (UX_TRACE_HOST_STACK_EVENTS_BASE + 13) /* I1 = hcd , I2 = parent , I3 = port index , I4 = device */ +#define UX_TRACE_HOST_STACK_DEVICE_RESOURCE_FREE (UX_TRACE_HOST_STACK_EVENTS_BASE + 14) /* I1 = device */ +#define UX_TRACE_HOST_STACK_ENDPOINT_INSTANCE_CREATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 15) /* I1 = device , I2 = endpoint */ +#define UX_TRACE_HOST_STACK_ENDPOINT_INSTANCE_DELETE (UX_TRACE_HOST_STACK_EVENTS_BASE + 16) /* I1 = device , I2 = endpoint */ +#define UX_TRACE_HOST_STACK_ENDPOINT_RESET (UX_TRACE_HOST_STACK_EVENTS_BASE + 17) /* I1 = device , I2 = endpoint */ +#define UX_TRACE_HOST_STACK_ENDPOINT_TRANSFER_ABORT (UX_TRACE_HOST_STACK_EVENTS_BASE + 18) /* I1 = endpoint */ +#define UX_TRACE_HOST_STACK_HCD_REGISTER (UX_TRACE_HOST_STACK_EVENTS_BASE + 19) /* I1 = hcd name */ +#define UX_TRACE_HOST_STACK_INITIALIZE (UX_TRACE_HOST_STACK_EVENTS_BASE + 20) /* */ +#define UX_TRACE_HOST_STACK_INTERFACE_ENDPOINT_GET (UX_TRACE_HOST_STACK_EVENTS_BASE + 21) /* I1 = interface , I2 = endpoint index */ +#define UX_TRACE_HOST_STACK_INTERFACE_INSTANCE_CREATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 22) /* I1 = interface */ +#define UX_TRACE_HOST_STACK_INTERFACE_INSTANCE_DELETE (UX_TRACE_HOST_STACK_EVENTS_BASE + 23) /* I1 = interface */ +#define UX_TRACE_HOST_STACK_INTERFACE_SET (UX_TRACE_HOST_STACK_EVENTS_BASE + 24) /* I1 = interface */ +#define UX_TRACE_HOST_STACK_INTERFACE_SETTING_SELECT (UX_TRACE_HOST_STACK_EVENTS_BASE + 25) /* I1 = interface */ +#define UX_TRACE_HOST_STACK_NEW_CONFIGURATION_CREATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 26) /* I1 = device , I2 = configuration */ +#define UX_TRACE_HOST_STACK_NEW_DEVICE_CREATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 27) /* I1 = hcd , I2 = device owner , I3 = port index , I4 = device */ +#define UX_TRACE_HOST_STACK_NEW_ENDPOINT_CREATE (UX_TRACE_HOST_STACK_EVENTS_BASE + 28) /* I1 = interface , I2 = endpoint */ +#define UX_TRACE_HOST_STACK_RH_CHANGE_PROCESS (UX_TRACE_HOST_STACK_EVENTS_BASE + 29) /* I1 = port index */ +#define UX_TRACE_HOST_STACK_RH_DEVICE_EXTRACTION (UX_TRACE_HOST_STACK_EVENTS_BASE + 30) /* I1 = hcd , I2 = port index */ +#define UX_TRACE_HOST_STACK_RH_DEVICE_INSERTION (UX_TRACE_HOST_STACK_EVENTS_BASE + 31) /* I1 = hcd , I2 = port index */ +#define UX_TRACE_HOST_STACK_TRANSFER_REQUEST (UX_TRACE_HOST_STACK_EVENTS_BASE + 32) /* I1 = device , I2 = endpoint , I3 = transfer request */ +#define UX_TRACE_HOST_STACK_TRANSFER_REQUEST_ABORT (UX_TRACE_HOST_STACK_EVENTS_BASE + 33) /* I1 = device , I2 = endpoint , I3 = transfer request */ + +/* Define the USBX host class events. */ + +#define UX_TRACE_HOST_CLASS_EVENTS_BASE 650 +#define UX_TRACE_HOST_CLASS_ASIX_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 1) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_ASIX_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 2) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_ASIX_INTERRUPT_NOTIFICATION (UX_TRACE_HOST_CLASS_EVENTS_BASE + 3) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_ASIX_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 4) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_ASIX_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 5) /* I1 = class instance , I2 = data pointer , I3 = requested length */ + +#define UX_TRACE_HOST_CLASS_AUDIO_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 10) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_AUDIO_CONTROL_VALUE_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 11) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_AUDIO_CONTROL_VALUE_SET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 12) /* I1 = class instance , I2 = audio control */ +#define UX_TRACE_HOST_CLASS_AUDIO_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 13) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_AUDIO_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 14) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_AUDIO_STREAMING_SAMPLING_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 15) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_AUDIO_STREAMING_SAMPLING_SET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 16) /* I1 = class instance , I2 = audio sampling */ +#define UX_TRACE_HOST_CLASS_AUDIO_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 17) /* I1 = class instance , I2 = data pointer , I3 = requested length */ + +#define UX_TRACE_HOST_CLASS_CDC_ACM_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 20) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 21) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING (UX_TRACE_HOST_CLASS_EVENTS_BASE + 22) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING (UX_TRACE_HOST_CLASS_EVENTS_BASE + 23) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 24) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_SEND_BREAK (UX_TRACE_HOST_CLASS_EVENTS_BASE + 25) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_ABORT_IN_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 26) /* I1 = class instance , I2 = endpoint */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_ABORT_OUT_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 27) /* I1 = class instance , I2 = endpointr */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_NOTIFICATION_CALLBACK (UX_TRACE_HOST_CLASS_EVENTS_BASE + 28) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_GET_DEVICE_STATUS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 29) /* I1 = class instance , I2 = device status */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 30) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_RECEPTION_START (UX_TRACE_HOST_CLASS_EVENTS_BASE + 31) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_RECEPTION_STOP (UX_TRACE_HOST_CLASS_EVENTS_BASE + 32) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_CDC_ACM_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 33) /* I1 = class instance , I2 = data pointer , I3 = requested length */ + +#define UX_TRACE_HOST_CLASS_CDC_ECM_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 35) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_CDC_ECM_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 36) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_CDC_ECM_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 37) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_CDC_ECM_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 38) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_CDC_ECM_INTERRUPT_NOTIFICATION (UX_TRACE_HOST_CLASS_EVENTS_BASE + 39) /* I1 = class instance */ + +#define UX_TRACE_HOST_CLASS_HID_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 40) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_HID_CLIENT_REGISTER (UX_TRACE_HOST_CLASS_EVENTS_BASE + 41) /* I1 = hid client name */ +#define UX_TRACE_HOST_CLASS_HID_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 42) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_HID_IDLE_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 43) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_HID_IDLE_SET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 44) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_HID_KEYBOARD_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 45) /* I1 = class instance , I2 = hid client instance */ +#define UX_TRACE_HOST_CLASS_HID_KEYBOARD_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 46) /* I1 = class instance , I2 = hid client instance */ +#define UX_TRACE_HOST_CLASS_HID_MOUSE_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 47) /* I1 = class instance , I2 = hid client instance */ +#define UX_TRACE_HOST_CLASS_HID_MOUSE_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 48) /* I1 = class instance , I2 = hid client instance */ +#define UX_TRACE_HOST_CLASS_HID_REMOTE_CONTROL_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 49) /* I1 = class instance , I2 = hid client instance */ +#define UX_TRACE_HOST_CLASS_HID_REMOTE_CONTROL_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 50) /* I1 = class instance , I2 = hid client instance */ +#define UX_TRACE_HOST_CLASS_HID_REPORT_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 51) /* I1 = class instance , I2 = client report */ +#define UX_TRACE_HOST_CLASS_HID_REPORT_SET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 52) /* I1 = class instance , I2 = client report */ +#define UX_TRACE_HOST_CLASS_HID_REMOTE_CONTROL_CALLBACK (UX_TRACE_HOST_CLASS_EVENTS_BASE + 53) /* I1 = client instance , I2 = remote control instance */ + +#define UX_TRACE_HOST_CLASS_HUB_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 60) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_HUB_CHANGE_DETECT (UX_TRACE_HOST_CLASS_EVENTS_BASE + 62) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION_PROCESS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 63) /* I1 = class instance , I2 = port , I3 = port status */ +#define UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_ENABLE_PROCESS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 64) /* I1 = class instance , I2 = port , I3 = port status */ +#define UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_OVER_CURRENT_PROCESS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 65) /* I1 = class instance , I2 = port , I3 = port status */ +#define UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_RESET_PROCESS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 66) /* I1 = class instance , I2 = port , I3 = port status */ +#define UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_SUSPEND_PROCESS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 67) /* I1 = class instance , I2 = port , I3 = port status */ +#define UX_TRACE_HOST_CLASS_HUB_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 68) /* I1 = class instance */ + +#define UX_TRACE_HOST_CLASS_PIMA_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 70) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PIMA_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 71) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PIMA_DEVICE_INFO_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 72) /* I1 = class instance , I2 = pima device */ +#define UX_TRACE_HOST_CLASS_PIMA_DEVICE_RESET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 73) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PIMA_NOTIFICATION (UX_TRACE_HOST_CLASS_EVENTS_BASE + 74) /* I1 = class instance , I2 = event code , I3 = transaction ID , I4 = parameter1 */ +#define UX_TRACE_HOST_CLASS_PIMA_NUM_OBJECTS_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 75) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_CLOSE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 76) /* I1 = class instance , I2 = object */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_COPY (UX_TRACE_HOST_CLASS_EVENTS_BASE + 77) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_DELETE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 78) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 79) /* I1 = class instance , I2 = object handle , I3 = object */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_INFO_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 80) /* I1 = class instance , I2 = object handle , I3 = object */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_INFO_SEND (UX_TRACE_HOST_CLASS_EVENTS_BASE + 81) /* I1 = class instance , I2 = object */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_MOVE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 82) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_SEND (UX_TRACE_HOST_CLASS_EVENTS_BASE + 83) /* I1 = class instance , I2 = object , I3 = object_buffer , I4 = object length */ +#define UX_TRACE_HOST_CLASS_PIMA_OBJECT_TRANSFER_ABORT (UX_TRACE_HOST_CLASS_EVENTS_BASE + 84) /* I1 = class instance , I2 = object handle , I3 = object */ +#define UX_TRACE_HOST_CLASS_PIMA_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 85) /* I1 = class instance , I2 = data pointer , I3 = data length */ +#define UX_TRACE_HOST_CLASS_PIMA_REQUEST_CANCEL (UX_TRACE_HOST_CLASS_EVENTS_BASE + 86) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PIMA_SESSION_CLOSE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 87) /* I1 = class instance , I2 = pima session */ +#define UX_TRACE_HOST_CLASS_PIMA_SESSION_OPEN (UX_TRACE_HOST_CLASS_EVENTS_BASE + 88) /* I1 = class instance , I2 = pima session */ +#define UX_TRACE_HOST_CLASS_PIMA_STORAGE_IDS_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 89) /* I1 = class instance , I2 = storage ID array, I3 = storage ID length */ +#define UX_TRACE_HOST_CLASS_PIMA_STORAGE_INFO_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 90) /* I1 = class instance , I2 = storage ID , I3 = storage */ +#define UX_TRACE_HOST_CLASS_PIMA_THUMB_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 91) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_HOST_CLASS_PIMA_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 92) /* I1 = class instance , I2 = data pointer , I3 = data length */ + +#define UX_TRACE_HOST_CLASS_PRINTER_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 100) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PRINTER_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 101) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PRINTER_NAME_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 102) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PRINTER_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 103) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_PRINTER_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 104) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_PRINTER_SOFT_RESET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 105) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PRINTER_STATUS_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 106) /* I1 = class instance , I2 = printer status */ + +#define UX_TRACE_HOST_CLASS_PROLIFIC_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 110) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 111) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_CODING (UX_TRACE_HOST_CLASS_EVENTS_BASE + 112) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_GET_LINE_CODING (UX_TRACE_HOST_CLASS_EVENTS_BASE + 113) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_STATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 114) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_PURGE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 115) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_SEND_BREAK (UX_TRACE_HOST_CLASS_EVENTS_BASE + 116) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_ABORT_IN_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 117) /* I1 = class instance , I2 = endpoint */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_ABORT_OUT_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 118) /* I1 = class instance , I2 = endpointr */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_REPORT_DEVICE_STATUS_CHANGE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 119) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_GET_DEVICE_STATUS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 120) /* I1 = class instance , I2 = device status */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 121) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_RECEPTION_START (UX_TRACE_HOST_CLASS_EVENTS_BASE + 122) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_RECEPTION_STOP (UX_TRACE_HOST_CLASS_EVENTS_BASE + 123) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_PROLIFIC_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 124) /* I1 = class instance , I2 = data pointer , I3 = requested length */ + +#define UX_TRACE_HOST_CLASS_STORAGE_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 130) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_STORAGE_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 131) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_STORAGE_MEDIA_CAPACITY_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 132) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_STORAGE_MEDIA_FORMAT_CAPACITY_GET (UX_TRACE_HOST_CLASS_EVENTS_BASE + 133) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_STORAGE_MEDIA_MOUNT (UX_TRACE_HOST_CLASS_EVENTS_BASE + 134) /* I1 = class instance , I2 = sector */ +#define UX_TRACE_HOST_CLASS_STORAGE_MEDIA_OPEN (UX_TRACE_HOST_CLASS_EVENTS_BASE + 135) /* I1 = class instance , I2 = media */ +#define UX_TRACE_HOST_CLASS_STORAGE_MEDIA_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 136) /* I1 = class instance , I2 = sector start , I3 = sector count , I4 = data pointer */ +#define UX_TRACE_HOST_CLASS_STORAGE_MEDIA_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 137) /* I1 = class instance , I2 = sector start , I3 = sector count , I4 = data pointer */ +#define UX_TRACE_HOST_CLASS_STORAGE_REQUEST_SENSE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 138) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_STORAGE_START_STOP (UX_TRACE_HOST_CLASS_EVENTS_BASE + 139) /* I1 = class instance , I2 = start stop signal */ +#define UX_TRACE_HOST_CLASS_STORAGE_UNIT_READY_TEST (UX_TRACE_HOST_CLASS_EVENTS_BASE + 140) /* I1 = class instance */ + +#define UX_TRACE_HOST_CLASS_DPUMP_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 150) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_DPUMP_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 151) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_DPUMP_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 152) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_DPUMP_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 153) /* I1 = class instance , I2 = data pointer , I3 = requested length */ + +#define UX_TRACE_HOST_CLASS_SWAR_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 160) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_SWAR_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 161) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_SWAR_IOCTL_ABORT_IN_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 162) /* I1 = class instance , I2 = endpoint */ +#define UX_TRACE_HOST_CLASS_SWAR_IOCTL_ABORT_OUT_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 163) /* I1 = class instance , I2 = endpointr */ +#define UX_TRACE_HOST_CLASS_SWAR_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 164) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_SWAR_RECEPTION_START (UX_TRACE_HOST_CLASS_EVENTS_BASE + 165) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_SWAR_RECEPTION_STOP (UX_TRACE_HOST_CLASS_EVENTS_BASE + 166) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_SWAR_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 167) /* I1 = class instance , I2 = data pointer , I3 = requested length */ + +#define UX_TRACE_HOST_CLASS_GSER_ACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 170) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_GSER_DEACTIVATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 171) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_SET_LINE_CODING (UX_TRACE_HOST_CLASS_EVENTS_BASE + 172) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_GET_LINE_CODING (UX_TRACE_HOST_CLASS_EVENTS_BASE + 173) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_SET_LINE_STATE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 174) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_PURGE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 175) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_SEND_BREAK (UX_TRACE_HOST_CLASS_EVENTS_BASE + 176) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_ABORT_IN_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 177) /* I1 = class instance , I2 = endpoint */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_ABORT_OUT_PIPE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 178) /* I1 = class instance , I2 = endpointr */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_REPORT_DEVICE_STATUS_CHANGE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 179) /* I1 = class instance , I2 = parameter */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_GET_DEVICE_STATUS (UX_TRACE_HOST_CLASS_EVENTS_BASE + 180) /* I1 = class instance , I2 = device status */ +#define UX_TRACE_HOST_CLASS_GSER_IOCTL_NOTIFICATION_CALLBACK (UX_TRACE_HOST_CLASS_EVENTS_BASE + 181) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_GSER_READ (UX_TRACE_HOST_CLASS_EVENTS_BASE + 182) /* I1 = class instance , I2 = data pointer , I3 = requested length */ +#define UX_TRACE_HOST_CLASS_GSER_RECEPTION_START (UX_TRACE_HOST_CLASS_EVENTS_BASE + 183) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_GSER_RECEPTION_STOP (UX_TRACE_HOST_CLASS_EVENTS_BASE + 184) /* I1 = class instance */ +#define UX_TRACE_HOST_CLASS_GSER_WRITE (UX_TRACE_HOST_CLASS_EVENTS_BASE + 185) /* I1 = class instance , I2 = data pointer , I3 = requested length */ + +/* Define the USBX device stack events. */ + +#define UX_TRACE_DEVICE_STACK_EVENTS_BASE 850 +#define UX_TRACE_DEVICE_STACK_ALTERNATE_SETTING_GET (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 1) /* I1 = interface value */ +#define UX_TRACE_DEVICE_STACK_ALTERNATE_SETTING_SET (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 2) /* I1 = interface value , I2 = alternate setting value */ +#define UX_TRACE_DEVICE_STACK_CLASS_REGISTER (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 3) /* I1 = class name , I2 = interface number, I3 = parameter */ +#define UX_TRACE_DEVICE_STACK_CLEAR_FEATURE (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 4) /* I1 = request type , I2 = request value , I3 = request index */ +#define UX_TRACE_DEVICE_STACK_CONFIGURATION_GET (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 5) /* I1 = configuration value */ +#define UX_TRACE_DEVICE_STACK_CONFIGURATION_SET (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 5) /* I1 = configuration value */ +#define UX_TRACE_DEVICE_STACK_CONNECT (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 6) /* */ +#define UX_TRACE_DEVICE_STACK_DESCRIPTOR_SEND (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 7) /* I1 = descriptor type , I2 = request index */ +#define UX_TRACE_DEVICE_STACK_DISCONNECT (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 8) /* I1 = device */ +#define UX_TRACE_DEVICE_STACK_ENDPOINT_STALL (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 9) /* I1 = endpoint */ +#define UX_TRACE_DEVICE_STACK_GET_STATUS (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 10) /* I1 = request type , I2 = request value , I3 = request index */ +#define UX_TRACE_DEVICE_STACK_HOST_WAKEUP (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 11) /* */ +#define UX_TRACE_DEVICE_STACK_INITIALIZE (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 12) /* */ +#define UX_TRACE_DEVICE_STACK_INTERFACE_DELETE (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 13) /* I1 = interface */ +#define UX_TRACE_DEVICE_STACK_INTERFACE_GET (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 14) /* I1 = interface value */ +#define UX_TRACE_DEVICE_STACK_INTERFACE_SET (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 15) /* I1 = alternate setting value */ +#define UX_TRACE_DEVICE_STACK_SET_FEATURE (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 16) /* I1 = request value , I2 = request index */ +#define UX_TRACE_DEVICE_STACK_TRANSFER_ABORT (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 17) /* I1 = transfer request, I2 = completion code */ +#define UX_TRACE_DEVICE_STACK_TRANSFER_ALL_REQUEST_ABORT (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 18) /* I1 = endpoint , I2 = completion code */ +#define UX_TRACE_DEVICE_STACK_TRANSFER_REQUEST (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 19) /* I1 = transfer request */ +#define UX_TRACE_DEVICE_STACK_MICROSOFT_EXTENSION_REGISTER (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 20) /* I1 = transfer request */ +#define UX_TRACE_DEVICE_STACK_CLASS_UNREGISTER (UX_TRACE_DEVICE_STACK_EVENTS_BASE + 21) /* I1 = class name */ + +/* Define the USBX device stack events first. */ + +#define UX_TRACE_DEVICE_CLASS_EVENTS_BASE 900 +#define UX_TRACE_DEVICE_CLASS_DPUMP_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 1) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_DPUMP_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 2) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_DPUMP_READ (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 3) /* I1 = class instance , I2 = buffer , I3 = requested_length */ +#define UX_TRACE_DEVICE_CLASS_DPUMP_WRITE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 4) /* I1 = class instance , I2 = buffer , I3 = requested_length */ +#define UX_TRACE_DEVICE_CLASS_DPUMP_CHANGE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 4) /* I1 = class instance , I2 = buffer , I3 = requested_length */ + +#define UX_TRACE_DEVICE_CLASS_CDC_ACM_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 10) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_CDC_ACM_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 11) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_CDC_ACM_READ (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 12) /* I1 = class instance , I2 = buffer , I3 = requested_length */ +#define UX_TRACE_DEVICE_CLASS_CDC_ACM_WRITE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 13) /* I1 = class instance , I2 = buffer , I3 = requested_length */ + +#define UX_TRACE_DEVICE_CLASS_HID_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 20) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_HID_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 21) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_HID_EVENT_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 22) /* I1 = class instance , I2 = hid event */ +#define UX_TRACE_DEVICE_CLASS_HID_EVENT_SET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 23) /* I1 = class instance , I2 = hid event */ +#define UX_TRACE_DEVICE_CLASS_HID_REPORT_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 24) /* I1 = class instance , I2 = descriptor type , I3 = request index */ +#define UX_TRACE_DEVICE_CLASS_HID_REPORT_SET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 25) /* I1 = class instance , I2 = descriptor type , I3 = request index */ +#define UX_TRACE_DEVICE_CLASS_HID_DESCRIPTOR_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 26) /* I1 = class instance , I2 = descriptor type , I3 = request index */ + +#define UX_TRACE_DEVICE_CLASS_PIMA_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 30) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 31) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_DEVICE_INFO_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 32) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_EVENT_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 33) /* I1 = class instance , I2 = pima event */ +#define UX_TRACE_DEVICE_CLASS_PIMA_EVENT_SET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 34) /* I1 = class instance , I2 = pima event */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_ADD (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 35) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_DATA_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 36) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_DATA_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 37) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_DELETE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 38) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_HANDLES_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 39) /* I1 = class instance , I2 = storage id , I3 = object format code, I4 = object association */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_INFO_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 40) /* I1 = class instance , I2 = object handle */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_INFO_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 41) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECTS_NUMBER_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 42) /* I1 = class instance , I2 = storage id , I3 = object format code, I4 = object association */ +#define UX_TRACE_DEVICE_CLASS_PIMA_PARTIAL_OBJECT_DATA_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 43) /* I1 = class instance , I2 = object handle , I3 = offset requested , I4 = length requested */ +#define UX_TRACE_DEVICE_CLASS_PIMA_RESPONSE_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 44) /* I1 = class instance , I2 = response code , I3 = number parameter , I4 = pima parameter 1 */ +#define UX_TRACE_DEVICE_CLASS_PIMA_STORAGE_ID_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 45) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_STORAGE_INFO_SEND (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 46) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_GET_DEVICE_PROP_DESC_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 47) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_GET_DEVICE_PROP_VALUE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 48) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_GET_DEVICE_PROP_VALUE_SET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 49) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_GET_OBJECT_PROP_DESC (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 50) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_GET_OBJECT_PROP_VALUE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 51) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECTS_PROPS_SUPPORTED_GET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 52) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_GET_OBJECT_REFERENCES (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 53) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_OBJECTS_PROPS_SUPPORTED_SET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 54) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_SET_OBJECT_REFERENCES (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 55) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_STORAGE_FORMAT (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 56) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_DEVICE_RESET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 57) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_PIMA_SET_OBJECT_PROP_VALUE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 58) /* I1 = class instance */ + +#define UX_TRACE_DEVICE_CLASS_RNDIS_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 60) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_RNDIS_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 61) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_RNDIS_PACKET_RECEIVE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 62) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_RNDIS_PACKET_TRANSMIT (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 63) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_RNDIS_MSG_QUERY (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 64) /* I1 = class instance , I2 = rndis OID */ +#define UX_TRACE_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 65) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_RNDIS_MSG_RESET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 66) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_RNDIS_MSG_SET (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 67) /* I1 = class instance , I2 = rndis OID */ + +#define UX_TRACE_DEVICE_CLASS_STORAGE_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 70) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 71) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_FORMAT (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 72) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_INQUIRY (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 73) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_MODE_SELECT (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 74) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_MODE_SENSE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 75) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_PREVENT_ALLOW_MEDIA_REMOVAL (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 76) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_READ (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 77) /* I1 = class instance , I2 = lun , I3 = sector , I4 = number sectors */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_READ_CAPACITY (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 78) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_READ_FORMAT_CAPACITY (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 79) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_READ_TOC (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 80) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_REQUEST_SENSE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 81) /* I1 = class instance , I2 = lun , I3 = sense key , I4 = code */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_TEST_READY (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 82) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_START_STOP (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 83) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_VERIFY (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 84) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_WRITE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 85) /* I1 = class instance , I2 = lun , I3 = sector , I4 = number sectors */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_GET_CONFIGURATION (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 86) /* I1 = class instance , I2 = lun */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_SYNCHRONIZE_CACHE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 87) /* I1 = class instance , I2 = lun , I3 = sector , I4 = number sectors */ +#define UX_TRACE_DEVICE_CLASS_STORAGE_OTHER (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 88) /* I1 = class instance , I2 = lun */ + +#define UX_TRACE_DEVICE_CLASS_CDC_ECM_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 90) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_CDC_ECM_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 91) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_CDC_ECM_CHANGE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 92) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_CDC_ECM_READ (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 93) /* I1 = class instance , I2 = buffer , I3 = requested_length */ +#define UX_TRACE_DEVICE_CLASS_CDC_ECM_WRITE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 94) /* I1 = class instance , I2 = buffer , I3 = requested_length */ +#define UX_TRACE_DEVICE_CLASS_CDC_ECM_PACKET_TRANSMIT (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 95) /* I1 = class instance , I2 = buffer , I3 = requested_length */ +#define UX_TRACE_DEVICE_CLASS_CDC_ECM_PACKET_RECEIVE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 96) /* I1 = class instance , I2 = buffer , I3 = requested_length */ + +#define UX_TRACE_DEVICE_CLASS_DFU_ACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 97) /* I1 = class instance */ +#define UX_TRACE_DEVICE_CLASS_DFU_DEACTIVATE (UX_TRACE_DEVICE_CLASS_EVENTS_BASE + 98) /* I1 = class instance */ + +/* Define the USBX Error Event. */ + +#define UX_TRACE_ERROR 999 + + +#else +#define UX_TRACE_OBJECT_REGISTER(t,p,n,a,b) +#define UX_TRACE_OBJECT_UNREGISTER(o) +#define UX_TRACE_IN_LINE_INSERT(i,a,b,c,d,f,g,h) +#define UX_TRACE_EVENT_UPDATE(e,t,i,a,b,c,d) +#endif + + +/* Define the system level for error trapping. */ +#define UX_SYSTEM_LEVEL_INTERRUPT 1 +#define UX_SYSTEM_LEVEL_THREAD 2 + +/* Define the system context for error trapping. */ +#define UX_SYSTEM_CONTEXT_HCD 1 +#define UX_SYSTEM_CONTEXT_DCD 2 +#define UX_SYSTEM_CONTEXT_INIT 3 +#define UX_SYSTEM_CONTEXT_ENUMERATOR 4 +#define UX_SYSTEM_CONTEXT_ROOT_HUB 5 +#define UX_SYSTEM_CONTEXT_HUB 6 +#define UX_SYSTEM_CONTEXT_CLASS 7 +#define UX_SYSTEM_CONTEXT_UTILITY 8 +#define UX_SYSTEM_CONTEXT_DEVICE_STACK 9 + + +/* Defines the number of ThreadX timer ticks per seconds. By default, the ThreadX timer tick is 10ms, + so the default value for this constant is 100. If TX_TIMER_TICKS_PER_SECOND is defined, + this value is derived from TX_TIMER_TICKS_PER_SECOND. */ + +#ifndef UX_PERIODIC_RATE +#ifdef TX_TIMER_TICKS_PER_SECOND +#define UX_PERIODIC_RATE (TX_TIMER_TICKS_PER_SECOND) +#else +#define UX_PERIODIC_RATE 100 +#endif +#endif + +/* Define basic USBX constants. */ + +#define UX_NULL ((void*)0) +#define UX_TRUE 1 +#define UX_FALSE 0 +#define UX_MAX_TT 8 +#define UX_TT_MASK 0x1FF +#define UX_TT_BANDWIDTH 6000 +#define UX_SLAVE_ENDPOINT_DEFAULT_BUFFER_SIZE 256 +#define UX_REGULAR_MEMORY 0 +#define UX_CACHE_SAFE_MEMORY 1 + +#define UX_MAX_BYTES_PER_FRAME_FS 1157 +#define UX_MAX_BYTES_PER_MICROFRAME_HS 5785 + +/* Define USBX command request constants. */ + +#define UX_SETUP_REQUEST_TYPE 0 +#define UX_SETUP_REQUEST 1 +#define UX_SETUP_VALUE 2 +#define UX_SETUP_INDEX 4 +#define UX_SETUP_LENGTH 6 +#define UX_SETUP_SIZE 8 + + +/* Define USBX standard commands. */ + +#define UX_GET_STATUS 0 +#define UX_CLEAR_FEATURE 1 +#define UX_SET_FEATURE 3 +#define UX_SET_ADDRESS 5 +#define UX_GET_DESCRIPTOR 6 +#define UX_SET_DESCRIPTOR 7 +#define UX_GET_CONFIGURATION 8 +#define UX_SET_CONFIGURATION 9 +#define UX_GET_INTERFACE 10 +#define UX_SET_INTERFACE 11 +#define UX_SYNCH_FRAME 12 + + +/* Define USBX command sub constants. */ + +#define UX_ENDPOINT_HALT 0 + +/* Define USBX feature selector constants. */ +#define UX_REQUEST_FEATURE_ENDPOINT_HALT 0 +#define UX_REQUEST_FEATURE_DEVICE_REMOTE_WAKEUP 1 +#define UX_REQUEST_FEATURE_TEST_MODE 2 + +/* Define Generic USBX constants. */ + +#define UX_WAIT_FOREVER 0xffffffff +#define UX_UNUSED 0 +#define UX_USED 1 +#define UX_MEMORY_UNUSED 0x12345678 +#define UX_MEMORY_USED 0x87654321 +#define UX_NO_ALIGN 0 +#define UX_ALIGN_16 0x0f +#define UX_ALIGN_MIN 0x0f +#define UX_ALIGN_32 0x1f +#define UX_ALIGN_64 0x3f +#define UX_ALIGN_128 0x7f +#define UX_ALIGN_256 0xff +#define UX_ALIGN_512 0x1ff +#define UX_ALIGN_1024 0x3ff +#define UX_ALIGN_2048 0x7ff +#define UX_ALIGN_4096 0xfff +#define UX_SAFE_ALIGN 0xffffffff +#define UX_MAX_SCATTER_GATHER_ALIGNMENT 4096 + +#define UX_MAX_USB_DEVICES 127 + +#define UX_ENDPOINT_DIRECTION 0x80 +#define UX_ENDPOINT_IN 0x80 +#define UX_ENDPOINT_OUT 0x00 + +#define UX_MASK_ENDPOINT_TYPE 3 +#define UX_CONTROL_ENDPOINT 0 +#define UX_ISOCHRONOUS_ENDPOINT 1 +#define UX_BULK_ENDPOINT 2 +#define UX_INTERRUPT_ENDPOINT 3 + +#define UX_ISOCHRONOUS_ENDPOINT_IN 0x81 +#define UX_ISOCHRONOUS_ENDPOINT_OUT 0x01 +#define UX_BULK_ENDPOINT_IN 0x82 +#define UX_BULK_ENDPOINT_OUT 0x02 +#define UX_INTERRUPT_ENDPOINT_IN 0x83 +#define UX_INTERRUPT_ENDPOINT_OUT 0x03 + +#define UX_MAX_PACKET_SIZE_MASK 0x7ff +#define UX_MAX_NUMBER_OF_TRANSACTIONS_MASK 0x1800 +#define UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT 11 + +#define UX_REQUEST_DIRECTION 0x80 +#define UX_REQUEST_IN 0x80 +#define UX_REQUEST_OUT 0x00 + +#define UX_REQUEST_TYPE 0x60 +#define UX_REQUEST_TYPE_STANDARD 0x00 +#define UX_REQUEST_TYPE_CLASS 0x20 +#define UX_REQUEST_TYPE_VENDOR 0x40 + +#define UX_REQUEST_TARGET 0x03 +#define UX_REQUEST_TARGET_DEVICE 0x00 +#define UX_REQUEST_TARGET_INTERFACE 0x01 +#define UX_REQUEST_TARGET_ENDPOINT 0x02 +#define UX_REQUEST_TARGET_OTHER 0x03 + +#define UX_DEVICE_RESET 0 +#define UX_DEVICE_ATTACHED 1 +#define UX_DEVICE_ADDRESSED 2 +#define UX_DEVICE_CONFIGURED 3 +#define UX_DEVICE_SUSPENDED 4 +#define UX_DEVICE_RESUMED 5 +#define UX_DEVICE_SELF_POWERED_STATE 6 +#define UX_DEVICE_BUS_POWERED_STATE 7 +#define UX_DEVICE_REMOTE_WAKEUP 8 +#define UX_DEVICE_BUS_RESET_COMPLETED 9 +#define UX_DEVICE_REMOVED 10 +#define UX_DEVICE_FORCE_DISCONNECT 11 + +#define UX_ENDPOINT_RESET 0 +#define UX_ENDPOINT_RUNNING 1 +#define UX_ENDPOINT_HALTED 2 + +#define UX_DEVICE_DESCRIPTOR_ITEM 1 +#define UX_CONFIGURATION_DESCRIPTOR_ITEM 2 +#define UX_STRING_DESCRIPTOR_ITEM 3 +#define UX_INTERFACE_DESCRIPTOR_ITEM 4 +#define UX_ENDPOINT_DESCRIPTOR_ITEM 5 +#define UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM 6 +#define UX_OTHER_SPEED_DESCRIPTOR_ITEM 7 +#define UX_OTG_DESCRIPTOR_ITEM 9 +#define UX_INTERFACE_ASSOCIATION_DESCRIPTOR_ITEM 11 +#define UX_DFU_FUNCTIONAL_DESCRIPTOR_ITEM 0x21 +#define UX_HUB_DESCRIPTOR_ITEM 0x29 + + +#define UX_CONTROL_TRANSFER_TIMEOUT 1000 +#define UX_NON_CONTROL_TRANSFER_TIMEOUT 5000 +#define UX_PORT_ENABLE_WAIT 50 +#define UX_DEVICE_ADDRESS_SET_WAIT 50 +#define UX_HIGH_SPEED_DETECTION_HANDSHAKE_SUSPEND_WAIT 200 +#define UX_ENUMERATION_THREAD_WAIT 200 + + +/* USBX 5.8 BACKWARD COMPATIBILITY DEFINITIONS. THESE DEFINITIONS ARE NOW OBSOLETE + BUT DEFINED HERE FOR COMPATIBILITY REASONS. */ + +#ifndef UX_CONTROL_TRANSFER_TIMEOUT_IN_MS +#define UX_CONTROL_TRANSFER_TIMEOUT_IN_MS 10000 +#endif + +#ifndef UX_NON_CONTROL_TRANSFER_TIMEOUT_IN_MS +#define UX_NON_CONTROL_TRANSFER_TIMEOUT_IN_MS 50000 +#endif + +#ifndef UX_PORT_ENABLE_WAIT_IN_MS +#define UX_PORT_ENABLE_WAIT_IN_MS 500 +#endif + +#ifndef UX_DEVICE_ADDRESS_SET_WAIT_IN_MS +#define UX_DEVICE_ADDRESS_SET_WAIT_IN_MS 500 +#endif + +#ifndef UX_HIGH_SPEED_DETECTION_HANDSHAKE_SUSPEND_WAIT_IN_MS +#define UX_HIGH_SPEED_DETECTION_HANDSHAKE_SUSPEND_WAIT_IN_MS 2000 +#endif + +/* END OF 5.8 BACKWARD COMPATIBILITY DEFINITIONS. */ + +#define UX_TRANSFER_PHASE_SETUP 1 +#define UX_TRANSFER_PHASE_DATA_IN 2 +#define UX_TRANSFER_PHASE_DATA_OUT 3 +#define UX_TRANSFER_PHASE_STATUS_IN 4 +#define UX_TRANSFER_PHASE_STATUS_OUT 5 + +#define UX_DEVICE_INSERTION 1 +#define UX_DEVICE_REMOVAL 2 +#define UX_HID_CLIENT_INSERTION 3 +#define UX_HID_CLIENT_REMOVAL 4 + + +/* Define USBX transfer request status constants. */ + +#define UX_TRANSFER_STATUS_NOT_PENDING 0 +#define UX_TRANSFER_STATUS_PENDING 1 +#define UX_TRANSFER_STATUS_COMPLETED 2 +#define UX_TRANSFER_STATUS_ABORT 4 + +/* Define USBX device power constants. */ + +#define UX_DEVICE_BUS_POWERED 1 +#define UX_DEVICE_SELF_POWERED 2 +#define UX_MAX_SELF_POWER (500/2) +#define UX_MAX_BUS_POWER (100/2) +#define UX_CONFIGURATION_DEVICE_BUS_POWERED 0x80 +#define UX_CONFIGURATION_DEVICE_SELF_POWERED 0x40 +#define UX_STATUS_DEVICE_SELF_POWERED 1 + +/* Define USBX OTG constants. */ + +#define UX_OTG_BM_ATTRIBUTES 2 +#define UX_OTG_SRP_SUPPORT 1 +#define UX_OTG_HNP_SUPPORT 2 +#define UX_HCD_OTG_CAPABLE 1 +#define UX_DCD_OTG_CAPABLE 1 + +#define UX_OTG_FEATURE_B_HNP_ENABLE 3 +#define UX_OTG_FEATURE_A_HNP_SUPPORT 4 +#define UX_OTG_FEATURE_A_ALT_HNP_SUPPORT 5 +#define UX_OTG_STATUS_SELECTOR 0xF000 +#define UX_OTG_HOST_REQUEST_FLAG 0x01 + +#define UX_OTG_IDLE 0 +#define UX_OTG_IDLE_TO_HOST 1 +#define UX_OTG_IDLE_TO_SLAVE 2 +#define UX_OTG_HOST_TO_IDLE 3 +#define UX_OTG_HOST_TO_SLAVE 4 +#define UX_OTG_SLAVE_TO_IDLE 5 +#define UX_OTG_SLAVE_TO_HOST 6 +#define UX_OTG_SLAVE_SRP 7 + +#define UX_OTG_MODE_IDLE 0 +#define UX_OTG_MODE_SLAVE 1 +#define UX_OTG_MODE_HOST 2 + +#define UX_OTG_DEVICE_IDLE 0 +#define UX_OTG_DEVICE_A 1 +#define UX_OTG_DEVICE_B 2 + +#define UX_OTG_VBUS_IDLE 0 +#define UX_OTG_VBUS_ON 1 +#define UX_OTG_VBUS_OFF 2 + + +#define UX_OTG_HNP_THREAD_SLEEP_TIME (2 * UX_PERIODIC_RATE) + +/* Define USBX device speed constants. */ + +#define UX_DEFAULT_HS_MPS 64 +#define UX_DEFAULT_MPS 8 + +#define UX_LOW_SPEED_DEVICE 0 +#define UX_FULL_SPEED_DEVICE 1 +#define UX_HIGH_SPEED_DEVICE 2 + + +/* Define USBX generic port status constants. */ + +#define UX_PS_CCS 0x01 +#define UX_PS_CPE 0x01 +#define UX_PS_PES 0x02 +#define UX_PS_PSS 0x04 +#define UX_PS_POCI 0x08 +#define UX_PS_PRS 0x10 +#define UX_PS_PPS 0x20 +#define UX_PS_DS_LS 0x00 +#define UX_PS_DS_FS 0x40 +#define UX_PS_DS_HS 0x80 + +#define UX_PS_DS 6 + + +/* Define USBX Error Code constants. The following format describes + their meaning: + + 0x1x : Configuration errors + 0x2x : USB transport errors + 0x3x : USB controller errors + 0x4x : USB topology errors + 0x5x : USB API errors + 0x6x : USB Generic Class errors + 0x7x : USB HID Class errors + 0x8x : USB Audio Class errors + 0x9x : USB CDC-ECM Class errors +*/ + +#define UX_SUCCESS 0 +#define UX_ERROR 0xff +#define UX_TOO_MANY_DEVICES 0x11 +#define UX_MEMORY_INSUFFICIENT 0x12 +#define UX_NO_TD_AVAILABLE 0x13 +#define UX_NO_ED_AVAILABLE 0x14 +#define UX_SEMAPHORE_ERROR 0x15 +#define UX_THREAD_ERROR 0x16 +#define UX_MUTEX_ERROR 0x17 +#define UX_EVENT_ERROR 0x18 +#define UX_MEMORY_CORRUPTED 0x19 +#define UX_MEMORY_ARRAY_FULL 0x1a +#define UX_FATAL_ERROR 0x1b + +#define UX_TRANSFER_STALLED 0x21 +#define UX_TRANSFER_NO_ANSWER 0x22 +#define UX_TRANSFER_ERROR 0x23 +#define UX_TRANSFER_MISSED_FRAME 0x24 +#define UX_TRANSFER_NOT_READY 0x25 +#define UX_TRANSFER_BUS_RESET 0x26 +#define UX_TRANSFER_BUFFER_OVERFLOW 0x27 +#define UX_TRANSFER_APPLICATION_RESET 0x28 +#define UX_TRANSFER_DATA_LESS_THAN_EXPECTED 0x29 + +#define UX_PORT_RESET_FAILED 0x31 +#define UX_CONTROLLER_INIT_FAILED 0x32 +#define UX_CONTROLLER_DEAD 0x33 + +#define UX_NO_BANDWIDTH_AVAILABLE 0x41 +#define UX_DESCRIPTOR_CORRUPTED 0x42 +#define UX_OVER_CURRENT_CONDITION 0x43 +#define UX_DEVICE_ENUMERATION_FAILURE 0x44 + +#define UX_DEVICE_HANDLE_UNKNOWN 0x50 +#define UX_CONFIGURATION_HANDLE_UNKNOWN 0x51 +#define UX_INTERFACE_HANDLE_UNKNOWN 0x52 +#define UX_ENDPOINT_HANDLE_UNKNOWN 0x53 +#define UX_FUNCTION_NOT_SUPPORTED 0x54 +#define UX_CONTROLLER_UNKNOWN 0x55 +#define UX_PORT_INDEX_UNKNOWN 0x56 +#define UX_NO_CLASS_MATCH 0x57 +#define UX_HOST_CLASS_ALREADY_INSTALLED 0x58 +#define UX_HOST_CLASS_UNKNOWN 0x59 +#define UX_CONNECTION_INCOMPATIBLE 0x5a +#define UX_HOST_CLASS_INSTANCE_UNKNOWN 0x5b +#define UX_TRANSFER_TIMEOUT 0x5c +#define UX_BUFFER_OVERFLOW 0x5d +#define UX_NO_ALTERNATE_SETTING 0x5e +#define UX_NO_DEVICE_CONNECTED 0x5f + +#define UX_HOST_CLASS_PROTOCOL_ERROR 0x60 +#define UX_HOST_CLASS_MEMORY_ERROR 0x61 +#define UX_HOST_CLASS_MEDIA_NOT_SUPPORTED 0x62 +#define UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR 0x63 + +#define UX_HOST_CLASS_HID_REPORT_OVERFLOW 0x70 +#define UX_HOST_CLASS_HID_USAGE_OVERFLOW 0x71 +#define UX_HOST_CLASS_HID_TAG_UNSUPPORTED 0x72 +#define UX_HOST_CLASS_HID_PUSH_OVERFLOW 0x73 +#define UX_HOST_CLASS_HID_POP_UNDERFLOW 0x74 +#define UX_HOST_CLASS_HID_COLLECTION_OVERFLOW 0x75 +#define UX_HOST_CLASS_HID_COLLECTION_UNDERFLOW 0x76 +#define UX_HOST_CLASS_HID_MIN_MAX_ERROR 0x77 +#define UX_HOST_CLASS_HID_DELIMITER_ERROR 0x78 +#define UX_HOST_CLASS_HID_REPORT_ERROR 0x79 +#define UX_HOST_CLASS_HID_PERIODIC_REPORT_ERROR 0x7A +#define UX_HOST_CLASS_HID_UNKNOWN 0x7B + +#define UX_HOST_CLASS_AUDIO_WRONG_TYPE 0x80 +#define UX_HOST_CLASS_AUDIO_WRONG_INTERFACE 0x81 + +#define UX_CLASS_CDC_ECM_LINK_STATE_DOWN_ERROR 0x90 + + +/* Define USBX HCD API function constants. */ + +#define UX_HCD_DISABLE_CONTROLLER 1 +#define UX_HCD_GET_PORT_STATUS 2 +#define UX_HCD_ENABLE_PORT 3 +#define UX_HCD_DISABLE_PORT 4 +#define UX_HCD_POWER_ON_PORT 5 +#define UX_HCD_POWER_DOWN_PORT 6 +#define UX_HCD_SUSPEND_PORT 7 +#define UX_HCD_RESUME_PORT 8 +#define UX_HCD_RESET_PORT 9 +#define UX_HCD_GET_FRAME_NUMBER 10 +#define UX_HCD_SET_FRAME_NUMBER 11 +#define UX_HCD_TRANSFER_REQUEST 12 +#define UX_HCD_TRANSFER_ABORT 13 +#define UX_HCD_CREATE_ENDPOINT 14 +#define UX_HCD_DESTROY_ENDPOINT 15 +#define UX_HCD_RESET_ENDPOINT 16 +#define UX_HCD_PROCESS_DONE_QUEUE 17 + +/* Define USBX DCD API function constants. */ + +#define UX_DCD_DISABLE_CONTROLLER 1 +#define UX_DCD_GET_PORT_STATUS 2 +#define UX_DCD_ENABLE_PORT 3 +#define UX_DCD_DISABLE_PORT 4 +#define UX_DCD_POWER_ON_PORT 5 +#define UX_DCD_POWER_DOWN_PORT 6 +#define UX_DCD_SUSPEND_PORT 7 +#define UX_DCD_RESUME_PORT 8 +#define UX_DCD_RESET_PORT 9 +#define UX_DCD_GET_FRAME_NUMBER 10 +#define UX_DCD_SET_FRAME_NUMBER 11 +#define UX_DCD_TRANSFER_REQUEST 12 +#define UX_DCD_TRANSFER_ABORT 13 +#define UX_DCD_CREATE_ENDPOINT 14 +#define UX_DCD_DESTROY_ENDPOINT 15 +#define UX_DCD_RESET_ENDPOINT 16 +#define UX_DCD_SET_DEVICE_ADDRESS 17 +#define UX_DCD_ISR_PENDING 18 +#define UX_DCD_CHANGE_STATE 19 +#define UX_DCD_STALL_ENDPOINT 20 +#define UX_DCD_ENDPOINT_STATUS 21 + + +/* Define USBX generic host controller constants. */ + +#define UX_HCD_STATUS_HALTED 0 +#define UX_HCD_STATUS_OPERATIONAL 1 +#define UX_HCD_STATUS_DEAD 2 + +/* Define USBX generic SLAVE controller constants. */ + +#define UX_DCD_STATUS_HALTED 0 +#define UX_DCD_STATUS_OPERATIONAL 1 +#define UX_DCD_STATUS_DEAD 2 + +/* Define USBX SLAVE controller VBUS constants. */ + +#define UX_DCD_VBUS_RESET 0 +#define UX_DCD_VBUS_SET 1 + +/* Define USBX class interface constants. */ + +#define UX_HOST_CLASS_COMMAND_QUERY 1 +#define UX_HOST_CLASS_COMMAND_ACTIVATE 2 +#define UX_HOST_CLASS_COMMAND_DEACTIVATE 3 + +#define UX_SLAVE_CLASS_COMMAND_QUERY 1 +#define UX_SLAVE_CLASS_COMMAND_ACTIVATE 2 +#define UX_SLAVE_CLASS_COMMAND_DEACTIVATE 3 +#define UX_SLAVE_CLASS_COMMAND_REQUEST 4 +#define UX_SLAVE_CLASS_COMMAND_INITIALIZE 5 +#define UX_SLAVE_CLASS_COMMAND_CHANGE 6 +#define UX_SLAVE_CLASS_COMMAND_UNINITIALIZE 7 + +#define UX_HOST_CLASS_COMMAND_USAGE_PIDVID 1 +#define UX_HOST_CLASS_COMMAND_USAGE_CSP 2 + +#define UX_HOST_CLASS_INSTANCE_FREE 0 +#define UX_HOST_CLASS_INSTANCE_LIVE 1 +#define UX_HOST_CLASS_INSTANCE_SHUTDOWN 2 +#define UX_HOST_CLASS_INSTANCE_MOUNTING 3 + + +/* Define USBX root HUB constants. */ + +#define UX_RH_ENUMERATION_RETRY 3 +#define UX_RH_ENUMERATION_RETRY_DELAY 100 + + +/* Define USBX PCI driver constants. */ + +#define UX_PCI_NB_FUNCTIONS 7 +#define UX_PCI_NB_DEVICE 32 +#define UX_PCI_NB_BUS 0xff + +#define UX_PCI_CMD_IO_ENABLE 0x0001 +#define UX_PCI_CMD_MEM_ENABLE 0x0002 +#define UX_PCI_CMD_MASTER_ENABLE 0x0004 +#define UX_PCI_CMD_MONITOR_ENABLE 0x0008 +#define UX_PCI_CMD_MEM_WRITE_INV_ENABLE 0x0010 +#define UX_PCI_CMD_SNOOP_PALETTE_ENABLE 0x0020 +#define UX_PCI_CMD_PARITY_ERROR_ENABLE 0x0040 +#define UX_PCI_CMD_WAIT_CYCLE_CTRL_ENABLE 0x0080 +#define UX_PCI_CMD_SERR_ENABLE 0x0100 +#define UX_PCI_CMD_FBB_ENABLE 0x0200 + +#define UX_PCI_CFG_CTRL_ADDRESS 0x0cf8 +#define UX_PCI_CFG_DATA_ADDRESS 0x0cfc + +#define UX_PCI_CFG_VENDOR_ID 0x00 +#define UX_PCI_CFG_DEVICE_ID 0x02 +#define UX_PCI_CFG_COMMAND 0x04 +#define UX_PCI_CFG_STATUS 0x06 +#define UX_PCI_CFG_REVISION 0x08 +#define UX_PCI_CFG_PROGRAMMING_IF 0x09 +#define UX_PCI_CFG_SUBCLASS 0x0a +#define UX_PCI_CFG_CLASS 0x0b +#define UX_PCI_CFG_CACHE_LINE_SIZE 0x0c +#define UX_PCI_CFG_LATENCY_TIMER 0x0d +#define UX_PCI_CFG_HEADER_TYPE 0x0e +#define UX_PCI_CFG_BIST 0x0f +#define UX_PCI_CFG_BASE_ADDRESS_0 0x10 +#define UX_PCI_CFG_BASE_ADDRESS_1 0x14 +#define UX_PCI_CFG_BASE_ADDRESS_2 0x18 +#define UX_PCI_CFG_BASE_ADDRESS_3 0x1c +#define UX_PCI_CFG_BASE_ADDRESS_4 0x20 +#define UX_PCI_CFG_BASE_ADDRESS_5 0x24 +#define UX_PCI_CFG_CARDBUS_CIS 0x28 +#define UX_PCI_CFG_SUB_VENDOR_ID 0x2c +#define UX_PCI_CFG_SUB_SYSTEM_ID 0x2e +#define UX_PCI_CFG_EXPANSION_ROM_ADDRESS 0x30 +#define UX_PCI_CFG_RESERVED_0 0x34 +#define UX_PCI_CFG_RESERVED_1 0x38 +#define UX_PCI_CFG_INT_LINE 0x3c +#define UX_PCI_CFG_INT_PIN 0x3d +#define UX_PCI_CFG_MIN_GNT 0x3e +#define UX_PCI_CFG_MAX_LATENCY 0x3f + +#define UX_PCI_CFG_SBRN 0x60 +#define UX_PCI_CFG_FLADJ 0x61 + +/* Define DFU constants. */ +#define UX_SYSTEM_DFU_STATE_APP_IDLE 0 +#define UX_SYSTEM_DFU_STATE_APP_DETACH 1 +#define UX_SYSTEM_DFU_STATE_DFU_IDLE 2 +#define UX_SYSTEM_DFU_STATE_DFU_DNLOAD_SYNC 3 +#define UX_SYSTEM_DFU_STATE_DFU_DNBUSY 4 +#define UX_SYSTEM_DFU_STATE_DFU_DNLOAD_IDLE 5 +#define UX_SYSTEM_DFU_STATE_DFU_MANIFEST_SYNC 6 +#define UX_SYSTEM_DFU_STATE_DFU_MANIFEST 7 +#define UX_SYSTEM_DFU_STATE_DFU_MANIFEST_WAIT_RESET 8 +#define UX_SYSTEM_DFU_STATE_DFU_UPLOAD_IDLE 9 +#define UX_SYSTEM_DFU_STATE_DFU_ERROR 10 + +/* Define basic class constants. */ + +#define UX_HOST_CLASS_PRINTER_NAME_LENGTH 64 + + +/* Define USBX 2.0 TT Instance structure. */ + +typedef struct UX_HUB_TT_STRUCT +{ + + ULONG ux_hub_tt_port_mapping; + ULONG ux_hub_tt_max_bandwidth; +} UX_HUB_TT; + + +/* Define USBX Class calling command structure. */ + +typedef struct UX_HOST_CLASS_COMMAND_STRUCT +{ + + UINT ux_host_class_command_request; + VOID *ux_host_class_command_container; + VOID *ux_host_class_command_instance; + UINT ux_host_class_command_usage; + UINT ux_host_class_command_pid; + UINT ux_host_class_command_vid; + UINT ux_host_class_command_class; + UINT ux_host_class_command_subclass; + UINT ux_host_class_command_protocol; + UINT ux_host_class_command_iad_class; + UINT ux_host_class_command_iad_subclass; + UINT ux_host_class_command_iad_protocol; + + struct UX_HOST_CLASS_STRUCT + *ux_host_class_command_class_ptr; +} UX_HOST_CLASS_COMMAND; + + +/* Define USBX Class container structure. */ + +typedef struct UX_HOST_CLASS_STRUCT +{ + + UCHAR ux_host_class_name[UX_MAX_CLASS_NAME_LENGTH + 1]; /* "+1" for string null-terminator */ + UINT ux_host_class_status; + UINT (*ux_host_class_entry_function) (struct UX_HOST_CLASS_COMMAND_STRUCT *) ; + UINT ux_host_class_nb_devices_owned; + VOID *ux_host_class_first_instance; + VOID *ux_host_class_client; + TX_THREAD ux_host_class_thread; + VOID *ux_host_class_thread_stack; + VOID *ux_host_class_media; +} UX_HOST_CLASS; + + +/* Define USBX transfer request structure. */ + +typedef struct UX_TRANSFER_STRUCT +{ + + ULONG ux_transfer_request_status; + struct UX_ENDPOINT_STRUCT + *ux_transfer_request_endpoint; + UCHAR * ux_transfer_request_data_pointer; + ULONG ux_transfer_request_requested_length; + ULONG ux_transfer_request_actual_length; + UINT ux_transfer_request_type; + UINT ux_transfer_request_function; + UINT ux_transfer_request_value; + UINT ux_transfer_request_index; + VOID (*ux_transfer_request_completion_function) (struct UX_TRANSFER_STRUCT *); + TX_SEMAPHORE ux_transfer_request_semaphore; + VOID *ux_transfer_request_class_instance; + ULONG ux_transfer_request_maximum_length; + ULONG ux_transfer_request_timeout_value; + UINT ux_transfer_request_completion_code; + ULONG ux_transfer_request_packet_length; + struct UX_TRANSFER_STRUCT + *ux_transfer_request_next_transfer_request; + VOID *ux_transfer_request_user_specific; + TX_THREAD *ux_transfer_request_thread_pending; +} UX_TRANSFER; + + +/* Define USBX Endpoint Descriptor structure. */ + +typedef struct UX_ENDPOINT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bEndpointAddress; + ULONG bmAttributes; + ULONG wMaxPacketSize; + ULONG bInterval; +} UX_ENDPOINT_DESCRIPTOR; + +#define UX_ENDPOINT_DESCRIPTOR_ENTRIES 6 +#define UX_ENDPOINT_DESCRIPTOR_LENGTH 7 + + +/* Define USBX Endpoint Container structure. */ + +typedef struct UX_ENDPOINT_STRUCT +{ + + ULONG ux_endpoint; + ULONG ux_endpoint_state; + void *ux_endpoint_ed; + struct UX_ENDPOINT_DESCRIPTOR_STRUCT + ux_endpoint_descriptor; + struct UX_ENDPOINT_STRUCT + *ux_endpoint_next_endpoint; + struct UX_INTERFACE_STRUCT + *ux_endpoint_interface; + struct UX_DEVICE_STRUCT + *ux_endpoint_device; + struct UX_TRANSFER_STRUCT + ux_endpoint_transfer_request; +} UX_ENDPOINT; + + +/* Define USBX Device Descriptor structure. */ + +typedef struct UX_DEVICE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bcdUSB; + ULONG bDeviceClass; + ULONG bDeviceSubClass; + ULONG bDeviceProtocol; + ULONG bMaxPacketSize0; + ULONG idVendor; + ULONG idProduct; + ULONG bcdDevice; + ULONG iManufacturer; + ULONG iProduct; + ULONG iSerialNumber; + ULONG bNumConfigurations; +} UX_DEVICE_DESCRIPTOR; + +#define UX_DEVICE_DESCRIPTOR_ENTRIES 14 +#define UX_DEVICE_DESCRIPTOR_LENGTH 18 + +/* Define USBX Device Qualifier Descriptor structure. */ + +typedef struct UX_DEVICE_QUALIFIER_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bcdUSB; + ULONG bDeviceClass; + ULONG bDeviceSubClass; + ULONG bDeviceProtocol; + ULONG bMaxPacketSize0; + ULONG bNumConfigurations; + ULONG bReserved; +} UX_DEVICE_QUALIFIER_DESCRIPTOR; + +#define UX_DEVICE_QUALIFIER_DESCRIPTOR_ENTRIES 9 +#define UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH 10 + + +/* Define USBX Other Speed Descriptor structure. */ + +typedef struct UX_OTHER_SPEED_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG wTotalLength; + ULONG bNumInterfaces; + ULONG bConfigurationValue; + ULONG iConfiguration; + ULONG bmAttributes; + ULONG MaxPower; +} UX_OTHER_SPEED_DESCRIPTOR; + +#define UX_OTHER_SPEED_DESCRIPTOR_ENTRIES 8 +#define UX_OTHER_SPEED_DESCRIPTOR_LENGTH 9 + +/* Define USBX OTG Descriptor structure. */ + +typedef struct UX_OTG_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bmAttributes; + ULONG bcdOTG; +} UX_OTG_DESCRIPTOR; + +#define UX_OTG_DESCRIPTOR_ENTRIES 4 +#define UX_OTG_DESCRIPTOR_LENGTH 5 + +/* Define USBX Interface Association Descriptor structure. */ + +typedef struct UX_INTERFACE_ASSOCIATION_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bFirstInterface; + ULONG bInterfaceCount; + ULONG bFunctionClass; + ULONG bFunctionSubClass; + ULONG bFunctionProtocol; + ULONG iFunction; + +} UX_INTERFACE_ASSOCIATION_DESCRIPTOR; + +#define UX_INTERFACE_ASSOCIATION_DESCRIPTOR_ENTRIES 8 +#define UX_INTERFACE_ASSOCIATION_DESCRIPTOR_LENGTH 8 + + +/* Define USBX Device Container structure. */ + +typedef struct UX_DEVICE_STRUCT +{ + + ULONG ux_device_handle; + ULONG ux_device_type; + ULONG ux_device_state; + ULONG ux_device_address; + ULONG ux_device_speed; + ULONG ux_device_port_location; + ULONG ux_device_max_power; + ULONG ux_device_power_source; + UINT ux_device_current_configuration; + TX_SEMAPHORE ux_device_protection_semaphore; + struct UX_DEVICE_STRUCT + *ux_device_parent; + struct UX_HOST_CLASS_STRUCT + *ux_device_class; + VOID *ux_device_class_instance; + struct UX_HCD_STRUCT + *ux_device_hcd; + struct UX_CONFIGURATION_STRUCT + *ux_device_first_configuration; + struct UX_DEVICE_STRUCT + *ux_device_next_device; + struct UX_DEVICE_DESCRIPTOR_STRUCT + ux_device_descriptor; + struct UX_ENDPOINT_STRUCT + ux_device_control_endpoint; + struct UX_HUB_TT_STRUCT + ux_device_hub_tt[UX_MAX_TT]; +} UX_DEVICE; + + +/* Define USBX Configuration Descriptor structure. */ + +typedef struct UX_CONFIGURATION_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG wTotalLength; + ULONG bNumInterfaces; + ULONG bConfigurationValue; + ULONG iConfiguration; + ULONG bmAttributes; + ULONG MaxPower; +} UX_CONFIGURATION_DESCRIPTOR; + +#define UX_CONFIGURATION_DESCRIPTOR_ENTRIES 8 +#define UX_CONFIGURATION_DESCRIPTOR_LENGTH 9 + + +/* Define USBX Configuration Container structure. */ + +typedef struct UX_CONFIGURATION_STRUCT +{ + + ULONG ux_configuration_handle; + ULONG ux_configuration_state; + ULONG ux_configuration_otg_capabilities; + struct UX_CONFIGURATION_DESCRIPTOR_STRUCT + ux_configuration_descriptor; + struct UX_INTERFACE_STRUCT + *ux_configuration_first_interface; + struct UX_CONFIGURATION_STRUCT + *ux_configuration_next_configuration; + struct UX_DEVICE_STRUCT + *ux_configuration_device; + ULONG ux_configuration_iad_class; + ULONG ux_configuration_iad_subclass; + ULONG ux_configuration_iad_protocol; +} UX_CONFIGURATION; + + +/* Define USBX Interface Descriptor structure. */ + +typedef struct UX_INTERFACE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bInterfaceNumber; + ULONG bAlternateSetting; + ULONG bNumEndpoints; + ULONG bInterfaceClass; + ULONG bInterfaceSubClass; + ULONG bInterfaceProtocol; + ULONG iInterface; +} UX_INTERFACE_DESCRIPTOR; + +#define UX_INTERFACE_DESCRIPTOR_ENTRIES 9 +#define UX_INTERFACE_DESCRIPTOR_LENGTH 9 + + +/* Define USBX Interface Container structure. */ + +typedef struct UX_INTERFACE_STRUCT +{ + + ULONG ux_interface_handle; + ULONG ux_interface_state; + UINT ux_interface_current_alternate_setting; + struct UX_INTERFACE_DESCRIPTOR_STRUCT + ux_interface_descriptor; + struct UX_HOST_CLASS_STRUCT + *ux_interface_class; + VOID *ux_interface_class_instance; + struct UX_ENDPOINT_STRUCT + *ux_interface_first_endpoint; + struct UX_INTERFACE_STRUCT + *ux_interface_next_interface; + struct UX_CONFIGURATION_STRUCT + *ux_interface_configuration; + ULONG ux_interface_iad_class; + ULONG ux_interface_iad_subclass; + ULONG ux_interface_iad_protocol; + +} UX_INTERFACE; + + +/* Define USBX String Descriptor structure. */ + +typedef struct UX_STRING_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bString[1]; +} UX_STRING_DESCRIPTOR; + +#define UX_STRING_DESCRIPTOR_ENTRIES 3 +#define UX_STRING_DESCRIPTOR_LENGTH 4 + + +/* Define USBX DFU functional descriptor. */ + +typedef struct UX_DFU_FUNCTIONAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bmAttributes; + ULONG wDetachTimeOut; + ULONG wTransferSize; + ULONG bcdDFUVersion; +} UX_DFU_FUNCTIONAL_DESCRIPTOR; + +#define UX_DFU_FUNCTIONAL_DESCRIPTOR_ENTRIES 6 +#define UX_DFU_FUNCTIONAL_DESCRIPTOR_LENGTH 9 + +/* Define USBX Host Controller structure. */ + +typedef struct UX_HCD_STRUCT +{ + + UCHAR ux_hcd_name[UX_MAX_HCD_NAME_LENGTH + 1]; /* "+1" for string null-terminator */ + UCHAR ux_hcd_address[16]; + UINT ux_hcd_status; + UINT ux_hcd_controller_type; + UINT ux_hcd_otg_capabilities; + UINT ux_hcd_irq; + UINT ux_hcd_nb_root_hubs; + UINT ux_hcd_root_hub_signal[16]; + UINT ux_hcd_nb_devices; + UINT ux_hcd_power_switch; + UINT ux_hcd_thread_signal; + ULONG ux_hcd_rh_device_connection; + ULONG ux_hcd_io; + ULONG ux_hcd_available_bandwidth; + ULONG ux_hcd_maximum_transfer_request_size; + ULONG ux_hcd_version; + UINT (*ux_hcd_entry_function) (struct UX_HCD_STRUCT *, UINT, VOID *); + void *ux_hcd_controller_hardware; +} UX_HCD; + + +/* Define USBX Device Transfer Request structure. */ + +typedef struct UX_SLAVE_TRANSFER_STRUCT +{ + + ULONG ux_slave_transfer_request_status; + ULONG ux_slave_transfer_request_type; + struct UX_SLAVE_ENDPOINT_STRUCT + *ux_slave_transfer_request_endpoint; + UCHAR *ux_slave_transfer_request_data_pointer; + UCHAR *ux_slave_transfer_request_current_data_pointer; + ULONG ux_slave_transfer_request_requested_length; + ULONG ux_slave_transfer_request_actual_length; + ULONG ux_slave_transfer_request_in_transfer_length; + ULONG ux_slave_transfer_request_completion_code; + ULONG ux_slave_transfer_request_phase; + VOID (*ux_slave_transfer_request_completion_function) (struct UX_SLAVE_TRANSFER_STRUCT *); + TX_SEMAPHORE ux_slave_transfer_request_semaphore; + ULONG ux_slave_transfer_request_timeout; + ULONG ux_slave_transfer_request_force_zlp; + UCHAR ux_slave_transfer_request_setup[UX_SETUP_SIZE]; + ULONG ux_slave_transfer_request_status_phase_ignore; +} UX_SLAVE_TRANSFER; + + +/* Define USBX Device Controller Endpoint structure. */ + +typedef struct UX_SLAVE_ENDPOINT_STRUCT +{ + + ULONG ux_slave_endpoint_status; + ULONG ux_slave_endpoint_state; + void *ux_slave_endpoint_ed; + struct UX_ENDPOINT_DESCRIPTOR_STRUCT + ux_slave_endpoint_descriptor; + struct UX_SLAVE_ENDPOINT_STRUCT + *ux_slave_endpoint_next_endpoint; + struct UX_SLAVE_INTERFACE_STRUCT + *ux_slave_endpoint_interface; + struct UX_SLAVE_DEVICE_STRUCT + *ux_slave_endpoint_device; + struct UX_SLAVE_TRANSFER_STRUCT + ux_slave_endpoint_transfer_request; +} UX_SLAVE_ENDPOINT; + + +/* Define USBX Device Controller Interface structure. */ + +typedef struct UX_SLAVE_INTERFACE_STRUCT +{ + ULONG ux_slave_interface_status; + struct UX_SLAVE_CLASS_STRUCT + *ux_slave_interface_class; + VOID *ux_slave_interface_class_instance; + + struct UX_INTERFACE_DESCRIPTOR_STRUCT + ux_slave_interface_descriptor; + struct UX_SLAVE_INTERFACE_STRUCT + *ux_slave_interface_next_interface; + struct UX_SLAVE_ENDPOINT_STRUCT + *ux_slave_interface_first_endpoint; +} UX_SLAVE_INTERFACE; + + +/* Define USBX Device Controller structure. */ + +typedef struct UX_SLAVE_DEVICE_STRUCT +{ + + ULONG ux_slave_device_state; + struct UX_DEVICE_DESCRIPTOR_STRUCT + ux_slave_device_descriptor; + struct UX_SLAVE_ENDPOINT_STRUCT + ux_slave_device_control_endpoint; + ULONG ux_slave_device_configuration_selected; + struct UX_CONFIGURATION_DESCRIPTOR_STRUCT + ux_slave_device_configuration_descriptor; + struct UX_SLAVE_INTERFACE_STRUCT + *ux_slave_device_first_interface; + struct UX_SLAVE_INTERFACE_STRUCT + *ux_slave_device_interfaces_pool; + ULONG ux_slave_device_interfaces_pool_number; + struct UX_SLAVE_ENDPOINT_STRUCT + *ux_slave_device_endpoints_pool; + ULONG ux_slave_device_endpoints_pool_number; + ULONG ux_slave_device_power_state; + +} UX_SLAVE_DEVICE; + + +/* Define USBX Device Controller structure. */ + +typedef struct UX_SLAVE_DCD_STRUCT +{ + + UCHAR ux_slave_dcd_name[32]; + UINT ux_slave_dcd_status; + UINT ux_slave_dcd_controller_type; + UINT ux_slave_dcd_otg_capabilities; + UINT ux_slave_dcd_irq; + ULONG ux_slave_dcd_io; + ULONG ux_slave_dcd_device_address; + UINT (*ux_slave_dcd_function) (struct UX_SLAVE_DCD_STRUCT *,UINT, VOID *); + void *ux_slave_dcd_controller_hardware; +} UX_SLAVE_DCD; + +/* Define USBX Device Class Command container structure. */ + +typedef struct UX_SLAVE_CLASS_COMMAND_STRUCT +{ + + UINT ux_slave_class_command_request; + VOID *ux_slave_class_command_container; + VOID *ux_slave_class_command_interface; + UINT ux_slave_class_command_pid; + UINT ux_slave_class_command_vid; + UINT ux_slave_class_command_class; + UINT ux_slave_class_command_subclass; + UINT ux_slave_class_command_protocol; + struct UX_SLAVE_CLASS_STRUCT + *ux_slave_class_command_class_ptr; + VOID *ux_slave_class_command_parameter; + VOID *ux_slave_class_command_interface_number; + +} UX_SLAVE_CLASS_COMMAND; + + +/* Define USBX Device Class container structure. */ + +typedef struct UX_SLAVE_CLASS_STRUCT +{ + + UCHAR ux_slave_class_name[UX_MAX_CLASS_NAME_LENGTH + 1]; /* "+1" for string null-terminator */ + UINT ux_slave_class_status; + UINT (*ux_slave_class_entry_function) (struct UX_SLAVE_CLASS_COMMAND_STRUCT *) ; + VOID *ux_slave_class_instance; + VOID *ux_slave_class_client; + TX_THREAD ux_slave_class_thread; + VOID *ux_slave_class_thread_stack; + VOID *ux_slave_class_interface_parameter; + ULONG ux_slave_class_interface_number; + ULONG ux_slave_class_configuration_number; + struct UX_SLAVE_INTERFACE_STRUCT + *ux_slave_class_interface; + +} UX_SLAVE_CLASS; + +/* Define USBX Memory Management structure. */ + +typedef struct UX_MEMORY_BLOCK_STRUCT +{ + + ULONG ux_memory_block_size; + ULONG ux_memory_block_status; + struct UX_MEMORY_BLOCK_STRUCT + *ux_memory_block_next; + struct UX_MEMORY_BLOCK_STRUCT + *ux_memory_block_previous; +} UX_MEMORY_BLOCK; + + +typedef struct UX_SYSTEM_STRUCT +{ + + UX_MEMORY_BLOCK *ux_system_regular_memory_pool_start; + ULONG ux_system_regular_memory_pool_size; + ULONG ux_system_regular_memory_pool_free; + UX_MEMORY_BLOCK *ux_system_cache_safe_memory_pool_start; + ULONG ux_system_cache_safe_memory_pool_size; + ULONG ux_system_cache_safe_memory_pool_free; + UINT ux_system_thread_lowest_priority; + TX_MUTEX ux_system_mutex; + ULONG ux_system_debug_code; + ULONG ux_system_debug_count; + UINT ux_system_last_error; + UINT ux_system_error_count; + UCHAR *ux_system_debug_log_buffer; + UCHAR *ux_system_debug_log_head; + UCHAR *ux_system_debug_log_tail; + ULONG ux_system_debug_log_size; + VOID (*ux_system_debug_callback_function) (UCHAR *debug_message, ULONG debug_value) ; + VOID (*ux_system_error_callback_function) (UINT system_level, UINT system_context, UINT error_code) ; +} UX_SYSTEM; + + +/* Define USBX System Host Data structure. */ + +typedef struct UX_SYSTEM_HOST_STRUCT +{ + + UINT ux_system_host_max_class; + UINT ux_system_host_registered_class; + UX_HOST_CLASS *ux_system_host_class_array; + UINT ux_system_host_max_hcd; + UX_HCD *ux_system_host_hcd_array; + UINT ux_system_host_registered_hcd; + UX_DEVICE *ux_system_host_device_array; + ULONG ux_system_host_max_devices; + ULONG ux_system_host_max_ed; + ULONG ux_system_host_max_td; + ULONG ux_system_host_max_iso_td; + UINT ux_system_host_rhsc_hcd; + UCHAR *ux_system_host_enum_thread_stack; + TX_THREAD ux_system_host_enum_thread; + TX_SEMAPHORE ux_system_host_enum_semaphore; + VOID (*ux_system_host_enum_hub_function) (VOID); + UCHAR *ux_system_host_hcd_thread_stack; + TX_THREAD ux_system_host_hcd_thread; + UCHAR *ux_system_host_hnp_polling_thread_stack; + TX_THREAD ux_system_host_hnp_polling_thread; + TX_SEMAPHORE ux_system_host_hcd_semaphore; + UINT (*ux_system_host_change_function) (ULONG, UX_HOST_CLASS *, VOID *); +} UX_SYSTEM_HOST; + + +typedef struct UX_SYSTEM_SLAVE_STRUCT +{ + + UX_SLAVE_DCD ux_system_slave_dcd; + UX_SLAVE_DEVICE ux_system_slave_device; + UCHAR *ux_system_slave_device_framework; + ULONG ux_system_slave_device_framework_length; + UCHAR *ux_system_slave_device_framework_full_speed; + ULONG ux_system_slave_device_framework_length_full_speed; + UCHAR *ux_system_slave_device_framework_high_speed; + ULONG ux_system_slave_device_framework_length_high_speed; + UCHAR *ux_system_slave_string_framework; + ULONG ux_system_slave_string_framework_length; + UCHAR *ux_system_slave_language_id_framework; + ULONG ux_system_slave_language_id_framework_length; + UCHAR *ux_system_slave_dfu_framework; + ULONG ux_system_slave_dfu_framework_length; + UINT ux_system_slave_max_class; + UINT ux_system_slave_registered_class; + UX_SLAVE_CLASS *ux_system_slave_class_array; + UX_SLAVE_CLASS *ux_system_slave_interface_class_array[UX_MAX_SLAVE_INTERFACES]; + ULONG ux_system_slave_speed; + ULONG ux_system_slave_power_state; + ULONG ux_system_slave_remote_wakeup_capability; + ULONG ux_system_slave_remote_wakeup_enabled; + ULONG ux_system_slave_device_dfu_capabilities; + ULONG ux_system_slave_device_dfu_detach_timeout; + ULONG ux_system_slave_device_dfu_transfer_size; + ULONG ux_system_slave_device_dfu_state_machine; + ULONG ux_system_slave_device_dfu_mode; + UINT (*ux_system_slave_change_function) (ULONG); + ULONG ux_system_slave_device_vendor_request; + UINT (*ux_system_slave_device_vendor_request_function) (ULONG, ULONG, ULONG, ULONG, UCHAR *, ULONG *); + +} UX_SYSTEM_SLAVE; + +typedef struct UX_SYSTEM_OTG_STRUCT +{ + + TX_THREAD ux_system_otg_thread; + UCHAR *ux_system_otg_thread_stack; + TX_SEMAPHORE ux_system_otg_semaphore; + UINT (*ux_system_otg_function) (ULONG); + ULONG ux_system_otg_mode; + ULONG ux_system_otg_io; + ULONG ux_system_otg_vbus_state; + ULONG ux_system_otg_change_mode_event; + ULONG ux_system_otg_change_vbus_event; + ULONG ux_system_otg_slave_role_swap_flag; + ULONG ux_system_otg_slave_set_feature_flag; + ULONG ux_system_otg_device_type; + VOID (*ux_system_otg_vbus_function) (ULONG); + VOID (*ux_system_otg_change_mode_callback) (ULONG); +} UX_SYSTEM_OTG; + +/* Define Data Pump Class instance structure. */ + + +typedef struct UX_HOST_CLASS_DPUMP_STRUCT +{ + + struct UX_HOST_CLASS_DPUMP_STRUCT + *ux_host_class_dpump_next_instance; + UX_HOST_CLASS *ux_host_class_dpump_class; + UX_DEVICE *ux_host_class_dpump_device; + UX_INTERFACE *ux_host_class_dpump_interface; + UX_ENDPOINT *ux_host_class_dpump_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_dpump_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_dpump_interrupt_endpoint; + UINT ux_host_class_dpump_state; + TX_SEMAPHORE ux_host_class_dpump_semaphore; +} UX_HOST_CLASS_DPUMP; + + +/* Define the system API mappings based on the error checking + selected by the user. Note: this section is only applicable to + application source code, hence the conditional that turns off this + stuff when the include file is processed by the ThreadX source. */ + +#ifndef UX_SOURCE_CODE + + +/* Define USBX Services. */ + +#define ux_system_initialize _ux_system_initialize +#define ux_system_uninitialize _ux_system_uninitialize + +#define ux_host_class_hub_entry _ux_host_class_hub_entry + +#define ux_host_class_storage_entry _ux_host_class_storage_entry + +#define ux_host_stack_class_get _ux_host_stack_class_get +#define ux_host_stack_class_instance_create _ux_host_stack_class_instance_create +#define ux_host_stack_class_instance_destroy _ux_host_stack_class_instance_destroy +#define ux_host_stack_class_instance_get _ux_host_stack_class_instance_get +#define ux_host_stack_class_register _ux_host_stack_class_register +#define ux_host_stack_configuration_interface_get _ux_host_stack_configuration_interface_get +#define ux_host_stack_device_configuration_get _ux_host_stack_device_configuration_get +#define ux_host_stack_device_configuration_select _ux_host_stack_device_configuration_select +#define ux_host_stack_device_get _ux_host_stack_device_get +#define ux_host_stack_endpoint_transfer_abort _ux_host_stack_endpoint_transfer_abort +#define ux_host_stack_hcd_register _ux_host_stack_hcd_register +#define ux_host_stack_initialize _ux_host_stack_initialize +#define ux_host_stack_interface_endpoint_get _ux_host_stack_interface_endpoint_get +#define ux_host_stack_interface_setting_select _ux_host_stack_interface_setting_select +#define ux_host_stack_transfer_request _ux_host_stack_transfer_request +#define ux_host_stack_transfer_request_abort _ux_host_stack_transfer_request_abort +#define ux_host_stack_hnp_polling_thread_entry _ux_host_stack_hnp_polling_thread_entry +#define ux_host_stack_role_swap _ux_host_stack_role_swap +#define ux_host_stack_device_configuration_reset _ux_host_stack_device_configuration_reset + +#define ux_utility_pci_class_scan _ux_utility_pci_class_scan +#define ux_utility_pci_read _ux_utility_pci_read +#define ux_utility_pci_write _ux_utility_pci_write + +#define ux_device_stack_alternate_setting_get _ux_device_stack_alternate_setting_get +#define ux_device_stack_alternate_setting_set _ux_device_stack_alternate_setting_set +#define ux_device_stack_class_register _ux_device_stack_class_register +#define ux_device_stack_class_unregister _ux_device_stack_class_unregister +#define ux_device_stack_configuration_get _ux_device_stack_configuration_get +#define ux_device_stack_configuration_set _ux_device_stack_configuration_set +#define ux_device_stack_descriptor_send _ux_device_stack_descriptor_send +#define ux_device_stack_connect _ux_device_stack_connect +#define ux_device_stack_disconnect _ux_device_stack_disconnect +#define ux_device_stack_endpoint_stall _ux_device_stack_endpoint_stall +#define ux_device_stack_host_wakeup _ux_device_stack_host_wakeup +#define ux_device_stack_initialize _ux_device_stack_initialize +#define ux_device_stack_uninitialize _ux_device_stack_uninitialize +#define ux_device_stack_interface_delete _ux_device_stack_interface_delete +#define ux_device_stack_interface_get _ux_device_stack_interface_get +#define ux_device_stack_interface_set _ux_device_stack_interface_set +#define ux_device_stack_interface_start _ux_device_stack_interface_start +#define ux_device_stack_transfer_request _ux_device_stack_transfer_request +#define ux_device_stack_transfer_abort _ux_device_stack_transfer_abort + +#define ux_hcd_ehci_initialize _ux_hcd_ehci_initialize +#define ux_hcd_isp1161_initialize _ux_hcd_isp1161_initialize +#define ux_hcd_ohci_initialize _ux_hcd_ohci_initialize +#define ux_hcd_sim_host_initialize _ux_hcd_sim_host_initialize +#define ux_dcd_sim_slave_initialize _ux_dcd_sim_slave_initialize + +#define ux_network_driver_init _ux_network_driver_init +#endif + + +/* Define USBX API prototypes. */ + +UINT ux_system_initialize(VOID *non_cached_memory_pool_start, ULONG non_cached_memory_size, + VOID *cached_memory_pool_start, ULONG cached_memory_size); +UINT ux_system_uninitialize(VOID); + +/* Define USBX Host API prototypes. */ + +UINT ux_hcd_ehci_initialize(UX_HCD *hcd); +UINT ux_hcd_isp1161_initialize(UX_HCD *hcd); +UINT ux_hcd_ohci_initialize(UX_HCD *hcd); +UINT ux_hcd_sim_host_initialize(UX_HCD *hcd); + +UINT ux_host_stack_class_get(UCHAR *class_name, UX_HOST_CLASS **host_class); +UINT ux_host_stack_class_instance_create(UX_HOST_CLASS *host_class, VOID *class_instance); +UINT ux_host_stack_class_instance_destroy(UX_HOST_CLASS *host_class, VOID *class_instance); +UINT ux_host_stack_class_instance_get(UX_HOST_CLASS *host_class, UINT class_index, VOID **class_instance); +UINT ux_host_stack_class_register(UCHAR *class_name, UINT (*class_entry_function)(struct UX_HOST_CLASS_COMMAND_STRUCT *)); +UINT ux_host_stack_configuration_interface_get(UX_CONFIGURATION *configuration, UINT interface_index, + UINT alternate_setting_index, UX_INTERFACE **interface); +UINT ux_host_stack_device_configuration_get(UX_DEVICE *device, UINT configuration_index, UX_CONFIGURATION **configuration); +UINT ux_host_stack_device_configuration_select(UX_CONFIGURATION *configuration); +UINT ux_host_stack_device_get(ULONG device_index, UX_DEVICE **device); +UINT ux_host_stack_endpoint_transfer_abort(UX_ENDPOINT *endpoint); +UINT ux_host_stack_hcd_register(UCHAR *hcd_name, UINT (*hcd_initialize_function)(struct UX_HCD_STRUCT *), ULONG hcd_param1, ULONG hcd_param2); +UINT ux_host_stack_initialize(UINT (*ux_system_host_change_function)(ULONG, UX_HOST_CLASS *, VOID *)); +UINT ux_host_stack_interface_endpoint_get(UX_INTERFACE *interface, UINT endpoint_index, UX_ENDPOINT **endpoint); +UINT ux_host_stack_interface_setting_select(UX_INTERFACE *interface); +UINT ux_host_stack_transfer_request(UX_TRANSFER *transfer_request); +UINT ux_host_stack_transfer_request_abort(UX_TRANSFER *transfer_request); +VOID ux_host_stack_hnp_polling_thread_entry(ULONG id); +UINT ux_host_stack_role_swap(UX_DEVICE *device); +UINT ux_host_stack_device_configuration_reset(UX_DEVICE *device); + + +/* Define USBX Device API prototypes. */ + +UINT ux_dcd_at91_initialize(ULONG dcd_io); +UINT ux_dcd_isp1181_initialize(ULONG dcd_io, ULONG dcd_irq, ULONG dcd_vbus_address); +UINT ux_dcd_ml6965_initialize(ULONG dcd_io, ULONG dcd_irq, ULONG dcd_vbus_address); +UINT ux_dcd_sim_slave_initialize(VOID); + +UINT ux_device_class_storage_entry(UX_SLAVE_CLASS_COMMAND *command); +VOID ux_device_class_storage_thread(ULONG); +UINT ux_device_stack_alternate_setting_get(ULONG interface_value); +UINT ux_device_stack_alternate_setting_set(ULONG interface_value, ULONG alternate_setting_value); +UINT ux_device_stack_class_register(UCHAR *class_name, + UINT (*class_entry_function)(struct UX_SLAVE_CLASS_COMMAND_STRUCT *), + ULONG configuration_number, + ULONG interface_number, + VOID *parameter); +UINT ux_device_stack_class_unregister(UCHAR *class_name, + UINT (*class_entry_function)(struct UX_SLAVE_CLASS_COMMAND_STRUCT *)); +UINT ux_device_stack_configuration_get(VOID); +UINT ux_device_stack_configuration_set(ULONG configuration_value); +UINT ux_device_stack_descriptor_send(ULONG descriptor_type, ULONG request_index, ULONG host_length); +UINT ux_device_stack_disconnect(VOID); +UINT ux_device_stack_endpoint_stall(UX_SLAVE_ENDPOINT *endpoint); +UINT ux_device_stack_host_wakeup(VOID); +UINT ux_device_stack_initialize(UCHAR * device_framework_high_speed, ULONG device_framework_length_high_speed, + UCHAR * device_framework_full_speed, ULONG device_framework_length_full_speed, + UCHAR * string_framework, ULONG string_framework_length, + UCHAR * language_id_framework, ULONG language_id_framework_length, + UINT (*ux_system_slave_change_function)(ULONG)); +UINT ux_device_stack_uninitialize(VOID); +UINT ux_device_stack_interface_delete(UX_SLAVE_INTERFACE *interface); +UINT ux_device_stack_interface_get(UINT interface_value); +UINT ux_device_stack_interface_set(UCHAR * device_framework, ULONG device_framework_length, + ULONG alternate_setting_value); +UINT ux_device_stack_interface_start(UX_SLAVE_INTERFACE *interface); +UINT ux_device_stack_transfer_request(UX_SLAVE_TRANSFER *transfer_request, ULONG slave_length, ULONG host_length); +UINT ux_device_stack_transfer_request_abort(UX_SLAVE_TRANSFER *transfer_request, ULONG completion_code); + +/* Include USBX utility and system file. */ + +#include "ux_utility.h" +#include "ux_system.h" + + + + +/* Determine if a C++ compiler is being used. If so, complete the standard + C conditional started above. */ +#ifdef __cplusplus + } +#endif + + +#endif + + diff --git a/common/core/inc/ux_dcd_sim_slave.h b/common/core/inc/ux_dcd_sim_slave.h new file mode 100644 index 0000000..82bf23c --- /dev/null +++ b/common/core/inc/ux_dcd_sim_slave.h @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_dcd_sim_slave.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX slave simulator. It is designed to work ONLY with the USBX */ +/* host simulator. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DCD_SIM_SLAVE_H +#define UX_DCD_SIM_SLAVE_H + + +/* Define USB slave simulator major equivalences. */ + +#define UX_DCD_SIM_SLAVE_SLAVE_CONTROLLER 98 +#define UX_DCD_SIM_SLAVE_MAX_ED 16 + + +/* Define USB slave simulator error code register bits. */ + +#define UX_DCD_SIM_SLAVE_ERROR_TRANSMISSION_OK 0x00000001 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_MASK 0x0000000e +#define UX_DCD_SIM_SLAVE_ERROR_CODE_SHIFT 0x00000001 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_PID_ERROR 0x00000001 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_PID_UNKNOWN 0x00000002 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_UNEXPECTED_PACKET 0x00000003 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_TOKEN_CRC 0x00000004 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_DATA_CRC 0x00000005 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_TIME_OUT 0x00000006 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_BABBLE 0x00000007 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_UNEXPECTED_EOP 0x00000008 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_NAK 0x00000009 +#define UX_DCD_SIM_SLAVE_ERROR_CODE_STALLED 0x0000000a +#define UX_DCD_SIM_SLAVE_ERROR_CODE_OVERFLOW 0x0000000b +#define UX_DCD_SIM_SLAVE_ERROR_CODE_EMPTY_PACKET 0x0000000c +#define UX_DCD_SIM_SLAVE_ERROR_CODE_BIT_STUFFING 0x0000000d +#define UX_DCD_SIM_SLAVE_ERROR_CODE_SYNC_ERROR 0x0000000e +#define UX_DCD_SIM_SLAVE_ERROR_CODE_DATA_TOGGLE 0x0000000f + + +/* Define USB slave simulator physical endpoint status definition. */ + +#define UX_DCD_SIM_SLAVE_ED_STATUS_UNUSED 0 +#define UX_DCD_SIM_SLAVE_ED_STATUS_USED 1 +#define UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER 2 +#define UX_DCD_SIM_SLAVE_ED_STATUS_STALLED 4 + + +/* Define USB slave simulator physical endpoint structure. */ + +typedef struct UX_DCD_SIM_SLAVE_ED_STRUCT +{ + + ULONG ux_sim_slave_ed_status; + ULONG ux_sim_slave_ed_index; + ULONG ux_sim_slave_ed_payload_length; + ULONG ux_sim_slave_ed_ping_pong; + ULONG ux_sim_slave_ed_status_register; + ULONG ux_sim_slave_ed_configuration_value; + struct UX_SLAVE_ENDPOINT_STRUCT + *ux_sim_slave_ed_endpoint; +} UX_DCD_SIM_SLAVE_ED; + + +/* Define USB slave simulator DCD structure definition. */ + +typedef struct UX_DCD_SIM_SLAVE_STRUCT +{ + + struct UX_SLAVE_DCD_STRUCT + *ux_dcd_sim_slave_dcd_owner; + struct UX_DCD_SIM_SLAVE_ED_STRUCT + ux_dcd_sim_slave_ed[UX_DCD_SIM_SLAVE_MAX_ED]; + UINT (*ux_dcd_sim_slave_dcd_control_request_process_hub)(UX_SLAVE_TRANSFER *transfer_request); +} UX_DCD_SIM_SLAVE; + + +/* Define slave simulator function prototypes. */ + +UINT _ux_dcd_sim_slave_address_set(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG address); +UINT _ux_dcd_sim_slave_endpoint_create(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint); +UINT _ux_dcd_sim_slave_endpoint_destroy(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint); +UINT _ux_dcd_sim_slave_endpoint_reset(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint); +UINT _ux_dcd_sim_slave_endpoint_stall(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint); +UINT _ux_dcd_sim_slave_endpoint_status(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG endpoint_index); +UINT _ux_dcd_sim_slave_frame_number_get(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG *frame_number); +UINT _ux_dcd_sim_slave_function(UX_SLAVE_DCD *dcd, UINT function, VOID *parameter); +UINT _ux_dcd_sim_slave_initialize(VOID); +UINT _ux_dcd_sim_slave_initialize_complete(VOID); +UINT _ux_dcd_sim_slave_state_change(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG state); +UINT _ux_dcd_sim_slave_transfer_request(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_TRANSFER *transfer_request); +UINT _ux_dcd_sim_slave_transfer_abort(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_TRANSFER *transfer_request); + +/* Define Device Simulator Class API prototypes. */ + +#define ux_dcd_sim_slave_initialize _ux_dcd_sim_slave_initialize +#endif + diff --git a/common/core/inc/ux_device_class_dpump.h b/common/core/inc/ux_device_class_dpump.h new file mode 100644 index 0000000..cfed0b2 --- /dev/null +++ b/common/core/inc/ux_device_class_dpump.h @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_dpump.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX device dpump class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_DPUMP_H +#define UX_DEVICE_CLASS_DPUMP_H + + +/* Define Storage Class USB Class constants. */ + +#define UX_SLAVE_CLASS_DPUMP_CLASS 0x99 +#define UX_SLAVE_CLASS_DPUMP_SUBCLASS 0x99 +#define UX_SLAVE_CLASS_DPUMP_PROTOCOL 0x99 + +/* Define Data Pump Class packet equivalences. */ +#define UX_DEVICE_CLASS_DPUMP_PACKET_SIZE 128 + + +/* Define Slave DPUMP Class Calling Parameter structure */ + +typedef struct UX_SLAVE_CLASS_DPUMP_PARAMETER_STRUCT +{ + VOID (*ux_slave_class_dpump_instance_activate)(VOID *); + VOID (*ux_slave_class_dpump_instance_deactivate)(VOID *); + +} UX_SLAVE_CLASS_DPUMP_PARAMETER; + +/* Define Slave Data Pump Class structure. */ + +typedef struct UX_SLAVE_CLASS_DPUMP_STRUCT +{ + UX_SLAVE_INTERFACE *ux_slave_class_dpump_interface; + UX_SLAVE_CLASS_DPUMP_PARAMETER ux_slave_class_dpump_parameter; + UX_SLAVE_ENDPOINT *ux_slave_class_dpump_bulkin_endpoint; + UX_SLAVE_ENDPOINT *ux_slave_class_dpump_bulkout_endpoint; + ULONG ux_slave_class_dpump_alternate_setting; + + +} UX_SLAVE_CLASS_DPUMP; + +/* Define Device Data Pump Class prototypes. */ + +UINT _ux_device_class_dpump_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dpump_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dpump_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dpump_entry(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dpump_read(UX_SLAVE_CLASS_DPUMP *dpump, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_device_class_dpump_write(UX_SLAVE_CLASS_DPUMP *dpump, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_device_class_dpump_change(UX_SLAVE_CLASS_COMMAND *command); + +/* Define Device DPUMP Class API prototypes. */ + +#define ux_device_class_dpump_entry _ux_device_class_dpump_entry +#define ux_device_class_dpump_read _ux_device_class_dpump_read +#define ux_device_class_dpump_write _ux_device_class_dpump_write + +#endif diff --git a/common/core/inc/ux_device_stack.h b/common/core/inc/ux_device_stack.h new file mode 100644 index 0000000..f9ad783 --- /dev/null +++ b/common/core/inc/ux_device_stack.h @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_stack.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the equivalences for the USBX Device Stack */ +/* component. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_STACK_H +#define UX_DEVICE_STACK_H + + +/* Define USB Device Stack prototypes. */ + +UINT _ux_device_stack_alternate_setting_get(ULONG interface_value); +UINT _ux_device_stack_alternate_setting_set(ULONG interface_value, ULONG alternate_setting_value); +UINT _ux_device_stack_class_register(UCHAR *class_name, + UINT (*class_entry_function)(struct UX_SLAVE_CLASS_COMMAND_STRUCT *), + ULONG configuration_number, + ULONG interface_number, + VOID *parameter); +UINT _ux_device_stack_clear_feature(ULONG request_type, ULONG request_value, ULONG request_index); +UINT _ux_device_stack_configuration_get(VOID); +UINT _ux_device_stack_configuration_set(ULONG configuration_value); +UINT _ux_device_stack_control_request_process(UX_SLAVE_TRANSFER *transfer_request); +UINT _ux_device_stack_descriptor_send(ULONG descriptor_type, ULONG request_index, ULONG host_length); +UINT _ux_device_stack_disconnect(VOID); +UINT _ux_device_stack_endpoint_stall(UX_SLAVE_ENDPOINT *endpoint); +UINT _ux_device_stack_get_status(ULONG request_type, ULONG request_index, ULONG request_length); +UINT _ux_device_stack_host_wakeup(VOID); +UINT _ux_device_stack_initialize(UCHAR * device_framework_high_speed, ULONG device_framework_length_high_speed, + UCHAR * device_framework_full_speed, ULONG device_framework_length_full_speed, + UCHAR * string_framework, ULONG string_framework_length, + UCHAR * language_id_framework, ULONG language_id_framework_length, + UINT (*ux_system_slave_change_function)(ULONG)); +UINT _ux_device_stack_interface_delete(UX_SLAVE_INTERFACE *interface); +UINT _ux_device_stack_interface_get(UINT interface_value); +UINT _ux_device_stack_interface_set(UCHAR * device_framework, ULONG device_framework_length, + ULONG alternate_setting_value); +UINT _ux_device_stack_interface_start(UX_SLAVE_INTERFACE *interface); +UINT _ux_device_stack_set_feature(ULONG request_type, ULONG request_value, ULONG request_index); +UINT _ux_device_stack_transfer_all_request_abort(UX_SLAVE_ENDPOINT *endpoint, ULONG completion_code); +UINT _ux_device_stack_transfer_request(UX_SLAVE_TRANSFER *transfer_request, ULONG slave_length, ULONG host_length); +UINT _ux_device_stack_transfer_abort(UX_SLAVE_TRANSFER *transfer_request, ULONG completion_code); +UINT _ux_device_stack_class_unregister(UCHAR *class_name, UINT (*class_entry_function)(struct UX_SLAVE_CLASS_COMMAND_STRUCT *)); +UINT _ux_device_stack_microsoft_extension_register(ULONG vendor_request, UINT (*vendor_request_function)(ULONG, ULONG, ULONG, ULONG, UCHAR *, ULONG *)); +UINT _ux_device_stack_uninitialize(VOID); + +#endif + diff --git a/common/core/inc/ux_hcd_sim_host.h b/common/core/inc/ux_hcd_sim_host.h new file mode 100644 index 0000000..fae2c9d --- /dev/null +++ b/common/core/inc/ux_hcd_sim_host.h @@ -0,0 +1,236 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_hcd_sim_host.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX host simulator. It is designed to work ONLY with the USBX */ +/* device (slave) simulator. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HCD_SIM_HOST_H +#define UX_HCD_SIM_HOST_H + + +/* Define simulator host generic definitions. */ + +#define UX_HCD_SIM_HOST_CONTROLLER 99 +#define UX_HCD_SIM_HOST_MAX_PAYLOAD 4096 +#define UX_HCD_SIM_HOST_FRAME_DELAY 4 +#define UX_HCD_SIM_HOST_PERIODIC_ENTRY_NB 32 +#define UX_HCD_SIM_HOST_PERIODIC_ENTRY_MASK 0x1f +#define UX_HCD_SIM_HOST_AVAILABLE_BANDWIDTH 6000 + + + +/* Define simulator host completion code errors. */ + +#define UX_HCD_SIM_HOST_NO_ERROR 0x00 +#define UX_HCD_SIM_HOST_ERROR_CRC 0x01 +#define UX_HCD_SIM_HOST_ERROR_BIT_STUFFING 0x02 +#define UX_HCD_SIM_HOST_ERROR_DATA_TOGGLE 0x03 +#define UX_HCD_SIM_HOST_ERROR_STALL 0x04 +#define UX_HCD_SIM_HOST_ERROR_DEVICE_NOT_RESPONDING 0x05 +#define UX_HCD_SIM_HOST_ERROR_PID_FAILURE 0x06 +#define UX_HCD_SIM_HOST_ERROR_PID_UNEXPECTED 0x07 +#define UX_HCD_SIM_HOST_ERROR_DATA_OVERRRUN 0x08 +#define UX_HCD_SIM_HOST_ERROR_DATA_UNDERRUN 0x09 +#define UX_HCD_SIM_HOST_ERROR_BUFFER_OVERRRUN 0x0c +#define UX_HCD_SIM_HOST_ERROR_BUFFER_UNDERRUN 0x0d +#define UX_HCD_SIM_HOST_NOT_ACCESSED 0x0e +#define UX_HCD_SIM_HOST_NAK 0x0f + + +/* Define simulator host structure. */ + +typedef struct UX_HCD_SIM_HOST_STRUCT +{ + + struct UX_HCD_STRUCT + *ux_hcd_sim_host_hcd_owner; + ULONG ux_hcd_sim_host_hcor; + UINT ux_hcd_sim_host_nb_root_hubs; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_hcd_sim_host_ed_list; + struct UX_HCD_SIM_HOST_TD_STRUCT + *ux_hcd_sim_host_td_list; + struct UX_HCD_SIM_HOST_ISO_TD_STRUCT + *ux_hcd_sim_host_iso_td_list; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_hcd_sim_host_asynch_head_ed; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_hcd_sim_host_asynch_current_ed; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_hcd_sim_host_iso_head_ed; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_hcd_sim_host_interrupt_ed_list[32]; + UINT ux_hcd_sim_host_queue_empty; + UINT ux_hcd_sim_host_periodic_scheduler_active; + UINT ux_hcd_sim_host_interruptible; + ULONG ux_hcd_sim_host_interrupt_count; + TX_TIMER ux_hcd_sim_host_timer; +} UX_HCD_SIM_HOST; + + +/* Define simulator host ED structure. */ + +typedef struct UX_HCD_SIM_HOST_ED_STRUCT +{ + + struct UX_HCD_SIM_HOST_TD_STRUCT + *ux_sim_host_ed_tail_td; + struct UX_HCD_SIM_HOST_TD_STRUCT + *ux_sim_host_ed_head_td; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_sim_host_ed_next_ed; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_sim_host_ed_previous_ed; + ULONG ux_sim_host_ed_status; + struct UX_ENDPOINT_STRUCT + *ux_sim_host_ed_endpoint; + ULONG ux_sim_host_ed_toggle; + ULONG ux_sim_host_ed_frame; +} UX_HCD_SIM_HOST_ED; + + +/* Define simulator host ED bitmap. */ + +#define UX_HCD_SIM_HOST_ED_STATIC 0x80000000 +#define UX_HCD_SIM_HOST_ED_SKIP 0x40000000 + + +/* Define simulator host TD structure. */ + +typedef struct UX_HCD_SIM_HOST_TD_STRUCT +{ + + UCHAR * ux_sim_host_td_buffer; + ULONG ux_sim_host_td_length; + struct UX_HCD_SIM_HOST_TD_STRUCT + *ux_sim_host_td_next_td; + struct UX_TRANSFER_STRUCT + *ux_sim_host_td_transfer_request; + struct UX_HCD_SIM_HOST_TD_STRUCT + *ux_sim_host_td_next_td_transfer_request; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_sim_host_td_ed; + ULONG ux_sim_host_td_actual_length; + ULONG ux_sim_host_td_status; + ULONG ux_sim_host_td_direction; + ULONG ux_sim_host_td_toggle; +} UX_HCD_SIM_HOST_TD; + + +/* Define simulator host TD bitmap. */ + +#define UX_HCD_SIM_HOST_TD_SETUP_PHASE 0x00010000 +#define UX_HCD_SIM_HOST_TD_DATA_PHASE 0x00020000 +#define UX_HCD_SIM_HOST_TD_STATUS_PHASE 0x00040000 +#define UX_HCD_SIM_HOST_TD_OUT 0x00000800 +#define UX_HCD_SIM_HOST_TD_IN 0x00001000 +#define UX_HCD_SIM_HOST_TD_ACK_PENDING 0x00002000 +#define UX_HCD_SIM_HOST_TD_TOGGLE_FROM_ED 0x80000000 + + +/* Define simulator host ISOCHRONOUS TD structure. */ + +typedef struct UX_HCD_SIM_HOST_ISO_TD_STRUCT +{ + + UCHAR * ux_sim_host_iso_td_buffer; + ULONG ux_sim_host_iso_td_length; + struct UX_HCD_SIM_HOST_ISO_TD_STRUCT + *ux_sim_host_iso_td_next_td; + struct UX_TRANSFER_STRUCT + *ux_sim_host_iso_td_transfer_request; + struct UX_HCD_SIM_HOST_ISO_TD_STRUCT + *ux_sim_host_iso_td_next_td_transfer_request; + struct UX_HCD_SIM_HOST_ED_STRUCT + *ux_sim_host_iso_td_ed; + ULONG ux_sim_host_iso_td_actual_length; + ULONG ux_sim_host_iso_td_status; + ULONG ux_sim_host_iso_td_direction; +} UX_HCD_SIM_HOST_ISO_TD; + + +/* Define simulator host function prototypes. */ + +VOID _ux_hcd_sim_host_asynch_queue_process(UX_HCD_SIM_HOST *hcd_sim_host); +VOID _ux_hcd_sim_host_asynch_schedule(UX_HCD_SIM_HOST *hcd_sim_host); +UINT _ux_hcd_sim_host_asynchronous_endpoint_create(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint); +UINT _ux_hcd_sim_host_asynchronous_endpoint_destroy(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint); +UX_HCD_SIM_HOST_ED + *_ux_hcd_sim_host_ed_obtain(UX_HCD_SIM_HOST *hcd_sim_host); +VOID _ux_hcd_sim_host_ed_td_clean(UX_HCD_SIM_HOST_ED *ed); +UINT _ux_hcd_sim_host_endpoint_reset(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint); +UINT _ux_hcd_sim_host_entry(UX_HCD *hcd, UINT function, VOID *parameter); +UINT _ux_hcd_sim_host_frame_number_get(UX_HCD_SIM_HOST *hcd_sim_host, ULONG *frame_number); +VOID _ux_hcd_sim_host_frame_number_set(UX_HCD_SIM_HOST *hcd_sim_host, ULONG frame_number); +UINT _ux_hcd_sim_host_initialize(UX_HCD *hcd); +UINT _ux_hcd_sim_host_interrupt_endpoint_create(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint); +VOID _ux_hcd_sim_host_iso_queue_process(UX_HCD_SIM_HOST *hcd_sim_host); +VOID _ux_hcd_sim_host_iso_schedule(UX_HCD_SIM_HOST *hcd_sim_host); +UINT _ux_hcd_sim_host_isochronous_endpoint_create(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint); +UX_HCD_SIM_HOST_ISO_TD + *_ux_hcd_sim_host_isochronous_td_obtain(UX_HCD_SIM_HOST *hcd_sim_host); +UX_HCD_SIM_HOST_ED + *_ux_hcd_sim_host_least_traffic_list_get(UX_HCD_SIM_HOST *hcd_sim_host); +UINT _ux_hcd_sim_host_periodic_endpoint_destroy(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint); +VOID _ux_hcd_sim_host_periodic_schedule(UX_HCD_SIM_HOST *hcd_sim_host); +UINT _ux_hcd_sim_host_periodic_tree_create(UX_HCD_SIM_HOST *hcd_sim_host); +ULONG _ux_hcd_sim_host_port_status_get(UX_HCD_SIM_HOST *hcd_sim_host, ULONG port_index); +UX_HCD_SIM_HOST_TD + *_ux_hcd_sim_host_regular_td_obtain(UX_HCD_SIM_HOST *hcd_sim_host); +UINT _ux_hcd_sim_host_request_bulk_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request); +UINT _ux_hcd_sim_host_request_control_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request); +UINT _ux_hcd_sim_host_request_interrupt_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request); +UINT _ux_hcd_sim_host_request_isochronous_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request); +UINT _ux_hcd_sim_host_request_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request); +VOID _ux_hcd_sim_host_timer_function(ULONG hcd_sim_host_addr); +UINT _ux_hcd_sim_host_transaction_schedule(UX_HCD_SIM_HOST *hcd_sim_host, UX_HCD_SIM_HOST_ED *ed); +UINT _ux_hcd_sim_host_transfer_abort(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request); +UINT _ux_hcd_sim_host_port_reset(UX_HCD_SIM_HOST *hcd_sim_host, ULONG port_index); + + +/* Define Device Simulator Class API prototypes. */ + +#define ux_hcd_sim_host_initialize _ux_hcd_sim_host_initialize +#endif + diff --git a/common/core/inc/ux_host_class_dpump.h b/common/core/inc/ux_host_class_dpump.h new file mode 100644 index 0000000..4b3907e --- /dev/null +++ b/common/core/inc/ux_host_class_dpump.h @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_dpump.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX demo data pump class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_DPUMP_H +#define UX_HOST_CLASS_DPUMP_H + + +/* Define Data Pump Class constants. */ + +#define UX_HOST_CLASS_DPUMP_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_HOST_CLASS_DPUMP_CLASS 0x99 +#define UX_HOST_CLASS_DPUMP_SUBCLASS 0x99 +#define UX_HOST_CLASS_DPUMP_PROTOCOL 0x99 + +/* Define Data Pump Class packet equivalences. */ +#define UX_HOST_CLASS_DPUMP_PACKET_SIZE 128 + +/* Define Data Pump Class Ioctl functions. */ +#define UX_HOST_CLASS_DPUMP_SELECT_ALTERNATE_SETTING 1 + +/* Define Data Pump Class string constants. */ + +#define UX_HOST_CLASS_DPUMP_GENERIC_NAME "USB DPUMP" + + +/* Define Printer Class function prototypes. */ + +UINT _ux_host_class_dpump_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_dpump_configure(UX_HOST_CLASS_DPUMP *dpump); +UINT _ux_host_class_dpump_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_dpump_endpoints_get(UX_HOST_CLASS_DPUMP *dpump); +UINT _ux_host_class_dpump_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_dpump_read (UX_HOST_CLASS_DPUMP *dpump, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_dpump_write(UX_HOST_CLASS_DPUMP *dpump, UCHAR * data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_dpump_ioctl(UX_HOST_CLASS_DPUMP *dpump, ULONG ioctl_function, + VOID *parameter); + + +#define ux_host_class_dpump_entry _ux_host_class_dpump_entry +#define ux_host_class_dpump_read _ux_host_class_dpump_read +#define ux_host_class_dpump_write _ux_host_class_dpump_write +#define ux_host_class_dpump_ioctl _ux_host_class_dpump_ioctl + +#endif diff --git a/common/core/inc/ux_host_stack.h b/common/core/inc/ux_host_stack.h new file mode 100644 index 0000000..ec62880 --- /dev/null +++ b/common/core/inc/ux_host_stack.h @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_stack.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX Host Stack component. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_STACK_H +#define UX_HOST_STACK_H + + +/* Define Host Stack component function prototypes. */ + +VOID _ux_host_stack_bandwidth_release(UX_HCD *hcd, UX_ENDPOINT *endpoint); +VOID _ux_host_stack_bandwidth_claim(UX_HCD *hcd, UX_ENDPOINT *endpoint); +UINT _ux_host_stack_bandwidth_check(UX_HCD *hcd, UX_ENDPOINT *endpoint); +UX_HOST_CLASS * _ux_host_stack_class_call(UX_HOST_CLASS_COMMAND *class_command); +UINT _ux_host_stack_class_device_scan(UX_DEVICE *device); +UINT _ux_host_stack_class_get(UCHAR *class_name, UX_HOST_CLASS **class); +UINT _ux_host_stack_class_instance_destroy(UX_HOST_CLASS *class, VOID *class_instance); +UINT _ux_host_stack_class_instance_create(UX_HOST_CLASS *class, VOID *class_instance); +UINT _ux_host_stack_class_instance_get(UX_HOST_CLASS *class, UINT class_index, VOID **class_instance); +UINT _ux_host_stack_class_instance_verify(UCHAR *class_name, VOID *class_instance); +UINT _ux_host_stack_class_interface_scan(UX_DEVICE *device); +UINT _ux_host_stack_class_register(UCHAR *class_name, + UINT (*class_entry_function)(struct UX_HOST_CLASS_COMMAND_STRUCT *)); +UINT _ux_host_stack_configuration_descriptor_parse(UX_DEVICE *device, UX_CONFIGURATION *configuration, UINT configuration_index); +UINT _ux_host_stack_configuration_enumerate(UX_DEVICE *device); +UINT _ux_host_stack_configuration_instance_create(UX_CONFIGURATION *configuration); +VOID _ux_host_stack_configuration_instance_delete(UX_CONFIGURATION *configuration); +UINT _ux_host_stack_configuration_interface_get(UX_CONFIGURATION *configuration, + UINT interface_index, UINT alternate_setting_index, + UX_INTERFACE **interface); +UINT _ux_host_stack_configuration_set(UX_CONFIGURATION *configuration); +VOID _ux_host_stack_delay_ms(ULONG time); +UINT _ux_host_stack_device_address_set(UX_DEVICE *device); +UINT _ux_host_stack_device_configuration_get(UX_DEVICE *device, UINT configuration_index, + UX_CONFIGURATION **configuration); +UINT _ux_host_stack_device_configuration_select(UX_CONFIGURATION *configuration); +UINT _ux_host_stack_device_configuration_reset(UX_DEVICE *device); +UINT _ux_host_stack_device_descriptor_read(UX_DEVICE *device); +UINT _ux_host_stack_device_get(ULONG device_index, UX_DEVICE **device); +UINT _ux_host_stack_device_remove(UX_HCD *hcd, UX_DEVICE *parent, UINT port_index); +UINT _ux_host_stack_device_resources_free(UX_DEVICE *device); +UINT _ux_host_stack_endpoint_instance_create(UX_ENDPOINT *endpoint); +VOID _ux_host_stack_endpoint_instance_delete(UX_ENDPOINT *endpoint); +UINT _ux_host_stack_endpoint_reset(UX_ENDPOINT *endpoint); +UINT _ux_host_stack_endpoint_transfer_abort(UX_ENDPOINT *endpoint); +VOID _ux_host_stack_enum_thread_entry(ULONG input); +UINT _ux_host_stack_hcd_register(UCHAR *hcd_name, + UINT (*hcd_init_function)(struct UX_HCD_STRUCT *), ULONG hcd_param1, ULONG hcd_param2); +VOID _ux_host_stack_hcd_thread_entry(ULONG input); +UINT _ux_host_stack_hcd_transfer_request(UX_TRANSFER *transfer_request); +UINT _ux_host_stack_initialize(UINT (*ux_system_host_change_function)(ULONG, UX_HOST_CLASS *, VOID *)); +UINT _ux_host_stack_interface_endpoint_get(UX_INTERFACE *interface, UINT endpoint_index, UX_ENDPOINT **endpoint); +UINT _ux_host_stack_interface_instance_create(UX_INTERFACE *interface); +VOID _ux_host_stack_interface_instance_delete(UX_INTERFACE *interface); +UINT _ux_host_stack_interface_set(UX_INTERFACE *interface); +UINT _ux_host_stack_interface_setting_select(UX_INTERFACE *interface); +UINT _ux_host_stack_interfaces_scan(UX_CONFIGURATION *configuration, UCHAR * descriptor); +VOID _ux_host_stack_new_configuration_create(UX_DEVICE *device, UX_CONFIGURATION *configuration); +UX_DEVICE *_ux_host_stack_new_device_get(VOID); +UINT _ux_host_stack_new_device_create(UX_HCD *hcd, UX_DEVICE *device_owner, + UINT port_index, UINT device_speed, + UINT port_max_power); +UINT _ux_host_stack_new_endpoint_create(UX_INTERFACE *interface, UCHAR * interface_endpoint); +UINT _ux_host_stack_new_interface_create(UX_CONFIGURATION *configuration, UCHAR * descriptor, ULONG length); +VOID _ux_host_stack_rh_change_process(VOID); +UINT _ux_host_stack_rh_device_extraction(UX_HCD *hcd, UINT port_index); +UINT _ux_host_stack_rh_device_insertion(UX_HCD *hcd, UINT port_index); +UINT _ux_host_stack_transfer_request(UX_TRANSFER *transfer_request); +UINT _ux_host_stack_transfer_request_abort(UX_TRANSFER *transfer_request); +VOID _ux_host_stack_hnp_polling_thread_entry(ULONG id); +UINT _ux_host_stack_role_swap(UX_DEVICE *device); + +#endif + diff --git a/common/core/inc/ux_system.h b/common/core/inc/ux_system.h new file mode 100644 index 0000000..06f898b --- /dev/null +++ b/common/core/inc/ux_system.h @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_system.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX main system component. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_SYSTEM_HOST_H +#define UX_SYSTEM_HOST_H + +/* Define System component function prototypes. Note that since ux_api.h + includes this file, the APIs are only declared if this file is included + by internal code in order to prevent duplicate declarations for + applications. */ + + +#ifdef UX_SOURCE_CODE +UINT _ux_system_initialize(VOID *regular_memory_pool_start, ULONG regular_memory_size, + VOID *cache_safe_memory_pool_start, ULONG cache_safe_memory_size); +UINT _ux_system_uninitialize(VOID); +#endif + +/* Define System component external data references. */ + +extern UX_SYSTEM *_ux_system; +extern UX_SYSTEM_HOST *_ux_system_host; +extern UX_SYSTEM_SLAVE *_ux_system_slave; +extern UX_SYSTEM_OTG *_ux_system_otg; +extern UCHAR _ux_system_endpoint_descriptor_structure[]; +extern UCHAR _ux_system_device_descriptor_structure[]; +extern UCHAR _ux_system_configuration_descriptor_structure[]; +extern UCHAR _ux_system_interface_descriptor_structure[]; +extern UCHAR _ux_system_interface_association_descriptor_structure[]; +extern UCHAR _ux_system_string_descriptor_structure[]; +extern UCHAR _ux_system_dfu_functional_descriptor_structure[]; +extern UCHAR _ux_system_hub_descriptor_structure[]; +extern UCHAR _ux_system_hid_descriptor_structure[]; +extern UCHAR _ux_system_class_audio_interface_descriptor_structure[]; +extern UCHAR _ux_system_class_audio_input_terminal_descriptor_structure[]; +extern UCHAR _ux_system_class_audio_output_terminal_descriptor_structure[]; +extern UCHAR _ux_system_class_audio_feature_unit_descriptor_structure[]; +extern UCHAR _ux_system_class_audio_streaming_interface_descriptor_structure[]; +extern UCHAR _ux_system_class_audio_streaming_endpoint_descriptor_structure[]; +extern UCHAR _ux_system_class_pima_storage_structure[]; +extern UCHAR _ux_system_class_pima_object_structure[]; +extern UCHAR _ux_system_ecm_interface_descriptor_structure[]; + +extern UINT _ux_system_host_hcd_periodic_tree_entries[32]; + +extern UCHAR _ux_system_host_class_hub_name[]; +extern UCHAR _ux_system_host_class_printer_name[]; +extern UCHAR _ux_system_host_class_storage_name[]; +extern UCHAR _ux_system_host_class_hid_name[]; +extern UCHAR _ux_system_host_class_audio_name[]; +extern UCHAR _ux_system_host_class_cdc_acm_name[]; +extern UCHAR _ux_system_host_class_cdc_dlc_name[]; +extern UCHAR _ux_system_host_class_cdc_ecm_name[]; +extern UCHAR _ux_system_host_class_prolific_name[]; +extern UCHAR _ux_system_host_class_dpump_name[]; +extern UCHAR _ux_system_host_class_pima_name[]; +extern UCHAR _ux_system_host_class_asix_name[]; +extern UCHAR _ux_system_host_class_swar_name[]; +extern UCHAR _ux_system_host_class_gser_name[]; +extern UCHAR _ux_system_host_class_hid_client_remote_control_name[]; +extern UCHAR _ux_system_host_class_hid_client_mouse_name[]; +extern UCHAR _ux_system_host_class_hid_client_keyboard_name[]; + +extern UCHAR _ux_system_host_hcd_ohci_name[]; +extern UCHAR _ux_system_host_hcd_ehci_name[]; +extern UCHAR _ux_system_host_hcd_isp1161_name[]; +extern UCHAR _ux_system_host_hcd_isp1362_name[]; +extern UCHAR _ux_system_host_hcd_sh2_name[]; +extern UCHAR _ux_system_host_hcd_rx_name[]; +extern UCHAR _ux_system_host_hcd_pic32_name[]; +extern UCHAR _ux_system_host_hcd_stm32_name[]; +extern UCHAR _ux_system_host_hcd_musb_name[]; +extern UCHAR _ux_system_host_hcd_atm7_name[]; +extern UCHAR _ux_system_host_hcd_simulator_name[]; + +extern UCHAR _ux_system_slave_class_storage_name[]; +extern UCHAR _ux_system_slave_class_storage_vendor_id[]; +extern UCHAR _ux_system_slave_class_storage_product_id[]; +extern UCHAR _ux_system_slave_class_storage_product_rev[]; +extern UCHAR _ux_system_slave_class_storage_product_serial[]; +extern UCHAR _ux_system_slave_class_audio_name[]; +extern UCHAR _ux_system_slave_class_cdc_acm_name[]; +extern UCHAR _ux_system_slave_class_dpump_name[]; +extern UCHAR _ux_system_slave_class_pima_name[]; +extern UCHAR _ux_system_slave_class_hid_name[]; +extern UCHAR _ux_system_slave_class_rndis_name[]; +extern UCHAR _ux_system_slave_class_cdc_ecm_name[]; +extern UCHAR _ux_system_slave_class_dfu_name[]; + + + +#endif + diff --git a/common/core/inc/ux_user.h b/common/core/inc/ux_user.h new file mode 100644 index 0000000..b7bb606 --- /dev/null +++ b/common/core/inc/ux_user.h @@ -0,0 +1,314 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** User Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* ux_user.h PORTABLE C */ +/* 6.0 */ +/* */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains user defines for configuring USBX in specific */ +/* ways. This file will have an effect only if the application and */ +/* USBX library are built with UX_INCLUDE_USER_DEFINE_FILE defined. */ +/* Note that all the defines in this file may also be made on the */ +/* command line when building USBX library and application objects. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_USER_H +#define UX_USER_H + + +/* Define various build options for the USBX port. The application should either make changes + here by commenting or un-commenting the conditional compilation defined OR supply the defines + though the compiler's equivalent of the -D option. */ +/* #define UX_THREAD_STACK_SIZE (2 * 1024) */ + +/* Define USBX Host Enum Thread Stack Size. The default is to use UX_THREAD_STACK_SIZE */ +/* +#define UX_HOST_ENUM_THREAD_STACK_SIZE UX_THREAD_STACK_SIZE +*/ + + +/* Define USBX Host Thread Stack Size. The default is to use UX_THREAD_STACK_SIZE */ +/* +#define UX_HOST_HCD_THREAD_STACK_SIZE UX_THREAD_STACK_SIZE +*/ + +/* Define USBX Host HNP Polling Thread Stack Size. The default is to use UX_THREAD_STACK_SIZE */ +/* +#define UX_HOST_HNP_POLLING_THREAD_STACK UX_THREAD_STACK_SIZE +*/ + +/* Override various options with default values already assigned in ux_api.h or ux_port.h. Please + also refer to ux_port.h for descriptions on each of these options. */ + +/* Defined, this value represents how many ticks per seconds for a specific hardware platform. + The default is 1000 indicating 1 tick per millisecond. */ + +/* #define UX_PERIODIC_RATE 1000 +*/ +#define UX_PERIODIC_RATE (TX_TIMER_TICKS_PER_SECOND) + +/* Defined, this value is the maximum number of classes that can be loaded by USBX. This value + represents the class container and not the number of instances of a class. For instance, if a + particular implementation of USBX needs the hub class, the printer class, and the storage + class, then the UX_MAX_CLASSES value can be set to 3 regardless of the number of devices + that belong to these classes. */ + +/* #define UX_MAX_CLASSES 3 +*/ + + +/* Defined, this value is the maximum number of classes in the device stack that can be loaded by + USBX. */ + +/* #define UX_MAX_SLAVE_CLASS_DRIVER 1 +*/ + +/* Defined, this value is the maximum number of interfaces in the device framework. */ + +/* #define UX_MAX_SLAVE_INTERFACES 16 +*/ + +/* Defined, this value represents the number of different host controllers available in the system. + For USB 1.1 support, this value will usually be 1. For USB 2.0 support, this value can be more + than 1. This value represents the number of concurrent host controllers running at the same time. + If for instance there are two instances of OHCI running, or one EHCI and one OHCI controller + running, the UX_MAX_HCD should be set to 2. */ + +/* #define UX_MAX_HCD 1 +*/ + + +/* Defined, this value represents the maximum number of devices that can be attached to the USB. + Normally, the theoretical maximum number on a single USB is 127 devices. This value can be + scaled down to conserve memory. Note that this value represents the total number of devices + regardless of the number of USB buses in the system. */ + +/* #define UX_MAX_DEVICES 127 +*/ + + +/* Defined, this value represents the current number of SCSI logical units represented in the device + storage class driver. */ + +/* #define UX_MAX_SLAVE_LUN 1 +*/ + + +/* Defined, this value represents the maximum number of SCSI logical units represented in the + host storage class driver. */ + +/* #define UX_MAX_HOST_LUN 1 +*/ + + +/* Defined, this value represents the maximum number of bytes received on a control endpoint in + the device stack. The default is 256 bytes but can be reduced in memory constraint environments. */ + +/* #define UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH 256 +*/ + + +/* Defined, this value represents the maximum number of bytes that can be received or transmitted + on any endpoint. This value cannot be less than the maximum packet size of any endpoint. The default + is 4096 bytes but can be reduced in memory constraint environments. For cd-rom support in the storage + class, this value cannot be less than 2048. */ + +#define UX_SLAVE_REQUEST_DATA_MAX_LENGTH (1024 * 2) + + +/* Defined, this value includes code to handle storage Multi-Media Commands (MMC). E.g., DVD-ROM. +*/ + +/* #define UX_SLAVE_CLASS_STORAGE_INCLUDE_MMC */ + + +/* Defined, this value represents the maximum number of bytes that a storage payload can send/receive. + The default is 8K bytes but can be reduced in memory constraint environments. */ +#define UX_HOST_CLASS_STORAGE_MEMORY_BUFFER_SIZE (1024 * 8) + +/* Define USBX Mass Storage Thread Stack Size. The default is to use UX_THREAD_STACK_SIZE. */ + +/* #define UX_HOST_CLASS_STORAGE_THREAD_STACK_SIZE UX_THREAD_STACK_SIZE + */ + +/* Defined, this value represents the maximum number of Ed, regular TDs and Isochronous TDs. These values + depend on the type of host controller and can be reduced in memory constraint environments. */ + +#define UX_MAX_ED 80 +#define UX_MAX_TD 128 +#define UX_MAX_ISO_TD 1 + +/* Defined, this value represents the maximum size of the HID decompressed buffer. This cannot be determined + in advance so we allocate a big block, usually 4K but for simple HID devices like keyboard and mouse + it can be reduced a lot. */ + +#define UX_HOST_CLASS_HID_DECOMPRESSION_BUFFER 4096 + +/* Defined, this value represents the maximum number of HID usages for a HID device. + Default is 2048 but for simple HID devices like keyboard and mouse it can be reduced a lot. */ + +#define UX_HOST_CLASS_HID_USAGES 2048 + + +/* By default, each key in each HID report from the device is reported by ux_host_class_hid_keyboard_key_get + (a HID report from the device is received whenever there is a change in a key state i.e. when a key is pressed + or released. The report contains every key that is down). There are limitations to this method such as not being + able to determine when a key has been released. + + Defined, this value causes ux_host_class_hid_keyboard_key_get to only report key changes i.e. key presses + and key releases. */ + +/* #define UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE */ + +/* Works when UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE is defined. + + Defined, this value causes ux_host_class_hid_keyboard_key_get to only report key pressed/down changes; + key released/up changes are not reported. + */ + +/* #define UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_KEY_DOWN_ONLY */ + +/* Works when UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE is defined. + + Defined, this value causes ux_host_class_hid_keyboard_key_get to report lock key (CapsLock/NumLock/ScrollLock) changes. + */ + +/* #define UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_LOCK_KEYS */ + +/* Works when UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE is defined. + + Defined, this value causes ux_host_class_hid_keyboard_key_get to report modifier key (Ctrl/Alt/Shift/GUI) changes. + */ + +/* #define UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_MODIFIER_KEYS */ + + +/* Defined, this value represents the maximum number of media for the host storage class. + Default is 8 but for memory contrained resource systems this can ne reduced to 1. */ + +#define UX_HOST_CLASS_STORAGE_MAX_MEDIA 2 + +/* Defined, this value includes code to handle storage devices that use the CB + or CBI protocol (such as floppy disks). It is off by default because these + protocols are obsolete, being superseded by the Bulk Only Transport (BOT) protocol + which virtually all modern storage devices use. +*/ + +/* #define UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT */ + +/* Defined, this value forces the memory allocation scheme to enforce alignement + of memory with the UX_SAFE_ALIGN field. +*/ + +/* #define UX_ENFORCE_SAFE_ALIGNMENT */ + +/* Defined, this value represents the number of packets in the CDC_ECM device class. + The default is 16. +*/ + +#define UX_DEVICE_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES 4 + +/* Defined, this value represents the number of packets in the CDC_ECM host class. + The default is 16. +*/ + +/* #define UX_HOST_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES 16 */ + +/* Defined, this value represents the number of milliseconds to wait for packet + allocation until invoking the application's error callback and retrying. + The default is 1000 milliseconds. +*/ + +/* #define UX_HOST_CLASS_CDC_ECM_PACKET_POOL_WAIT 10 */ + +/* Defined, this value represents the number of milliseconds to wait for packet + allocation until invoking the application's error callback and retrying. +*/ + +/* #define UX_DEVICE_CLASS_CDC_ECM_PACKET_POOL_WAIT 10 */ + +/* Defined, this value represents the the maximum length of HID reports on the + device. + */ + +/* #define UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH 64 */ + +/* Defined, this value represents the the maximum number of HID events/reports + that can be queued at once. + */ + +/* #define UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE 8 */ + +/* Defined, this value will only enable the host side of usbx. */ +/* #define UX_HOST_SIDE_ONLY */ + +/* Defined, this value will only enable the device side of usbx. */ +/* #define UX_DEVICE_SIDE_ONLY */ + +/* Defined, this value will include the OTG polling thread. OTG can only be active if both host/device are present. +*/ + +#ifndef UX_HOST_SIDE_ONLY +#ifndef UX_DEVICE_SIDE_ONLY + +/* #define UX_OTG_SUPPORT */ + +#endif +#endif + +/* Defined, this value represents the maximum size of single tansfers for the SCSI data phase. +*/ + +#define UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE (1024 * 1) + +/* Defined, this value represents the size of the log pool. +*/ +#define UX_DEBUG_LOG_SIZE (1024 * 16) + + +/* DEBUG includes and macros for a specific platform go here. */ +#ifdef UX_INCLUDE_USER_DEFINE_BSP +#include "usb_bsp.h" +#include "usbh_hcs.h" +#include "usbh_stdreq.h" +#include "usbh_core.h" +#endif + +#endif + diff --git a/common/core/inc/ux_utility.h b/common/core/inc/ux_utility.h new file mode 100644 index 0000000..3f2b6e4 --- /dev/null +++ b/common/core/inc/ux_utility.h @@ -0,0 +1,234 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_utility.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX components that utilize utility functions. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_UTILITY_H +#define UX_UTILITY_H + + +/* Define Utility component function prototypes. */ + +VOID _ux_utility_descriptor_parse(UCHAR * raw_descriptor, UCHAR * descriptor_structure, + UINT descriptor_entries, UCHAR * descriptor); +VOID _ux_utility_descriptor_pack(UCHAR * descriptor, UCHAR * descriptor_structure, + UINT descriptor_entries, UCHAR * raw_descriptor); +ULONG _ux_utility_long_get(UCHAR * address); +VOID _ux_utility_long_put(UCHAR * address, ULONG value); +VOID _ux_utility_long_put_big_endian(UCHAR * address, ULONG value); +ULONG _ux_utility_long_get_big_endian(UCHAR * address); +VOID *_ux_utility_memory_allocate(ULONG memory_alignment,ULONG memory_cache_flag, ULONG memory_size_requested); +UINT _ux_utility_memory_compare(VOID *memory_source, VOID *memory_destination, ULONG length); +VOID _ux_utility_memory_copy(VOID *memory_destination, VOID *memory_source, ULONG length); +VOID _ux_utility_memory_free(VOID *memory); +ULONG _ux_utility_string_length_get(UCHAR *string); +UINT _ux_utility_string_length_check(UCHAR *input_string, UINT *string_length_ptr, UINT max_string_length); +UX_MEMORY_BLOCK *_ux_utility_memory_free_block_best_get(ULONG memory_cache_flag, ULONG memory_size_requested); +VOID _ux_utility_memory_set(VOID *destination, UCHAR value, ULONG length); +UINT _ux_utility_mutex_create(TX_MUTEX *mutex, CHAR *mutex_name); +UINT _ux_utility_mutex_delete(TX_MUTEX *mutex); +VOID _ux_utility_mutex_off(TX_MUTEX *mutex); +VOID _ux_utility_mutex_on(TX_MUTEX *mutex); +ULONG _ux_utility_pci_class_scan(ULONG pci_class, ULONG bus_number, ULONG device_number, + ULONG function_number, ULONG *current_bus_number, + ULONG *current_device_number, ULONG *current_function_number); +ULONG _ux_utility_pci_read(ULONG bus_number, ULONG device_number, ULONG function_number, + ULONG offset, UINT read_size); +VOID _ux_utility_pci_write(ULONG bus_number, ULONG device_number, ULONG function_number, + ULONG offset, ULONG value, UINT write_size); +VOID *_ux_utility_physical_address(VOID *virtual_address); +UINT _ux_utility_semaphore_create(TX_SEMAPHORE *semaphore, CHAR *semaphore_name, UINT initial_count); +UINT _ux_utility_semaphore_delete(TX_SEMAPHORE *semaphore); +UINT _ux_utility_semaphore_get(TX_SEMAPHORE *semaphore, ULONG semaphore_signal); +UINT _ux_utility_semaphore_put(TX_SEMAPHORE *semaphore); +VOID _ux_utility_set_interrupt_handler(UINT irq, VOID (*interrupt_handler)(VOID)); +ULONG _ux_utility_short_get(UCHAR * address); +ULONG _ux_utility_short_get_big_endian(UCHAR * address); +VOID _ux_utility_short_put(UCHAR * address, USHORT value); +VOID _ux_utility_short_put_big_endian(UCHAR * address, USHORT value); +UINT _ux_utility_thread_create(TX_THREAD *thread_ptr, CHAR *name, + VOID (*entry_function)(ULONG), ULONG entry_input, + VOID *stack_start, ULONG stack_size, + UINT priority, UINT preempt_threshold, + ULONG time_slice, UINT auto_start); +UINT _ux_utility_thread_delete(TX_THREAD *thread_ptr); +VOID _ux_utility_thread_relinquish(VOID); +UINT _ux_utility_thread_schedule_other(UINT caller_priority); +UINT _ux_utility_thread_resume(TX_THREAD *thread_ptr); +UINT _ux_utility_thread_sleep(ULONG ticks); +UINT _ux_utility_thread_suspend(TX_THREAD *thread_ptr); +TX_THREAD *_ux_utility_thread_identify(VOID); +UINT _ux_utility_timer_create(TX_TIMER *timer, CHAR *timer_name, VOID (*expiration_function) (ULONG), + ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks, + UINT activation_flag); +VOID *_ux_utility_virtual_address(VOID *physical_address); +UINT _ux_utility_event_flags_create(TX_EVENT_FLAGS_GROUP *group_ptr, CHAR *name); +UINT _ux_utility_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr); +UINT _ux_utility_event_flags_get(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG requested_flags, + UINT get_option, ULONG *actual_flags_ptr, ULONG wait_option); +UINT _ux_utility_event_flags_set(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG flags_to_set, + UINT set_option); +VOID _ux_utility_unicode_to_string(UCHAR *source, UCHAR *destination); +VOID _ux_utility_string_to_unicode(UCHAR *source, UCHAR *destination); +VOID _ux_system_error_handler(UINT system_level, UINT system_context, UINT error_code); +VOID _ux_utility_debug_callback_register(VOID (*debug_callback)(UCHAR *, ULONG)); +VOID _ux_utility_error_callback_register(VOID (*error_callback)(UINT system_level, UINT system_context, UINT error_code)); +VOID _ux_utility_delay_ms(ULONG ms_wait); + +#define UX_UTILITY_ADD_SAFE(add_a, add_b, result, status) do { \ + if (UX_OVERFLOW_CHECK_ADD_ULONG(add_a, add_b)) \ + status = UX_ERROR; \ + else \ + result = (add_a) + (add_b); \ + } while(0) + +#define UX_UTILITY_MULC_SAFE(mul_v, mul_c, result, status) do { \ + if (UX_OVERFLOW_CHECK_MULC_ULONG(mul_v, mul_c)) \ + status = UX_ERROR; \ + else \ + result = (mul_v) * (mul_c); \ + } while(0) + +#define UX_UTILITY_MULV_SAFE(mul_v0, mul_v1, result, status) do { \ + if (UX_OVERFLOW_CHECK_MULC_ULONG(mul_v0, mul_v1)) \ + status = UX_ERROR; \ + else \ + result = (mul_v0) * (mul_v1); \ + } while(0) + +#define UX_UTILITY_MEMORY_ALLOCATE_MULC_SAFE(align,cache,size_mul_v,size_mul_c) \ + (UX_OVERFLOW_CHECK_MULC_ULONG(size_mul_v, size_mul_c) ? UX_NULL : _ux_utility_memory_allocate((align), (cache), (size_mul_v)*(size_mul_c))) +#define UX_UTILITY_MEMORY_ALLOCATE_MULV_SAFE(align,cache,size_mul_v0,size_mul_v1) \ + (UX_OVERFLOW_CHECK_MULV_ULONG(size_mul_v0, size_mul_v1) ? UX_NULL : _ux_utility_memory_allocate((align), (cache), (size_mul_v0)*(size_mul_v1))) +#define UX_UTILITY_MEMORY_ALLOCATE_ADD_SAFE(align,cache,size_add_a,size_add_b) \ + (UX_OVERFLOW_CHECK_ADD_ULONG(size_add_a, size_add_b) ? UX_NULL : _ux_utility_memory_allocate((align), (cache), (size_add_a)+(size_add_b))) + +#ifdef UX_DISABLE_ARITHMETIC_CHECK + +/* No arithmetic check, calculate directly. */ + +#define _ux_utility_memory_allocate_mulc_safe(align,cache,size_mul_v,size_mul_c) _ux_utility_memory_allocate((align), (cache), (size_mul_v)*(size_mul_c)) +#define _ux_utility_memory_allocate_mulv_safe(align,cache,size_mul_v0,size_mul_v1) _ux_utility_memory_allocate((align), (cache), (size_mul_v0)*(size_mul_v1)) +#define _ux_utility_memory_allocate_add_safe(align,cache,size_add_a,size_add_b) _ux_utility_memory_allocate((align), (cache), (size_add_a)+(size_add_b)) + +#else /* UX_DISABLE_ARITHMETIC_CHECK */ + +#ifdef UX_ENABLE_MEMORY_ARITHMETIC_OPTIMIZE + +/* Uses macro to enable code optimization on compiling. */ + +#define _ux_utility_memory_allocate_mulc_safe(align,cache,size_mul_v,size_mul_c) UX_UTILITY_MEMORY_ALLOCATE_MULC_SAFE(align,cache,size_mul_v,size_mul_c) +#define _ux_utility_memory_allocate_mulv_safe(align,cache,size_mul_v0,size_mul_v1) UX_UTILITY_MEMORY_ALLOCATE_MULV_SAFE(align,cache,size_mul_v0,size_mul_v1) +#define _ux_utility_memory_allocate_add_safe(align,cache,size_add_a,size_add_b) UX_UTILITY_MEMORY_ALLOCATE_ADD_SAFE(align,cache,size_add_a,size_add_b) + +#else /* UX_ENABLE_MEMORY_ARITHMETIC_OPTIMIZE */ + +/* Uses functions to be most flexible. */ + +VOID* _ux_utility_memory_allocate_mulc_safe(ULONG align,ULONG cache,ULONG size_mul_v,ULONG size_mul_c); +VOID* _ux_utility_memory_allocate_mulv_safe(ULONG align,ULONG cache,ULONG size_mul_v0,ULONG size_mul_v1); +VOID* _ux_utility_memory_allocate_add_safe(ULONG align,ULONG cache,ULONG size_add_a,ULONG size_add_b); + +#endif /* UX_ENABLE_MEMORY_ARITHMETIC_OPTIMIZE */ + +#endif /* UX_DISABLE_ARITHMETIC_CHECK */ + +/* Define the system API mappings. + Note: this section is only applicable to + application source code, hence the conditional that turns off this + stuff when the include file is processed by the ThreadX source. */ + +#ifndef UX_SOURCE_CODE + + +#define ux_utility_descriptor_parse _ux_utility_descriptor_parse +#define ux_utility_descriptor_pack _ux_utility_descriptor_pack +#define ux_utility_long_get _ux_utility_long_get +#define ux_utility_long_put _ux_utility_long_put +#define ux_utility_long_put_big_endian _ux_utility_long_put_big_endian +#define ux_utility_long_get_big_endian _ux_utility_long_get_big_endian +#define ux_utility_memory_allocate _ux_utility_memory_allocate +#define ux_utility_memory_compare _ux_utility_memory_compare +#define ux_utility_memory_copy _ux_utility_memory_copy +#define ux_utility_memory_free _ux_utility_memory_free +#define ux_utility_string_length_get _ux_utility_string_length_get +#define ux_utility_string_length_check _ux_utility_string_length_check +#define ux_utility_memory_set _ux_utility_memory_set +#define ux_utility_mutex_create _ux_utility_mutex_create +#define ux_utility_mutex_delete _ux_utility_mutex_delete +#define ux_utility_mutex_off _ux_utility_mutex_off +#define ux_utility_mutex_on _ux_utility_mutex_on +#define ux_utility_pci_class_scan _ux_utility_pci_class_scan +#define ux_utility_pci_read _ux_utility_pci_read +#define ux_utility_pci_write _ux_utility_pci_write +#define ux_utility_physical_address _ux_utility_physical_address +#define ux_utility_semaphore_create _ux_utility_semaphore_create +#define ux_utility_semaphore_delete _ux_utility_semaphore_delete +#define ux_utility_semaphore_get _ux_utility_semaphore_get +#define ux_utility_semaphore_put _ux_utility_semaphore_put +#define ux_utility_set_interrupt_handler _ux_utility_set_interrupt_handler +#define ux_utility_short_get _ux_utility_short_get +#define ux_utility_short_get_big_endian _ux_utility_short_get_big_endian +#define ux_utility_short_put _ux_utility_short_put +#define ux_utility_short_put_big_endian _ux_utility_short_put_big_endian +#define ux_utility_thread_create _ux_utility_thread_create +#define ux_utility_thread_delete _ux_utility_thread_delete +#define ux_utility_thread_relinquish _ux_utility_thread_relinquish +#define ux_utility_thread_resume _ux_utility_thread_resume +#define ux_utility_thread_sleep _ux_utility_thread_sleep +#define ux_utility_thread_suspend _ux_utility_thread_suspend +#define ux_utility_thread_identify _ux_utility_thread_identify +#define ux_utility_timer_create _ux_utility_timer_create +#define ux_utility_event_flags_create _ux_utility_event_flags_create +#define ux_utility_event_flags_delete _ux_utility_event_flags_delete +#define ux_utility_event_flags_get _ux_utility_event_flags_get +#define ux_utility_event_flags_set _ux_utility_event_flags_set +#define ux_utility_unicode_to_string _ux_utility_unicode_to_string +#define ux_utility_string_to_unicode _ux_utility_string_to_unicode +#define ux_utility_delay_ms _ux_utility_delay_ms +#define ux_utility_error_callback_register _ux_utility_error_callback_register +#define ux_system_error_handler _ux_system_error_handler +#endif + +#endif diff --git a/common/core/src/ux_dcd_sim_slave_address_set.c b/common/core/src/ux_dcd_sim_slave_address_set.c new file mode 100644 index 0000000..703312e --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_address_set.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_address_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will set the address of the device after we have */ +/* received a SET_ADDRESS command from the host. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* address Address to set */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_address_set(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG address) +{ + + UX_PARAMETER_NOT_USED(dcd_sim_slave); + UX_PARAMETER_NOT_USED(address); + + /* This function always succeeds. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_endpoint_create.c b/common/core/src/ux_dcd_sim_slave_endpoint_create.c new file mode 100644 index 0000000..3dcc2d2 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_endpoint_create.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create a physical endpoint. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* endpoint Pointer to endpoint container */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_endpoint_create(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint) +{ + +UX_DCD_SIM_SLAVE_ED *ed; +ULONG sim_slave_endpoint_index; + + + /* The simulator slave controller has 16 endpoints maximum. Endpoint 0 is always control. + The other endpoints are generic. We can use the endpoint number as an index. */ + sim_slave_endpoint_index = endpoint ->ux_slave_endpoint_descriptor.bEndpointAddress & ~(ULONG)UX_ENDPOINT_DIRECTION; + + /* Fetch the address of the physical endpoint. */ + ed = &dcd_sim_slave -> ux_dcd_sim_slave_ed[sim_slave_endpoint_index]; + + /* Check the endpoint status, if it is free, reserve it. If not reject this endpoint. */ + if ((ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_USED) == 0) + { + + /* We can use this endpoint. */ + ed -> ux_sim_slave_ed_status |= UX_DCD_SIM_SLAVE_ED_STATUS_USED; + + /* Keep the physical endpoint address in the endpoint container. */ + endpoint -> ux_slave_endpoint_ed = (VOID *) ed; + + /* And its mask. */ + ed -> ux_sim_slave_ed_index = sim_slave_endpoint_index; + + /* Save the endpoint pointer. */ + ed -> ux_sim_slave_ed_endpoint = endpoint; + + /* If this is endpoint 0, it is always ready for transactions. */ + if ( sim_slave_endpoint_index == 0) + ed -> ux_sim_slave_ed_status |= UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER; + + /* Enable this endpoint. */ + return(UX_SUCCESS); + } + + /* Notify application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DCD, UX_MEMORY_INSUFFICIENT); + + /* Return error to caller. */ + return(UX_NO_ED_AVAILABLE); +} + diff --git a/common/core/src/ux_dcd_sim_slave_endpoint_destroy.c b/common/core/src/ux_dcd_sim_slave_endpoint_destroy.c new file mode 100644 index 0000000..0fabe5f --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_endpoint_destroy.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy a physical endpoint. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* endpoint Pointer to endpoint container */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_endpoint_destroy(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint) +{ + +UX_DCD_SIM_SLAVE_ED *ed; + + UX_PARAMETER_NOT_USED(dcd_sim_slave); + + /* Keep the physical endpoint address in the endpoint container. */ + ed = (UX_DCD_SIM_SLAVE_ED *) endpoint -> ux_slave_endpoint_ed; + + /* We can free this endpoint. */ + ed -> ux_sim_slave_ed_status = UX_DCD_SIM_SLAVE_ED_STATUS_UNUSED; + + /* This function never fails. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_endpoint_reset.c b/common/core/src/ux_dcd_sim_slave_endpoint_reset.c new file mode 100644 index 0000000..a2659f6 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_endpoint_reset.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_endpoint_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reset a physical endpoint. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* endpoint Pointer to endpoint container */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_endpoint_reset(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint) +{ + +UX_DCD_SIM_SLAVE_ED *ed; + + UX_PARAMETER_NOT_USED(dcd_sim_slave); + + /* Get the physical endpoint address in the endpoint container. */ + ed = (UX_DCD_SIM_SLAVE_ED *) endpoint -> ux_slave_endpoint_ed; + + /* Set the state of the endpoint to not stalled. */ + ed -> ux_sim_slave_ed_status &= ~(ULONG)UX_DCD_SIM_SLAVE_ED_STATUS_STALLED; + + /* This function never fails. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_endpoint_stall.c b/common/core/src/ux_dcd_sim_slave_endpoint_stall.c new file mode 100644 index 0000000..00c53f0 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_endpoint_stall.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_endpoint_stall PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will stall a physical endpoint. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* endpoint Pointer to endpoint container */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_endpoint_stall(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_ENDPOINT *endpoint) +{ + +UX_DCD_SIM_SLAVE_ED *ed; + + UX_PARAMETER_NOT_USED(dcd_sim_slave); + + /* Get the physical endpoint address in the endpoint container. */ + ed = (UX_DCD_SIM_SLAVE_ED *) endpoint -> ux_slave_endpoint_ed; + + /* Set the state of the endpoint to stalled. */ + ed -> ux_sim_slave_ed_status |= UX_DCD_SIM_SLAVE_ED_STATUS_STALLED; + + /* This function never fails. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_endpoint_status.c b/common/core/src/ux_dcd_sim_slave_endpoint_status.c new file mode 100644 index 0000000..1830cb6 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_endpoint_status.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_endpoint_status PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the status of the endpoint. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* endpoint_index Endpoint index */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_endpoint_status(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG endpoint_index) +{ + +UX_DCD_SIM_SLAVE_ED *ed; + + + /* Fetch the address of the physical endpoint. */ + ed = &dcd_sim_slave -> ux_dcd_sim_slave_ed[endpoint_index]; + + /* Check the endpoint status, if it is free, we have a illegal endpoint. */ + if ((ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_USED) == 0) + return(UX_ERROR); + + /* Check if the endpoint is stalled. */ + if ((ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_STALLED) == 0) + return(UX_FALSE); + else + return(UX_TRUE); +} + diff --git a/common/core/src/ux_dcd_sim_slave_frame_number_get.c b/common/core/src/ux_dcd_sim_slave_frame_number_get.c new file mode 100644 index 0000000..6f87b2d --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_frame_number_get.c @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_frame_number_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the frame number currently used by the */ +/* controller. This function is mostly used for isochronous purposes. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* frame_number Destination for frame number */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_frame_number_get(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG *frame_number) +{ + + UX_PARAMETER_NOT_USED(dcd_sim_slave); + + /* There is no frame number from the slave controller. */ + *frame_number = 0; + + /* This function never fails. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_function.c b/common/core/src/ux_dcd_sim_slave_function.c new file mode 100644 index 0000000..bd58839 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_function.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_function PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function dispatches the DCD function internally to the */ +/* slave simulator controller. */ +/* */ +/* INPUT */ +/* */ +/* dcd Pointer to device controller */ +/* function Function requested */ +/* parameter Pointer to parameter structure*/ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_dcd_sim_slave_address_set Set address */ +/* _ux_dcd_sim_slave_endpoint_create Create endpoint */ +/* _ux_dcd_sim_slave_endpoint_destroy Destroy endpoint */ +/* _ux_dcd_sim_slave_endpoint_reset Reset endpoint */ +/* _ux_dcd_sim_slave_endpoint_stall Stall endpoint */ +/* _ux_dcd_sim_slave_endpoint_status Get endpoint status */ +/* _ux_dcd_sim_slave_frame_number_get Get frame number */ +/* _ux_dcd_sim_slave_state_change Change state */ +/* _ux_dcd_sim_slave_transfer_abort Abort transfer */ +/* _ux_dcd_sim_slave_transfer_request Request transfer */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_function(UX_SLAVE_DCD *dcd, UINT function, VOID *parameter) +{ + +UINT status; +UX_DCD_SIM_SLAVE *dcd_sim_slave; + + + /* Check the status of the controller. */ + if (dcd -> ux_slave_dcd_status == UX_UNUSED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DCD, UX_CONTROLLER_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONTROLLER_UNKNOWN, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONTROLLER_UNKNOWN); + } + + /* Get the pointer to the Slave simulation DCD. */ + dcd_sim_slave = (UX_DCD_SIM_SLAVE *) dcd -> ux_slave_dcd_controller_hardware; + + /* Look at the function and route it. */ + switch(function) + { + + case UX_DCD_GET_FRAME_NUMBER: + + status = _ux_dcd_sim_slave_frame_number_get(dcd_sim_slave, (ULONG *) parameter); + break; + + case UX_DCD_TRANSFER_REQUEST: + + status = _ux_dcd_sim_slave_transfer_request(dcd_sim_slave, (UX_SLAVE_TRANSFER *) parameter); + break; + + case UX_DCD_TRANSFER_ABORT: + + status = _ux_dcd_sim_slave_transfer_abort(dcd_sim_slave, (UX_SLAVE_TRANSFER *) parameter); + break; + + case UX_DCD_CREATE_ENDPOINT: + + status = _ux_dcd_sim_slave_endpoint_create(dcd_sim_slave, parameter); + break; + + case UX_DCD_DESTROY_ENDPOINT: + + status = _ux_dcd_sim_slave_endpoint_destroy(dcd_sim_slave, parameter); + break; + + case UX_DCD_RESET_ENDPOINT: + + status = _ux_dcd_sim_slave_endpoint_reset(dcd_sim_slave, parameter); + break; + + case UX_DCD_STALL_ENDPOINT: + + status = _ux_dcd_sim_slave_endpoint_stall(dcd_sim_slave, parameter); + break; + + case UX_DCD_SET_DEVICE_ADDRESS: + + status = _ux_dcd_sim_slave_address_set(dcd_sim_slave, (ULONG) (ALIGN_TYPE) parameter); + break; + + case UX_DCD_CHANGE_STATE: + + status = _ux_dcd_sim_slave_state_change(dcd_sim_slave, (ULONG) (ALIGN_TYPE) parameter); + break; + + case UX_DCD_ENDPOINT_STATUS: + + status = _ux_dcd_sim_slave_endpoint_status(dcd_sim_slave, (ULONG) (ALIGN_TYPE) parameter); + break; + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DCD, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_dcd_sim_slave_initialize.c b/common/core/src/ux_dcd_sim_slave_initialize.c new file mode 100644 index 0000000..4499ae6 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_initialize.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB simulation slave controller. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_initialize(VOID) +{ + +UX_SLAVE_DCD *dcd; +UX_DCD_SIM_SLAVE *dcd_sim_slave; + + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* The controller initialized here is of Slave simulation type. */ + dcd -> ux_slave_dcd_controller_type = UX_DCD_SIM_SLAVE_SLAVE_CONTROLLER; + + /* Allocate memory for this Slave simulation DCD instance. */ + dcd_sim_slave = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_DCD_SIM_SLAVE)); + + /* Check if memory was properly allocated. */ + if(dcd_sim_slave == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set the pointer to the Slave simulation DCD. */ + dcd -> ux_slave_dcd_controller_hardware = (VOID *) dcd_sim_slave; + + /* Set the generic DCD owner for the Slave simulation DCD. */ + dcd_sim_slave -> ux_dcd_sim_slave_dcd_owner = dcd; + + /* Initialize the function collector for this DCD. */ + dcd -> ux_slave_dcd_function = _ux_dcd_sim_slave_function; + + /* Set the state of the controller to OPERATIONAL now. */ + dcd -> ux_slave_dcd_status = UX_DCD_STATUS_OPERATIONAL; + + /* This operation completed with success. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_initialize_complete.c b/common/core/src/ux_dcd_sim_slave_initialize_complete.c new file mode 100644 index 0000000..7e22c85 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_initialize_complete.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_initialize_complete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function completes the initialization of the slave controller */ +/* simulator. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_initialize_complete(VOID) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UCHAR * device_framework; +UX_SLAVE_TRANSFER *transfer_request; + + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Slave simulator is a Full speed controller. */ + _ux_system_slave -> ux_system_slave_device_framework = _ux_system_slave -> ux_system_slave_device_framework_full_speed; + _ux_system_slave -> ux_system_slave_device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length_full_speed; + + /* Get the device framework pointer. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework; + + /* And create the decompressed device descriptor structure. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_device_descriptor_structure, + UX_DEVICE_DESCRIPTOR_ENTRIES, + (UCHAR *) &device -> ux_slave_device_descriptor); + + /* Now we create a transfer request to accept the first SETUP packet + and get the ball running. First get the address of the endpoint + transfer request container. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Set the timeout to be for Control Endpoint. */ + transfer_request -> ux_slave_transfer_request_timeout = MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT); + + /* Adjust the current data pointer as well. */ + transfer_request -> ux_slave_transfer_request_current_data_pointer = + transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Update the transfer request endpoint pointer with the default endpoint. */ + transfer_request -> ux_slave_transfer_request_endpoint = &device -> ux_slave_device_control_endpoint; + + /* The control endpoint max packet size needs to be filled manually in its descriptor. */ + transfer_request -> ux_slave_transfer_request_endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize = + device -> ux_slave_device_descriptor.bMaxPacketSize0; + + /* On the control endpoint, always expect the maximum. */ + transfer_request -> ux_slave_transfer_request_requested_length = + device -> ux_slave_device_descriptor.bMaxPacketSize0; + + /* Attach the control endpoint to the transfer request. */ + transfer_request -> ux_slave_transfer_request_endpoint = &device -> ux_slave_device_control_endpoint; + + /* Create the default control endpoint attached to the device. + Once this endpoint is enabled, the host can then send a setup packet + The device controller will receive it and will call the setup function + module. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_CREATE_ENDPOINT, + (VOID *) &device -> ux_slave_device_control_endpoint); + + /* Ensure the control endpoint is properly reset. */ + device -> ux_slave_device_control_endpoint.ux_slave_endpoint_state = UX_ENDPOINT_RESET; + + /* A SETUP packet is a DATA IN operation. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_IN; + + /* We are now ready for the USB device to accept the first packet when connected. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_state_change.c b/common/core/src/ux_dcd_sim_slave_state_change.c new file mode 100644 index 0000000..eacd3d8 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_state_change.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_state_change PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will set the state of the controller to the desired */ +/* value. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* state Desired state */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_state_change(UX_DCD_SIM_SLAVE *dcd_sim_slave, ULONG state) +{ + + UX_PARAMETER_NOT_USED(dcd_sim_slave); + UX_PARAMETER_NOT_USED(state); + + /* Nothing to do in simulation mode. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_transfer_abort.c b/common/core/src/ux_dcd_sim_slave_transfer_abort.c new file mode 100644 index 0000000..f3c56b4 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_transfer_abort.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_transfer_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will terminate a transfer. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_transfer_abort(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_TRANSFER *transfer_request) +{ + +UX_DCD_SIM_SLAVE_ED *ed; +UX_SLAVE_ENDPOINT *endpoint; + + UX_PARAMETER_NOT_USED(dcd_sim_slave); + + /* Get the pointer to the logical endpoint from the transfer request. */ + endpoint = transfer_request -> ux_slave_transfer_request_endpoint; + + /* Keep the physical endpoint address in the endpoint container. */ + ed = (UX_DCD_SIM_SLAVE_ED *) endpoint -> ux_slave_endpoint_ed; + + /* Turn off the transfer bit. */ + ed -> ux_sim_slave_ed_status &= ~(ULONG)UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER; + + /* This function never fails. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_dcd_sim_slave_transfer_request.c b/common/core/src/ux_dcd_sim_slave_transfer_request.c new file mode 100644 index 0000000..5e1d530 --- /dev/null +++ b/common/core/src/ux_dcd_sim_slave_transfer_request.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Slave Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_dcd_sim_slave.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_dcd_sim_slave_transfer_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will initiate a transfer to a specific endpoint. */ +/* If the endpoint is IN, the endpoint register will be set to accept */ +/* the request. */ +/* */ +/* If the endpoint is IN, the endpoint FIFO will be filled with the */ +/* buffer and the endpoint register set. */ +/* */ +/* INPUT */ +/* */ +/* dcd_sim_slave Pointer to device controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_dcd_sim_slave_transfer_abort Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Slave Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_dcd_sim_slave_transfer_request(UX_DCD_SIM_SLAVE *dcd_sim_slave, UX_SLAVE_TRANSFER *transfer_request) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_DCD_SIM_SLAVE_ED *ed; +UINT status; + + + /* Get the pointer to the logical endpoint from the transfer request. */ + endpoint = transfer_request -> ux_slave_transfer_request_endpoint; + + /* Get the slave endpoint. */ + ed = (UX_DCD_SIM_SLAVE_ED *) endpoint -> ux_slave_endpoint_ed; + + /* We have a request for a OUT or IN transaction from the host. + If the endpoint is a Control endpoint, all this is happening under Interrupt and there is no + thread to suspend. */ + if (ed -> ux_sim_slave_ed_index != 0) + { + + /* Set the ED to TRANSFER status. */ + ed -> ux_sim_slave_ed_status |= UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER; + + /* We should wait for the semaphore to wake us up. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_slave_transfer_request_semaphore, + transfer_request -> ux_slave_transfer_request_timeout); + + /* Reset the ED to TRANSFER status. */ + ed -> ux_sim_slave_ed_status &= ~(ULONG)UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER; + + /* Check the completion code. */ + if (status != UX_SUCCESS) + { + _ux_dcd_sim_slave_transfer_abort(dcd_sim_slave, transfer_request); + transfer_request -> ux_slave_transfer_request_status = UX_TRANSFER_STATUS_COMPLETED; + return(status); + } + + /* Check the transfer request completion code. We may have had a BUS reset or + a device disconnection. */ + if (transfer_request -> ux_slave_transfer_request_completion_code != UX_SUCCESS) + return(transfer_request -> ux_slave_transfer_request_completion_code); + } + + /* Return to caller with success. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_class_dpump_activate.c b/common/core/src/ux_device_class_dpump_activate.c new file mode 100644 index 0000000..1e0d307 --- /dev/null +++ b/common/core/src/ux_device_class_dpump_activate.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dpump_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates the USB dpump device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to dpump command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Device Data Pump Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dpump_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_DPUMP *dpump; +UX_SLAVE_CLASS *class; +UX_SLAVE_ENDPOINT *endpoint; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Store the class instance in the container. */ + dpump = (UX_SLAVE_CLASS_DPUMP *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)dpump; + + /* Now the opposite, store the interface in the class instance. */ + dpump -> ux_slave_class_dpump_interface = interface; + + /* Locate the endpoints. Interrupt for Control and Bulk in/out for Data. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Parse all endpoints. */ + while (endpoint != UX_NULL) + { + + /* Check the endpoint direction, and type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) + { + + /* Look at type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk in endpoint, save it. */ + dpump -> ux_slave_class_dpump_bulkin_endpoint = endpoint; + + } + else + { + /* Look at type for out endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk out endpoint, save it. */ + dpump -> ux_slave_class_dpump_bulkout_endpoint = endpoint; + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + + /* If there is a activate function call it. */ + if (dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_activate != UX_NULL) + { + + /* Invoke the application. */ + dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_activate(dpump); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_DPUMP_ACTIVATE, dpump, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, dpump, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_class_dpump_change.c b/common/core/src/ux_device_class_dpump_change.c new file mode 100644 index 0000000..e4e759a --- /dev/null +++ b/common/core/src/ux_device_class_dpump_change.c @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DPUMP Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dpump_change PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function changes the interface of the DPUMP device */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to dpump command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory */ +/* _ux_device_stack_transfer_all_request_abort */ +/* Abort request */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dpump_change(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_DPUMP *dpump; +UX_SLAVE_CLASS *class; +UX_SLAVE_ENDPOINT *endpoint; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + dpump = (UX_SLAVE_CLASS_DPUMP *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Locate the endpoints. Control and Bulk in/out for data. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Keep the alternate setting in the dpump structure. */ + dpump -> ux_slave_class_dpump_alternate_setting = interface -> ux_slave_interface_descriptor.bAlternateSetting; + + /* If the interface to mount has a non zero alternate setting, the class is really active with + the endpoints active. If the interface reverts to alternate setting 0, it needs to have + the pending transactions terminated. */ + if (interface -> ux_slave_interface_descriptor.bAlternateSetting != 0) + { + + /* Parse all endpoints. */ + while (endpoint != UX_NULL) + { + + /* Check the endpoint direction, and type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) + { + + /* Look at type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk in endpoint, save it. */ + dpump -> ux_slave_class_dpump_bulkin_endpoint = endpoint; + + } + else + { + /* Look at type for out endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk out endpoint, save it. */ + dpump -> ux_slave_class_dpump_bulkout_endpoint = endpoint; + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + + /* Now check if all endpoints have been found. */ + if (dpump -> ux_slave_class_dpump_bulkout_endpoint == UX_NULL || dpump -> ux_slave_class_dpump_bulkin_endpoint == UX_NULL) + + /* Not all endpoints have been found. Major error, do not proceed. */ + return(UX_ERROR); + + /* Reset the endpoint buffers. */ + _ux_utility_memory_set(dpump -> ux_slave_class_dpump_bulkout_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + _ux_utility_memory_set(dpump -> ux_slave_class_dpump_bulkin_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + + /* Keep the alternate setting in the dpump structure. */ + dpump -> ux_slave_class_dpump_alternate_setting = interface -> ux_slave_interface_descriptor.bAlternateSetting; + + /* If there is an activate function call it. */ + if (dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_activate != UX_NULL) + + /* Invoke the application. */ + dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_activate(dpump); + } + else + { + + /* In this case, we are reverting to the Alternate Setting 0. We need to terminate the pending transactions. */ + /* Terminate the transactions pending on the endpoints (bulk in, bulk out). */ + _ux_device_stack_transfer_all_request_abort(dpump -> ux_slave_class_dpump_bulkin_endpoint, UX_TRANSFER_APPLICATION_RESET); + _ux_device_stack_transfer_all_request_abort(dpump -> ux_slave_class_dpump_bulkout_endpoint, UX_TRANSFER_APPLICATION_RESET); + + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_DPUMP_CHANGE, dpump, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, dpump, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_class_dpump_deactivate.c b/common/core/src/ux_device_class_dpump_deactivate.c new file mode 100644 index 0000000..420ae11 --- /dev/null +++ b/common/core/src/ux_device_class_dpump_deactivate.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dpump_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the dpump class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort Abort all transfers */ +/* */ +/* CALLED BY */ +/* */ +/* Device Data Pump Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dpump_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_DPUMP *dpump; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Store the class instance in the container. */ + dpump = (UX_SLAVE_CLASS_DPUMP *) class -> ux_slave_class_instance; + + /* We need the interface to the class. */ + interface = dpump -> ux_slave_class_dpump_interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + } + + /* Terminate the transactions pending on the endpoints. */ + _ux_device_stack_transfer_all_request_abort(endpoint_in, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(endpoint_out, UX_TRANSFER_BUS_RESET); + + /* If there is a deactivate function call it. */ + if (dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_deactivate != UX_NULL) + { + + /* Invoke the application. */ + dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_deactivate(dpump); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_DPUMP_DEACTIVATE, dpump, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(dpump); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_class_dpump_entry.c b/common/core/src/ux_device_class_dpump_entry.c new file mode 100644 index 0000000..d91272d --- /dev/null +++ b/common/core/src/ux_device_class_dpump_entry.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_dpump_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the device dpump class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the dpump interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_dpump_initialize Initialize dpump class */ +/* _ux_device_class_dpump_activate Activate dpump class */ +/* _ux_device_class_dpump_deactivate Deactivate dpump class */ +/* _ux_device_class_dpump_change Alternate setting change */ +/* */ +/* CALLED BY */ +/* */ +/* Device Data Pump Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dpump_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the DPUMP class. */ + status = _ux_device_class_dpump_initialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_SLAVE_CLASS_DPUMP_CLASS) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the dpump thread needs to be activated. */ + status = _ux_device_class_dpump_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_CHANGE: + + /* The change command is used when the host has sent a SET_INTERFACE command + to go from Alternate Setting 0 to 1 or revert to the default mode. */ + status = _ux_device_class_dpump_change(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the dpump thread canceled. */ + status = _ux_device_class_dpump_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* Return the completion status. */ + return(UX_SUCCESS); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/core/src/ux_device_class_dpump_initialize.c b/common/core/src/ux_device_class_dpump_initialize.c new file mode 100644 index 0000000..8096878 --- /dev/null +++ b/common/core/src/ux_device_class_dpump_initialize.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dpump_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB dpump device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to dpump command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Data Pump Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dpump_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_DPUMP *dpump; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_DPUMP_PARAMETER *dpump_parameter; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device dpump class. */ + dpump = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_DPUMP)); + + /* Check for successful allocation. */ + if (dpump == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save the address of the DPUMP instance inside the DPUMP container. */ + class -> ux_slave_class_instance = (VOID *) dpump; + + /* Get the pointer to the application parameters for the cdc class. */ + dpump_parameter = command -> ux_slave_class_command_parameter; + + /* Store the start and stop signals if needed by the application. */ + dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_activate = dpump_parameter -> ux_slave_class_dpump_instance_activate; + dpump -> ux_slave_class_dpump_parameter.ux_slave_class_dpump_instance_deactivate = dpump_parameter -> ux_slave_class_dpump_instance_deactivate; + + /* Return completion status. */ + return(UX_SUCCESS); +} + + diff --git a/common/core/src/ux_device_class_dpump_read.c b/common/core/src/ux_device_class_dpump_read.c new file mode 100644 index 0000000..2a607e4 --- /dev/null +++ b/common/core/src/ux_device_class_dpump_read.c @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DPUMP Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dpump_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the DPUMP class. */ +/* */ +/* INPUT */ +/* */ +/* dpump Address of dpump class */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dpump_read(UX_SLAVE_CLASS_DPUMP *dpump, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +ULONG local_requested_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_DPUMP_READ, dpump, buffer, requested_length, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Locate the OUT endpoint. */ + endpoint = dpump -> ux_slave_class_dpump_bulkout_endpoint; + + /* Check endpoint. If NULL, we have not yet received the proper SET_INTERFACE command. */ + if (endpoint == UX_NULL) + { + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* All DPUMP reading are on the endpoint OUT, from the host. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Reset the actual length. */ + *actual_length = 0; + + /* Set return status to SUCCESS to make certain compilers happy. */ + status = UX_SUCCESS; + + /* Check if we need more transactions. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED && requested_length != 0) + { + + /* Check if we have enough in the local buffer. */ + if (requested_length > UX_SLAVE_REQUEST_DATA_MAX_LENGTH) + + /* We have too much to transfer. */ + local_requested_length = UX_SLAVE_REQUEST_DATA_MAX_LENGTH; + + else + + /* We can proceed with the demanded length. */ + local_requested_length = requested_length; + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, local_requested_length, local_requested_length); + + /* Check the status */ + if (status == UX_SUCCESS) + { + + /* We need to copy the buffer locally. */ + _ux_utility_memory_copy(buffer, transfer_request -> ux_slave_transfer_request_data_pointer, + local_requested_length); + + /* Next buffer address. */ + buffer += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Set the length actually received. */ + *actual_length += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Decrement what left has to be done. */ + requested_length -= transfer_request -> ux_slave_transfer_request_actual_length; + + } + else + + /* We got an error. */ + return(status); + } + + /* Check why we got here, either completion or device was extracted. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_NO_ANSWER); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Device must have been extracted. */ + return (UX_TRANSFER_NO_ANSWER); + } + else + + /* Simply return the last transaction result. */ + return(status); +} + diff --git a/common/core/src/ux_device_class_dpump_thread.c b/common/core/src/ux_device_class_dpump_thread.c new file mode 100644 index 0000000..254ea75 --- /dev/null +++ b/common/core/src/ux_device_class_dpump_thread.c @@ -0,0 +1,165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" +#include "ux_device_stack.h" + +/* Remove compiling warning. */ +VOID _ux_device_class_dpump_thread(ULONG dpump_class); + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dpump_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the dpump class. */ +/* */ +/* INPUT */ +/* */ +/* class Address of dpump class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_thread_suspend Suspend thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_dpump_thread(ULONG dpump_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_DPUMP *dpump; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; +UINT status; +ULONG length; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* Cast properly the dpump instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, dpump_class) + + /* Get the dpump instance from this class container. */ + dpump = (UX_SLAVE_CLASS_DPUMP *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This is the first time we are activated. We need the interface to the class. */ + interface = dpump -> ux_slave_class_dpump_interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + } + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* We prepare to receive from the host on the OUT endpoint. */ + transfer_request = &endpoint_out -> ux_slave_endpoint_transfer_request; + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_DPUMP_PACKET_SIZE, UX_DEVICE_CLASS_DPUMP_PACKET_SIZE); + + /* Check the status */ + if (status == UX_SUCCESS) + { + + /* Obtain the length of the transaction. */ + length = transfer_request -> ux_slave_transfer_request_actual_length; + + /* Copy the buffer to the target in endpoint. */ + _ux_utility_memory_copy(endpoint_in -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer, + endpoint_out -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer, + length); + + /* Now we send the packet back to the host. On the endpoint In. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Sends the data payload back to the caller. */ + status = _ux_device_stack_transfer_request(transfer_request, length, length); + + /* Check error code. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + } + + /* We need to suspend ourselves. We will be resumed by the + device enumeration module. */ + _ux_utility_thread_suspend(&class -> ux_slave_class_thread); + } +} + diff --git a/common/core/src/ux_device_class_dpump_write.c b/common/core/src/ux_device_class_dpump_write.c new file mode 100644 index 0000000..676bf69 --- /dev/null +++ b/common/core/src/ux_device_class_dpump_write.c @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DPUMP Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dpump.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dpump_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the DPUMP class. */ +/* */ +/* INPUT */ +/* */ +/* dpump Address of dpump class */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_copy */ +/* _ux_device_stack_transfer_request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dpump_write(UX_SLAVE_CLASS_DPUMP *dpump, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +ULONG local_requested_length; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_DPUMP_WRITE, dpump, buffer, requested_length, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Locate the IN endpoint. */ + endpoint = dpump -> ux_slave_class_dpump_bulkin_endpoint; + + /* Check endpoint. If NULL, we have not yet received the proper SET_INTERFACE command. */ + if (endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* We are writing to the IN endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Reset the actual length. */ + *actual_length = 0; + + /* Set return status to SUCCESS to make certain compilers happy. */ + status = UX_SUCCESS; + + /* Check if we need more transactions. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED && requested_length != 0) + { + + /* Check if we have enough in the local buffer. */ + if (requested_length > UX_SLAVE_REQUEST_DATA_MAX_LENGTH) + + /* We have too much to transfer. */ + local_requested_length = UX_SLAVE_REQUEST_DATA_MAX_LENGTH; + + else + + /* We can proceed with the demanded length. */ + local_requested_length = requested_length; + + /* On a out, we copy the buffer to the caller. Not very efficient but it makes the API + easier. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + buffer, local_requested_length); + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, local_requested_length, local_requested_length); + + /* Check the status */ + if (status == UX_SUCCESS) + { + + /* Next buffer address. */ + buffer += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Set the length actually received. */ + *actual_length += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Decrement what left has to be done. */ + requested_length -= transfer_request -> ux_slave_transfer_request_actual_length; + + } + + else + + /* We had an error, abort. */ + return(status); + } + + /* Check why we got here, either completion or device was extracted. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_NO_ANSWER); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Device must have been extracted. */ + return (UX_TRANSFER_NO_ANSWER); + } + else + + /* Simply return the last transaction result. */ + return(status); + +} + diff --git a/common/core/src/ux_device_stack_alternate_setting_get.c b/common/core/src/ux_device_stack_alternate_setting_get.c new file mode 100644 index 0000000..8d26f59 --- /dev/null +++ b/common/core/src/ux_device_stack_alternate_setting_get.c @@ -0,0 +1,133 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_alternate_setting_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the alternate setting for a specific interface. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Pointer to endpoint */ +/* interface_value Interface value */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_alternate_setting_get(ULONG interface_value) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_ALTERNATE_SETTING_GET, interface_value, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* If the device was in the configured state, there may be interfaces + attached to the configuration. */ + if (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Obtain the pointer to the first interface attached. */ + interface = device -> ux_slave_device_first_interface; + + /* Start parsing each interface. */ + while (interface != UX_NULL) + { + + /* Check if this is the interface we have an inquiry for. */ + if (interface -> ux_slave_interface_descriptor.bInterfaceNumber == interface_value) + { + + /* Get the control endpoint of the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Set the value of the alternate setting in the buffer. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = + (UCHAR) interface -> ux_slave_interface_descriptor.bAlternateSetting; + + /* Setup the length appropriately. */ + transfer_request -> ux_slave_transfer_request_requested_length = 1; + + /* Set the phase of the transfer to data out. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Send the descriptor with the appropriate length to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, 1, 1); + + /* Return the function status. */ + return(status); + } + + /* Get the next interface. */ + interface = interface -> ux_slave_interface_next_interface; + } + } + + /* Return error completion. */ + return(UX_ERROR); +} + diff --git a/common/core/src/ux_device_stack_alternate_setting_set.c b/common/core/src/ux_device_stack_alternate_setting_set.c new file mode 100644 index 0000000..5e67f76 --- /dev/null +++ b/common/core/src/ux_device_stack_alternate_setting_set.c @@ -0,0 +1,408 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_alternate_setting_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the alternate setting for a specific interface. */ +/* The previous interface is unmounted and all the endpoints */ +/* associated with the alternate setting are mounted. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Pointer to endpoint */ +/* interface_value Interface value */ +/* alternate_setting_value Alternate setting value */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_device_stack_transfer_all_request_abort */ +/* Abort transfer */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_alternate_setting_set(ULONG interface_value, ULONG alternate_setting_value) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *device_framework; +ULONG device_framework_length; +ULONG descriptor_length; +UCHAR descriptor_type; +UX_CONFIGURATION_DESCRIPTOR configuration_descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_ENDPOINT *next_endpoint; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_ENDPOINT *endpoint_link; +ULONG endpoints_pool_number; +UX_SLAVE_CLASS_COMMAND class_command; +UX_SLAVE_CLASS *class; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_ALTERNATE_SETTING_SET, interface_value, alternate_setting_value, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave->ux_system_slave_dcd; + + /* We may have multiple configurations! */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework; + device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Protocol error must be reported when it's unconfigured */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + return UX_FUNCTION_NOT_SUPPORTED; + + /* Find the current interface. */ + interface = device -> ux_slave_device_first_interface; + + /* Scan all interfaces if any. */ + while (interface != UX_NULL) + { + + if (interface -> ux_slave_interface_descriptor.bInterfaceNumber == interface_value) + break; + else + interface = interface -> ux_slave_interface_next_interface; + } + + /* We must have found the interface pointer for the interface value + requested by the caller. */ + if (interface == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_INTERFACE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_INTERFACE_HANDLE_UNKNOWN); + } + + /* If the host is requesting a change of alternate setting to the current one, + we do not need to do any work. */ + if (interface -> ux_slave_interface_descriptor.bAlternateSetting == alternate_setting_value) + return(UX_SUCCESS); + + /* Parse the device framework and locate a configuration descriptor. */ + while (device_framework_length != 0) + { + + /* Get the length of the current descriptor. */ + descriptor_length = (ULONG) *device_framework; + + /* And its length. */ + descriptor_type =* (device_framework + 1); + + /* Check if this is a configuration descriptor. */ + if (descriptor_type == UX_CONFIGURATION_DESCRIPTOR_ITEM) + { + + /* Parse the descriptor in something more readable. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, + (UCHAR *) &configuration_descriptor); + + /* Now we need to check the configuration value. */ + if (configuration_descriptor.bConfigurationValue == device -> ux_slave_device_configuration_selected) + { + + /* Limit the search in current configuration descriptor. */ + device_framework_length = configuration_descriptor.wTotalLength; + + /* We have found the configuration value that was selected by the host + We need to scan all the interface descriptors following this + configuration descriptor and locate the interface for which the alternate + setting must be changed. */ + while (device_framework_length != 0) + { + + /* Get the length of the current descriptor. */ + descriptor_length = (ULONG) *device_framework; + + /* And its type. */ + descriptor_type = *(device_framework + 1); + + /* Check if this is an interface descriptor. */ + if (descriptor_type == UX_INTERFACE_DESCRIPTOR_ITEM) + { + + /* Parse the descriptor in something more readable. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, + (UCHAR *) &interface_descriptor); + + /* Check if this is the interface we are searching. */ + if (interface_descriptor.bInterfaceNumber == interface_value && + interface_descriptor.bAlternateSetting == alternate_setting_value) + { + + /* We have found the right interface and alternate setting. Before + we mount all the endpoints for this interface, we need to + unmount the endpoints associated with the previous alternate setting. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + while (endpoint != UX_NULL) + { + + /* Abort any pending transfer. */ + _ux_device_stack_transfer_all_request_abort(endpoint, UX_TRANSFER_BUS_RESET); + + /* The device controller must be called to destroy the endpoint. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_DESTROY_ENDPOINT, (VOID *) endpoint); + + /* Get the next endpoint. */ + next_endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + + /* Free the endpoint. */ + endpoint -> ux_slave_endpoint_status = UX_UNUSED; + + /* Make sure the endpoint instance is now cleaned up. */ + endpoint -> ux_slave_endpoint_state = 0; + endpoint -> ux_slave_endpoint_next_endpoint = UX_NULL; + endpoint -> ux_slave_endpoint_interface = UX_NULL; + endpoint -> ux_slave_endpoint_device = UX_NULL; + + /* Now we refresh the endpoint pointer. */ + endpoint = next_endpoint; + } + + /* Now clear the interface endpoint entry. */ + interface -> ux_slave_interface_first_endpoint = UX_NULL; + + /* Point beyond the interface descriptor. */ + device_framework_length -= (ULONG) *device_framework; + device_framework += (ULONG) *device_framework; + + /* Parse the device framework and locate endpoint descriptor(s). */ + while (device_framework_length != 0) + { + + /* Get the length of the current descriptor. */ + descriptor_length = (ULONG) *device_framework; + + /* And its type. */ + descriptor_type = *(device_framework + 1); + + /* Check if this is an endpoint descriptor. */ + switch(descriptor_type) + { + + case UX_ENDPOINT_DESCRIPTOR_ITEM: + + /* Find a free endpoint in the pool and hook it to the + existing interface after it's created by DCD. */ + endpoint = device -> ux_slave_device_endpoints_pool; + endpoints_pool_number = device -> ux_slave_device_endpoints_pool_number; + while (endpoints_pool_number != 0) + { + /* Check if this endpoint is free. */ + if (endpoint -> ux_slave_endpoint_status == UX_UNUSED) + { + /* Mark this endpoint as used now. */ + endpoint -> ux_slave_endpoint_status = UX_USED; + break; + } + + /* Try the next endpoint. */ + endpoint++; + + /* Decrement the number of endpoints to scan from the pool. */ + endpoints_pool_number--; + } + + /* Did we find a free endpoint ? */ + if (endpoints_pool_number == 0) + return(UX_MEMORY_INSUFFICIENT); + + /* Parse the descriptor in something more readable. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_endpoint_descriptor_structure, + UX_ENDPOINT_DESCRIPTOR_ENTRIES, + (UCHAR *) &endpoint -> ux_slave_endpoint_descriptor); + + /* Now we create a transfer request to accept transfer on this endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* We store the endpoint in the transfer request as well. */ + transfer_request -> ux_slave_transfer_request_endpoint = endpoint; + + /* By default the timeout is infinite on request. */ + transfer_request -> ux_slave_transfer_request_timeout = UX_WAIT_FOREVER; + + /* Attach the interface to the endpoint. */ + endpoint -> ux_slave_endpoint_interface = interface; + + /* Attach the device to the endpoint. */ + endpoint -> ux_slave_endpoint_device = device; + + /* Create the endpoint at the DCD level. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_CREATE_ENDPOINT, (VOID *) endpoint); + + /* Do a sanity check on endpoint creation. */ + if (status != UX_SUCCESS) + { + + /* Error was returned, endpoint cannot be created. */ + endpoint -> ux_slave_endpoint_status = UX_UNUSED; + return(status); + } + + /* Attach this endpoint to the end of the endpoint chain. */ + if (interface -> ux_slave_interface_first_endpoint == UX_NULL) + { + + interface -> ux_slave_interface_first_endpoint = endpoint; + } + else + { + /* Multiple endpoints exist, so find the end of the chain. */ + endpoint_link = interface -> ux_slave_interface_first_endpoint; + while (endpoint_link -> ux_slave_endpoint_next_endpoint != UX_NULL) + endpoint_link = endpoint_link -> ux_slave_endpoint_next_endpoint; + endpoint_link -> ux_slave_endpoint_next_endpoint = endpoint; + } + + break; + + case UX_CONFIGURATION_DESCRIPTOR_ITEM: + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* We have found a new configuration or interface descriptor, this is the end of the current + interface. The search for the endpoints must be terminated as if it was the end of the + entire descriptor. */ + device_framework_length = descriptor_length; + + break; + + + default: + + /* We have found another descriptor embedded in the interface. Ignore it. */ + break; + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* The interface descriptor in the current class must be changed to the new alternate setting. */ + _ux_utility_memory_copy(&interface -> ux_slave_interface_descriptor, &interface_descriptor, sizeof(UX_INTERFACE_DESCRIPTOR)); + + /* Get the class for the interface. */ + class = _ux_system_slave -> ux_system_slave_interface_class_array[interface -> ux_slave_interface_descriptor.bInterfaceNumber]; + + /* Check if class driver is available. */ + if (class == UX_NULL || class -> ux_slave_class_status == UX_UNUSED) + { + + return (UX_NO_CLASS_MATCH); + } + + /* The interface attached to this configuration must be changed at the class + level. */ + class_command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_CHANGE; + class_command.ux_slave_class_command_interface = (VOID *) interface; + + /* And store it. */ + class_command.ux_slave_class_command_class_ptr = class; + + /* We can now memorize the interface pointer associated with this class. */ + class -> ux_slave_class_interface = interface; + + /* We have found a potential candidate. Call this registered class entry function to change the alternate setting. */ + status = class -> ux_slave_class_entry_function(&class_command); + + /* We are done here. */ + return(status); + } + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* In case alter setting not found, report protocol error. */ + break; + } + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* Return error completion. */ + return(UX_ERROR); +} + diff --git a/common/core/src/ux_device_stack_class_register.c b/common/core/src/ux_device_stack_class_register.c new file mode 100644 index 0000000..b65b725 --- /dev/null +++ b/common/core/src/ux_device_stack_class_register.c @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_class_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a slave class to the slave stack. */ +/* */ +/* Note: The C string of class_name must be NULL-terminated and the */ +/* length of it (without the NULL-terminator itself) must be no larger */ +/* than UX_MAX_CLASS_NAME_LENGTH. */ +/* */ +/* INPUT */ +/* */ +/* class_name Name of class */ +/* class_function_entry Class entry function */ +/* configuration_number Configuration # for this class*/ +/* interface_number Interface # for this class */ +/* parameter Parameter specific for class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check C string and return */ +/* its length if null-terminated */ +/* _ux_utility_memory_copy Memory copy */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_class_register(UCHAR *class_name, + UINT (*class_entry_function)(struct UX_SLAVE_CLASS_COMMAND_STRUCT *), + ULONG configuration_number, + ULONG interface_number, + VOID *parameter) +{ + +UX_SLAVE_CLASS *class; +ULONG class_index; +UINT status; +UX_SLAVE_CLASS_COMMAND command; +UINT class_name_length = 0; + + + /* Get the length of the class name (exclude null-terminator). */ + status = _ux_utility_string_length_check(class_name, &class_name_length, UX_MAX_CLASS_NAME_LENGTH); + if (status) + return(status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_CLASS_REGISTER, class_name, interface_number, parameter, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* We need to parse the class table to find an empty spot. */ + class = _ux_system_slave -> ux_system_slave_class_array; + for (class_index = 0; class_index < _ux_system_slave -> ux_system_slave_max_class; class_index++) + { + + /* Check if this class is already used. */ + if (class -> ux_slave_class_status == UX_UNUSED) + { + + /* We have found a free container for the class. Copy the name (with null-terminator). */ + _ux_utility_memory_copy(class -> ux_slave_class_name, class_name, class_name_length + 1); + + /* Memorize the entry function of this class. */ + class -> ux_slave_class_entry_function = class_entry_function; + + /* Memorize the pointer to the application parameter. */ + class -> ux_slave_class_interface_parameter = parameter; + + /* Memorize the configuration number on which this instance will be called. */ + class -> ux_slave_class_configuration_number = configuration_number; + + /* Memorize the interface number on which this instance will be called. */ + class -> ux_slave_class_interface_number = interface_number; + + /* Build all the fields of the Class Command to initialize the class. */ + command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_INITIALIZE; + command.ux_slave_class_command_parameter = parameter; + command.ux_slave_class_command_class_ptr = class; + + /* Call the class initialization routine. */ + status = class_entry_function(&command); + + /* Check the status. */ + if (status != UX_SUCCESS) + return(status); + + /* Make this class used now. */ + class -> ux_slave_class_status = UX_USED; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + + /* Move to the next class. */ + class++; + } + + /* No more entries in the class table. */ + return(UX_MEMORY_INSUFFICIENT); +} + diff --git a/common/core/src/ux_device_stack_class_unregister.c b/common/core/src/ux_device_stack_class_unregister.c new file mode 100644 index 0000000..9fa2d1c --- /dev/null +++ b/common/core/src/ux_device_stack_class_unregister.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_class_unregister PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a slave class to the slave stack. */ +/* */ +/* Note: The C string of class_name must be NULL-terminated and the */ +/* length of it (without the NULL-terminator itself) must be no larger */ +/* than UX_MAX_CLASS_NAME_LENGTH. */ +/* */ +/* INPUT */ +/* */ +/* class_name Name of class */ +/* class_function_entry Class entry function */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check C string and return */ +/* its length if null-terminated */ +/* _ux_utility_memory_compare Memory compare */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_class_unregister(UCHAR *class_name, + UINT (*class_entry_function)(struct UX_SLAVE_CLASS_COMMAND_STRUCT *)) +{ + +UX_SLAVE_CLASS *class; +ULONG class_index; +UINT status; +UX_SLAVE_CLASS_COMMAND command; +UINT class_name_length = 0; + + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_CLASS_UNREGISTER, class_name, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the length of the class name (exclude null-terminator). */ + status = _ux_utility_string_length_check(class_name, &class_name_length, UX_MAX_CLASS_NAME_LENGTH); + if (status) + return(status); + + /* We need to parse the class table to find the right class. */ + class = _ux_system_slave -> ux_system_slave_class_array; + for (class_index = 0; class_index < _ux_system_slave -> ux_system_slave_max_class; class_index++) + { + + /* Check if this class is the right one. */ + if (class -> ux_slave_class_status == UX_USED) + { + + /* We have found a used container with a class. Compare the name (include null-terminator). */ + if (_ux_utility_memory_compare(class -> ux_slave_class_name, class_name, class_name_length + 1) == UX_SUCCESS) + { + + /* Build all the fields of the Class Command to uninitialize the class. */ + command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_UNINITIALIZE; + command.ux_slave_class_command_class_ptr = class; + + /* Call the class uninitialization routine. */ + status = class_entry_function(&command); + + /* Check the status. */ + if (status != UX_SUCCESS) + return(status); + + /* Make this class unused now. */ + class -> ux_slave_class_status = UX_UNUSED; + + /* Erase the instance of the class. */ + class -> ux_slave_class_instance = UX_NULL; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + + /* Move to the next class. */ + class++; + } + + /* No class match. */ + return(UX_NO_CLASS_MATCH); +} + diff --git a/common/core/src/ux_device_stack_clear_feature.c b/common/core/src/ux_device_stack_clear_feature.c new file mode 100644 index 0000000..02356d0 --- /dev/null +++ b/common/core/src/ux_device_stack_clear_feature.c @@ -0,0 +1,175 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_clear_feature PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function clears a specific feature (Device, Interface, */ +/* Endpoint ....). */ +/* */ +/* INPUT */ +/* */ +/* request_type Request type */ +/* request_value Request value */ +/* request_index Request index */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* */ +/* CALLED BY */ +/* */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_clear_feature(ULONG request_type, ULONG request_value, ULONG request_index) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_ENDPOINT *endpoint_target; + + UX_PARAMETER_NOT_USED(request_value); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_CLEAR_FEATURE, request_type, request_value, request_index, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint for the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* The request can be for either the device or the endpoint. */ + switch (request_type & UX_REQUEST_TARGET) + { + + case UX_REQUEST_TARGET_DEVICE: + + /* Check if we have a DEVICE_REMOTE_WAKEUP Feature. */ + if (request_value == UX_REQUEST_FEATURE_DEVICE_REMOTE_WAKEUP) + { + + /* Check if we have the capability. */ + if (_ux_system_slave -> ux_system_slave_remote_wakeup_capability) + { + + /* Disable the feature. */ + _ux_system_slave -> ux_system_slave_remote_wakeup_enabled = UX_FALSE; + } + + else + + /* Protocol error. */ + return (UX_FUNCTION_NOT_SUPPORTED); + } + + break; + + case UX_REQUEST_TARGET_ENDPOINT: + + /* The only clear feature for endpoint is ENDPOINT_STALL. This clears + the endpoint of the stall situation and resets its data toggle. + We need to find the endpoint through the interface(s). */ + interface = device -> ux_slave_device_first_interface; + + while (interface != UX_NULL) + { + + /* Get the first endpoint for this interface. */ + endpoint_target = interface -> ux_slave_interface_first_endpoint; + + /* Parse all the endpoints. */ + while (endpoint_target != UX_NULL) + { + + /* Check the endpoint index. */ + if (endpoint_target -> ux_slave_endpoint_descriptor.bEndpointAddress == request_index) + { + + /* Reset the endpoint. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_RESET_ENDPOINT, endpoint_target); + + /* Mark its state now. */ + endpoint_target -> ux_slave_endpoint_state = UX_ENDPOINT_RESET; + + /* Return the function status. */ + return(UX_SUCCESS); + } + + /* Next endpoint. */ + endpoint_target = endpoint_target -> ux_slave_endpoint_next_endpoint; + } + + /* Next interface. */ + interface = interface -> ux_slave_interface_next_interface; + } + /* Intentional fallthrough and go into the default case. */ + /* fall through */ + + /* We get here when the endpoint is wrong. Should not happen though. */ + default: + + /* We stall the command. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + + /* No more work to do here. The command failed but the upper layer does not depend on it. */ + return(UX_SUCCESS); + } + + /* Return the function status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_stack_configuration_get.c b/common/core/src/ux_device_stack_configuration_get.c new file mode 100644 index 0000000..f069038 --- /dev/null +++ b/common/core/src/ux_device_stack_configuration_get.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_configuration_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the current configuration for the device. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_configuration_get(VOID) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UINT status; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint for the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Set the value of the configuration in the buffer. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = + (UCHAR) device -> ux_slave_device_configuration_selected; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_CONFIGURATION_GET, device -> ux_slave_device_configuration_selected, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Set the phase of the transfer to data out. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Send the descriptor with the appropriate length to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, 1, 1); + + /* Return the function status. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_configuration_set.c b/common/core/src/ux_device_stack_configuration_set.c new file mode 100644 index 0000000..6c55218 --- /dev/null +++ b/common/core/src/ux_device_stack_configuration_set.c @@ -0,0 +1,374 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_configuration_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the configuration from the host and will enable */ +/* the default alternate setting 0 for all the interfaces attached to */ +/* this configuration. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Pointer to endpoint */ +/* configuration_value Configuration selected */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_entry_function) Device class entry function */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_device_stack_interface_delete Delete interface */ +/* _ux_device_stack_interface_set Set interface */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_configuration_set(ULONG configuration_value) +{ + +UX_SLAVE_DCD *dcd; +UCHAR * device_framework; +ULONG device_framework_length; +ULONG descriptor_length; +UCHAR descriptor_type; +UX_CONFIGURATION_DESCRIPTOR configuration_descriptor = { 0 }; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_INTERFACE *next_interface; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS *current_class = UX_NULL; +UX_SLAVE_CLASS_COMMAND class_command; +UX_SLAVE_DEVICE *device; +ULONG iad_flag; +ULONG iad_first_interface = 0; +ULONG iad_number_interfaces = 0; +ULONG class_index; + + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_CONFIGURATION_SET, configuration_value, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Reset the IAD flag. */ + iad_flag = UX_FALSE; + + /* If the configuration value is already selected, keep it. */ + if (device -> ux_slave_device_configuration_selected == configuration_value) + return(UX_SUCCESS); + + /* We may have multiple configurations !, the index will tell us what + configuration descriptor we need to return. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework; + device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length; + + /* Parse the device framework and locate a configuration descriptor. */ + while (device_framework_length != 0) + { + /* Get the length of the current descriptor. */ + descriptor_length = (ULONG) *device_framework; + + /* And its type. */ + descriptor_type = *(device_framework + 1); + + /* Check if this is a configuration descriptor. */ + if (descriptor_type == UX_CONFIGURATION_DESCRIPTOR_ITEM) + { + /* Parse the descriptor in something more readable. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, + (UCHAR *) &configuration_descriptor); + + /* Now we need to check the configuration value. It has + to be the same as the one specified in the setup function. */ + if (configuration_descriptor.bConfigurationValue == configuration_value) + /* The configuration is found. */ + break; + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* Configuration not found. */ + if (device_framework_length == 0 && configuration_value != 0) + return(UX_ERROR); + + /* We unmount the configuration if there is previous configuration selected. */ + if (device -> ux_slave_device_configuration_selected) + { + + /* Get the pointer to the first interface. */ + interface = device -> ux_slave_device_first_interface; + + /* Deactivate all the interfaces if any. */ + while (interface != UX_NULL) + { + + /* Build all the fields of the Class Command. */ + class_command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_DEACTIVATE; + class_command.ux_slave_class_command_interface = (VOID *) interface; + + /* Get the pointer to the class container of this interface. */ + class = interface -> ux_slave_interface_class; + + /* Store the class container. */ + class_command.ux_slave_class_command_class_ptr = class; + + /* If there is a class container for this instance, deactivate it. */ + if (class != UX_NULL) + + /* Call the class with the DEACTIVATE signal. */ + class -> ux_slave_class_entry_function(&class_command); + + /* Get the next interface. */ + next_interface = interface -> ux_slave_interface_next_interface; + + /* Remove the interface and all endpoints associated with it. */ + _ux_device_stack_interface_delete(interface); + + /* Now we refresh the interface pointer. */ + interface = next_interface; + } + } + + /* No configuration is selected. */ + device -> ux_slave_device_configuration_selected = 0; + + /* Mark the device as attached now. */ + device -> ux_slave_device_state = UX_DEVICE_ATTACHED; + + /* The DCD needs to update the device state too. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_CHANGE_STATE, (VOID *) UX_DEVICE_ATTACHED); + + /* If the host tries to unconfigure, we are done. */ + if (configuration_value == 0) + return(UX_SUCCESS); + + /* Memorize the configuration selected. */ + device -> ux_slave_device_configuration_selected = configuration_value; + + /* We have found the configuration value requested by the host. + Create the configuration descriptor and attach it to the device. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, + (UCHAR *) &device -> ux_slave_device_configuration_descriptor); + + /* Configuration character D6 is for Self-powered */ + _ux_system_slave -> ux_system_slave_power_state = (configuration_descriptor.bmAttributes & 0x40) ? UX_DEVICE_SELF_POWERED : UX_DEVICE_BUS_POWERED; + + /* Configuration character D5 is for Remote Wakeup */ + _ux_system_slave -> ux_system_slave_remote_wakeup_capability = (configuration_descriptor.bmAttributes & 0x20) ? UX_TRUE : UX_FALSE; + + /* Search only in current configuration */ + device_framework_length = configuration_descriptor.wTotalLength; + + /* We need to scan all the interface descriptors following this + configuration descriptor and enable all endpoints associated + with the default alternate setting of each interface. */ + while (device_framework_length != 0) + { + + /* Get the length of the current descriptor. */ + descriptor_length = (ULONG) *device_framework; + + /* And its type. */ + descriptor_type = *(device_framework + 1); + + /* Check if this is an interface association descriptor. */ + if(descriptor_type == UX_INTERFACE_ASSOCIATION_DESCRIPTOR_ITEM) + { + + /* Set the IAD flag. */ + iad_flag = UX_TRUE; + + /* Get the first interface we have in the IAD. */ + iad_first_interface = (ULONG) *(device_framework + 2); + + /* Get the number of interfaces we have in the IAD. */ + iad_number_interfaces = (ULONG) *(device_framework + 3); + } + + /* Check if this is an interface descriptor. */ + if(descriptor_type == UX_INTERFACE_DESCRIPTOR_ITEM) + { + + /* Parse the descriptor in something more readable. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, + (UCHAR *) &interface_descriptor); + + /* If the alternate setting is 0 for this interface, we need to + memorize its class association and start it. */ + if (interface_descriptor.bAlternateSetting == 0) + { + + /* Are we in a IAD scenario ? */ + if (iad_flag == UX_TRUE) + { + + /* Check if this is the first interface from the IAD. In this case, + we need to match a class to this interface. */ + if (interface_descriptor.bInterfaceNumber == iad_first_interface) + { + + /* First interface. Scan the list of classes to find a match. */ + class = _ux_system_slave -> ux_system_slave_class_array; + + /* Parse all the class drivers. */ + for (class_index = 0; class_index < _ux_system_slave -> ux_system_slave_max_class; class_index++) + { + + /* Check if this class driver is used. */ + if (class -> ux_slave_class_status == UX_USED) + { + + /* Check if this is the same interface for the same configuration. */ + if ((interface_descriptor.bInterfaceNumber == class -> ux_slave_class_interface_number) && + (configuration_value == class -> ux_slave_class_configuration_number)) + { + + /* Memorize the class in the class/interface array. */ + _ux_system_slave -> ux_system_slave_interface_class_array[interface_descriptor.bInterfaceNumber] = class; + + /* And again as the current class. */ + current_class = class; + + /* We are done here. */ + break; + } + } + + /* Move to the next registered class. */ + class++; + } + } + else + + /* Memorize the class in the class/interface array. We use the current class. */ + _ux_system_slave -> ux_system_slave_interface_class_array[interface_descriptor.bInterfaceNumber] = current_class; + + /* Decrement the number of interfaces found in the same IAD. */ + iad_number_interfaces--; + + /* If none are left, get out of the IAD state machine. */ + if (iad_number_interfaces == 0) + + /* We have exhausted the interfaces within the IAD. */ + iad_flag = UX_FALSE; + + } + else + { + + /* First interface. Scan the list of classes to find a match. */ + class = _ux_system_slave -> ux_system_slave_class_array; + + /* Parse all the class drivers. */ + for (class_index = 0; class_index < _ux_system_slave -> ux_system_slave_max_class; class_index++) + { + + /* Check if this class driver is used. */ + if (class -> ux_slave_class_status == UX_USED) + { + + /* Check if this is the same interface for the same configuration. */ + if ((interface_descriptor.bInterfaceNumber == class -> ux_slave_class_interface_number) && + (configuration_value == class -> ux_slave_class_configuration_number)) + { + + /* Memorize the class in the class/interface array. */ + _ux_system_slave -> ux_system_slave_interface_class_array[interface_descriptor.bInterfaceNumber] = class; + + /* We are done here. */ + break; + } + } + + /* Move to the next registered class. */ + class++; + } + } + + /* Set the interface. */ + _ux_device_stack_interface_set(device_framework, device_framework_length, 0); + } + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* Mark the device as configured now. */ + device -> ux_slave_device_state = UX_DEVICE_CONFIGURED; + + /* The DCD needs to update the device state too. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_CHANGE_STATE, (VOID *) UX_DEVICE_CONFIGURED); + + /* Configuration mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_stack_control_request_process.c b/common/core/src/ux_device_stack_control_request_process.c new file mode 100644 index 0000000..3fbf671 --- /dev/null +++ b/common/core/src/ux_device_stack_control_request_process.c @@ -0,0 +1,312 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_control_request_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the DCD when the device has received a */ +/* SETUP packet. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_entry_function) Device class entry function */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_device_stack_alternate_setting_get */ +/* Get alternate settings */ +/* _ux_device_stack_alternate_setting_set */ +/* Set alternate settings */ +/* _ux_device_stack_clear_feature Clear feature */ +/* _ux_device_stack_configuration_get Get configuration */ +/* _ux_device_stack_configuration_set Set configuration */ +/* _ux_device_stack_descriptor_send Send descriptor */ +/* _ux_device_stack_get_status Get status */ +/* _ux_device_stack_set_feature Set feature */ +/* _ux_utility_short_get Get short value */ +/* */ +/* CALLED BY */ +/* */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_control_request_process(UX_SLAVE_TRANSFER *transfer_request) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_COMMAND class_command; +ULONG request_type; +ULONG request; +ULONG request_value; +ULONG request_index; +ULONG request_length; +ULONG class_index; +UINT status = UX_ERROR; +UX_SLAVE_ENDPOINT *endpoint; +ULONG application_data_length; + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Ensure that the Setup request has been received correctly. */ + if (transfer_request -> ux_slave_transfer_request_completion_code == UX_SUCCESS) + { + + /* Seems so far, the Setup request is valid. Extract all fields of + the request. */ + request_type = *transfer_request -> ux_slave_transfer_request_setup; + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + request_value = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE); + request_index = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_INDEX); + request_length = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); + + /* Filter for GET_DESCRIPTOR/SET_DESCRIPTOR commands. If the descriptor to be returned is not a standard descriptor, + treat the command as a CLASS command. */ + if ((request == UX_GET_DESCRIPTOR || request == UX_SET_DESCRIPTOR) && (((request_value >> 8) & UX_REQUEST_TYPE) != UX_REQUEST_TYPE_STANDARD)) + { + + /* This request is to be handled by the class layer. */ + request_type &= (UINT)~UX_REQUEST_TYPE; + request_type |= UX_REQUEST_TYPE_CLASS; + } + + /* Check if there is a vendor registered function at the application layer. If the request + is VENDOR and the request match, pass the request to the application. */ + if ((request_type & UX_REQUEST_TYPE) == UX_REQUEST_TYPE_VENDOR) + { + + /* Check the request demanded and compare it to the application registered one. */ + if (request == _ux_system_slave -> ux_system_slave_device_vendor_request) + { + + /* This is a Microsoft extended function. It happens before the device is configured. + The request is passed to the application directly. */ + status = _ux_system_slave -> ux_system_slave_device_vendor_request_function(request, request_value, + request_index, request_length, + transfer_request -> ux_slave_transfer_request_data_pointer, + &application_data_length); + + /* Check the status from the application. */ + if (status == UX_SUCCESS) + { + + /* Get the control endpoint associated with the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Set the direction to OUT. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Perform the data transfer. */ + _ux_device_stack_transfer_request(transfer_request, request_length, application_data_length); + + /* We are done here. */ + return(UX_SUCCESS); + } + else + { + + /* The application did not like the vendor command format, stall the control endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + + /* We are done here. */ + return(UX_SUCCESS); + } + } + } + + /* Check the destination of the request. If the request is of type CLASS or VENDOR_SPECIFIC, + the function has to be passed to the class layer. */ + if (((request_type & UX_REQUEST_TYPE) == UX_REQUEST_TYPE_CLASS) || + ((request_type & UX_REQUEST_TYPE) == UX_REQUEST_TYPE_VENDOR)) + { + + /* Build all the fields of the Class Command. */ + class_command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_REQUEST; + + /* We need to find which class this request is for. */ + for (class_index = 0; class_index < UX_MAX_SLAVE_INTERFACES; class_index ++) + { + + /* Is the request target to an interface? */ + if ((request_type & UX_REQUEST_TARGET) == UX_REQUEST_TARGET_INTERFACE) + { + + /* Yes, so the request index contains the index of the interface + the request is for. So if the current index does not match + the request index, we should go to the next one. */ + if ((request_index & 0xFF) != class_index) + continue; + } + + /* Get the class for the interface. */ + class = _ux_system_slave -> ux_system_slave_interface_class_array[class_index]; + + /* If class is not ready, try next. */ + if (class == UX_NULL) + continue; + + /* Memorize the class in the command. */ + class_command.ux_slave_class_command_class_ptr = class; + + /* We have found a potential candidate. Call this registered class entry function. */ + status = class -> ux_slave_class_entry_function(&class_command); + + /* The status simply tells us if the registered class handled the + command - if there was an issue processing the command, it would've + stalled the control endpoint, notifying the host (and not us). */ + if (status == UX_SUCCESS) + + /* We are done, break the loop! */ + break; + + /* Not handled, try next. */ + } + + /* If no class handled the command, then we have an error here. */ + if (status != UX_SUCCESS) + + /* We stall the command (request not supported). */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + + /* We are done for class/vendor request. */ + return(status); + } + + /* At this point, the request must be a standard request that the device stack should handle. */ + switch (request) + { + + case UX_GET_STATUS: + + status = _ux_device_stack_get_status(request_type, request_index, request_length); + break; + + case UX_CLEAR_FEATURE: + + status = _ux_device_stack_clear_feature(request_type, request_value, request_index); + break; + + case UX_SET_FEATURE: + + status = _ux_device_stack_set_feature(request_type, request_value, request_index); + break; + + case UX_SET_ADDRESS: + + /* Memorize the address. Some controllers memorize the address here. Some don't. */ + dcd -> ux_slave_dcd_device_address = request_value; + + /* Force the new address. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_SET_DEVICE_ADDRESS, (VOID *) (ALIGN_TYPE) request_value); + break; + + case UX_GET_DESCRIPTOR: + + status = _ux_device_stack_descriptor_send(request_value, request_index, request_length); + break; + + case UX_SET_DESCRIPTOR: + + status = UX_FUNCTION_NOT_SUPPORTED; + break; + + case UX_GET_CONFIGURATION: + + status = _ux_device_stack_configuration_get(); + break; + + case UX_SET_CONFIGURATION: + + status = _ux_device_stack_configuration_set(request_value); + break; + + case UX_GET_INTERFACE: + + status = _ux_device_stack_alternate_setting_get(request_index); + break; + + case UX_SET_INTERFACE: + + status = _ux_device_stack_alternate_setting_set(request_index,request_value); + break; + + + case UX_SYNCH_FRAME: + + status = UX_SUCCESS; + break; + + default : + + status = UX_FUNCTION_NOT_SUPPORTED; + break; + } + + if (status != UX_SUCCESS) + + /* Stall the control endpoint to issue protocol error. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + } + + /* Return the function status. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_descriptor_send.c b/common/core/src/ux_device_stack_descriptor_send.c new file mode 100644 index 0000000..a1906fb --- /dev/null +++ b/common/core/src/ux_device_stack_descriptor_send.c @@ -0,0 +1,544 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_descriptor_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends back the device descriptor required by the host.*/ +/* */ +/* INPUT */ +/* */ +/* descriptor_type Descriptor type */ +/* descriptor_index Index of descriptor */ +/* host_length Length requested by host */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_device_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_copy Memory copy */ +/* _ux_utility_short_get Get short value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_descriptor_send(ULONG descriptor_type, ULONG request_index, ULONG host_length) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +ULONG descriptor_index; +ULONG parsed_descriptor_index; +UX_SLAVE_TRANSFER *transfer_request; +UX_CONFIGURATION_DESCRIPTOR configuration_descriptor; +UX_SLAVE_ENDPOINT *endpoint; +UCHAR *device_framework; +UCHAR *device_framework_end; +ULONG device_framework_length; +ULONG descriptor_length; +ULONG configuration_descriptor_length; +UINT status = UX_ERROR; +ULONG length; +UCHAR *string_memory; +UCHAR *string_framework; +ULONG string_framework_length; +ULONG string_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_DESCRIPTOR_SEND, descriptor_type, request_index, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint associated with the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Set the direction to OUT. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Isolate the descriptor index. */ + descriptor_index = descriptor_type & 0xff; + + /* Reset the parsed index. */ + parsed_descriptor_index = 0; + + /* Shift the descriptor type in the low byte field. */ + descriptor_type = (UCHAR) ((descriptor_type >> 8) & 0xff); + + /* What type of descriptor do we need to return? */ + switch (descriptor_type) + { + + case UX_DEVICE_DESCRIPTOR_ITEM: + + /* Setup the length appropriately. */ + if (host_length > UX_DEVICE_DESCRIPTOR_LENGTH) + length = UX_DEVICE_DESCRIPTOR_LENGTH; + else + length = host_length; + + /* Copy the device descriptor into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + _ux_system_slave -> ux_system_slave_device_framework, length); + + /* Perform the data transfer. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + + case UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM: + + /* Setup the length appropriately. */ + if (host_length > UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH) + length = UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH; + else + length = host_length; + + /* We may or may not have a device qualifier descriptor. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework; + device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length; + device_framework_end = device_framework + device_framework_length; + + /* Parse the device framework and locate a device qualifier descriptor. */ + while (device_framework < device_framework_end) + { + + /* Get the type of the current descriptor. */ + descriptor_type = *(device_framework + 1); + + /* And its length. */ + descriptor_length = (ULONG) *device_framework; + + /* Check if this is a device qualifier descriptor. */ + if (descriptor_type == UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM) + { + + /* Copy the device qualifier descriptor into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + device_framework, length); + + /* Perform the data transfer. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + break; + + case UX_OTG_DESCRIPTOR_ITEM: + + /* Setup the length appropriately. */ + if (host_length > UX_OTG_DESCRIPTOR_LENGTH) + length = UX_OTG_DESCRIPTOR_LENGTH; + else + length = host_length; + + /* We may or may not have a OTG descriptor. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework; + device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length; + device_framework_end = device_framework + device_framework_length; + + /* Parse the device framework and locate a OTG descriptor. */ + while (device_framework < device_framework_end) + { + + /* Get the type of the current descriptor. */ + descriptor_type = *(device_framework + 1); + + /* And its length. */ + descriptor_length = (ULONG) *device_framework; + + /* Check if this is a OTG descriptor. */ + if (descriptor_type == UX_OTG_DESCRIPTOR_ITEM) + { + + /* Copy the device OTG descriptor into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + device_framework, length); + + /* Perform the data transfer. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + break; + + case UX_OTHER_SPEED_DESCRIPTOR_ITEM: + + /* This request is used by the host to find out the capability of this device + if it was running at full speed. The behavior is the same as in a GET_CONFIGURATIOn descriptor + but we do not use the current device framework but rather the full speed framework. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework_full_speed; + device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length_full_speed; + device_framework_end = device_framework + device_framework_length; + + /* Parse the device framework and locate a configuration descriptor. */ + while (device_framework < device_framework_end) + { + + /* Get the type of the current descriptor. */ + descriptor_type = *(device_framework + 1); + + /* And its length. */ + descriptor_length = (ULONG) *device_framework; + + /* Check if this is a configuration descriptor. We are cheating here. Instead of creating + a OTHER SPEED descriptor, we simply scan the configuration descriptor for the Full Speed + framework and return this configuration after we manually changed the configuration descriptor + item into a Other Speed Descriptor. */ + if (descriptor_type == UX_CONFIGURATION_DESCRIPTOR_ITEM) + { + + /* Check the index. It must be the same as the one requested. */ + if (parsed_descriptor_index == descriptor_index) + { + + /* Parse the configuration descriptor. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, + (UCHAR *) &configuration_descriptor); + + /* Get the length of entire configuration descriptor. */ + configuration_descriptor_length = configuration_descriptor.wTotalLength; + + /* Ensure the host does not demand a length beyond our descriptor (Windows does that) + and do not return more than what is allowed. */ + if (configuration_descriptor_length < host_length) + length = configuration_descriptor_length; + else + length = host_length; + + /* Check buffer length, since total descriptors length may exceed buffer... */ + if (length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) + { + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Stall the endpoint. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + break; + } + + /* Copy the device descriptor into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + device_framework, length); + + /* Now we need to hack the found descriptor because this request expect a OTHER_SPEED + descriptor instead of the regular CONFIGURATION descriptor. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + 1) = UX_OTHER_SPEED_DESCRIPTOR_ITEM; + + /* We can return the configuration descriptor. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + } + else + { + + /* There may be more configuration descriptors in this framework. */ + parsed_descriptor_index++; + } + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + break; + + case UX_CONFIGURATION_DESCRIPTOR_ITEM: + + /* We may have multiple configurations !, the index will tell us what + configuration descriptor we need to return. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework; + device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length; + device_framework_end = device_framework + device_framework_length; + + /* Parse the device framework and locate a configuration descriptor. */ + while (device_framework < device_framework_end) + { + + /* Get the type of the current descriptor. */ + descriptor_type = *(device_framework + 1); + + /* And its length. */ + descriptor_length = (ULONG) *device_framework; + + /* Check if this is a configuration descriptor. */ + if (descriptor_type == UX_CONFIGURATION_DESCRIPTOR_ITEM) + { + + /* Check the index. It must be the same as the one requested. */ + if (parsed_descriptor_index == descriptor_index) + { + + /* Parse the configuration descriptor. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, + (UCHAR *) &configuration_descriptor); + + /* Get the length of entire configuration descriptor. */ + configuration_descriptor_length = configuration_descriptor.wTotalLength; + + /* Ensure the host does not demand a length beyond our descriptor (Windows does that) + and do not return more than what is allowed. */ + if (configuration_descriptor_length < host_length) + length = configuration_descriptor_length; + else + length = host_length; + + /* Check buffer length, since total descriptors length may exceed buffer... */ + if (length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) + { + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Stall the endpoint. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + break; + } + + /* Copy the device descriptor into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + device_framework, length); + + /* We can return the configuration descriptor. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + } + else + { + + /* There may be more configuration descriptors in this framework. */ + parsed_descriptor_index++; + } + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + break; + + case UX_STRING_DESCRIPTOR_ITEM: + + /* We need to filter for the index 0 which is the language ID string. */ + if (descriptor_index == 0) + { + + /* We need to check request buffer size in case it's possible exceed. */ + if (_ux_system_slave -> ux_system_slave_language_id_framework_length + 2 > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Stall the endpoint. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + break; + } + + /* We have a request to send back the language ID list. Use the transfer request buffer. */ + string_memory = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Store the total length of the response. */ + *string_memory = (UCHAR)(_ux_system_slave -> ux_system_slave_language_id_framework_length + 2); + + /* Store the descriptor type. */ + *(string_memory +1) = UX_STRING_DESCRIPTOR_ITEM; + + /* Store the language ID into the buffer. */ + _ux_utility_memory_copy(string_memory+2, _ux_system_slave -> ux_system_slave_language_id_framework, + _ux_system_slave -> ux_system_slave_language_id_framework_length); + + /* Filter the length asked/required. */ + if (host_length > _ux_system_slave -> ux_system_slave_language_id_framework_length + 2) + length = _ux_system_slave -> ux_system_slave_language_id_framework_length + 2; + else + length = host_length; + + /* We can return the string language ID descriptor. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + } + else + { + + /* The host wants a specific string index returned. Get the string framework pointer + and length. */ + string_framework = _ux_system_slave -> ux_system_slave_string_framework; + string_framework_length = _ux_system_slave -> ux_system_slave_string_framework_length; + + /* We search through the string framework until we find the right index. + The index is in the lower byte of the descriptor type. */ + while (string_framework_length != 0) + { + + /* Ensure we have the correct language page. */ + if (_ux_utility_short_get(string_framework) == request_index) + { + + /* Check the index. */ + if (*(string_framework + 2) == descriptor_index) + { + + /* We need to check request buffer size in case it's possible exceed. */ + if (((*(string_framework + 3)*2) + 2) > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Stall the endpoint. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + break; + } + + /* We have a request to send back a string. Use the transfer request buffer. */ + string_memory = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Store the length in the string buffer. The length + of the string descriptor is stored in the third byte, + hence the ' + 3'. The encoding must be in 16-bit + unicode, hence the '*2'. The length includes the size + of the length itself as well as the descriptor type, + hence the ' + 2'. */ + *string_memory = (UCHAR)((*(string_framework + 3)*2) + 2); + + /* Store the Descriptor type. */ + *(string_memory + 1) = UX_STRING_DESCRIPTOR_ITEM; + + /* Create the Unicode string. */ + for (string_length = 0; string_length < *(string_framework + 3) ; string_length ++) + { + + /* Insert a Unicode byte. */ + *(string_memory + 2 + (string_length * 2)) = *(string_framework + 4 + string_length); + + /* Insert a zero after the Unicode byte. */ + *(string_memory + 2 + (string_length * 2) + 1) = 0; + } + + /* Filter the length asked/required. */ + if (host_length > (UINT)((*(string_framework + 3)*2) + 2)) + length = (ULONG)((*(string_framework + 3)*2) + 2); + else + length = host_length; + + /* We can return the string descriptor. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + } + } + + /* This is the wrong string descriptor, jump to the next. */ + string_framework_length -= (ULONG) *(string_framework + 3) + 4; + string_framework += (ULONG) *(string_framework + 3) + 4; + } + + /* Have we exhausted all the string descriptors? */ + if (string_framework_length == 0) + { + + /* Could not find the required string index. Stall the endpoint. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + return(UX_ERROR); + } + } + break; + + default: + + /* Stall the endpoint. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + return(UX_ERROR); + } + + /* Return the status to the caller. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_disconnect.c b/common/core/src/ux_device_stack_disconnect.c new file mode 100644 index 0000000..bb2586d --- /dev/null +++ b/common/core/src/ux_device_stack_disconnect.c @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_disconnect PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when the device gets disconnected from the */ +/* host. All the device resources are freed. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_entry_function) Device class entry function */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_device_stack_interface_delete Delete interface */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_disconnect(VOID) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_INTERFACE *next_interface; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_COMMAND class_command; +UINT status = UX_ERROR; + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_DISCONNECT, device, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(device); + + /* If the device was in the configured state, there may be interfaces + attached to the configuration. */ + if (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + /* Get the pointer to the first interface. */ + interface = device -> ux_slave_device_first_interface; + + /* Parse all the interfaces if any. */ + while (interface != UX_NULL) + { + + /* Build all the fields of the Class Command. */ + class_command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_DEACTIVATE; + class_command.ux_slave_class_command_interface = (VOID *) interface; + + /* Get the pointer to the class container of this interface. */ + class = interface -> ux_slave_interface_class; + + /* Store the class container. */ + class_command.ux_slave_class_command_class_ptr = class; + + /* If there is a class container for this instance, deactivate it. */ + if (class != UX_NULL) + + /* Call the class with the DEACTIVATE signal. */ + class -> ux_slave_class_entry_function(&class_command); + + /* Get the next interface. */ + next_interface = interface -> ux_slave_interface_next_interface; + + /* Remove the interface and all endpoints associated with it. */ + _ux_device_stack_interface_delete(interface); + + /* Now we refresh the interface pointer. */ + interface = next_interface; + } + + /* Mark the device as attached now. */ + device -> ux_slave_device_state = UX_DEVICE_ATTACHED; + } + + /* If the device was attached, we need to destroy the control endpoint. */ + if (device -> ux_slave_device_state == UX_DEVICE_ATTACHED) + + /* Now we can destroy the default control endpoint. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_DESTROY_ENDPOINT, + (VOID *) &device -> ux_slave_device_control_endpoint); + + /* We are reverting to configuration 0. */ + device -> ux_slave_device_configuration_selected = 0; + + /* Set the device to be non attached. */ + device -> ux_slave_device_state = UX_DEVICE_RESET; + + /* Check the status change callback. */ + if(_ux_system_slave -> ux_system_slave_change_function != UX_NULL) + { + + /* Inform the application if a callback function was programmed. */ + _ux_system_slave -> ux_system_slave_change_function(UX_DEVICE_REMOVED); + } + + /* Return the status to the caller. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_endpoint_stall.c b/common/core/src/ux_device_stack_endpoint_stall.c new file mode 100644 index 0000000..53127aa --- /dev/null +++ b/common/core/src/ux_device_stack_endpoint_stall.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_endpoint_stall PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function stalls an endpoint. The host will need to reissue */ +/* a reset to clear the endpoint. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_endpoint_stall(UX_SLAVE_ENDPOINT *endpoint) +{ + +TX_INTERRUPT_SAVE_AREA + +UX_SLAVE_DCD *dcd; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_ENDPOINT_STALL, endpoint, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Assume device is in an invalid state here in order to reduce code in following + section where interrupts are disabled. */ + status = UX_ERROR; + + /* Ensure we don't change the endpoint's state after disconnection routine + resets it. */ + TX_DISABLE + + /* Check if the device is in a valid state; as soon as the device is out + of the RESET state, transfers occur and thus endpoints may be stalled. */ + if (_ux_system_slave -> ux_system_slave_device.ux_slave_device_state != UX_DEVICE_RESET) + { + + /* Stall the endpoint. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + + /* Mark the endpoint state. */ + endpoint -> ux_slave_endpoint_state = UX_ENDPOINT_HALTED; + } + + /* Restore interrupts. */ + TX_RESTORE + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_get_status.c b/common/core/src/ux_device_stack_get_status.c new file mode 100644 index 0000000..1a14d9e --- /dev/null +++ b/common/core/src/ux_device_stack_get_status.c @@ -0,0 +1,184 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_get_status PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the status of a USB component of the device */ +/* such as device or endpoint. */ +/* */ +/* INPUT */ +/* */ +/* request_type Request type */ +/* request_index Request index */ +/* request_length Request length */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* */ +/* CALLED BY */ +/* */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_get_status(ULONG request_type, ULONG request_index, ULONG request_length) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UINT status; +ULONG data_length; + + UX_PARAMETER_NOT_USED(request_length); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_GET_STATUS, request_type, request_index, request_length, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint for the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Reset the status buffer. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = 0; + *(transfer_request -> ux_slave_transfer_request_data_pointer + 1) = 0; + + /* The default length for GET_STATUS is 2, except for OTG get Status. */ + data_length = 2; + + /* The status can be for either the device or the endpoint. */ + switch (request_type & UX_REQUEST_TARGET) + { + + case UX_REQUEST_TARGET_DEVICE: + + /* When the device is probed, it is either for the power/remote capabilities or OTG role swap. + We differentiate with the Windex, 0 or OTG status Selector. */ + if (request_index == UX_OTG_STATUS_SELECTOR) + { + + /* Set the data length to 1. */ + data_length = 1; + +#ifdef UX_OTG_SUPPORT + /* Store the Role Swap flag. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) _ux_system_otg -> ux_system_otg_slave_role_swap_flag; +#endif + + } + else + { + + /* Store the current power state in the status buffer. */ + if (_ux_system_slave -> ux_system_slave_power_state == UX_DEVICE_SELF_POWERED) + *transfer_request -> ux_slave_transfer_request_data_pointer = 1; + + /* Store the remote wakeup capability state in the status buffer. */ + + if (_ux_system_slave -> ux_system_slave_remote_wakeup_enabled) + *transfer_request -> ux_slave_transfer_request_data_pointer |= 2; + } + + break; + + case UX_REQUEST_TARGET_ENDPOINT: + + /* This feature returns the halt state of a specific endpoint. The endpoint index + is used to retrieve the endpoint container. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_ENDPOINT_STATUS, (VOID *)(ALIGN_TYPE)(request_index & (UINT)~UX_ENDPOINT_DIRECTION)); + + /* Check the status. We may have a unknown endpoint. */ + if (status != UX_ERROR) + { + + if (status == UX_TRUE) + *transfer_request -> ux_slave_transfer_request_data_pointer = 1; + } + else + { + + /* We stall the command. Endpoint is wrong. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + + /* No more work to do here. The command failed but the upper layer does not depend on it. */ + return(UX_SUCCESS); + } + break; + + default: + + /* We stall the command. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + + /* No more work to do here. The command failed but the upper layer does not depend on it. */ + return(UX_SUCCESS); + } + + /* Set the phase of the transfer to data out. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Send the descriptor with the appropriate length to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, data_length, data_length); + + /* Return the function status. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_host_wakeup.c b/common/core/src/ux_device_stack_host_wakeup.c new file mode 100644 index 0000000..b10d85e --- /dev/null +++ b/common/core/src/ux_device_stack_host_wakeup.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_host_wakeup PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when the device wants to wake up the host. */ +/* This command is only valid when the device is in suspend mode. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_host_wakeup(VOID) +{ + +UX_SLAVE_DCD *dcd; +UINT status = UX_FUNCTION_NOT_SUPPORTED; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_HOST_WAKEUP, 0, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Check if DEVICE_REMOTE_WAKEUP feature is enabled. */ + if (_ux_system_slave -> ux_system_slave_remote_wakeup_enabled) + + /* Send the change signal to the controller driver. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_CHANGE_STATE, (VOID *) UX_DEVICE_REMOTE_WAKEUP); + + /* Return the status to the caller. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_initialize.c b/common/core/src/ux_device_stack_initialize.c new file mode 100644 index 0000000..4b9ca52 --- /dev/null +++ b/common/core/src/ux_device_stack_initialize.c @@ -0,0 +1,414 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + +UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(sizeof(UX_SLAVE_CLASS), UX_MAX_SLAVE_CLASS_DRIVER), UX_MAX_SLAVE_CLASS_DRIVER_mul_ovf) + +/* Define the names of all the USB Classes of USBX. */ + +UCHAR _ux_system_slave_class_storage_name[] = "ux_slave_class_storage"; +UCHAR _ux_system_slave_class_cdc_acm_name[] = "ux_slave_class_cdc_acm"; +UCHAR _ux_system_slave_class_dpump_name[] = "ux_slave_class_dpump"; +UCHAR _ux_system_slave_class_pima_name[] = "ux_slave_class_pima"; +UCHAR _ux_system_slave_class_hid_name[] = "ux_slave_class_hid"; +UCHAR _ux_system_slave_class_rndis_name[] = "ux_slave_class_rndis"; +UCHAR _ux_system_slave_class_cdc_ecm_name[] = "ux_slave_class_cdc_ecm"; +UCHAR _ux_system_slave_class_dfu_name[] = "ux_slave_class_dfu"; +UCHAR _ux_system_slave_class_audio_name[] = "ux_slave_class_audio"; + +/* Define USBX Host variable. */ +UX_SYSTEM_SLAVE *_ux_system_slave; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the generic portion of the device side of */ +/* USBX. */ +/* */ +/* INPUT */ +/* */ +/* device_framework_high_speed Pointer to high speed FW */ +/* device_framework_length_high_speed Length of high speed FW */ +/* device_framework_full_speed Pointer to full speed FW */ +/* device_framework_length_full_speed Length of full speed FW */ +/* string_framework Pointer to string FW */ +/* string_framework_length Length of string FW */ +/* language_id_framework Pointer to language ID FW */ +/* language_id_framework_length Length of language ID FW */ +/* (ux_system_slave_change_function) Pointer to callback function */ +/* for device changes */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_initialize(UCHAR * device_framework_high_speed, ULONG device_framework_length_high_speed, + UCHAR * device_framework_full_speed, ULONG device_framework_length_full_speed, + UCHAR * string_framework, ULONG string_framework_length, + UCHAR * language_id_framework, ULONG language_id_framework_length, + UINT (*ux_system_slave_change_function)(ULONG)) +{ +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoints_pool; +UX_SLAVE_INTERFACE *interfaces_pool; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +ULONG interfaces_found; +ULONG local_interfaces_found; +ULONG endpoints_found; +ULONG local_endpoints_found; +ULONG endpoints_in_interface_found; +UCHAR *device_framework; +ULONG device_framework_length; +UCHAR descriptor_type; +ULONG descriptor_length; +UCHAR *memory; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_INITIALIZE, 0, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Store the high speed device framework address and length in the project structure. */ + _ux_system_slave -> ux_system_slave_device_framework_high_speed = device_framework_high_speed; + _ux_system_slave -> ux_system_slave_device_framework_length_high_speed = device_framework_length_high_speed; + + /* Store the string framework address and length in the project structure. */ + _ux_system_slave -> ux_system_slave_device_framework_full_speed = device_framework_full_speed; + _ux_system_slave -> ux_system_slave_device_framework_length_full_speed = device_framework_length_full_speed; + + /* Store the string framework address and length in the project structure. */ + _ux_system_slave -> ux_system_slave_string_framework = string_framework; + _ux_system_slave -> ux_system_slave_string_framework_length = string_framework_length; + + /* Store the language ID list in the project structure. */ + _ux_system_slave -> ux_system_slave_language_id_framework = language_id_framework; + _ux_system_slave -> ux_system_slave_language_id_framework_length = language_id_framework_length; + + /* Store the max number of slave class drivers in the project structure. */ + _ux_system_slave -> ux_system_slave_max_class = UX_MAX_SLAVE_CLASS_DRIVER; + + /* Store the device state change function callback. */ + _ux_system_slave -> ux_system_slave_change_function = ux_system_slave_change_function; + + /* Allocate memory for the classes. + * sizeof(UX_SLAVE_CLASS) * UX_MAX_SLAVE_CLASS_DRIVER) overflow is checked + * outside of the function. + */ + memory = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS) * UX_MAX_SLAVE_CLASS_DRIVER); + if (memory == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save this memory allocation in the USBX project. */ + _ux_system_slave -> ux_system_slave_class_array = (UX_SLAVE_CLASS *) ((void *) memory); + + /* Allocate some memory for the Control Endpoint. First get the address of the transfer request for the + control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Acquire a buffer for the size of the endpoint. */ + transfer_request -> ux_slave_transfer_request_data_pointer = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH); + + /* Ensure we have enough memory. */ + if (transfer_request -> ux_slave_transfer_request_data_pointer == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + status = UX_SUCCESS; + + /* Reset all values we are using during the scanning of the framework. */ + interfaces_found = 0; + endpoints_found = 0; + + /* Go on to scan interfaces if no error. */ + if (status == UX_SUCCESS) + { + + /* We need to determine the maximum number of interfaces and endpoints declared in the device framework. + This mechanism requires that both framework behave the same way regarding the number of interfaces + and endpoints. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework_full_speed; + device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length_full_speed; + + /* Reset all values we are using during the scanning of the framework. */ + local_interfaces_found = 0; + local_endpoints_found = 0; + endpoints_in_interface_found = 0; + + /* Parse the device framework and locate interfaces and endpoint descriptor(s). */ + while (device_framework_length != 0) + { + + /* Get the length of this descriptor. */ + descriptor_length = (ULONG) *device_framework; + + /* And its type. */ + descriptor_type = *(device_framework + 1); + + /* Check if this is an endpoint descriptor. */ + switch(descriptor_type) + { + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Check if this is alternate setting 0. If not, do not add another interface found. + If this is alternate setting 0, reset the endpoints count for this interface. */ + if (*(device_framework + 3) == 0) + { + + /* Add the cumulated number of endpoints in the previous interface. */ + local_endpoints_found += endpoints_in_interface_found; + + /* Read the number of endpoints for this alternate setting. */ + endpoints_in_interface_found = (ULONG) *(device_framework + 4); + + /* Increment the number of interfaces found in the current configuration. */ + local_interfaces_found++; + } + else + { + + /* Compare the number of endpoints found in this non 0 alternate setting. */ + if (endpoints_in_interface_found < (ULONG) *(device_framework + 4)) + + /* Adjust the number of maximum endpoints in this interface. */ + endpoints_in_interface_found = (ULONG) *(device_framework + 4); + } + + break; + + case UX_CONFIGURATION_DESCRIPTOR_ITEM: + + /* Check if the number of interfaces found in this configuration is the maximum so far. */ + if (local_interfaces_found > interfaces_found) + + /* We need to adjust the number of maximum interfaces. */ + interfaces_found = local_interfaces_found; + + /* We have a new configuration. We need to reset the number of local interfaces. */ + local_interfaces_found = 0; + + /* Add the cumulated number of endpoints in the previous interface. */ + local_endpoints_found += endpoints_in_interface_found; + + /* Check if the number of endpoints found in the previous configuration is the maximum so far. */ + if (local_endpoints_found > endpoints_found) + + /* We need to adjust the number of maximum endpoints. */ + endpoints_found = local_endpoints_found; + + /* We have a new configuration. We need to reset the number of local endpoints. */ + local_endpoints_found = 0; + endpoints_in_interface_found = 0; + + break; + + default: + break; + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* Add the cumulated number of endpoints in the previous interface. */ + local_endpoints_found += endpoints_in_interface_found; + + /* Check if the number of endpoints found in the previous interface is the maximum so far. */ + if (local_endpoints_found > endpoints_found) + + /* We need to adjust the number of maximum endpoints. */ + endpoints_found = local_endpoints_found; + + + /* Check if the number of interfaces found in this configuration is the maximum so far. */ + if (local_interfaces_found > interfaces_found) + + /* We need to adjust the number of maximum interfaces. */ + interfaces_found = local_interfaces_found; + + /* We do a sanity check on the finding. At least there must be one interface but endpoints are + not necessary. */ + if (interfaces_found == 0) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_INIT, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, device_framework, 0, 0, UX_TRACE_ERRORS, 0, 0) + + status = UX_DESCRIPTOR_CORRUPTED; + } + } + + /* Go on to allocate endpoints pool if no error. */ + if (status == UX_SUCCESS) + { + + /* Memorize both pool sizes. */ + device -> ux_slave_device_interfaces_pool_number = interfaces_found; + device -> ux_slave_device_endpoints_pool_number = endpoints_found; + + /* We assign a pool for the interfaces. */ + interfaces_pool = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, interfaces_found, sizeof(UX_SLAVE_INTERFACE)); + if (interfaces_pool == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + + /* Save the interface pool address in the device container. */ + device -> ux_slave_device_interfaces_pool = interfaces_pool; + } + + /* Do we need an endpoint pool ? */ + if (endpoints_found != 0 && status == UX_SUCCESS) + { + + /* We assign a pool for the endpoints. */ + endpoints_pool = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, endpoints_found, sizeof(UX_SLAVE_ENDPOINT)); + if (endpoints_pool == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + { + + /* Save the endpoint pool address in the device container. */ + device -> ux_slave_device_endpoints_pool = endpoints_pool; + + /* We need to assign a transfer buffer to each endpoint. Each endpoint is assigned the + maximum buffer size. We also assign the semaphore used by the endpoint to synchronize transfer + completion. */ + while (endpoints_pool < (device -> ux_slave_device_endpoints_pool + endpoints_found)) + { + + /* Obtain some memory. */ + endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + + /* Ensure we could allocate memory. */ + if (endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer == UX_NULL) + { + status = UX_MEMORY_INSUFFICIENT; + break; + } + + /* Create the semaphore for the endpoint. */ + status = _ux_utility_semaphore_create(&endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_semaphore, + "ux_transfer_request_semaphore", 0); + + /* Check completion status. */ + if (status != UX_SUCCESS) + { + status = UX_SEMAPHORE_ERROR; + break; + } + + /* Next endpoint. */ + endpoints_pool++; + } + } + } + else + endpoints_pool = UX_NULL; + + /* Return successful completion. */ + if (status == UX_SUCCESS) + return(UX_SUCCESS); + + /* Free resources when there is error. */ + + /* Free device -> ux_slave_device_endpoints_pool. */ + if (endpoints_pool) + { + + /* In error cases creating endpoint resources, endpoints_pool is endpoint that failed. + * Previously allocated things should be freed. */ + while(endpoints_pool >= device -> ux_slave_device_endpoints_pool) + { + + /* Delete ux_slave_transfer_request_semaphore. */ + if (endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_semaphore); + + /* Free ux_slave_transfer_request_data_pointer buffer. */ + if (endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer) + _ux_utility_memory_free(endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer); + + /* Move to previous endpoint. */ + endpoints_pool --; + } + + _ux_utility_memory_free(device -> ux_slave_device_endpoints_pool); + } + + /* Free device -> ux_slave_device_interfaces_pool. */ + if (device -> ux_slave_device_interfaces_pool) + _ux_utility_memory_free(device -> ux_slave_device_interfaces_pool); + + /* Free device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer. */ + if (device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer) + _ux_utility_memory_free(device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer); + + /* Free _ux_system_slave -> ux_system_slave_class_array. */ + _ux_utility_memory_free(_ux_system_slave -> ux_system_slave_class_array); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_interface_delete.c b/common/core/src/ux_device_stack_interface_delete.c new file mode 100644 index 0000000..9d8ca4e --- /dev/null +++ b/common/core/src/ux_device_stack_interface_delete.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_interface_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deletes an interface. Semaphore and memory are */ +/* released and the controller driver is invoked to disable the */ +/* hardware endpoint. The interface is then removed from the */ +/* configuration. */ +/* */ +/* INPUT */ +/* */ +/* interface Pointer to interface */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_interface_delete(UX_SLAVE_INTERFACE *interface) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_ENDPOINT *next_endpoint; + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(interface); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_INTERFACE_DELETE, interface, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Find the first endpoints associated with this interface. */ + next_endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Parse all the endpoints. */ + while (next_endpoint != UX_NULL) + { + + /* Save this endpoint. */ + endpoint = next_endpoint; + + /* Find the next endpoint. */ + next_endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave->ux_system_slave_dcd; + + /* The endpoint must be destroyed. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_DESTROY_ENDPOINT, endpoint); + + /* Free the endpoint. */ + endpoint -> ux_slave_endpoint_status = UX_UNUSED; + + /* Make sure the endpoint instance is now cleaned up. */ + endpoint -> ux_slave_endpoint_state = 0; + endpoint -> ux_slave_endpoint_next_endpoint = UX_NULL; + endpoint -> ux_slave_endpoint_interface = UX_NULL; + endpoint -> ux_slave_endpoint_device = UX_NULL; + } + + /* It's always from first one (to delete). */ + /* Rebuild the first link. */ + device -> ux_slave_device_first_interface = interface -> ux_slave_interface_next_interface; + + /* The interface is removed from the link, its memory must be cleaned and returned to the pool. */ + interface -> ux_slave_interface_class = UX_NULL; + interface -> ux_slave_interface_class_instance = UX_NULL; + interface -> ux_slave_interface_next_interface = UX_NULL; + interface -> ux_slave_interface_first_endpoint = UX_NULL; + interface -> ux_slave_interface_status = UX_UNUSED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_stack_interface_get.c b/common/core/src/ux_device_stack_interface_get.c new file mode 100644 index 0000000..38b444b --- /dev/null +++ b/common/core/src/ux_device_stack_interface_get.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_interface_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is deprecated, ux_device_stack_alternate_setting_get */ +/* does the same thing and used by the core stack. */ +/* */ +/* This function gets the current alternate setting for an interface. */ +/* */ +/* INPUT */ +/* */ +/* interface_value Value of the interface */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_interface_get(UINT interface_value) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_INTERFACE_GET, interface_value, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint for the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* If the device was in the configured state, there may be interfaces + attached to the configuration. */ + if (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Get the pointer to the first interface. */ + interface = device -> ux_slave_device_first_interface; + + /* Parse the interfaces if any. */ + while (interface != UX_NULL) + { + + /* Check if this is the interface we have an inquiry for. */ + if (interface -> ux_slave_interface_descriptor.bInterfaceNumber == interface_value) + { + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Set the value of the alternate setting in the buffer. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = + (UCHAR) interface -> ux_slave_interface_descriptor.bAlternateSetting; + + /* Setup the length appropriately. */ + transfer_request -> ux_slave_transfer_request_requested_length = 1; + + /* Set the phase of the transfer to data out. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Send the descriptor with the appropriate length to the host. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_TRANSFER_REQUEST, transfer_request); + + /* Return the function status code. */ + return(status); + } + + /* Get the next interface. */ + interface = interface -> ux_slave_interface_next_interface; + } + } + + /* The alternate setting value was not found, so we return a stall error. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + + /* Return the status to the caller. */ + return(UX_ERROR); +} + diff --git a/common/core/src/ux_device_stack_interface_set.c b/common/core/src/ux_device_stack_interface_set.c new file mode 100644 index 0000000..82bc3fd --- /dev/null +++ b/common/core/src/ux_device_stack_interface_set.c @@ -0,0 +1,277 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_interface_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets one alternate setting of one interface and */ +/* enable all endpoints associated with this alternate setting. */ +/* configuration. */ +/* */ +/* INPUT */ +/* */ +/* device_framework Address in device framework */ +/* for selected alternate setting*/ +/* device_framework_length Length of device framework */ +/* alternate_setting_value Alternate setting */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_device_stack_interface_start Start interface */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_interface_set(UCHAR * device_framework, ULONG device_framework_length, + ULONG alternate_setting_value) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_INTERFACE *interface_link; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_ENDPOINT *endpoint_link; +ULONG descriptor_length; +UCHAR descriptor_type; +ULONG endpoints_pool_number; +ULONG interfaces_pool_number; +UINT status; + + UX_PARAMETER_NOT_USED(alternate_setting_value); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_INTERFACE_SET, alternate_setting_value, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Find a free interface in the pool and hook it to the + existing interface. */ + interface = device -> ux_slave_device_interfaces_pool; + interfaces_pool_number = device -> ux_slave_device_interfaces_pool_number; + while (interfaces_pool_number != 0) + { + /* Check if this interface is free. */ + if (interface -> ux_slave_interface_status == UX_UNUSED) + { + /* Mark this interface as used now. */ + interface -> ux_slave_interface_status = UX_USED; + break; + } + + /* Try the next interface. */ + interface++; + + /* Decrement the number of interfaces left to scan in the pool. */ + interfaces_pool_number--; + } + + /* Did we find a free interface ? */ + if (interfaces_pool_number == 0) + return(UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, interface, 0, 0, 0) + + /* Parse the descriptor in something more readable. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, + (UCHAR *) &interface -> ux_slave_interface_descriptor); + + /* Attach this interface to the end of the interface chain. */ + if (device -> ux_slave_device_first_interface == UX_NULL) + { + + device -> ux_slave_device_first_interface = interface; + } + else + { + /* Multiple interfaces exist, so find the end of the chain. */ + interface_link = device -> ux_slave_device_first_interface; + while (interface_link -> ux_slave_interface_next_interface != UX_NULL) + interface_link = interface_link -> ux_slave_interface_next_interface; + interface_link -> ux_slave_interface_next_interface = interface; + } + + /* Point beyond the interface descriptor. */ + device_framework_length -= (ULONG) *device_framework; + device_framework += (ULONG) *device_framework; + + /* Parse the device framework and locate endpoint descriptor(s). */ + while (device_framework_length != 0) + { + + /* Get the length of the current descriptor. */ + descriptor_length = (ULONG) *device_framework; + + /* And its type. */ + descriptor_type = *(device_framework + 1); + + /* Check if this is an endpoint descriptor. */ + switch(descriptor_type) + { + + case UX_ENDPOINT_DESCRIPTOR_ITEM: + + /* Find a free endpoint in the pool and hook it to the + existing interface after it's created by DCD. */ + endpoint = device -> ux_slave_device_endpoints_pool; + endpoints_pool_number = device -> ux_slave_device_endpoints_pool_number; + while (endpoints_pool_number != 0) + { + /* Check if this endpoint is free. */ + if (endpoint -> ux_slave_endpoint_status == UX_UNUSED) + { + /* Mark this endpoint as used now. */ + endpoint -> ux_slave_endpoint_status = UX_USED; + break; + } + + /* Try the next endpoint. */ + endpoint++; + + /* Decrement the number of endpoints to scan from the pool. */ + endpoints_pool_number--; + } + + /* Did we find a free endpoint ? */ + if (endpoints_pool_number == 0) + return(UX_MEMORY_INSUFFICIENT); + + /* Parse the descriptor in something more readable. */ + _ux_utility_descriptor_parse(device_framework, + _ux_system_endpoint_descriptor_structure, + UX_ENDPOINT_DESCRIPTOR_ENTRIES, + (UCHAR *) &endpoint -> ux_slave_endpoint_descriptor); + + /* Now we create a transfer request to accept transfer on this endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* We store the endpoint in the transfer request as well. */ + transfer_request -> ux_slave_transfer_request_endpoint = endpoint; + + /* By default the timeout is infinite on request. */ + transfer_request -> ux_slave_transfer_request_timeout = UX_WAIT_FOREVER; + + /* Attach the interface to the endpoint. */ + endpoint -> ux_slave_endpoint_interface = interface; + + /* Attach the device to the endpoint. */ + endpoint -> ux_slave_endpoint_device = device; + + /* Create the endpoint at the DCD level. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_CREATE_ENDPOINT, (VOID *) endpoint); + + /* Do a sanity check on endpoint creation. */ + if (status != UX_SUCCESS) + { + + /* Error was returned, endpoint cannot be created. */ + endpoint -> ux_slave_endpoint_status = UX_UNUSED; + return(status); + } + + /* Attach this endpoint to the end of the endpoint chain. */ + if (interface -> ux_slave_interface_first_endpoint == UX_NULL) + { + + interface -> ux_slave_interface_first_endpoint = endpoint; + } + else + { + /* Multiple endpoints exist, so find the end of the chain. */ + endpoint_link = interface -> ux_slave_interface_first_endpoint; + while (endpoint_link -> ux_slave_endpoint_next_endpoint != UX_NULL) + endpoint_link = endpoint_link -> ux_slave_endpoint_next_endpoint; + endpoint_link -> ux_slave_endpoint_next_endpoint = endpoint; + } + break; + + case UX_CONFIGURATION_DESCRIPTOR_ITEM: + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* If the descriptor is a configuration or interface, + we have parsed and mounted all endpoints. + The interface attached to this configuration must be started at the class level. */ + status = _ux_device_stack_interface_start(interface); + + /* Return the status to the caller. */ + return(status); + + default: + break; + } + + /* Adjust what is left of the device framework. */ + device_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* The interface attached to this configuration must be started at the class + level. */ + status = _ux_device_stack_interface_start(interface); + + /* Return the status to the caller. */ + return(status); +} + diff --git a/common/core/src/ux_device_stack_interface_start.c b/common/core/src/ux_device_stack_interface_start.c new file mode 100644 index 0000000..98747ef --- /dev/null +++ b/common/core/src/ux_device_stack_interface_start.c @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_interface_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts an interface associated with the enabled */ +/* configuration. */ +/* */ +/* INPUT */ +/* */ +/* interface Pointer to interface */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_entry_function) Device class entry function */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_interface_start(UX_SLAVE_INTERFACE *interface) +{ + +UX_SLAVE_DEVICE *device; +UX_SLAVE_CLASS *class; +UINT status; +UX_SLAVE_CLASS_COMMAND class_command; + + + /* Get the class for the interface. */ + class = _ux_system_slave -> ux_system_slave_interface_class_array[interface -> ux_slave_interface_descriptor.bInterfaceNumber]; + + /* Check if class driver is available. */ + if (class == UX_NULL) + + /* There is no class driver supported. */ + return (UX_NO_CLASS_MATCH); + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Build all the fields of the Class Command. */ + class_command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_QUERY; + class_command.ux_slave_class_command_interface = (VOID *)interface; + class_command.ux_slave_class_command_class = interface -> ux_slave_interface_descriptor.bInterfaceClass; + class_command.ux_slave_class_command_subclass = interface -> ux_slave_interface_descriptor.bInterfaceSubClass; + class_command.ux_slave_class_command_protocol = interface -> ux_slave_interface_descriptor.bInterfaceProtocol; + class_command.ux_slave_class_command_vid = device -> ux_slave_device_descriptor.idVendor; + class_command.ux_slave_class_command_pid = device -> ux_slave_device_descriptor.idProduct; + + /* We can now memorize the interface pointer associated with this class. */ + class -> ux_slave_class_interface = interface; + + /* We have found a potential candidate. Call this registered class entry function. */ + status = class -> ux_slave_class_entry_function(&class_command); + + /* The status tells us if the registered class wants to own this class. */ + if (status == UX_SUCCESS) + { + + /* Store the class container. */ + class_command.ux_slave_class_command_class_ptr = class; + + /* Store the command. */ + class_command.ux_slave_class_command_request = UX_SLAVE_CLASS_COMMAND_ACTIVATE; + + /* Activate the class. */ + status = class -> ux_slave_class_entry_function(&class_command); + + /* If the class was successfully activated, set the class for the interface. */ + if(status == UX_SUCCESS) + interface -> ux_slave_interface_class = class; + + return(status); + } + + /* There is no driver who want to own this class! */ + return(UX_NO_CLASS_MATCH); +} + diff --git a/common/core/src/ux_device_stack_microsoft_extension_register.c b/common/core/src/ux_device_stack_microsoft_extension_register.c new file mode 100644 index 0000000..983e535 --- /dev/null +++ b/common/core/src/ux_device_stack_microsoft_extension_register.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_microsoft_extension_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers the Microsoft extensions to support vendor */ +/* commands before the device is configured. */ +/* */ +/* INPUT */ +/* */ +/* vendor_command Vendor Command. */ +/* application_callback Application Callback */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_microsoft_extension_register(ULONG vendor_request, + UINT (*vendor_request_function)(ULONG, ULONG, ULONG, ULONG, UCHAR *, ULONG *)) +{ + + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_MICROSOFT_EXTENSION_REGISTER, 0, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Store the vendor command. */ + _ux_system_slave -> ux_system_slave_device_vendor_request = vendor_request; + _ux_system_slave -> ux_system_slave_device_vendor_request_function = vendor_request_function; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_stack_set_feature.c b/common/core/src/ux_device_stack_set_feature.c new file mode 100644 index 0000000..e8cca96 --- /dev/null +++ b/common/core/src/ux_device_stack_set_feature.c @@ -0,0 +1,195 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_set_feature PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets a specific feature (Device, Interface, */ +/* Endpoint ....). */ +/* */ +/* INPUT */ +/* */ +/* request_type Request type */ +/* request_value Request value */ +/* request_index Request index */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD controller function */ +/* */ +/* CALLED BY */ +/* */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_set_feature(ULONG request_type, ULONG request_value, ULONG request_index) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_ENDPOINT *endpoint_target; + + UX_PARAMETER_NOT_USED(request_value); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_SET_FEATURE, request_value, request_index, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint for the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* The feature can be for either the device or the endpoint. */ + switch (request_type & UX_REQUEST_TARGET) + { + + case UX_REQUEST_TARGET_DEVICE: + + /* Check if we have a DEVICE_REMOTE_WAKEUP Feature. */ + if (request_value == UX_REQUEST_FEATURE_DEVICE_REMOTE_WAKEUP) + { + + /* Check if we have the capability. */ + if (_ux_system_slave -> ux_system_slave_remote_wakeup_capability) + { + + /* Enable the feature. */ + _ux_system_slave -> ux_system_slave_remote_wakeup_enabled = UX_TRUE; + + /* OK. */ + return (UX_SUCCESS); + } + else + + /* Protocol error. */ + return (UX_FUNCTION_NOT_SUPPORTED); + } + +#ifdef UX_OTG_SUPPORT + /* Check if we have a A_HNP_SUPPORT Feature. This is set when the Host is HNP capable. */ + if (request_value == UX_OTG_FEATURE_A_HNP_SUPPORT) + + /* Store the A_HNP_SUPPORT flag. */ + _ux_system_otg -> ux_system_otg_slave_set_feature_flag |= UX_OTG_FEATURE_A_HNP_SUPPORT; + else + { + + /* Check if the host asks us to perform HNP. If also we become the host. */ + if (request_value == UX_OTG_FEATURE_B_HNP_ENABLE) + { + + /* The ISR will pick up the suspend event and check if we need to become IDLE or HOST. */ + _ux_system_otg -> ux_system_otg_slave_set_feature_flag |= UX_OTG_FEATURE_B_HNP_ENABLE; + + } + + } +#endif + + break; + + case UX_REQUEST_TARGET_ENDPOINT: + + /* The only set feature for endpoint is ENDPOINT_STALL. This forces + the endpoint to the stall situation. + We need to find the endpoint through the interface(s). */ + interface = device -> ux_slave_device_first_interface; + + while (interface != UX_NULL) + { + + /* Get the first endpoint for this interface. */ + endpoint_target = interface -> ux_slave_interface_first_endpoint; + + /* Parse all the endpoints. */ + while (endpoint_target != UX_NULL) + { + + /* Check the endpoint index. */ + if (endpoint_target -> ux_slave_endpoint_descriptor.bEndpointAddress == request_index) + { + + /* Stall the endpoint. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint_target); + + /* Return the function status. */ + return(UX_SUCCESS); + } + + /* Next endpoint. */ + endpoint_target = endpoint_target -> ux_slave_endpoint_next_endpoint; + } + + /* Next interface. */ + interface = interface -> ux_slave_interface_next_interface; + } + + /* We get here when the endpoint is wrong. Should not happen though. */ + /* Intentionally fall through into the default case. */ + /* fall through */ + default: + + /* We stall the command. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + + /* No more work to do here. The command failed but the upper layer does not depend on it. */ + return(UX_SUCCESS); + } + + /* Return the function status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_stack_transfer_abort.c b/common/core/src/ux_device_stack_transfer_abort.c new file mode 100644 index 0000000..49a58d9 --- /dev/null +++ b/common/core/src/ux_device_stack_transfer_abort.c @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_transfer_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function aborts a pending transfer request that has been */ +/* previously submitted. This function only cancels a specific */ +/* transfer request. */ +/* */ +/* The call back to the function will have the */ +/* UX_TRANSFER_STATUS_ABORT status */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* completion_code Completion code */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_transfer_abort(UX_SLAVE_TRANSFER *transfer_request, ULONG completion_code) +{ + +TX_INTERRUPT_SAVE_AREA + +UX_SLAVE_DCD *dcd; + + UX_PARAMETER_NOT_USED(completion_code); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_TRANSFER_ABORT, transfer_request, completion_code, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Sets the completion code due to bus reset. */ + transfer_request -> ux_slave_transfer_request_completion_code = UX_TRANSFER_BUS_RESET; + + /* Ensure we're not preempted by the transfer completion ISR. */ + TX_DISABLE + + /* It's possible the transfer already completed. Ensure it hasn't before doing the abort. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_PENDING) + { + + /* Call the DCD if necessary for cleaning up the pending transfer. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_TRANSFER_ABORT, (VOID *) transfer_request); + + /* Restore interrupts. Note that the transfer request should not be modified now. */ + TX_RESTORE + + /* We need to set the completion code for the transfer to aborted. Note + that the transfer request function cannot simultaneously modify this + because if the transfer was pending, then the transfer's thread is + currently waiting for it to complete. */ + transfer_request -> ux_slave_transfer_request_status = UX_TRANSFER_STATUS_ABORT; + + /* Wake up the device driver who is waiting on the semaphore. */ + _ux_utility_semaphore_put(&transfer_request -> ux_slave_transfer_request_semaphore); + } + else + { + + /* Restore interrupts. */ + TX_RESTORE + } + + /* This function never fails. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_stack_transfer_all_request_abort.c b/common/core/src/ux_device_stack_transfer_all_request_abort.c new file mode 100644 index 0000000..48c7d3f --- /dev/null +++ b/common/core/src/ux_device_stack_transfer_all_request_abort.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_transfer_all_request_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cancels all the transfer requests attached to an */ +/* endpoint. The endpoint is not reset and its toggle state is left */ +/* the same. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* completion_code Completion code */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_abort Transfer abort */ +/* */ +/* CALLED BY */ +/* */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_transfer_all_request_abort(UX_SLAVE_ENDPOINT *endpoint, ULONG completion_code) +{ + +UX_SLAVE_TRANSFER *transfer_request; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_TRANSFER_ALL_REQUEST_ABORT, endpoint, completion_code, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the transfer request for this endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Abort this request. */ + _ux_device_stack_transfer_abort(transfer_request, completion_code); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_device_stack_transfer_request.c b/common/core/src/ux_device_stack_transfer_request.c new file mode 100644 index 0000000..20f3aa9 --- /dev/null +++ b/common/core/src/ux_device_stack_transfer_request.c @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_transfer_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a USB transaction. On entry the */ +/* transfer request gives the endpoint pipe selected for this */ +/* transaction and the parameters associated with the transfer */ +/* (data payload, length of transaction). */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* slave_length Length returned by host */ +/* host_length Length asked by host */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) Slave DCD dispatch function */ +/* _ux_utility_delay_ms Delay ms */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_transfer_request(UX_SLAVE_TRANSFER *transfer_request, + ULONG slave_length, + ULONG host_length) +{ + +TX_INTERRUPT_SAVE_AREA + +UX_SLAVE_DCD *dcd; +UINT status; +UX_SLAVE_ENDPOINT *endpoint; +ULONG device_state; + + + /* Do we have to skip this transfer? */ + if (transfer_request -> ux_slave_transfer_request_status_phase_ignore == UX_TRUE) + return(UX_SUCCESS); + + /* Disable interrupts to prevent the disconnection ISR from preempting us + while we check the device state and set the transfer status. */ + TX_DISABLE + + /* Get the device state. */ + device_state = _ux_system_slave -> ux_system_slave_device.ux_slave_device_state; + + /* We can only transfer when the device is ATTACHED, ADDRESSED OR CONFIGURED. */ + if ((device_state == UX_DEVICE_ATTACHED) || (device_state == UX_DEVICE_ADDRESSED) + || (device_state == UX_DEVICE_CONFIGURED)) + + /* Set the transfer to pending. */ + transfer_request -> ux_slave_transfer_request_status = UX_TRANSFER_STATUS_PENDING; + + else + { + + /* The device is in an invalid state. Restore interrupts and return error. */ + TX_RESTORE + return(UX_TRANSFER_NOT_READY); + } + + /* Restore interrupts. */ + TX_RESTORE + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_TRANSFER_REQUEST, transfer_request, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the endpoint associated with this transaction. */ + endpoint = transfer_request -> ux_slave_transfer_request_endpoint; + + /* If the endpoint is non Control, check the endpoint direction and set the data phase direction. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) != UX_CONTROL_ENDPOINT) + { + + /* Check if the endpoint is STALLED. In this case, we must refuse the transaction until the endpoint + has been reset by the host. */ + while (endpoint -> ux_slave_endpoint_state == UX_ENDPOINT_HALTED) + + /* Wait for 100ms for endpoint to be reset by a CLEAR_FEATURE command. */ + _ux_utility_delay_ms(100); + + /* Isolate the direction from the endpoint address. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + else + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_IN; + } + + /* See if we need to force a zero length packet at the end of the transfer. + This happens on a DATA IN and when the host requested length is not met + and the last packet is on a boundary. If slave_length is zero, then it is + a explicit ZLP request, no need to force ZLP. */ + if ((transfer_request -> ux_slave_transfer_request_phase == UX_TRANSFER_PHASE_DATA_OUT) && + (slave_length != 0) && (host_length != slave_length) && + (slave_length % endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize) == 0) + { + + /* If so force Zero Length Packet. */ + transfer_request -> ux_slave_transfer_request_force_zlp = UX_TRUE; + } + else + { + + /* Condition is not met, do not force a Zero Length Packet. */ + transfer_request -> ux_slave_transfer_request_force_zlp = UX_FALSE; + } + + /* Reset the number of bytes sent/received. */ + transfer_request -> ux_slave_transfer_request_actual_length = 0; + + /* Determine how many bytes to send in this transaction. We keep track of the original + length and have a working length. */ + transfer_request -> ux_slave_transfer_request_requested_length = slave_length; + transfer_request -> ux_slave_transfer_request_in_transfer_length = slave_length; + + /* Save the buffer pointer. */ + transfer_request -> ux_slave_transfer_request_current_data_pointer = + transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Call the DCD driver transfer function. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_TRANSFER_REQUEST, transfer_request); + + /* And return the status. */ + return(status); + +} + diff --git a/common/core/src/ux_device_stack_uninitialize.c b/common/core/src/ux_device_stack_uninitialize.c new file mode 100644 index 0000000..7a1c63e --- /dev/null +++ b/common/core/src/ux_device_stack_uninitialize.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_stack_uninitialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function uninitializes the generic portion of the device side */ +/* of USBX. */ +/* */ +/* INPUT */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_free Free */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_stack_uninitialize(VOID) +{ +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoints_pool; +UX_SLAVE_TRANSFER *transfer_request; +ULONG endpoints_found; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_INITIALIZE, 0, 0, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Free class memory. */ + _ux_utility_memory_free(_ux_system_slave -> ux_system_slave_class_array); + + /* Allocate some memory for the Control Endpoint. First get the address of the transfer request for the + control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Free memory for the control endpoint buffer. */ + _ux_utility_memory_free(transfer_request -> ux_slave_transfer_request_data_pointer); + + /* Get the number of endpoints found in the device framework. */ + endpoints_found = device -> ux_slave_device_endpoints_pool_number; + + /* Get the endpoint pool address in the device container. */ + endpoints_pool = device -> ux_slave_device_endpoints_pool; + + /* Parse all endpoints and fee memory and semaphore. */ + while (endpoints_found-- != 0) + { + /* Free the memory for endpoint data pointer. */ + _ux_utility_memory_free(endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_data_pointer); + + /* Remove the TX semaphore for the endpoint. */ + _ux_utility_semaphore_delete(&endpoints_pool -> ux_slave_endpoint_transfer_request.ux_slave_transfer_request_semaphore); + + /* Next endpoint. */ + endpoints_pool++; + } + + /* Free the endpoint pool address in the device container. */ + _ux_utility_memory_free(device -> ux_slave_device_endpoints_pool); + + /* Free memory for interface pool. */ + _ux_utility_memory_free(device -> ux_slave_device_interfaces_pool); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + + + + diff --git a/common/core/src/ux_hcd_sim_host_asynch_queue_process.c b/common/core/src/ux_hcd_sim_host_asynch_queue_process.c new file mode 100644 index 0000000..d771e9e --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_asynch_queue_process.c @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_asynch_queue_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function process the asynchronous transactions that happened */ +/* in the last frame. We read the ATL buffer and search for completed */ +/* PTDs and reflect the completion in the higher level ED/TD. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_asynch_queue_process(UX_HCD_SIM_HOST *hcd_sim_host) +{ + + UX_PARAMETER_NOT_USED(hcd_sim_host); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_hcd_sim_host_asynch_schedule.c b/common/core/src/ux_hcd_sim_host_asynch_schedule.c new file mode 100644 index 0000000..a0cefc4 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_asynch_schedule.c @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_asynch_schedule PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function schedules new transfers from the control/bulk lists. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_transaction_schedule Schedule simulator transaction*/ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_asynch_schedule(UX_HCD_SIM_HOST *hcd_sim_host) +{ + +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_ED *first_ed; +UINT status; + + + /* Get the pointer to the current ED in the asynchronous list. */ + ed = hcd_sim_host -> ux_hcd_sim_host_asynch_current_ed; + + /* Check if there is any ED candidate in the asynch list. */ + if (ed == UX_NULL) + { + + /* Check if there is any ED in the asynch list. If none, nothing to do. */ + if (hcd_sim_host -> ux_hcd_sim_host_asynch_head_ed == UX_NULL) + return; + else + ed = hcd_sim_host -> ux_hcd_sim_host_asynch_head_ed; + } + + /* Remember this ED. */ + first_ed = ed; + + /* In simulation, we are not tied to bandwidth limitation. */ + do + { + + /* Check if this ED has a tail and head TD different. */ + if (ed -> ux_sim_host_ed_tail_td != ed -> ux_sim_host_ed_head_td) + { + + /* Schedule this transaction with the device simulator. */ + status = _ux_hcd_sim_host_transaction_schedule(hcd_sim_host, ed); + + /* If the TD has been added to the list, we can memorize this ED has + being served and make the next ED as the one to be first scanned + at the next SOF. */ + if (status == UX_SUCCESS) + { + + if (ed -> ux_sim_host_ed_next_ed == UX_NULL) + hcd_sim_host -> ux_hcd_sim_host_asynch_current_ed = hcd_sim_host -> ux_hcd_sim_host_asynch_head_ed; + else + hcd_sim_host -> ux_hcd_sim_host_asynch_current_ed = ed -> ux_sim_host_ed_next_ed; + } + } + + /* Point to the next ED in the list. Check if at end of list. */ + if (ed -> ux_sim_host_ed_next_ed == UX_NULL) + ed = hcd_sim_host -> ux_hcd_sim_host_asynch_head_ed; + else + ed = ed -> ux_sim_host_ed_next_ed; + + } while ((ed) && (ed != first_ed)); +} + diff --git a/common/core/src/ux_hcd_sim_host_asynchronous_endpoint_create.c b/common/core/src/ux_hcd_sim_host_asynchronous_endpoint_create.c new file mode 100644 index 0000000..0cb523f --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_asynchronous_endpoint_create.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_asynchronous_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an asynchronous endpoint. The control */ +/* and bulk endpoints fall into this category. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_ed_obtain Obtain host ED */ +/* _ux_hcd_sim_host_regular_td_obtain Obtain host regular TD */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_asynchronous_endpoint_create(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint) +{ + +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_ED *head_ed; +UX_HCD_SIM_HOST_TD *td; + + + /* We need to take into account the nature of the HCD to define the max size + of any transfer in the transfer request. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_maximum_length = UX_HCD_SIM_HOST_MAX_PAYLOAD; + + /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint is active + and will be the container for the TDs. */ + ed = _ux_hcd_sim_host_ed_obtain(hcd_sim_host); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Obtain a dummy TD for terminating the ED transfer chain. */ + td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + if (td == UX_NULL) + { + + ed -> ux_sim_host_ed_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *) ed; + + /* Now do the opposite, attach the ED container to the physical ED. */ + ed -> ux_sim_host_ed_endpoint = endpoint; + + /* Hook the TD to both the tail and head of the ED. */ + ed -> ux_sim_host_ed_tail_td = td; + ed -> ux_sim_host_ed_head_td = td; + + /* Attach this ED to the asynch list. */ + head_ed = hcd_sim_host -> ux_hcd_sim_host_asynch_head_ed; + ed -> ux_sim_host_ed_next_ed = head_ed; + hcd_sim_host -> ux_hcd_sim_host_asynch_head_ed = ed; + + /* Build the back chaining pointer. The previous head ED needs to know about the + inserted ED. */ + if (head_ed != UX_NULL) + head_ed -> ux_sim_host_ed_previous_ed = ed; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_asynchronous_endpoint_destroy.c b/common/core/src/ux_hcd_sim_host_asynchronous_endpoint_destroy.c new file mode 100644 index 0000000..71bbda2 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_asynchronous_endpoint_destroy.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_asynchronous_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy an asynchronous endpoint. The control */ +/* and bulk endpoints fall into this category. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_asynchronous_endpoint_destroy(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint) +{ + +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_ED *previous_ed; +UX_HCD_SIM_HOST_ED *next_ed; +UX_HCD_SIM_HOST_TD *td; + + + /* From the endpoint container fetch the host simulator ED descriptor. */ + ed = (UX_HCD_SIM_HOST_ED *) endpoint -> ux_endpoint_ed; + + /* Get the previous ED in the list for this ED. */ + previous_ed = ed -> ux_sim_host_ed_previous_ed; + + /* Get the next ED in the list for this ED. */ + next_ed = ed -> ux_sim_host_ed_next_ed; + + /* If the previous ED is NULL, we are at trying to remove the head ED. */ + if (previous_ed == UX_NULL) + + /* Store the new endpoint in the control list. */ + hcd_sim_host -> ux_hcd_sim_host_asynch_head_ed = next_ed; + else + + /* The previous ED points now to the ED after the ED we are removing. */ + previous_ed -> ux_sim_host_ed_next_ed = next_ed; + + /* Update the previous ED pointer in the next ED if exists. */ + if (next_ed != UX_NULL) + next_ed -> ux_sim_host_ed_previous_ed = previous_ed; + + /* Determine if we need to adjust the current ED. */ + if (hcd_sim_host -> ux_hcd_sim_host_asynch_current_ed == ed) + { + + /* Move the current to the next. */ + hcd_sim_host -> ux_hcd_sim_host_asynch_current_ed = next_ed; + } + + /* We need to free the dummy TD that was attached to the ED. */ + td = ed -> ux_sim_host_ed_tail_td; + td -> ux_sim_host_td_status = UX_UNUSED; + + /* Now we can safely make the ED free. */ + ed -> ux_sim_host_ed_status = UX_UNUSED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_ed_obtain.c b/common/core/src/ux_hcd_sim_host_ed_obtain.c new file mode 100644 index 0000000..1045bd5 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_ed_obtain.c @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_ed_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free ED from the ED list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_HCD_SIM_HOST_ED * Pointer to ED */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_HCD_SIM_HOST_ED *_ux_hcd_sim_host_ed_obtain(UX_HCD_SIM_HOST *hcd_sim_host) +{ + +UX_HCD_SIM_HOST_ED *ed; +ULONG ed_index; + + + /* Start the search from the beginning of the list. */ + ed = hcd_sim_host -> ux_hcd_sim_host_ed_list; + for(ed_index = 0; ed_index < _ux_system_host -> ux_system_host_max_ed; ed_index++) + { + + /* Check the ED status, a free ED is marked with the UNUSED flag. */ + if (ed -> ux_sim_host_ed_status == UX_UNUSED) + { + + /* The ED may have been used, so we reset all fields. */ + _ux_utility_memory_set(ed, 0, sizeof(UX_HCD_SIM_HOST_ED)); + + /* This ED is now marked as USED. */ + ed -> ux_sim_host_ed_status = UX_USED; + + /* Return ED pointer. */ + return(ed); + } + + /* Point to the next ED. */ + ed++; + } + + /* There is no available ED in the ED list. */ + return(UX_NULL); +} + diff --git a/common/core/src/ux_hcd_sim_host_ed_td_clean.c b/common/core/src/ux_hcd_sim_host_ed_td_clean.c new file mode 100644 index 0000000..658c616 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_ed_td_clean.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_ed_td_clean PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function process cleans the ED of all TDs except the last */ +/* dummy TD. */ +/* */ +/* INPUT */ +/* */ +/* ed Pointer to ED */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_ed_td_clean(UX_HCD_SIM_HOST_ED *ed) +{ + +UX_HCD_SIM_HOST_TD *head_td; +UX_HCD_SIM_HOST_TD *tail_td; + + + /* Remove all the tds from this ED and leave the head and tail pointing + to the dummy TD. */ + head_td = ed -> ux_sim_host_ed_head_td; + tail_td = ed -> ux_sim_host_ed_tail_td; + + /* Free all tds attached to the ED. */ + while (head_td != tail_td) + { + + /* Mark the current head TD as free. */ + head_td -> ux_sim_host_td_status = UX_UNUSED; + + /* Update the head TD with the next TD. */ + ed -> ux_sim_host_ed_head_td = head_td -> ux_sim_host_td_next_td; + + /* Now the new head_td is the next TD in the chain. */ + head_td = ed -> ux_sim_host_ed_head_td; + } +} + diff --git a/common/core/src/ux_hcd_sim_host_endpoint_reset.c b/common/core/src/ux_hcd_sim_host_endpoint_reset.c new file mode 100644 index 0000000..49eed9e --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_endpoint_reset.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_endpoint_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reset an endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_endpoint_reset(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint) +{ + +UX_HCD_SIM_HOST_ED *ed; + + UX_PARAMETER_NOT_USED(hcd_sim_host); + + /* From the endpoint container fetch the host simulator ED descriptor. */ + ed = (UX_HCD_SIM_HOST_ED *) endpoint -> ux_endpoint_ed; + + /* Reset the data0/data1 toggle bit in the Head TD. */ + ed -> ux_sim_host_ed_toggle = 0; + + /* This operation never fails. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_entry.c b/common/core/src/ux_hcd_sim_host_entry.c new file mode 100644 index 0000000..02cce35 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_entry.c @@ -0,0 +1,273 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function dispatch the HCD function internally to the simulator */ +/* controller driver. */ +/* */ +/* INPUT */ +/* */ +/* hcd Pointer to HCD */ +/* function Function for driver to perform*/ +/* parameter Pointer to parameter(s) */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_asynch_queue_process Process asynch queue */ +/* _ux_hcd_sim_host_asynch_schedule Schedule async work */ +/* _ux_hcd_sim_host_asynchronous_endpoint_create Create async endpoint */ +/* _ux_hcd_sim_host_asynchronous_endpoint_destroy Destroy async endpoint */ +/* _ux_hcd_sim_host_endpoint_reset Reset endpoint */ +/* _ux_hcd_sim_host_frame_number_get Get frame number */ +/* _ux_hcd_sim_host_interrupt_endpoint_create Create endpoint */ +/* _ux_hcd_sim_host_iso_queue_process Process iso queue */ +/* _ux_hcd_sim_host_iso_schedule Schedule iso work */ +/* _ux_hcd_sim_host_isochronous_endpoint_create Create iso endpoint */ +/* _ux_hcd_sim_host_periodic_endpoint_destroy Destroy endpoint */ +/* _ux_hcd_sim_host_periodic_schedule Schedule periodic */ +/* _ux_hcd_sim_host_port_status_get Get port status */ +/* _ux_hcd_sim_host_port_reset Reset port */ +/* _ux_hcd_sim_host_request_transfer Request transfer */ +/* _ux_hcd_sim_host_transfer_abort Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_entry(UX_HCD *hcd, UINT function, VOID *parameter) +{ + +UINT status = 0; +UX_HCD_SIM_HOST *hcd_sim_host; + + + /* Check the status of the controller. */ + if (hcd -> ux_hcd_status == UX_UNUSED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_CONTROLLER_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONTROLLER_UNKNOWN, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONTROLLER_UNKNOWN); + } + + /* Get the pointer to the host simulator HCD. */ + hcd_sim_host = (UX_HCD_SIM_HOST *) hcd -> ux_hcd_controller_hardware; + + /* look at the function and route it. */ + switch(function) + { + + case UX_HCD_DISABLE_CONTROLLER: + + status = UX_SUCCESS; + break; + + + case UX_HCD_GET_PORT_STATUS: + + status = _ux_hcd_sim_host_port_status_get(hcd_sim_host, (ULONG) (ALIGN_TYPE) parameter); + break; + + + case UX_HCD_ENABLE_PORT: + + status = UX_SUCCESS; + break; + + + case UX_HCD_DISABLE_PORT: + + status = UX_SUCCESS; + break; + + + case UX_HCD_POWER_ON_PORT: + + status = UX_SUCCESS; + break; + + + case UX_HCD_POWER_DOWN_PORT: + + status = UX_SUCCESS; + break; + + + case UX_HCD_SUSPEND_PORT: + + status = UX_SUCCESS; + break; + + + case UX_HCD_RESUME_PORT: + + status = UX_SUCCESS; + break; + + + case UX_HCD_RESET_PORT: + + status = _ux_hcd_sim_host_port_reset(hcd_sim_host, (ULONG) (ALIGN_TYPE) parameter); + break; + + + case UX_HCD_GET_FRAME_NUMBER: + + status = _ux_hcd_sim_host_frame_number_get(hcd_sim_host, (ULONG *) parameter); + break; + + + case UX_HCD_SET_FRAME_NUMBER: + + status = UX_SUCCESS; + break; + + + case UX_HCD_TRANSFER_REQUEST: + + status = _ux_hcd_sim_host_request_transfer(hcd_sim_host, (UX_TRANSFER *) parameter); + break; + + + case UX_HCD_TRANSFER_ABORT: + + status = _ux_hcd_sim_host_transfer_abort(hcd_sim_host, (UX_TRANSFER *) parameter); + break; + + + case UX_HCD_CREATE_ENDPOINT: + + switch ((((UX_ENDPOINT*) parameter) -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + + status = _ux_hcd_sim_host_asynchronous_endpoint_create(hcd_sim_host, (UX_ENDPOINT*) parameter); + break; + + + case UX_INTERRUPT_ENDPOINT: + + status = _ux_hcd_sim_host_interrupt_endpoint_create(hcd_sim_host, (UX_ENDPOINT*) parameter); + break; + + + case UX_ISOCHRONOUS_ENDPOINT: + + status = _ux_hcd_sim_host_isochronous_endpoint_create(hcd_sim_host, (UX_ENDPOINT*) parameter); + break; + + } + break; + + + case UX_HCD_DESTROY_ENDPOINT: + + switch ((((UX_ENDPOINT*) parameter) -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + + status = _ux_hcd_sim_host_asynchronous_endpoint_destroy(hcd_sim_host, (UX_ENDPOINT*) parameter); + break; + + + case UX_INTERRUPT_ENDPOINT: + case UX_ISOCHRONOUS_ENDPOINT: + + status = _ux_hcd_sim_host_periodic_endpoint_destroy(hcd_sim_host, (UX_ENDPOINT*) parameter); + break; + + } + break; + + + case UX_HCD_RESET_ENDPOINT: + + status = _ux_hcd_sim_host_endpoint_reset(hcd_sim_host, (UX_ENDPOINT*) parameter); + break; + + + case UX_HCD_PROCESS_DONE_QUEUE: + + _ux_hcd_sim_host_iso_queue_process(hcd_sim_host); + _ux_hcd_sim_host_asynch_queue_process(hcd_sim_host); + _ux_hcd_sim_host_iso_schedule(hcd_sim_host); + _ux_hcd_sim_host_periodic_schedule(hcd_sim_host); + _ux_hcd_sim_host_asynch_schedule(hcd_sim_host); + status = UX_SUCCESS; + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Unknown request, return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_hcd_sim_host_frame_number_get.c b/common/core/src/ux_hcd_sim_host_frame_number_get.c new file mode 100644 index 0000000..2b90a2d --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_frame_number_get.c @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_frame_number_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the frame number currently used by the */ +/* controller. This function is mostly used for isochronous purposes. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* frame_number Frame number to set */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_frame_number_get(UX_HCD_SIM_HOST *hcd_sim_host, ULONG *frame_number) +{ + + /* Pickup the frame number. */ + *frame_number = hcd_sim_host -> ux_hcd_sim_host_interrupt_count; + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_frame_number_set.c b/common/core/src/ux_hcd_sim_host_frame_number_set.c new file mode 100644 index 0000000..e593b74 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_frame_number_set.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_frame_number_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will set the current frame number to the one */ +/* specified. This function is mostly used for isochronous purposes. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* frame_number Frame number to set */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_frame_number_set(UX_HCD_SIM_HOST *hcd_sim_host, ULONG frame_number) +{ + + UX_PARAMETER_NOT_USED(hcd_sim_host); + UX_PARAMETER_NOT_USED(frame_number); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_hcd_sim_host_initialize.c b/common/core/src/ux_hcd_sim_host_initialize.c new file mode 100644 index 0000000..5d83260 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_initialize.c @@ -0,0 +1,191 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the simulated host controller */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_periodic_tree_create Create periodic tree */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_semaphore_put Semaphore put */ +/* _ux_utility_timer_create Create timer */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_initialize(UX_HCD *hcd) +{ + +UX_HCD_SIM_HOST *hcd_sim_host; +UINT status; + + + /* The controller initialized here is of host simulator type. */ + hcd -> ux_hcd_controller_type = UX_HCD_SIM_HOST_CONTROLLER; + + /* Allocate memory for this host simulator HCD instance. */ + hcd_sim_host = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HCD_SIM_HOST)); + if (hcd_sim_host == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set the pointer to the host simulator HCD. */ + hcd -> ux_hcd_controller_hardware = (VOID *) hcd_sim_host; + + /* Set the generic HCD owner for the host simulator HCD. */ + hcd_sim_host -> ux_hcd_sim_host_hcd_owner = hcd; + + /* Initialize the function collector for this HCD. */ + hcd -> ux_hcd_entry_function = _ux_hcd_sim_host_entry; + + /* Initialize the max bandwidth for periodic endpoints. In simulation this is + not very important. */ + hcd -> ux_hcd_available_bandwidth = UX_HCD_SIM_HOST_AVAILABLE_BANDWIDTH; + + /* Set the state of the controller to HALTED first. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED; + + /* Allocate the list of EDs. All EDs are allocated on 16 byte memory boundary. */ + hcd_sim_host -> ux_hcd_sim_host_ed_list = _ux_utility_memory_allocate(UX_ALIGN_16, UX_REGULAR_MEMORY, (ULONG)sizeof(UX_HCD_SIM_HOST_ED) * _ux_system_host -> ux_system_host_max_ed); + if (hcd_sim_host -> ux_hcd_sim_host_ed_list == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + status = UX_SUCCESS; + + /* Allocate the list of TDs. All TDs are allocated on 32 byte memory boundary. */ + if (status == UX_SUCCESS) + { + hcd_sim_host -> ux_hcd_sim_host_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_REGULAR_MEMORY, (ULONG)sizeof(UX_HCD_SIM_HOST_TD) * _ux_system_host -> ux_system_host_max_td); + if (hcd_sim_host -> ux_hcd_sim_host_td_list == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Allocate the list of isochronous TDs. All TDs are allocated on 32 byte memory boundary. */ + if (status == UX_SUCCESS) + { + hcd_sim_host -> ux_hcd_sim_host_iso_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_REGULAR_MEMORY, (ULONG)sizeof(UX_HCD_SIM_HOST_ISO_TD) * _ux_system_host -> ux_system_host_max_iso_td); + if (hcd_sim_host -> ux_hcd_sim_host_iso_td_list == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Initialize the periodic tree. */ + if (status == UX_SUCCESS) + status = _ux_hcd_sim_host_periodic_tree_create(hcd_sim_host); + + /* Initialize the scheduler. */ + if (status == UX_SUCCESS) + { + /* Set the host controller into the operational state. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_OPERATIONAL; + + /* The asynchronous queues are empty for now. */ + hcd_sim_host -> ux_hcd_sim_host_queue_empty = UX_TRUE; + + /* The periodic scheduler is not active. */ + hcd_sim_host -> ux_hcd_sim_host_periodic_scheduler_active = 0; + + /* We start a timer that will invoke the simulator every timer tick. */ + status = _ux_utility_timer_create(&hcd_sim_host -> ux_hcd_sim_host_timer, "USBX Simulation Timer", + _ux_hcd_sim_host_timer_function, (ULONG) (ALIGN_TYPE) hcd_sim_host, 1, 1, TX_AUTO_ACTIVATE); + } + + UX_TIMER_EXTENSION_PTR_SET(&(hcd_sim_host -> ux_hcd_sim_host_timer), hcd_sim_host) + + /* Free up resources and return when there is error. */ + if (status != UX_SUCCESS) + { + + /* Set the host controller into the halt state. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED; + + /* The last resource, timer is not created or created error, + * no need to delete. */ + + if (hcd_sim_host -> ux_hcd_sim_host_iso_td_list) + _ux_utility_memory_free(hcd_sim_host -> ux_hcd_sim_host_iso_td_list); + if (hcd_sim_host -> ux_hcd_sim_host_td_list) + _ux_utility_memory_free(hcd_sim_host -> ux_hcd_sim_host_td_list); + if (hcd_sim_host -> ux_hcd_sim_host_ed_list) + _ux_utility_memory_free(hcd_sim_host -> ux_hcd_sim_host_ed_list); + _ux_utility_memory_free(hcd_sim_host); + + return(status); + } + + /* Get the number of ports on the controller. The number of ports needs to be reflected both + for the generic HCD container and the local sim_host container. In the simulator, + the number of ports is hardwired to 1 only. */ + hcd -> ux_hcd_nb_root_hubs = 1; + hcd_sim_host -> ux_hcd_sim_host_nb_root_hubs = 1; + + /* Something happened on this port. Signal it to the root hub thread. */ + hcd -> ux_hcd_root_hub_signal[0] = 1; + + /* We need to simulate a Root HUB Status Change for the USB stack since the simulator + has not root HUB per se. */ + status = _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_enum_semaphore); + if (status != UX_SUCCESS) + + /* Resources are still ready but + * failed to simulate Root HUB change! */ + return(UX_SEMAPHORE_ERROR); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_interrupt_endpoint_create.c b/common/core/src/ux_hcd_sim_host_interrupt_endpoint_create.c new file mode 100644 index 0000000..a42fa8d --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_interrupt_endpoint_create.c @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_interrupt_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an interrupt endpoint. The interrupt */ +/* endpoint has an interval of operation from 1 to 255. The host */ +/* has no hardware scheduler but we still build an interrupt tree */ +/* similar to the host simulator controller. */ +/* */ +/* This routine will match the best interval for the host */ +/* simulator. It will also determine the best node to hook the */ +/* endpoint based on the load that already exists on the horizontal */ +/* ED chain. */ +/* */ +/* For the ones curious about this coding. The tricky part is to */ +/* understand how the interrupt matrix is constructed. We have used */ +/* EDs with the skip bit on to build a frame of anchor EDs. Each ED */ +/* creates a node for an appropriate combination of interval frequency */ +/* in the list. */ +/* */ +/* After obtaining a pointer to the list with the lowest traffic, we */ +/* traverse the list from the highest interval until we reach the */ +/* interval required. At that node, we anchor our real ED to the node */ +/* and link the ED that was attached to the node to our ED. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_ed_obtain Obtain ED */ +/* _ux_hcd_sim_host_regular_td_obtain Obtain regular TD */ +/* _ux_hcd_sim_host_least_traffic_list_get Get least traffic list */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_interrupt_endpoint_create(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint) +{ + +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_ED *ed_list; +UX_HCD_SIM_HOST_ED *next_ed; +UX_HCD_SIM_HOST_TD *td; +UINT interval; +UINT interval_index; +UINT interval_sim_host; + + + /* Obtain a ED for this new endpoint. This ED will live as long as + the endpoint is active and will be the container for the TDs. */ + ed = _ux_hcd_sim_host_ed_obtain(hcd_sim_host); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Obtain a dummy TD for terminating the ED transfer chain. */ + td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + if (td == UX_NULL) + { + + ed -> ux_sim_host_ed_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *)ed; + + /* Now do the opposite, attach the ED container to the physical ED. */ + ed -> ux_sim_host_ed_endpoint = endpoint; + + /* Hook the TD to both the tail and head of the ED. */ + ed -> ux_sim_host_ed_tail_td = td; + ed -> ux_sim_host_ed_head_td = td; + + /* Get the list index with the least traffic. */ + ed_list = _ux_hcd_sim_host_least_traffic_list_get(hcd_sim_host); + + /* Get the interval for the endpoint and match it to a host simulator list. We match anything + that is > 32ms to the 32ms interval list, the 32ms list is list 0, 16ms list is 1... + the 1ms list is number 5. */ + interval = endpoint -> ux_endpoint_descriptor.bInterval; + interval_index = 1; + interval_sim_host = 5; + if (interval >= 32) + { + + interval_sim_host = 0; + } + else + { + + while (interval_index < 32) + { + + if (interval&interval_index) + interval_sim_host--; + interval_index = interval_index << 1; + } + } + + /* Now we need to scan the list of eds from the lowest load entry until we reach + the appropriate interval node. The depth index is the interval_sim_host value + and the 1st entry is pointed by the ED list entry. */ + while (interval_sim_host--) + { + + ed_list = ed_list -> ux_sim_host_ed_next_ed; + while (!(ed_list -> ux_sim_host_ed_status & UX_HCD_SIM_HOST_ED_STATIC)) + ed_list = ed_list -> ux_sim_host_ed_next_ed; + } + + /* We found the node entry of the ED pointer that will be the anchor for this interrupt + endpoint. Now we attach this endpoint to the anchor and rebuild the chain . */ + next_ed = ed_list -> ux_sim_host_ed_next_ed; + /* Note that if there is a crash here, it is most likely due to an invalid bInterval. */ + next_ed -> ux_sim_host_ed_previous_ed = ed; + ed -> ux_sim_host_ed_next_ed = next_ed; + ed -> ux_sim_host_ed_previous_ed = ed_list; + ed_list -> ux_sim_host_ed_next_ed = ed; + + /* There is activity in the periodic tree, the scheduler has to be active all the time. */ + hcd_sim_host -> ux_hcd_sim_host_periodic_scheduler_active++; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_iso_queue_process.c b/common/core/src/ux_hcd_sim_host_iso_queue_process.c new file mode 100644 index 0000000..7b88faa --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_iso_queue_process.c @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_iso_queue_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function process the isochronous transactions that happened */ +/* in the last frame. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_iso_queue_process(UX_HCD_SIM_HOST *hcd_sim_host) +{ + + UX_PARAMETER_NOT_USED(hcd_sim_host); + + /* Not implemented - just return. */ + return; +} + diff --git a/common/core/src/ux_hcd_sim_host_iso_schedule.c b/common/core/src/ux_hcd_sim_host_iso_schedule.c new file mode 100644 index 0000000..f4206b5 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_iso_schedule.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_iso_schedule PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function schedules new transfers from isochronous list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_iso_schedule(UX_HCD_SIM_HOST *hcd_sim_host) +{ + + UX_PARAMETER_NOT_USED(hcd_sim_host); + + /* Not currently implemented - just return. */ + return; +} + diff --git a/common/core/src/ux_hcd_sim_host_isochronous_endpoint_create.c b/common/core/src/ux_hcd_sim_host_isochronous_endpoint_create.c new file mode 100644 index 0000000..ef7f738 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_isochronous_endpoint_create.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_isochronous_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates an isochronous endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_ed_obtain Obtain host ED */ +/* _ux_hcd_sim_host_isochronous_td_obtain Obtain host ISO TD */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_isochronous_endpoint_create(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint) +{ + +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_ED *head_ed; +UX_HCD_SIM_HOST_ISO_TD *td; + + + /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint is + active and will be the container for the TDs. */ + ed = _ux_hcd_sim_host_ed_obtain(hcd_sim_host); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Obtain a dummy isoch TD for terminating the ED transfer chain. */ + td = _ux_hcd_sim_host_isochronous_td_obtain(hcd_sim_host); + if (td == UX_NULL) + { + + ed -> ux_sim_host_ed_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *) ed; + + /* Hook the TD to both the tail and head of the ED. */ + ed -> ux_sim_host_ed_tail_td = (UX_HCD_SIM_HOST_TD *) ((void *) td); + ed -> ux_sim_host_ed_head_td = (UX_HCD_SIM_HOST_TD *) ((void *) td); + + /* Attach this ED to the ISO list. */ + head_ed = hcd_sim_host -> ux_hcd_sim_host_iso_head_ed; + ed -> ux_sim_host_ed_next_ed = head_ed; + hcd_sim_host -> ux_hcd_sim_host_iso_head_ed = ed; + + /* Build the back chaining pointer. The previous head ED needs to know about the + inserted ED. */ + if (head_ed != UX_NULL) + head_ed -> ux_sim_host_ed_previous_ed = ed; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_isochronous_td_obtain.c b/common/core/src/ux_hcd_sim_host_isochronous_td_obtain.c new file mode 100644 index 0000000..fbd44b0 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_isochronous_td_obtain.c @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_isochronous_td_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free TD from the isochronous TD list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_HCD_SIM_HOST_ISO_TD * Pointer to host ISO ED */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_HCD_SIM_HOST_ISO_TD *_ux_hcd_sim_host_isochronous_td_obtain(UX_HCD_SIM_HOST *hcd_sim_host) +{ + +UX_HCD_SIM_HOST_ISO_TD *td; +ULONG td_index; + + + /* Start the search from the beginning of the list. */ + td = hcd_sim_host -> ux_hcd_sim_host_iso_td_list; + for (td_index = 0; td_index < _ux_system_host -> ux_system_host_max_iso_td; td_index++) + { + + /* Check the TD status, a free TD is marked with the UX_USED flag. */ + if (td -> ux_sim_host_iso_td_status == UX_UNUSED) + { + + /* The TD may have been used, so we reset all fields. */ + _ux_utility_memory_set(td, 0, sizeof(UX_HCD_SIM_HOST_ISO_TD)); + + /* This TD is now marked as USED. */ + td -> ux_sim_host_iso_td_status = UX_USED; + + /* Success, return pointer to TD. */ + return(td); + } + + /* Move to next TD. */ + td++; + } + + /* There is no available TD in the TD list, return a NULL. */ + return(UX_NULL); +} + diff --git a/common/core/src/ux_hcd_sim_host_least_traffic_list_get.c b/common/core/src/ux_hcd_sim_host_least_traffic_list_get.c new file mode 100644 index 0000000..24982b4 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_least_traffic_list_get.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_least_traffic_list_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function return a pointer to the first ED in the periodic */ +/* tree that has the least traffic registered. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_HCD_SIM_HOST_ED * Pointer to host ED */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_HCD_SIM_HOST_ED *_ux_hcd_sim_host_least_traffic_list_get(UX_HCD_SIM_HOST *hcd_sim_host) +{ + +UX_HCD_SIM_HOST_ED *min_bandwidth_ed; +UX_HCD_SIM_HOST_ED *begin_ed; +UX_HCD_SIM_HOST_ED *ed; +UX_ENDPOINT *endpoint; +UINT list_index; +ULONG min_bandwidth_used; +ULONG bandwidth_used; + + + /* Reset the min bandwidth used. */ + min_bandwidth_used = 0; + + /* The first ED is the list candidate for now. */ + min_bandwidth_ed = hcd_sim_host -> ux_hcd_sim_host_interrupt_ed_list[0]; + + /* All list will be scanned. */ + for (list_index = 0; list_index < 32; list_index++) + { + + /* Reset the bandwidth for this list. */ + bandwidth_used = 0; + + /* Get the ED of the beginning of the list we parse now. */ + ed = hcd_sim_host -> ux_hcd_sim_host_interrupt_ed_list[list_index]; + + /* We keep track of the first ED for the current list. */ + begin_ed = ed; + + /* Parse the EDs in the list. */ + while (ed -> ux_sim_host_ed_next_ed != UX_NULL) + { + + /* Get the endpoint pointer from the physical ED. */ + endpoint = ed -> ux_sim_host_ed_endpoint; + + if (endpoint != UX_NULL) + { + + /* Add to the bandwidth used the max packet size pointed by this ED. */ + bandwidth_used += (ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + } + + /* Move to next ED. */ + ed = ed -> ux_sim_host_ed_next_ed; + } + + /* We have processed a list, check the bandwidth used by this list. + If this bandwidth is the minimum, we memorize the ED. */ + if (bandwidth_used < min_bandwidth_used) + { + + /* We have found a better list with a lower used bandwidth, memorize the bandwidth + for this list. */ + min_bandwidth_used = bandwidth_used; + + /* Memorize the begin ED for this list. */ + min_bandwidth_ed = begin_ed; + } + } + + /* Return the ED list with the lowest bandwidth. */ + return(min_bandwidth_ed); +} + diff --git a/common/core/src/ux_hcd_sim_host_periodic_endpoint_destroy.c b/common/core/src/ux_hcd_sim_host_periodic_endpoint_destroy.c new file mode 100644 index 0000000..2ae407c --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_periodic_endpoint_destroy.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_periodic_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy an interrupt or isochronous endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_periodic_endpoint_destroy(UX_HCD_SIM_HOST *hcd_sim_host, UX_ENDPOINT *endpoint) +{ + +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_ED *previous_ed; +UX_HCD_SIM_HOST_ED *next_ed; +UX_HCD_SIM_HOST_TD *td; + + + /* From the endpoint container fetch the host simulator ED descriptor. */ + ed = (UX_HCD_SIM_HOST_ED *) endpoint -> ux_endpoint_ed; + + /* Get the previous ED in the list for this ED. */ + previous_ed = ed -> ux_sim_host_ed_previous_ed; + + /* Get the next ED in the list for this ED. */ + next_ed = ed -> ux_sim_host_ed_next_ed; + + /* The previous ED points now to the ED after the ED we are removing. */ + if (previous_ed) + previous_ed -> ux_sim_host_ed_next_ed = next_ed; + + /* Update the previous ED pointer in the next ED. */ + if (next_ed) + next_ed -> ux_sim_host_ed_previous_ed = previous_ed; + + /* We need to free the dummy TD that was attached to the ED. */ + td = ed -> ux_sim_host_ed_tail_td; + td -> ux_sim_host_td_status = UX_UNUSED; + + /* Now we can safely make the ED free. */ + ed -> ux_sim_host_ed_status = UX_UNUSED; + + /* Decrement the number of interrupt endpoints active. When the counter + reaches 0, the periodic scheduler will be turned off. */ + hcd_sim_host -> ux_hcd_sim_host_periodic_scheduler_active--; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_periodic_schedule.c b/common/core/src/ux_hcd_sim_host_periodic_schedule.c new file mode 100644 index 0000000..1a6c3e2 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_periodic_schedule.c @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_periodic_schedule PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function schedules new transfers from the periodic interrupt */ +/* list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_frame_number_get Get frame number */ +/* _ux_hcd_sim_host_transaction_schedule Schedule the transaction */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_periodic_schedule(UX_HCD_SIM_HOST *hcd_sim_host) +{ + +UX_HCD_SIM_HOST_ED *ed; +ULONG frame_number; + + /* Get the current frame number. */ + _ux_hcd_sim_host_frame_number_get(hcd_sim_host, &frame_number); + + /* Isolate the low bits to match an entry in the upper periodic entry list. */ + frame_number &= UX_HCD_SIM_HOST_PERIODIC_ENTRY_MASK; + + /* Get the first ED in the periodic list. */ + ed = hcd_sim_host -> ux_hcd_sim_host_interrupt_ed_list[frame_number]; + + /* Search for an entry in the periodic tree. */ + while (ed != UX_NULL) + { + + /* The ED has to be a real ED (not static) and has to have a different tail and head TD. */ + if ((ed -> ux_sim_host_ed_status != UX_HCD_SIM_HOST_ED_STATIC) && (ed -> ux_sim_host_ed_tail_td != ed -> ux_sim_host_ed_head_td)) + { + + /* Ensure this ED does not have the SKIP bit set and no TD are in progress. */ + if ((ed -> ux_sim_host_ed_head_td -> ux_sim_host_td_status & UX_HCD_SIM_HOST_TD_ACK_PENDING) == 0) + + /* Insert this transfer in the list of scheduled TDs if possible. */ + _ux_hcd_sim_host_transaction_schedule(hcd_sim_host, ed); + + } + + /* Point to the next ED in the list. */ + ed = ed -> ux_sim_host_ed_next_ed; + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_hcd_sim_host_periodic_tree_create.c b/common/core/src/ux_hcd_sim_host_periodic_tree_create.c new file mode 100644 index 0000000..87669a6 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_periodic_tree_create.c @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_periodic_tree_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the periodic static tree for the interrupt */ +/* and isochronous EDs. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_ed_obtain Obtain host ED */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_periodic_tree_create(UX_HCD_SIM_HOST *hcd_sim_host) +{ + +UX_HCD_SIM_HOST_ED *ed; +UINT list_index; +UINT list_entries; +UINT current_list_entry; +UX_HCD_SIM_HOST_ED *ed_list[32]; +UX_HCD_SIM_HOST_ED *ed_start_list[32]; + + + /* Start with the 1st list - it has 32 entries. */ + list_entries = 32; + + /* Create each list one by one starting from the 32ms list. */ + for (list_index = 0; list_index < 6; list_index++) + { + + for (current_list_entry = 0; current_list_entry < list_entries; current_list_entry++) + { + + /* In each list, insert an static ED as the anchor. There should not + be any errors when obtaining a new ED, still we do a sanity check. */ + ed = _ux_hcd_sim_host_ed_obtain(hcd_sim_host); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Mark this anchor ED as static by putting it as SKIPPED, the host simulator controller will + not look into its tail and head list and will simply jump to the next ED. */ + ed -> ux_sim_host_ed_status = UX_HCD_SIM_HOST_ED_STATIC; + + /* Either we hook this new ED to the start list for further processing + or we hook it to the 2 successive entries in the previous list. */ + if (list_index == 0) + { + + ed_start_list[current_list_entry] = ed; + } + else + { + + ed_list[current_list_entry * 2] -> ux_sim_host_ed_next_ed = ed; + ed_list[(current_list_entry * 2) + 1] -> ux_sim_host_ed_next_ed = ed; + } + + /* Memorize this ED in the local list. We do this operation now, otherwise + we would erase the previous list EDs. */ + ed_list[current_list_entry] = ed; + } + + /* Shift the number of entries in the next list by 1 (i.e. divide by 2). */ + list_entries = list_entries >> 1; + } + + /* The tree has been completed but the entries in the interrupt list are in the wrong order. + We need to swap each entry according to the host simulator specified entry order list + so that we have a fair interval frequency for each periodic ED. The primary EDs are + fetched from the start list, and stored in the interrupt list. */ + for (current_list_entry = 0; current_list_entry < 32; current_list_entry++) + { + + ed = ed_start_list[_ux_system_host_hcd_periodic_tree_entries[current_list_entry]]; + hcd_sim_host -> ux_hcd_sim_host_interrupt_ed_list[current_list_entry] = ed; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_port_reset.c b/common/core/src/ux_hcd_sim_host_port_reset.c new file mode 100644 index 0000000..6974740 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_port_reset.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" +#include "ux_dcd_sim_slave.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_port_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Implements the PORT_RESET request. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* port_index Port index to reset */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_disconnect Simulate device disconnection */ +/* _ux_dcd_sim_slave_initialize_complete Complete device initialization*/ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_port_reset(UX_HCD_SIM_HOST *hcd_sim_host, ULONG port_index) +{ + +UX_SLAVE_DEVICE *device; + + UX_PARAMETER_NOT_USED(hcd_sim_host); + UX_PARAMETER_NOT_USED(port_index); + + /* Get a pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Is this a connection? */ + if (device -> ux_slave_device_state == UX_DEVICE_RESET) + + /* Complete the device initialization. Note that everytime the device + is disconnected, this must be called again for connection. */ + _ux_dcd_sim_slave_initialize_complete(); + + else + { + + /* Host sent a PORT_RESET when the device is Attached, Addressed, or + Configured. Per the USB spec, we should go back to the default state. + We do this by 1) simulating disconnect to get rid of class and device + resources and 2) recreating necessary entities for control transfers. */ + _ux_device_stack_disconnect(); + _ux_dcd_sim_slave_initialize_complete(); + } + + /* In either case, mark the device as default/attached now. */ + device -> ux_slave_device_state = UX_DEVICE_ATTACHED; + + /* This function should never fail. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_port_status_get.c b/common/core/src/ux_hcd_sim_host_port_status_get.c new file mode 100644 index 0000000..50555f3 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_port_status_get.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_port_status_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the status for each port attached to the */ +/* root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* Host Simulator Port Status */ +/* */ +/* Where port status has the following format: */ +/* */ +/* bit 0 device connection status */ +/* if 0 : no device connected */ +/* if 1 : device connected to the port */ +/* bit 1 port enable status */ +/* if 0 : port disabled */ +/* if 1 : port enabled */ +/* bit 2 port suspend status */ +/* if 0 : port is not suspended */ +/* if 1 : port is suspended */ +/* bit 3 port overcurrent status */ +/* if 0 : port has no overcurrent condition */ +/* if 1 : port has overcurrent condition */ +/* bit 4 port reset status */ +/* if 0 : port is not in reset */ +/* if 1 : port is in reset */ +/* bit 5 port power status */ +/* if 0 : port power is off */ +/* if 1 : port power is on */ +/* bit 6-7 device attached speed */ +/* if 00 : low speed device attached */ +/* if 01 : full speed device attached */ +/* if 10 : high speed device attached */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_hcd_sim_host_port_status_get(UX_HCD_SIM_HOST *hcd_sim_host, ULONG port_index) +{ + +ULONG port_status; + + + /* Check to see if this port is valid on this controller. */ + if (hcd_sim_host -> ux_hcd_sim_host_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* The port is valid, build the status mask for this port. This function + returns always returned a device connected. */ + port_status = UX_PS_CCS; + + /* And this is a Full speed device. */ + port_status |= UX_PS_DS_FS; + + /* Return port status. */ + return(port_status); +} + diff --git a/common/core/src/ux_hcd_sim_host_regular_td_obtain.c b/common/core/src/ux_hcd_sim_host_regular_td_obtain.c new file mode 100644 index 0000000..ade397e --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_regular_td_obtain.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_regular_td_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free TD from the regular TD list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_HCD_SIM_HOST_TD * Pointer to host TD */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* _ux_utility_mutex_on Get mutex protection */ +/* _ux_utility_mutex_off Release mutex protection */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_HCD_SIM_HOST_TD *_ux_hcd_sim_host_regular_td_obtain(UX_HCD_SIM_HOST *hcd_sim_host) +{ + +UX_HCD_SIM_HOST_TD *td; +ULONG td_index; + + + /* Get the mutex as this is a critical section. */ + _ux_utility_mutex_on(&_ux_system -> ux_system_mutex); + + /* Start the search from the beginning of the list. */ + td = hcd_sim_host -> ux_hcd_sim_host_td_list; + + for (td_index = 0; td_index < _ux_system_host -> ux_system_host_max_td; td_index++) + { + + /* Check the TD status, a free TD is marked with the UNUSED flag. */ + if (td -> ux_sim_host_td_status == UX_UNUSED) + { + + /* The TD may have been used, so we reset all fields. */ + _ux_utility_memory_set(td, 0, sizeof(UX_HCD_SIM_HOST_TD)); + + /* This TD is now marked as USED. */ + td -> ux_sim_host_td_status = UX_USED; + + /* Release the mutex protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Return the TD pointer. */ + return(td); + } + + /* Move to next TD. */ + td++; + } + + /* There is no available TD in the TD list. */ + + /* Release the mutex protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Return a NULL pointer. */ + return(UX_NULL); +} + diff --git a/common/core/src/ux_hcd_sim_host_request_bulk_transfer.c b/common/core/src/ux_hcd_sim_host_request_bulk_transfer.c new file mode 100644 index 0000000..28c72aa --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_request_bulk_transfer.c @@ -0,0 +1,219 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_request_bulk_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a bulk transfer request. A bulk transfer */ +/* can be larger than the size of the sim_host buffer so it may be */ +/* required to chain multiple TDs to accommodate this transfer */ +/* request. A bulk transfer is non blocking, so we return before the */ +/* transfer request is completed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_regular_td_obtain Obtain regular TD */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_request_bulk_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_HCD_SIM_HOST_TD *data_td; +UX_HCD_SIM_HOST_TD *start_data_td; +UX_HCD_SIM_HOST_TD *next_data_td; +UX_HCD_SIM_HOST_TD *previous_td; +UX_HCD_SIM_HOST_TD *tail_td; +UX_HCD_SIM_HOST_ED *ed; +ULONG transfer_request_payload_length; +ULONG bulk_packet_payload_length; +UCHAR * data_pointer; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Use the TD pointer by ed -> tail for the first TD of this transfer + and chain from this one on. */ + data_td = ed -> ux_sim_host_ed_tail_td; + previous_td = data_td; + + /* Reset the first obtained data TD in case there is a TD shortage while building + the list of TDs. */ + start_data_td = 0; + + /* It may take more than one TD if the transfer_request length is more than the + maximum length for a host simulator TD (this is irrelevant of the MaxPacketSize value + in the endpoint descriptor). Host simulator data payload has a maximum size of 4K. */ + transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length; + data_pointer = transfer_request -> ux_transfer_request_data_pointer; + + while (transfer_request_payload_length != 0) + { + + if (transfer_request_payload_length > UX_HCD_SIM_HOST_MAX_PAYLOAD) + + bulk_packet_payload_length = UX_HCD_SIM_HOST_MAX_PAYLOAD; + else + + bulk_packet_payload_length = transfer_request_payload_length; + + /* Store the beginning of the buffer address in the TD. */ + data_td -> ux_sim_host_td_buffer = data_pointer; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_sim_host_td_length = bulk_packet_payload_length; + + /* Attach the endpoint and transfer_request to the TD. */ + data_td -> ux_sim_host_td_transfer_request = transfer_request; + data_td -> ux_sim_host_td_ed = ed; + + /* Adjust the data payload length and the data payload pointer. */ + transfer_request_payload_length -= bulk_packet_payload_length; + data_pointer += bulk_packet_payload_length; + + /* The direction of the transaction is set in the TD. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_IN; + else + data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_OUT; + + /* Mark the TD with the DATA phase. */ + data_td -> ux_sim_host_td_status |= UX_HCD_SIM_HOST_TD_DATA_PHASE; + + /* The Toggle value is in the ED. */ + data_td -> ux_sim_host_td_toggle = UX_HCD_SIM_HOST_TD_TOGGLE_FROM_ED; + + /* Check if there will be another transaction. */ + if (transfer_request_payload_length != 0) + { + + /* Get a new TD to hook this payload. */ + data_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + if (data_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while (data_td) + { + + next_data_td = data_td -> ux_sim_host_td_next_td; + data_td -> ux_sim_host_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* the first obtained TD in the chain has to be remembered. */ + if (start_data_td == UX_NULL) + start_data_td = data_td; + + /* Attach this new TD to the previous one. */ + previous_td -> ux_sim_host_td_next_td = data_td; + previous_td -> ux_sim_host_td_next_td_transfer_request = data_td; + previous_td = data_td; + } + } + + /* At this stage, the Head and Tail in the ED are still the same and the host simulator + controller will skip this ED until we have hooked the new tail TD. */ + tail_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + if (tail_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while (data_td) + { + + next_data_td = data_td -> ux_sim_host_td_next_td; + data_td -> ux_sim_host_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the tail TD to the last data TD. */ + data_td -> ux_sim_host_td_next_td = tail_td; + + /* Store the new tail TD. */ + ed -> ux_sim_host_ed_tail_td = tail_td; + + /* Now we can tell the scheduler to wake up. */ + hcd_sim_host -> ux_hcd_sim_host_queue_empty = UX_FALSE; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_request_control_transfer.c b/common/core/src/ux_hcd_sim_host_request_control_transfer.c new file mode 100644 index 0000000..9e0a2e0 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_request_control_transfer.c @@ -0,0 +1,321 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_request_control_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a control transfer from a transfer request. */ +/* The USB control transfer is in 3 phases (setup, data, status). */ +/* This function will chain all phases of the control sequence before */ +/* setting the sim_host endpoint as a candidate for transfer. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_regular_td_obtain Obtain regular TD */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_short_put Write 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_request_control_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UCHAR *setup_request; +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_TD *setup_td; +UX_HCD_SIM_HOST_TD *chain_td; +UX_HCD_SIM_HOST_TD *data_td; +UX_HCD_SIM_HOST_TD *tail_td; +UX_HCD_SIM_HOST_TD *status_td; +UX_HCD_SIM_HOST_TD *start_data_td; +UX_HCD_SIM_HOST_TD *next_data_td; +UINT status; +ULONG transfer_request_payload_length; +ULONG control_packet_payload_length; +UCHAR *data_pointer; + + + /* Get the pointer to the endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Build the SETUP packet (phase 1 of the control transfer). */ + setup_request = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_SETUP_SIZE); + if (setup_request == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + *setup_request = (UCHAR)transfer_request -> ux_transfer_request_function; + *(setup_request + UX_SETUP_REQUEST_TYPE) = (UCHAR)transfer_request -> ux_transfer_request_type; + *(setup_request + UX_SETUP_REQUEST) = (UCHAR)transfer_request -> ux_transfer_request_function; + _ux_utility_short_put(setup_request + UX_SETUP_VALUE, (USHORT)transfer_request -> ux_transfer_request_value); + _ux_utility_short_put(setup_request + UX_SETUP_INDEX, (USHORT)transfer_request -> ux_transfer_request_index); + _ux_utility_short_put(setup_request + UX_SETUP_LENGTH, (USHORT) transfer_request -> ux_transfer_request_requested_length); + + /* Use the TD pointer by ed -> tail for our setup TD and chain from this one on. */ + setup_td = ed -> ux_sim_host_ed_tail_td; + setup_td -> ux_sim_host_td_buffer = setup_request; + setup_td -> ux_sim_host_td_length = UX_SETUP_SIZE; + chain_td = setup_td; + + /* Attach the endpoint and transfer_request to the TD. */ + setup_td -> ux_sim_host_td_transfer_request = transfer_request; + setup_td -> ux_sim_host_td_ed = ed; + + /* Setup is OUT. */ + setup_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_OUT; + + /* Mark TD toggle as being DATA0. */ + setup_td -> ux_sim_host_td_toggle = 0; + + /* Mark the TD with the SETUP phase. */ + setup_td -> ux_sim_host_td_status |= UX_HCD_SIM_HOST_TD_SETUP_PHASE; + + /* Check if there is a data phase, if not jump to status phase. */ + data_td = UX_NULL; + start_data_td = UX_NULL; + + /* Use local variables to manipulate data pointer and length. */ + transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length; + data_pointer = transfer_request -> ux_transfer_request_data_pointer; + + /* Data starts with DATA1. For the data phase, we use the ED to contain the toggle. */ + ed -> ux_sim_host_ed_toggle = 1; + + /* The Control data payload may be split into several smaller blocks. */ + while (transfer_request_payload_length != 0) + { + + /* Get a new TD to hook this payload. */ + data_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + if (data_td == UX_NULL) + { + + /* Free the Setup packet resources. */ + _ux_utility_memory_free(setup_request); + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while (data_td) + { + + next_data_td = data_td -> ux_sim_host_td_next_td; + data_td -> ux_sim_host_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* Check the current payload requirement for the max size. */ + if (transfer_request_payload_length > UX_HCD_SIM_HOST_MAX_PAYLOAD) + + control_packet_payload_length = UX_HCD_SIM_HOST_MAX_PAYLOAD; + else + + control_packet_payload_length = transfer_request_payload_length; + + /* Store the beginning of the buffer address in the TD. */ + data_td -> ux_sim_host_td_buffer = data_pointer; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_sim_host_td_length = control_packet_payload_length; + + /* Attach the endpoint and transfer request to the TD. */ + data_td -> ux_sim_host_td_transfer_request = transfer_request; + data_td -> ux_sim_host_td_ed = ed; + + /* Adjust the data payload length and the data payload pointer. */ + transfer_request_payload_length -= control_packet_payload_length; + data_pointer += control_packet_payload_length; + + /* The direction of the transaction is set in the TD. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_IN; + else + + data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_OUT; + + /* Mark the TD with the DATA phase. */ + data_td -> ux_sim_host_td_status |= UX_HCD_SIM_HOST_TD_DATA_PHASE; + + /* The Toggle value is in the ED. */ + data_td -> ux_sim_host_td_toggle = UX_HCD_SIM_HOST_TD_TOGGLE_FROM_ED; + + /* The first obtained TD in the chain has to be remembered. */ + if (start_data_td == UX_NULL) + start_data_td = data_td; + + /* Attach this new TD to the previous one. */ + chain_td -> ux_sim_host_td_next_td = data_td; + chain_td -> ux_sim_host_td_next_td_transfer_request = data_td; + chain_td = data_td; + } + + /* Now, program the status phase. */ + status_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + + if (status_td == UX_NULL) + { + + _ux_utility_memory_free(setup_request); + if (data_td != UX_NULL) + { + + data_td = start_data_td; + while (data_td) + { + + next_data_td = data_td -> ux_sim_host_td_next_td; + data_td -> ux_sim_host_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the endpoint and transfer request to the TD. */ + status_td -> ux_sim_host_td_transfer_request = transfer_request; + status_td -> ux_sim_host_td_ed = ed; + + /* Mark the TD with the STATUS phase. */ + status_td -> ux_sim_host_td_status |= UX_HCD_SIM_HOST_TD_STATUS_PHASE; + + /* The direction of the status phase is IN if data phase is OUT and + vice versa. */ + if ((transfer_request -> ux_transfer_request_type&UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + status_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_OUT; + else + + status_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_IN; + + /* No data payload for the status phase. */ + status_td -> ux_sim_host_td_buffer = 0; + status_td -> ux_sim_host_td_length = 0; + + /* Status Phase toggle is ALWAYS 1. */ + status_td -> ux_sim_host_td_toggle = 1; + + /* Hook the status phase to the previous TD. */ + chain_td -> ux_sim_host_td_next_td = status_td; + + /* Since we have consumed out tail TD for the setup packet, we must get another one + and hook it to the ED's tail. */ + tail_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + if (tail_td == UX_NULL) + { + + _ux_utility_memory_free(setup_request); + if (data_td != UX_NULL) + data_td -> ux_sim_host_td_status = UX_UNUSED; + status_td -> ux_sim_host_td_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Hook the new TD to the status TD. */ + status_td -> ux_sim_host_td_next_td = tail_td; + + /* At this stage, the Head and Tail in the ED are still the same and + the host simulator controller will skip this ED until we have hooked the new + tail TD. */ + ed -> ux_sim_host_ed_tail_td = tail_td; + + /* Now we can tell the scheduler to wake up. */ + hcd_sim_host -> ux_hcd_sim_host_queue_empty = UX_FALSE; + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* There was an error, return to the caller. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + + /* Free the resources. */ + _ux_utility_memory_free(setup_request); + + /* Return completion to caller. */ + return(transfer_request -> ux_transfer_request_completion_code); +} + diff --git a/common/core/src/ux_hcd_sim_host_request_interupt_transfer.c b/common/core/src/ux_hcd_sim_host_request_interupt_transfer.c new file mode 100644 index 0000000..02ab430 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_request_interupt_transfer.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_request_interrupt_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs an interrupt transfer request. An interrupt */ +/* transfer can only be as large as the Maxpacket Field in the */ +/* endpoint descriptor. This was verified at the USB layer and does */ +/* not need to be reverified here. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_regular_td_obtain Obtain regular TD */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_request_interrupt_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_TD *data_td; +UX_HCD_SIM_HOST_TD *tail_td; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Use the TD pointer by ed -> tail for the first TD of this transfer + and chain from this one on. */ + data_td = ed -> ux_sim_host_ed_tail_td; + + /* Set the direction of the transfer. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_IN; + else + data_td -> ux_sim_host_td_direction = UX_HCD_SIM_HOST_TD_OUT; + + /* Mark the TD with the DATA phase. */ + data_td -> ux_sim_host_td_status |= UX_HCD_SIM_HOST_TD_DATA_PHASE; + + /* The Toggle value is in the ED. */ + data_td -> ux_sim_host_td_toggle = UX_HCD_SIM_HOST_TD_TOGGLE_FROM_ED; + + /* Store the beginning of the buffer address in the TD. */ + data_td -> ux_sim_host_td_buffer = transfer_request -> ux_transfer_request_data_pointer; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_sim_host_td_length = transfer_request -> ux_transfer_request_requested_length; + + /* Attach the endpoint and transfer request to the TD. */ + data_td -> ux_sim_host_td_transfer_request = transfer_request; + data_td -> ux_sim_host_td_ed = ed; + + /* At this stage, the Head and Tail in the ED are still the same and + the host simulator controller will skip this ED until we have hooked the new + tail TD. */ + tail_td = _ux_hcd_sim_host_regular_td_obtain(hcd_sim_host); + if (tail_td == UX_NULL) + return(UX_NO_TD_AVAILABLE); + + /* Attach the tail TD to the last data TD. */ + data_td -> ux_sim_host_td_next_td = tail_td; + + /* Store the new tail TD. */ + ed -> ux_sim_host_ed_tail_td = tail_td; + + /* Now we can tell the scheduler to wake up. */ + hcd_sim_host -> ux_hcd_sim_host_queue_empty = UX_FALSE; + + /* There is no need to wake up the sim_host controller on this transfer + since periodic transactions will be picked up when the interrupt + tree is scanned. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_request_isochronous_transfer.c b/common/core/src/ux_hcd_sim_host_request_isochronous_transfer.c new file mode 100644 index 0000000..fd037ce --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_request_isochronous_transfer.c @@ -0,0 +1,230 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_request_isochronous_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs an isochronous transfer request. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_frame_number_get Get frame number */ +/* _ux_hcd_sim_host_isochronous_td_obtain Obtain isochronous TD */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_request_isochronous_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_HCD_SIM_HOST_ISO_TD *data_td; +UX_HCD_SIM_HOST_ISO_TD *start_data_td; +UX_HCD_SIM_HOST_ISO_TD *next_data_td; +UX_HCD_SIM_HOST_ISO_TD *previous_td; +UX_HCD_SIM_HOST_ISO_TD *tail_td; +UX_HCD_SIM_HOST_ED *ed; +ULONG transfer_request_payload_length; +ULONG isoch_packet_payload_length; +UCHAR * data_pointer; +ULONG current_frame_number; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* If the transfer_request specifies a max packet length other than the endpoint + size, we force the transfer request value into the endpoint. */ + if (transfer_request -> ux_transfer_request_packet_length == 0) + transfer_request -> ux_transfer_request_packet_length = (ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + + /* Remember the packet length. */ + isoch_packet_payload_length = transfer_request -> ux_transfer_request_packet_length; + + /* Use the TD pointer by ed -> tail for the first TD of this transfer and chain from this one on. */ + data_td = (UX_HCD_SIM_HOST_ISO_TD *) ((void *) ed -> ux_sim_host_ed_tail_td); + previous_td = data_td; + + /* Reset the first obtained data TD in case there is a TD shortage while building the list of TDs. */ + start_data_td = UX_NULL; + + /* Calculate the frame number to be used to send this payload. If there are no current transfers, + we take the current frame number and add a safety value (2-5) to it. If here is pending transactions, + we use the frame number stored in the transfer request. */ + if (ed -> ux_sim_host_ed_tail_td == ed -> ux_sim_host_ed_head_td) + { + + _ux_hcd_sim_host_frame_number_get(hcd_sim_host, ¤t_frame_number); + ed -> ux_sim_host_ed_frame = current_frame_number + UX_HCD_SIM_HOST_FRAME_DELAY; + } + else + { + + current_frame_number = ed -> ux_sim_host_ed_frame; + } + + /* Load the start buffer address and URB length to split the URB in multiple TD transfer. */ + transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length; + data_pointer = transfer_request -> ux_transfer_request_data_pointer; + + while (transfer_request_payload_length != 0) + { + + /* Set the direction of the TD. */ + if ((transfer_request -> ux_transfer_request_type&UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + data_td -> ux_sim_host_iso_td_direction = UX_HCD_SIM_HOST_TD_IN; + else + + data_td -> ux_sim_host_iso_td_direction = UX_HCD_SIM_HOST_TD_OUT; + + /* Set the frame number. */ + ed -> ux_sim_host_ed_frame = current_frame_number; + + /* Set the buffer address. */ + data_td -> ux_sim_host_iso_td_buffer = data_pointer; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_sim_host_iso_td_length = isoch_packet_payload_length; + + /* Attach the endpoint and transfer request to the TD. */ + data_td -> ux_sim_host_iso_td_transfer_request = transfer_request; + data_td -> ux_sim_host_iso_td_ed = ed; + + /* Adjust the data payload length and the data payload pointer. */ + transfer_request_payload_length -= isoch_packet_payload_length; + data_pointer += isoch_packet_payload_length; + + /* Prepare the next frame for the next TD in advance. */ + current_frame_number++; + + /* Check if there will be another transaction. */ + if (transfer_request_payload_length != 0) + { + + /* Get a new TD to hook this payload. */ + data_td = _ux_hcd_sim_host_isochronous_td_obtain(hcd_sim_host); + if (data_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while(data_td) + { + + next_data_td = data_td -> ux_sim_host_iso_td_next_td; + data_td -> ux_sim_host_iso_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* the first obtained TD in the chain has to be remembered. */ + if (start_data_td == UX_NULL) + start_data_td = data_td; + + /* Attach this new TD to the previous one. */ + previous_td -> ux_sim_host_iso_td_next_td = data_td; + previous_td = data_td; + } + } + + /* Memorize the next frame number for this ED. */ + ed -> ux_sim_host_ed_frame = current_frame_number; + + /* At this stage, the Head and Tail in the ED are still the same and the host simulator controller + will skip this ED until we have hooked the new tail TD. */ + tail_td = _ux_hcd_sim_host_isochronous_td_obtain(hcd_sim_host); + if (tail_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while(data_td) + { + + next_data_td = data_td -> ux_sim_host_iso_td_next_td; + data_td -> ux_sim_host_iso_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the tail TD to the last data TD. */ + data_td -> ux_sim_host_iso_td_next_td = tail_td; + + /* Adjust the ED tail pointer, the controller can now start this transfer + at the chosen frame number. */ + ed -> ux_sim_host_ed_tail_td = (UX_HCD_SIM_HOST_TD *) ((void *) tail_td); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_request_transfer.c b/common/core/src/ux_hcd_sim_host_request_transfer.c new file mode 100644 index 0000000..827856b --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_request_transfer.c @@ -0,0 +1,124 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_request_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the handler for all the transactions on the USB. */ +/* The transfer request passed as parameter contains the endpoint and */ +/* the device descriptors in addition to the type of transaction de */ +/* be executed. This function routes the transfer request to */ +/* according to the type of transfer to be executed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_sim_host_request_bulk_transfer Request bulk transfer */ +/* _ux_hcd_sim_host_request_control_transfer Request control */ +/* transfer */ +/* _ux_hcd_sim_host_request_interrupt_transfer Request interrupt */ +/* transfer */ +/* _ux_hcd_sim_host_request_isochronous_transfer Request isochronous */ +/* transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_request_transfer(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UINT status = 0; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* We reset the actual length field of the transfer request as a safety measure. */ + transfer_request -> ux_transfer_request_actual_length = 0; + + /* Isolate the endpoint type and route the transfer request. */ + switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + + status = _ux_hcd_sim_host_request_control_transfer(hcd_sim_host, transfer_request); + break; + + + case UX_BULK_ENDPOINT: + + status = _ux_hcd_sim_host_request_bulk_transfer(hcd_sim_host, transfer_request); + break; + + case UX_INTERRUPT_ENDPOINT: + + status = _ux_hcd_sim_host_request_interrupt_transfer(hcd_sim_host, transfer_request); + break; + + case UX_ISOCHRONOUS_ENDPOINT: + + status = _ux_hcd_sim_host_request_isochronous_transfer(hcd_sim_host, transfer_request); + break; + + } + + /* Note that it is physically impossible to have a wrong endpoint type here + so no error checking. */ + return(status); +} + diff --git a/common/core/src/ux_hcd_sim_host_timer_function.c b/common/core/src/ux_hcd_sim_host_timer_function.c new file mode 100644 index 0000000..f65e0c0 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_timer_function.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" +#include "tx_timer.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_timer_function PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the timer function of the simulator. It is */ +/* invoked on a timer every tick. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host_addr Address of host controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_sim_host_timer_function(ULONG hcd_sim_host_addr) +{ + +UX_HCD_SIM_HOST *hcd_sim_host; +UX_HCD *hcd; + + + /* Setup pointer to simulator host structure. */ + UX_TIMER_EXTENSION_PTR_GET(hcd_sim_host, UX_HCD_SIM_HOST, hcd_sim_host_addr) + + /* Get the pointers to the generic HCD areas. */ + hcd = hcd_sim_host -> ux_hcd_sim_host_hcd_owner; + + /* Increase the interrupt count. This indicates the controller is still alive. */ + hcd_sim_host -> ux_hcd_sim_host_interrupt_count++; + + /* Check if the controller is operational, if not, skip it. */ + if (hcd -> ux_hcd_status == UX_HCD_STATUS_OPERATIONAL) + { + + /* Wake up the thread for the controller transaction processing. */ + hcd -> ux_hcd_thread_signal++; + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_hcd_semaphore); + } +} + + diff --git a/common/core/src/ux_hcd_sim_host_transaction_schedule.c b/common/core/src/ux_hcd_sim_host_transaction_schedule.c new file mode 100644 index 0000000..af75c68 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_transaction_schedule.c @@ -0,0 +1,495 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" +#include "ux_dcd_sim_slave.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_transaction_schedule PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function bridges a transaction from the host to the slave */ +/* simulation controller. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* ed Pointer to ED */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_transfer_request_completion_function) */ +/* Completion function */ +/* _ux_device_stack_control_request_process */ +/* Process request */ +/* _ux_utility_memory_copy Copy memory block */ +/* _ux_utility_semaphore_put Semaphore put */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_transaction_schedule(UX_HCD_SIM_HOST *hcd_sim_host, UX_HCD_SIM_HOST_ED *ed) +{ + +UX_DCD_SIM_SLAVE *dcd_sim_slave; +UX_HCD_SIM_HOST_TD *td; +UX_HCD_SIM_HOST_TD *head_td; +UX_HCD_SIM_HOST_TD *tail_td; +UX_HCD_SIM_HOST_TD *data_td; +UX_ENDPOINT *endpoint; +UX_SLAVE_ENDPOINT *slave_endpoint; +UX_DCD_SIM_SLAVE_ED *slave_ed; +ULONG slave_transfer_remaining; +UCHAR wake_host; +UCHAR wake_slave; +ULONG transaction_length; +UX_SLAVE_TRANSFER *slave_transfer_request; +UX_TRANSFER *transfer_request; +ULONG endpoint_index; +UX_SLAVE_DCD *dcd; + + UX_PARAMETER_NOT_USED(hcd_sim_host); + + /* Get the pointer to the DCD portion of the simulator. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Check the state of the controller if OPERATIONAL . */ + if (dcd -> ux_slave_dcd_status != UX_DCD_STATUS_OPERATIONAL) + return(UX_ERROR); + + /* Get the pointer to the candidate TD on the host. */ + td = ed -> ux_sim_host_ed_head_td; + + /* Get the pointer to the endpoint. */ + endpoint = ed -> ux_sim_host_ed_endpoint; + + /* Get the pointer to the transfer_request attached with this TD. */ + transfer_request = td -> ux_sim_host_td_transfer_request; + + /* Get the index of the endpoint from the host. */ + endpoint_index = endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~(ULONG)UX_ENDPOINT_DIRECTION; + + /* Get the address of the device controller. */ + dcd_sim_slave = (UX_DCD_SIM_SLAVE *) dcd -> ux_slave_dcd_controller_hardware; + + /* Get the endpoint as seen from the device side. */ + slave_ed = &dcd_sim_slave -> ux_dcd_sim_slave_ed[endpoint_index]; + + /* Is this ED used? */ + if ((slave_ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_USED) == 0) + return(UX_ERROR); + + /* Is this ED ready for transaction or stalled ? */ + if ((slave_ed -> ux_sim_slave_ed_status & (UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER | UX_DCD_SIM_SLAVE_ED_STATUS_STALLED)) == 0) + return(UX_ERROR); + + /* Get the logical endpoint from the physical endpoint. */ + slave_endpoint = slave_ed -> ux_sim_slave_ed_endpoint; + + /* Get the pointer to the transfer request. */ + slave_transfer_request = &slave_endpoint -> ux_slave_endpoint_transfer_request; + + /* Check the phase for this transfer, if this is the SETUP phase, treatment is different. Explanation of how + control transfers are handled in the simulator: if the data phase is OUT, we handle it immediately, meaning we + send all the data to the device and remove the STATUS TD in the same scheduler call. If the data phase is IN, we + only take out the SETUP TD and handle the data phase like any other non-control transactions (i.e. the scheduler + calls us again with the DATA TDs). */ + if (td -> ux_sim_host_td_status & UX_HCD_SIM_HOST_TD_SETUP_PHASE) + { + + /* For control transfer, stall is for protocol error and it's cleared any time when SETUP is received */ + slave_ed -> ux_sim_slave_ed_status &= ~(ULONG)UX_DCD_SIM_SLAVE_ED_STATUS_STALLED; + + /* Set the length to the setup transaction buffer. */ + slave_transfer_request -> ux_slave_transfer_request_actual_length = td -> ux_sim_host_td_length; + + /* Move the buffer from the host TD to the device TD. */ + _ux_utility_memory_copy(slave_transfer_request -> ux_slave_transfer_request_setup, td -> ux_sim_host_td_buffer, + td -> ux_sim_host_td_length); + + /* The setup phase never fails. We acknowledge the transfer code here by taking the TD out of the endpoint. */ + ed -> ux_sim_host_ed_head_td = td -> ux_sim_host_td_next_td; + + /* Free the TD that was used here. */ + td -> ux_sim_host_td_status = UX_UNUSED; + + /* Check if the transaction is OUT from the host and there is data payload. */ + if (((*slave_transfer_request -> ux_slave_transfer_request_setup & UX_REQUEST_IN) == 0) && + (*(slave_transfer_request -> ux_slave_transfer_request_setup + 6) != 0 || + *(slave_transfer_request -> ux_slave_transfer_request_setup + 7) != 0)) + { + + /* This is the case where there is a data payload OUT from host to device. + the data needs to be copied into the device buffer first before invoking the control + dispatcher. */ + + /* Get the length we expect from the SETUP packet. */ + slave_transfer_request -> ux_slave_transfer_request_requested_length = _ux_utility_short_get(slave_transfer_request -> ux_slave_transfer_request_setup + 6); + + /* Reset what we have received so far. */ + slave_transfer_request -> ux_slave_transfer_request_actual_length = 0; + + /* And reprogram the current buffer address to the beginning of the buffer. */ + slave_transfer_request -> ux_slave_transfer_request_current_data_pointer = slave_transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the pointer to the first data TD. this is the TD right after the SETUP one. */ + data_td = td -> ux_sim_host_td_next_td; + + /* Get the data length we expect. */ + transaction_length = slave_transfer_request -> ux_slave_transfer_request_requested_length; + + /* It may have taken multiple TDs to send all the data. */ + while (transaction_length != 0) + { + + /* Do a sanity check. TD must be data. */ + if (data_td -> ux_sim_host_td_status & UX_HCD_SIM_HOST_TD_DATA_PHASE) + { + + /* Copy the amount of data in the td into the slave transaction buffer. */ + _ux_utility_memory_copy(slave_transfer_request -> ux_slave_transfer_request_current_data_pointer, data_td -> ux_sim_host_td_buffer, + data_td -> ux_sim_host_td_length); + + /* Add to the actual payload length. */ + slave_transfer_request -> ux_slave_transfer_request_actual_length += data_td -> ux_sim_host_td_length; + + /* Update the host transfer's actual length. */ + transfer_request -> ux_transfer_request_actual_length += transaction_length; + + /* Decrement the total length. */ + transaction_length -= data_td -> ux_sim_host_td_length; + + /* Update buffer pointer. */ + slave_transfer_request -> ux_slave_transfer_request_current_data_pointer += data_td -> ux_sim_host_td_length; + + /* Update the td in the head. */ + ed -> ux_sim_host_ed_head_td = data_td; + + /* Get the pointer to the next data TD. */ + data_td = data_td -> ux_sim_host_td_next_td; + + /* Free the TD that was used here. */ + ed -> ux_sim_host_ed_head_td -> ux_sim_host_td_status = UX_UNUSED; + } + } + + /* Make the head TD point to the STATUS TD. */ + ed -> ux_sim_host_ed_head_td = ed -> ux_sim_host_ed_head_td -> ux_sim_host_td_next_td; + } + + /* Is there no hub? */ + if (dcd_sim_slave -> ux_dcd_sim_slave_dcd_control_request_process_hub == UX_NULL) + { + + /* There's no hub to worry about. This control transfer is for the + device itself. */ + + /* Pass the transfer to the regular device stack. */ + _ux_device_stack_control_request_process(slave_transfer_request); + } + else + { + + /* There is a hub. We need to call the correct Control Transfer dispatcher. + If the device is a hub and this transfer is for one of the devices on the + hub, then we must invoke a separate Control Transfer dispatcher besides + the regular device stack's. This is because the current device stack doesn't + handle control transfers to device's other than itself. */ + + /* Is this meant for the device itself? */ + if (/* If the device isn't ADDRESSED yet, then the address may be invalid since we don't + clear it upon disconnection/reconnection. So we assume that if the device is RESET + or ATTACHED, the control transfer is meant for the device itself. */ + (_ux_system_slave->ux_system_slave_device.ux_slave_device_state == UX_DEVICE_RESET || + _ux_system_slave->ux_system_slave_device.ux_slave_device_state == UX_DEVICE_ATTACHED) || + + /* If we get to this check, then the device has been ADDRESSED and we can compare addresses. */ + endpoint -> ux_endpoint_device -> ux_device_address == dcd -> ux_slave_dcd_device_address) + { + + /* Yes, this control transfer is meant for the device itself. */ + + /* Pass the transfer to the regular device stack. */ + _ux_device_stack_control_request_process(slave_transfer_request); + } + else + { + + /* No, this control transfer is meant for a device on the hub. */ + + /* Pass the transfer to the callback. */ + dcd_sim_slave -> ux_dcd_sim_slave_dcd_control_request_process_hub(slave_transfer_request); + } + } + + /* Check if the transaction is OUT from the host. */ + if ((*slave_transfer_request -> ux_slave_transfer_request_setup & UX_REQUEST_IN) == 0) + { + + /* Check if there is a problem with the endpoint (maybe stalled). */ + if (slave_ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_STALLED) + { + + /* Protocol error, stall the transaction. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STALLED; + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_STALLED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_STALLED, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + } + else + /* No error in simulation. */ + transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS; + + /* In this case the transfer is completed! We take out the status TD. */ + td = ed -> ux_sim_host_ed_head_td; + + /* Adjust the ED. */ + ed -> ux_sim_host_ed_head_td = td -> ux_sim_host_td_next_td; + + /* Free the TD that was used here. */ + td -> ux_sim_host_td_status = UX_UNUSED; + + /* Then, we wake up the host. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + } + } + else + { + + /* Check if there is a problem with the endpoint (maybe stalled). */ + if (slave_ed -> ux_sim_slave_ed_status & UX_DCD_SIM_SLAVE_ED_STATUS_STALLED) + { + + /* Stall the transaction. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STALLED; + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_STALLED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_STALLED, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Wake up the host side. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* Clean up this ED. */ + head_td = ed -> ux_sim_host_ed_head_td; + tail_td = ed -> ux_sim_host_ed_tail_td; + + /* Free all TDs attached to the ED. */ + while (head_td != tail_td) + { + + /* Mark the current head TD as free. */ + head_td -> ux_sim_host_td_status = UX_UNUSED; + + /* Update the head TD with the next TD. */ + ed -> ux_sim_host_ed_head_td = head_td -> ux_sim_host_td_next_td; + + /* Now the new head_td is the next TD in the chain. */ + head_td = ed -> ux_sim_host_ed_head_td; + } + } + else + { + + slave_transfer_remaining = 0; + + /* If the device tries to send a NULL packet, we don't reset the actual length to 0. */ + if (slave_transfer_request -> ux_slave_transfer_request_requested_length != 0) + slave_transfer_remaining = slave_transfer_request -> ux_slave_transfer_request_requested_length - slave_transfer_request -> ux_slave_transfer_request_actual_length; + + /* Get the transaction length to be transferred. It could be a ZLP condition. */ + if (slave_transfer_remaining <= td -> ux_sim_host_td_length) + transaction_length = slave_transfer_remaining; + else + transaction_length = td -> ux_sim_host_td_length; + + if (td -> ux_sim_host_td_direction == UX_HCD_SIM_HOST_TD_OUT) + + /* Send the requested host data to the device. */ + _ux_utility_memory_copy(slave_transfer_request -> ux_slave_transfer_request_current_data_pointer, td -> ux_sim_host_td_buffer, + transaction_length); + + else + + /* Send the requested host data to the device. */ + _ux_utility_memory_copy(td -> ux_sim_host_td_buffer, slave_transfer_request -> ux_slave_transfer_request_current_data_pointer, + transaction_length); + + /* Update buffers. */ + td -> ux_sim_host_td_buffer += transaction_length; + slave_transfer_request -> ux_slave_transfer_request_current_data_pointer += transaction_length; + + /* Update actual length values. */ + td -> ux_sim_host_td_actual_length += transaction_length; + transfer_request -> ux_transfer_request_actual_length += transaction_length; + slave_transfer_request -> ux_slave_transfer_request_actual_length += transaction_length; + + /* Update requested length values. */ + td -> ux_sim_host_td_length -= transaction_length; + + /* Are we done with this TD (It's possible for the TD to expect more data; for example, the slave + sent/received a smaller amount)? */ + if (td -> ux_sim_host_td_length == 0) + { + + /* Free the TD that was used here. */ + td -> ux_sim_host_td_status = UX_UNUSED; + + /* Adjust the ED. */ + ed -> ux_sim_host_ed_head_td = td -> ux_sim_host_td_next_td; + } + + /* Reset wake booleans. */ + wake_host = UX_FALSE; + wake_slave = UX_FALSE; + + /* Does the slave have absolutely no more data to send? */ + if ((slave_transfer_request -> ux_slave_transfer_request_actual_length == slave_transfer_request -> ux_slave_transfer_request_requested_length && + slave_transfer_request -> ux_slave_transfer_request_force_zlp == UX_TRUE) || + (transaction_length == 0) || + (transaction_length % slave_endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize)) + { + + wake_host = UX_TRUE; + wake_slave = UX_TRUE; + } + else + { + + /* Is the host's transfer completed? */ + if (transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length) + wake_host = UX_TRUE; + + /* Is the slaves's transfer completed? */ + if (slave_transfer_request -> ux_slave_transfer_request_actual_length == slave_transfer_request -> ux_slave_transfer_request_requested_length) + wake_slave = UX_TRUE; + } + + if (wake_slave == UX_TRUE) + { + + /* Set the completion code to no error. */ + slave_transfer_request -> ux_slave_transfer_request_completion_code = UX_SUCCESS; + + /* Set the transfer status to COMPLETED. */ + slave_transfer_request -> ux_slave_transfer_request_status = UX_TRANSFER_STATUS_COMPLETED; + + /* Is this not the control endpoint? */ + if (slave_ed -> ux_sim_slave_ed_index != 0) + { + + /* Reset the ED to NO TRANSFER status. */ + slave_ed -> ux_sim_slave_ed_status &= ~(ULONG)UX_DCD_SIM_SLAVE_ED_STATUS_TRANSFER; + + /* Wake up the slave side. */ + _ux_utility_semaphore_put(&slave_transfer_request -> ux_slave_transfer_request_semaphore); + } + } + + if (wake_host == UX_TRUE) + { + + /* If the slave has less data to send than the host wants to receive, then there may still be + TDs left to free. Note that this should only happen for IN transactions, since the only way + an OUT transfer can complete is if all the data was sent i.e. all the TDs were sent and freed. */ + if (ed -> ux_sim_host_ed_head_td != ed -> ux_sim_host_ed_tail_td) + { + + /* Free all TDs associated with this transfer. Note that if this is a control transfer (which + means it must be IN), then this also gets rid of the STATUS phase, which is okay. Also note + that assumes that there is only one transfer occurring on this endpoint; if there were + multiple, then we'd erase that one's TDs as well. Luckily, with the way USBX is designed, + there should never be multiple transfers occurring simultaneously on a single endpoint + (tldr; data pointer for transfers is shared). */ + head_td = ed -> ux_sim_host_ed_head_td; + while (head_td != ed -> ux_sim_host_ed_tail_td) + { + + /* Free the TD that was used here. */ + head_td -> ux_sim_host_td_status = UX_UNUSED; + + /* Move to the next. */ + head_td = head_td -> ux_sim_host_td_next_td; + } + + /* Update the head and tail TD. */ + ed -> ux_sim_host_ed_head_td = head_td; + ed -> ux_sim_host_ed_tail_td = head_td; + } + + /* Set the completion code to no error. */ + transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS; + + /* Set the transfer status to COMPLETED. */ + transfer_request -> ux_transfer_request_status = UX_TRANSFER_STATUS_COMPLETED; + + /* Is there a callback on the host? */ + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* Wake up the host side. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + } + } + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_hcd_sim_host_transfer_abort.c b/common/core/src/ux_hcd_sim_host_transfer_abort.c new file mode 100644 index 0000000..a205018 --- /dev/null +++ b/common/core/src/ux_hcd_sim_host_transfer_abort.c @@ -0,0 +1,131 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Simulator Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_sim_host.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_sim_host_transfer_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will abort transactions attached to a transfer */ +/* request. */ +/* */ +/* INPUT */ +/* */ +/* hcd_sim_host Pointer to host controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_delay_ms Delay */ +/* */ +/* CALLED BY */ +/* */ +/* Host Simulator Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_sim_host_transfer_abort(UX_HCD_SIM_HOST *hcd_sim_host, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_HCD_SIM_HOST_ED *ed; +UX_HCD_SIM_HOST_TD *head_td; +UX_HCD_SIM_HOST_TD *tail_td; + + UX_PARAMETER_NOT_USED(hcd_sim_host); + + /* Get the pointer to the endpoint associated with the transfer request. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* From the endpoint container, get the address of the physical endpoint. */ + ed = (UX_HCD_SIM_HOST_ED *) endpoint -> ux_endpoint_ed; + + /* Check if this physical endpoint has been initialized properly! */ + if (ed == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* The endpoint may be active. If so, set the skip bit. */ + ed -> ux_sim_host_ed_status |= UX_HCD_SIM_HOST_ED_SKIP; + + /* Wait for the controller to finish the current frame processing. */ + _ux_utility_delay_ms(1); + + /* Remove all the TDs from this ED and leave the head and tail pointing to the dummy TD. */ + head_td = ed -> ux_sim_host_ed_head_td; + tail_td = ed -> ux_sim_host_ed_tail_td; + + /* Free all TDs attached to the ED. */ + while (head_td != tail_td) + { + + /* Mark the current head TD as free. */ + head_td -> ux_sim_host_td_status = UX_UNUSED; + + /* Update the head TD with the next TD. */ + ed -> ux_sim_host_ed_head_td = head_td -> ux_sim_host_td_next_td; + + /* Now the new head TD is the next TD in the chain. */ + head_td = ed -> ux_sim_host_ed_head_td; + } + + /* Remove the reset bit in the ED. */ + ed -> ux_sim_host_ed_status = (ULONG)~UX_HCD_SIM_HOST_ED_SKIP; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_class_dpump_activate.c b/common/core/src/ux_host_class_dpump_activate.c new file mode 100644 index 0000000..1a69150 --- /dev/null +++ b/common/core/src/ux_host_class_dpump_activate.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to activate the class. */ +/* */ +/* INPUT */ +/* */ +/* command Dpump class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_dpump_configure Configure dpump class */ +/* _ux_host_class_dpump_endpoints_get Get endpoints of dpump */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_semaphore_create Create dpump semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_dpump_entry Entry of dpump class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_DPUMP *dpump; +UINT status; + + + /* The data pump is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + dpump = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_DPUMP)); + if (dpump == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + dpump -> ux_host_class_dpump_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the dpump class instance. */ + dpump -> ux_host_class_dpump_interface = interface; + + /* Store the device container into the dpump class instance. */ + dpump -> ux_host_class_dpump_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) dpump; + + /* Create this class instance. */ + status = _ux_host_stack_class_instance_create(dpump -> ux_host_class_dpump_class, (VOID *) dpump); + + /* Configure the dpump. */ + status = _ux_host_class_dpump_configure(dpump); + if (status != UX_SUCCESS) + { + + _ux_host_stack_class_instance_destroy(dpump -> ux_host_class_dpump_class, (VOID *) dpump); + return(status); + } + + /* Get the dpump endpoint(s). We will need to search for Bulk Out and Bulk In endpoints. Do not check for errors + here as the alternate setting for this interface may be 0 which has no endpoints. */ + status = _ux_host_class_dpump_endpoints_get(dpump); + + /* Create the semaphore to protect 2 threads from accessing the same dpump instance. */ + status = _ux_utility_semaphore_create(&dpump -> ux_host_class_dpump_semaphore, "ux_dpump_semaphore", 1); + if (status != UX_SUCCESS) + return(UX_SEMAPHORE_ERROR); + + /* Mark the dpump as live now. */ + dpump -> ux_host_class_dpump_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if ((status == UX_SUCCESS) && (_ux_system_host -> ux_system_host_change_function != UX_NULL)) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, dpump -> ux_host_class_dpump_class, (VOID *) dpump); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_DPUMP_ACTIVATE, dpump, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, dpump, 0, 0, 0) + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_class_dpump_configure.c b/common/core/src/ux_host_class_dpump_configure.c new file mode 100644 index 0000000..5b3867d --- /dev/null +++ b/common/core/src/ux_host_class_dpump_configure.c @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* dpump. Once the dpump is configured, its interface will be */ +/* activated. The bulk endpoints enumerated(1 IN, 1 OUT ). */ +/* */ +/* INPUT */ +/* */ +/* dpump Pointer to dpump class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_dpump_activate Data Pump class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_configure(UX_HOST_CLASS_DPUMP *dpump) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (dpump -> ux_host_class_dpump_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A dpump normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(dpump -> ux_host_class_dpump_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, dpump -> ux_host_class_dpump_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the dpump power source and check the parent power source for + incompatible connections. */ + if (dpump -> ux_host_class_dpump_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = dpump -> ux_host_class_dpump_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root dpump and we don't have to worry + if the parent is not the root dpump, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, dpump, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the dpump default alternate setting for the dpump interface is + active and the interrupt endpoint is now enabled. We have to memorize the first interface since + the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &dpump -> ux_host_class_dpump_interface); + if (status != UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + dpump -> ux_host_class_dpump_interface -> ux_interface_class_instance = (VOID *) dpump; + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/core/src/ux_host_class_dpump_deactivate.c b/common/core/src/ux_host_class_dpump_deactivate.c new file mode 100644 index 0000000..3fab273 --- /dev/null +++ b/common/core/src/ux_host_class_dpump_deactivate.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the dpump has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* pipes will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Data Pump class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_sleep Sleep thread */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_dpump_entry Entry of dpump class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_DPUMP *dpump; +UINT status; + + + /* Get the instance for this class. */ + dpump = (UX_HOST_CLASS_DPUMP *) command -> ux_host_class_command_instance; + + /* The dpump is being shut down. */ + dpump -> ux_host_class_dpump_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&dpump -> ux_host_class_dpump_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, dpump, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(dpump -> ux_host_class_dpump_bulk_out_endpoint); + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(dpump -> ux_host_class_dpump_bulk_in_endpoint); + + /* If the class instance was busy, let it finish properly and not return. */ + _ux_utility_thread_sleep(UX_ENUMERATION_THREAD_WAIT); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(dpump -> ux_host_class_dpump_class, (VOID *) dpump); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&dpump -> ux_host_class_dpump_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, dpump -> ux_host_class_dpump_class, (VOID *) dpump); + } + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_DPUMP_DEACTIVATE, dpump, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(dpump); + + /* Free the dpump instance memory. */ + _ux_utility_memory_free(dpump); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_class_dpump_endpoints_get.c b/common/core/src/ux_host_class_dpump_endpoints_get.c new file mode 100644 index 0000000..466c423 --- /dev/null +++ b/common/core/src/ux_host_class_dpump_endpoints_get.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function search for the handle of the bulk out and bulk in */ +/* endpoints. */ +/* */ +/* INPUT */ +/* */ +/* dpump Pointer to dpump class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_dpump_activate Activate dpump class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_endpoints_get(UX_HOST_CLASS_DPUMP *dpump) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; + + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < dpump -> ux_host_class_dpump_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(dpump -> ux_host_class_dpump_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* We have found the bulk endpoint, save it. */ + dpump -> ux_host_class_dpump_bulk_out_endpoint = endpoint; + break; + } + } + } + + /* The bulk out endpoint is mandatory. */ + if (dpump -> ux_host_class_dpump_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, dpump, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. It is attached to the interface container. */ + + for (endpoint_index = 0; endpoint_index < dpump -> ux_host_class_dpump_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(dpump -> ux_host_class_dpump_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the bulk endpoint, save it. */ + dpump -> ux_host_class_dpump_bulk_in_endpoint = endpoint; + break; + } + } + } + + /* The bulk in endpoint is mandatory. */ + if (dpump -> ux_host_class_dpump_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, dpump, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_class_dpump_entry.c b/common/core/src/ux_host_class_dpump_entry.c new file mode 100644 index 0000000..6be5385 --- /dev/null +++ b/common/core/src/ux_host_class_dpump_entry.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the dpump class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* dpump on the bus or when the USB dpump is removed. */ +/* */ +/* INPUT */ +/* */ +/* command Data pump class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_dpump_activate Activate dpump class */ +/* _ux_host_class_dpump_deactivate Deactivate dpump class */ +/* */ +/* CALLED BY */ +/* */ +/* Data pump Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + (command -> ux_host_class_command_class == UX_HOST_CLASS_DPUMP_CLASS) && + (command -> ux_host_class_command_subclass == UX_HOST_CLASS_DPUMP_SUBCLASS) && + (command -> ux_host_class_command_protocol == UX_HOST_CLASS_DPUMP_PROTOCOL)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_dpump_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_dpump_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/core/src/ux_host_class_dpump_ioctl.c b/common/core/src/ux_host_class_dpump_ioctl.c new file mode 100644 index 0000000..7e197ea --- /dev/null +++ b/common/core/src/ux_host_class_dpump_ioctl.c @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the application to change the alternate */ +/* setting of the dpump class. */ +/* */ +/* INPUT */ +/* */ +/* dpump Pointer to the dpump class */ +/* ioctl_function ioctl function */ +/* parameter pointer to structure */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_setting_select */ +/* Select alternate setting */ +/* */ +/* CALLED BY */ +/* */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_ioctl(UX_HOST_CLASS_DPUMP *dpump, ULONG ioctl_function, + VOID *parameter) +{ + +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UINT status; + + /* Ensure the instance is valid. */ + if ((dpump -> ux_host_class_dpump_state != UX_HOST_CLASS_INSTANCE_LIVE) && + (dpump -> ux_host_class_dpump_state != UX_HOST_CLASS_INSTANCE_MOUNTING)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, dpump, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* The command request will tell us we need to do here. */ + switch (ioctl_function) + { + + case UX_HOST_CLASS_DPUMP_SELECT_ALTERNATE_SETTING: + + + /* The parameter value has the alternate setting number. + We need to scan the entire device framework. Only one configuration for data pump device framework. */ + interface = dpump -> ux_host_class_dpump_interface; + configuration = interface -> ux_interface_configuration; + + /* Do some verification just in case ! */ + if (configuration == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Point to the first interface. */ + interface = configuration -> ux_configuration_first_interface; + + /* Loop on all interfaces and alternate settings for this device in search of the right alternate setting. */ + while (interface != UX_NULL) + { + + /* Check the alternate setting. */ + if (interface -> ux_interface_descriptor.bAlternateSetting == (ULONG) (ALIGN_TYPE) parameter) + { + + /* We have found the alternate setting. Select it now. */ + status = _ux_host_stack_interface_setting_select(interface); + + /* We are done here. */ + return(status); + } + + /* Next interface. */ + interface = interface -> ux_interface_next_interface; + } + + /* We come here when the alternate setting was not found. */ + status = UX_INTERFACE_HANDLE_UNKNOWN; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + break; + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/core/src/ux_host_class_dpump_read.c b/common/core/src/ux_host_class_dpump_read.c new file mode 100644 index 0000000..363eb0b --- /dev/null +++ b/common/core/src/ux_host_class_dpump_read.c @@ -0,0 +1,205 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the dpump interface. The call is */ +/* blocking and only returns when there is either an error or when */ +/* the transfer is complete. */ +/* */ +/* INPUT */ +/* */ +/* dpump Pointer to dpump class */ +/* data_pointer Pointer to buffer */ +/* requested_length Requested data read */ +/* actual_length Actual data read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_read(UX_HOST_CLASS_DPUMP *dpump, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_DPUMP_READ, dpump, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_dpump_name, (VOID *) dpump) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, dpump, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&dpump -> ux_host_class_dpump_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Start by resetting the actual length of the transfer to zero. */ + *actual_length = 0; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &dpump -> ux_host_class_dpump_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk in endpoint until either the transfer is + completed or until there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer_request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_HOST_CLASS_DPUMP_CLASS_TRANSFER_TIMEOUT); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer request in case some data was actually received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* There was a non transfer error, no partial transfer to be checked. */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually received and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to receive. */ + requested_length -= transfer_request_length; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_class_dpump_write.c b/common/core/src/ux_host_class_dpump_write.c new file mode 100644 index 0000000..ceba568 --- /dev/null +++ b/common/core/src/ux_host_class_dpump_write.c @@ -0,0 +1,205 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Data Pump Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_dpump.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_dpump_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the dpump interface. The call is blocking */ +/* and only returns when there is either an error or when the transfer */ +/* is complete. */ +/* */ +/* INPUT */ +/* */ +/* dpump Pointer to dpump class */ +/* data_pointer Pointer to data to write */ +/* requested_length Length of data to write */ +/* actual_length Actual length of data written */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_dpump_write(UX_HOST_CLASS_DPUMP *dpump, UCHAR * data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_DPUMP_WRITE, dpump, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if(_ux_host_stack_class_instance_verify((UCHAR *) _ux_system_host_class_dpump_name, (VOID *) dpump) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, dpump, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&dpump -> ux_host_class_dpump_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Start by resetting the actual length of the transfer. */ + *actual_length = 0; + + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &dpump -> ux_host_class_dpump_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk out endpoint until either the transfer is + completed or when there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer_request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_HOST_CLASS_DPUMP_CLASS_TRANSFER_TIMEOUT); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer_request in case some data actually went out. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be sent. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually sent and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to send out. */ + requested_length -= transfer_request_length; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&dpump -> ux_host_class_dpump_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_bandwidth_check.c b/common/core/src/ux_host_stack_bandwidth_check.c new file mode 100644 index 0000000..aa2e6c9 --- /dev/null +++ b/common/core/src/ux_host_stack_bandwidth_check.c @@ -0,0 +1,270 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_bandwidth_check PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will check if there is enough bandwidth on the USB */ +/* for the specified endpoint. The bandwidth requirement is calculated */ +/* by the MaxPacketSize field of endpoint and the speed of the */ +/* endpoint. If the device is on a 1.1 bus or it is a 1.1 device */ +/* behind a 2.0 hub on a 2.0 bus, the device bandwidth must be */ +/* multiplied by 8 on the 1.1 segment. */ +/* */ +/* This algorithm takes into account both TT bandwidth and HCD */ +/* bandwidth. The TTs are attached to the device structure and not */ +/* the hub structure in order to make the stack agnostic of the hub */ +/* class. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_bandwidth_check(UX_HCD *hcd, UX_ENDPOINT *endpoint) +{ + +UX_DEVICE *device; +UX_DEVICE *parent_device; +USHORT hcd_bandwidth_claimed; +USHORT max_packet_size; +LONG packet_size; +USHORT tt_bandwidth_claimed = 0; +ULONG port_index; +ULONG port_map; +ULONG tt_index; +const UCHAR overheads[4][3] = { +/* LS FS HS */ + {63, 45, 173}, /* Control */ + { 0, 9, 38}, /* Isochronous */ + { 0, 13, 55}, /* Bulk */ + {19, 13, 55} /* Interrupt */ +}; + + /* Get the pointer to the device. */ + device = endpoint -> ux_endpoint_device; + + /* Calculate the bandwidth. From USB spec. + * + * The frame unit consumed per byte is like follow: + * Bytes/FrameUnit FrameUnit/byte FrameUnit/byte + * (Overhead included) (HS baseline) (FS baseline) + * Low Speed 187.5 40 8 + * Full Speed 1500 5 1 + * High Speed 7500 1 1/5 + * + * The overhead is like follow: + * Control Isochronous Bulk Interrupt + * bmAttribute (0) (1) (2) (3) + * Low Speed 63 -- -- 19 + * Full Speed 45 9 13 13 + * High Speed 173 38 55 55 + * + * Worst case bit stuffing is calculated as 1.1667 (7/6) times the raw time. + */ + + /* Get maximum packet size. */ + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + + /* Rough time for possible Bit Stuffing. */ + packet_size = (max_packet_size * 7 + 5) / 6; + + /* Add overhead. */ + packet_size += overheads[endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE][device -> ux_device_speed]; + max_packet_size = (USHORT)packet_size; + + /* Check for high-speed endpoint. */ + if (device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) + { + + /* Get number of transactions. */ + max_packet_size = (USHORT)(max_packet_size * + (((endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> + UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1)); + } + + /* Calculate the bandwidth claimed by this endpoint for the main bus. */ + if (hcd -> ux_hcd_version != 0x200) + { + + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + /* Low speed transfer takes 40x more units than high speed. */ + hcd_bandwidth_claimed = (USHORT)(max_packet_size * 8 * 5); + else + { + + if (device -> ux_device_speed == UX_FULL_SPEED_DEVICE) + /* Full speed transfer takes 5x more units than high speed. */ + hcd_bandwidth_claimed = (USHORT)(max_packet_size * 5); + else + /* Use high speed timing as base for bus bandwidth calculation. */ + hcd_bandwidth_claimed = (USHORT)max_packet_size; + } + } + else + { + + hcd_bandwidth_claimed = (USHORT)max_packet_size; + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + /* Low speed transfer takes 8x more units than full speed. */ + tt_bandwidth_claimed = (USHORT)(max_packet_size * 8); + else + /* Use full speed timing as base for TT bandwidth calculation. */ + tt_bandwidth_claimed = (USHORT)max_packet_size; + } + + /* Do we have enough on the bus for this new endpoint? */ + if (hcd -> ux_hcd_available_bandwidth < hcd_bandwidth_claimed) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_BANDWIDTH_AVAILABLE, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_NO_BANDWIDTH_AVAILABLE); + + return(UX_NO_BANDWIDTH_AVAILABLE); + } + + /* We need to take care of the case where the endpoint belongs to a USB 1.1 + device that sits behind a 2.0 hub. We ignore cases where the device + is either high speed or the bus is 1.1. */ + if ((device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) || (hcd -> ux_hcd_version != 0x200)) + { + + /* The device is high speed, therefore no need for TT. */ + return(UX_SUCCESS); + } + + /* We have a 1.1 device, check if the parent is a 2.0 hub. */ + parent_device = device -> ux_device_parent; + if (parent_device == UX_NULL) + { + + /* We are at the root, this controller must support 1.1 then! */ + return(UX_SUCCESS); + } + + /* We get here when the parent is a hub. The problem occurs when the hub is itself + connected to a chain of hubs. We need to find the first 2.0 hub parent to this + chain to check the TT. We need to remember the port on which the first 1.1 + device is hooked to. */ + port_index = device -> ux_device_port_location - 1; + + /* Scan the chain of hubs upward. */ + while (parent_device != UX_NULL) + { + + /* Determine if the device is high speed. */ + if (parent_device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) + { + + /* The device is a high speed hub, find the TT that manages the port. + The first 1.1 device is connected to. First we calculate the port + mapping bit. */ + port_map = (ULONG)(1 << port_index); + + /* Parse all the TTs attached to the hub. */ + for (tt_index = 0; tt_index < UX_MAX_TT; tt_index++) + { + + /* Check if this TT owns the port where the device is attached. */ + if ((parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_port_mapping & port_map) != 0) + { + + /* We have found the port, check if the tt can give us the bandwidth + we want to claim. */ + if (parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_max_bandwidth < tt_bandwidth_claimed) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_BANDWIDTH_AVAILABLE, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_NO_BANDWIDTH_AVAILABLE); + + return(UX_NO_BANDWIDTH_AVAILABLE); + } + + else + return(UX_SUCCESS); + } + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_BANDWIDTH_AVAILABLE, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_NO_BANDWIDTH_AVAILABLE); + + /* We should never get here !!!!! */ + return(UX_NO_BANDWIDTH_AVAILABLE); + } + + /* We now remember where this hub is located on the parent. */ + port_index = parent_device -> ux_device_port_location - 1; + + /* We go up one level in the hub chain. */ + parent_device = parent_device -> ux_device_parent; + } + + /* We get here when we have not found a 2.0 hub in the list and we got to the root port. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_bandwidth_claim.c b/common/core/src/ux_host_stack_bandwidth_claim.c new file mode 100644 index 0000000..1227bc4 --- /dev/null +++ b/common/core/src/ux_host_stack_bandwidth_claim.c @@ -0,0 +1,244 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_bandwidth_claim PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reserve bandwidth for a periodic endpoint. The */ +/* bandwidth requirement is calculated by the MaxPacketSize field of */ +/* endpoint and the speed of the endpoint. If the device is on a 1.1 */ +/* bus or it is a 1.1 device behind a 2.0 hub on a 2.0 bus, the device */ +/* bandwidth must be multiplied by 8 on the 1.1 segment. */ +/* */ +/* This algorithm takes into account both TT bandwidth and HCD */ +/* bandwidth. The TTs are attached to the device structure and not */ +/* the hub structure in order to make the stack agnostic of the hub */ +/* class. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_bandwidth_claim(UX_HCD *hcd, UX_ENDPOINT *endpoint) +{ + +UX_DEVICE *device; +UX_DEVICE *parent_device; +USHORT hcd_bandwidth_claimed; +USHORT max_packet_size; +LONG packet_size; +USHORT tt_bandwidth_claimed = 0; +ULONG port_index; +ULONG port_map; +ULONG tt_index; +const UCHAR overheads[4][3] = { +/* LS FS HS */ + {63, 45, 173}, /* Control */ + { 0, 9, 38}, /* Isochronous */ + { 0, 13, 55}, /* Bulk */ + {19, 13, 55} /* Interrupt */ +}; + + /* Get the pointer to the device. */ + device = endpoint -> ux_endpoint_device; + + /* Calculate the bandwidth. From USB spec. + * + * The frame unit consumed per byte is like follow: + * Bytes/FrameUnit FrameUnit/byte FrameUnit/byte + * (Overhead included) (HS baseline) (FS baseline) + * Low Speed 187.5 40 8 + * Full Speed 1500 5 1 + * High Speed 7500 1 1/5 + * + * The overhead is like follow: + * Control Isochronous Bulk Interrupt + * bmAttribute (0) (1) (2) (3) + * Low Speed 63 -- -- 19 + * Full Speed 45 9 13 13 + * High Speed 173 38 55 55 + * + * Worst case bit stuffing is calculated as 1.1667 (7/6) times the raw time. + */ + + /* Get maximum packet size. */ + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + + /* Rough time for possible Bit Stuffing. */ + packet_size = (max_packet_size * 7 + 5) / 6; + + /* Add overhead. */ + packet_size += overheads[endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE][device -> ux_device_speed]; + max_packet_size = (USHORT)packet_size; + + /* Check for high-speed endpoint. */ + if (device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) + { + + /* Get number of transactions. */ + max_packet_size = (USHORT)(max_packet_size * + (((endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> + UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1)); + } + + /* Calculate the bandwidth claimed by this endpoint for the main bus. */ + if (hcd -> ux_hcd_version != 0x200) + { + + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + /* Low speed transfer takes 40x more units than high speed. */ + hcd_bandwidth_claimed = (USHORT)(max_packet_size * 8 * 5); + else + { + + if (device -> ux_device_speed == UX_FULL_SPEED_DEVICE) + /* Full speed transfer takes 5x more units than high speed. */ + hcd_bandwidth_claimed = (USHORT)(max_packet_size * 5); + else + /* Use high speed timing as base for bus bandwidth calculation. */ + hcd_bandwidth_claimed = (USHORT)max_packet_size; + } + } + else + { + + hcd_bandwidth_claimed = (USHORT)max_packet_size; + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + /* Low speed transfer takes 8x more units than full speed. */ + tt_bandwidth_claimed = (USHORT)(max_packet_size * 8); + else + /* Use full speed timing as base for TT bandwidth calculation. */ + tt_bandwidth_claimed = (USHORT)max_packet_size; + } + + /* Allocate the HCD bandwidth, since it's already checked by _bandwidth_check. */ + hcd -> ux_hcd_available_bandwidth -= hcd_bandwidth_claimed; + + /* We need to take care of the case where the endpoint belongs to a USB 1.1 + device that sits behind a 2.0 hub. We ignore cases where the device + is either high speed or the bus is 1.1. */ + if ((device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) || (hcd -> ux_hcd_version != 0x200)) + { + + /* The device is high speed, therefore no need for TT. */ + return; + } + + /* We have a 1.1 device, check if the parent is a 2.0 hub. */ + parent_device = device -> ux_device_parent; + if (parent_device == UX_NULL) + { + + /* We are at the root, must be a 1.1 controller then! */ + return; + } + + /* We get here when the parent is a hub. The problem occurs when the hub is + itself connected to a chain of hubs. We need to find the first 2.0 hub + parent to this chain to check the TT. We need to remember the port on + which the first 1.1 device is hooked to. */ + port_index = device -> ux_device_port_location - 1; + + /* Scan the chain of hubs upward. */ + while (parent_device != UX_NULL) + { + + /* Is the device high speed? */ + if (parent_device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) + { + + /* The device is a high speed hub, find the TT that manages the port. + The first 1.1 device is connected to. First we calculate the port + mapping bit. */ + port_map = (ULONG)(1 << port_index); + + /* Parse all the TTs attached to the hub. + Since we confirmed exist of TT in previous _check, + just do while loop here. + */ + tt_index = 0; + while(1) + { + /* Check if this TT owns the port where the device is attached. */ + if ((parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_port_mapping & port_map) != 0) + { + + /* We have found the port, check if the tt can give us the bandwidth + we want to claim. */ + parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_max_bandwidth -= tt_bandwidth_claimed; + return; + } + + /* Try next index. */ + tt_index ++; + } + } + + /* We now remember where this hub is located on the parent. */ + port_index = parent_device -> ux_device_port_location - 1; + + /* We go up one level in the hub chain. */ + parent_device = parent_device -> ux_device_parent; + } + + /* We get here when we have not found a 2.0 hub in the list and we got + to the root port. */ + return; +} diff --git a/common/core/src/ux_host_stack_bandwidth_release.c b/common/core/src/ux_host_stack_bandwidth_release.c new file mode 100644 index 0000000..082af6f --- /dev/null +++ b/common/core/src/ux_host_stack_bandwidth_release.c @@ -0,0 +1,240 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_bandwidth_release PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will release bandwidth for a periodic endpoint. The */ +/* bandwidth requirement is calculated by the MaxPacketSize field of */ +/* endpoint and the speed of the endpoint. If the device is on a 1.1 */ +/* bus or it is a 1.1 device behind a 2.0 hub on a 2.0 bus, the device */ +/* bandwidth must be multiplied by 8 on the 1.1 segment. */ +/* */ +/* This algorithm takes into account both TT bandwidth and HCD */ +/* bandwidth. The TTs are attached to the device structure and not the */ +/* hub structure in order to make the stack agnostic of the hub class. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_bandwidth_release(UX_HCD *hcd, UX_ENDPOINT *endpoint) +{ + +UX_DEVICE *device; +UX_DEVICE *parent_device; +USHORT hcd_bandwidth_claimed; +USHORT max_packet_size; +LONG packet_size; +USHORT tt_bandwidth_claimed = 0; +ULONG port_index; +ULONG port_map; +ULONG tt_index; +const UCHAR overheads[4][3] = { +/* LS FS HS */ + {63, 45, 173}, /* Control */ + { 0, 9, 38}, /* Isochronous */ + { 0, 13, 55}, /* Bulk */ + {19, 13, 55} /* Interrupt */ +}; + + /* Get the pointer to the device. */ + device = endpoint -> ux_endpoint_device; + + /* Calculate the bandwidth. From USB spec. + * + * The frame unit consumed per byte is like follow: + * Bytes/FrameUnit FrameUnit/byte FrameUnit/byte + * (Overhead included) (HS baseline) (FS baseline) + * Low Speed 187.5 40 8 + * Full Speed 1500 5 1 + * High Speed 7500 1 1/5 + * + * The overhead is like follow: + * Control Isochronous Bulk Interrupt + * bmAttribute (0) (1) (2) (3) + * Low Speed 63 -- -- 19 + * Full Speed 45 9 13 13 + * High Speed 173 38 55 55 + * + * Worst case bit stuffing is calculated as 1.1667 (7/6) times the raw time. + */ + + /* Get maximum packet size. */ + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + + /* Rough time for possible Bit Stuffing. */ + packet_size = (max_packet_size * 7 + 5) / 6; + + /* Add overhead. */ + packet_size += overheads[endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE][device -> ux_device_speed]; + max_packet_size = (USHORT)packet_size; + + /* Check for high-speed endpoint. */ + if (device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) + { + + /* Get number of transactions. */ + max_packet_size = (USHORT)(max_packet_size * + (((endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> + UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1)); + } + + /* Calculate the bandwidth claimed by this endpoint for the main bus. */ + if (hcd -> ux_hcd_version != 0x200) + { + + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + /* Low speed transfer takes 40x more units than high speed. */ + hcd_bandwidth_claimed = (USHORT)(max_packet_size * 8 * 5); + else + { + + if (device -> ux_device_speed == UX_FULL_SPEED_DEVICE) + /* Full speed transfer takes 5x more units than high speed. */ + hcd_bandwidth_claimed = (USHORT)(max_packet_size * 5); + else + /* Use high speed timing as base for bus bandwidth calculation. */ + hcd_bandwidth_claimed = (USHORT)max_packet_size; + } + } + else + { + + hcd_bandwidth_claimed = (USHORT)max_packet_size; + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + /* Low speed transfer takes 8x more units than full speed. */ + tt_bandwidth_claimed = (USHORT)(max_packet_size * 8); + else + /* Use full speed timing as base for TT bandwidth calculation. */ + tt_bandwidth_claimed = (USHORT)max_packet_size; + } + + /* Free the HCD bandwidth. */ + hcd -> ux_hcd_available_bandwidth += hcd_bandwidth_claimed; + + /* We need to take care of the case where the endpoint belongs to a USB 1.1 + device that sits behind a 2.0 hub. We ignore cases where the device + is either high speed or the bus is 1.1. */ + if ((device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) || (hcd -> ux_hcd_version != 0x200)) + { + + /* The device is high speed, therefore no need for TT. */ + return; + } + + /* We have a 1.1 device, check if the parent is a 2.0 hub. */ + parent_device = device -> ux_device_parent; + if (parent_device == UX_NULL) + { + + /* We are at the root, must be a 1.1 controller then! */ + return; + } + + /* We get here when the parent is a hub. The problem occurs when the hub is itself + connected to a chain of hubs. We need to find the first 2.0 hub parent to this chain + to check the TT. We need to remember the port on which the first 1.1 device is + hooked to. */ + port_index = device -> ux_device_port_location - 1; + + /* Scan the chain of hubs upward. */ + while (parent_device != UX_NULL) + { + + /* Check for a high speed device. */ + if (parent_device -> ux_device_speed == UX_HIGH_SPEED_DEVICE) + { + + /* The device is a high speed hub, find the TT that manages the port. + The first 1.1 device is connected to. First we calculate the port mapping bit. */ + port_map = (ULONG)(1 << port_index); + + /* Parse all the TTs attached to the hub. */ + for (tt_index = 0; tt_index < UX_MAX_TT; tt_index++) + { + + /* Check if this TT owns the port where the device is attached. */ + if ((parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_port_mapping & port_map) != 0) + { + + /* We have found the port, check if the tt can give us the bandwidth + we want to claim. */ + parent_device -> ux_device_hub_tt[tt_index].ux_hub_tt_max_bandwidth += tt_bandwidth_claimed; + return; + } + } + + /* We should never get here!!!!! */ + return; + } + + /* We now remember where this hub is located on the parent. */ + port_index = parent_device -> ux_device_port_location - 1; + + /* We go up one level in the hub chain. */ + parent_device = parent_device -> ux_device_parent; + } + + /* We get here when we have not found a 2.0 hub in the list and we got + to the root port. */ + return; +} + diff --git a/common/core/src/ux_host_stack_class_call.c b/common/core/src/ux_host_stack_class_call.c new file mode 100644 index 0000000..f3a8513 --- /dev/null +++ b/common/core/src/ux_host_stack_class_call.c @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_call PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will call all the registered classes to the USBX */ +/* stack. Each class will have the possibility to own the device or */ +/* one of the interfaces of a device. */ +/* */ +/* INPUT */ +/* */ +/* class_command Class command structure */ +/* */ +/* OUTPUT */ +/* */ +/* Number of owners */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_entry_function) Class entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_HOST_CLASS *_ux_host_stack_class_call(UX_HOST_CLASS_COMMAND *class_command) +{ + +UX_HOST_CLASS *class; +ULONG class_index; +UINT status; + + + /* Start from the 1st registered classes with USBX. */ + class = _ux_system_host -> ux_system_host_class_array; + + /* Parse all the class drivers. */ + for (class_index = 0; class_index < _ux_system_host -> ux_system_host_max_class; class_index++) + { + + /* Check if this class driver is used. */ + if (class -> ux_host_class_status == UX_USED) + { + + /* We have found a potential candidate. Call this registered class entry function. */ + status = class -> ux_host_class_entry_function(class_command); + + /* The status tells us if the registered class wants to own this class. */ + if (status == UX_SUCCESS) + { + + /* Yes, return this class pointer. */ + return(class); + } + } + + /* Move to the next registered class. */ + class++; + } + + /* There is no driver who want to own this class! */ + return(UX_NULL); +} + diff --git a/common/core/src/ux_host_stack_class_device_scan.c b/common/core/src/ux_host_stack_class_device_scan.c new file mode 100644 index 0000000..e3ffaa4 --- /dev/null +++ b/common/core/src/ux_host_stack_class_device_scan.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_device_scan PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will scan all registered classes with a device */ +/* candidate. Priority is given to the PID/VID and then the */ +/* Class/Subclass/Protocol. */ +/* */ +/* INPUT */ +/* */ +/* device Pointer to device */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_call Call host stack class */ +/* (ux_host_class_entry_function) Class entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_device_scan(UX_DEVICE *device) +{ + +UINT status; +UX_HOST_CLASS *class; +UX_HOST_CLASS_COMMAND class_command; + + /* Perform the command initialization. */ + class_command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_QUERY; + class_command.ux_host_class_command_container = (VOID *) device; + class_command.ux_host_class_command_vid = device -> ux_device_descriptor.idVendor; + class_command.ux_host_class_command_pid = device -> ux_device_descriptor.idProduct; + class_command.ux_host_class_command_class = device -> ux_device_descriptor.bDeviceClass; + class_command.ux_host_class_command_subclass = device -> ux_device_descriptor.bDeviceSubClass; + class_command.ux_host_class_command_subclass = device -> ux_device_descriptor.bDeviceSubClass; + class_command.ux_host_class_command_protocol = device -> ux_device_descriptor.bDeviceProtocol; + class_command.ux_host_class_command_iad_class = 0; + class_command.ux_host_class_command_iad_subclass = 0; + class_command.ux_host_class_command_iad_protocol = 0; + + /* We start with the PID/VID for this device. */ + class_command.ux_host_class_command_usage = UX_HOST_CLASS_COMMAND_USAGE_PIDVID; + class = _ux_host_stack_class_call(&class_command); + + /* On return, either we have found a class or the device is still an orphan. */ + if (class != UX_NULL) + { + + device -> ux_device_class = class; + class_command.ux_host_class_command_class_ptr = class; + class_command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_ACTIVATE; + status = device -> ux_device_class -> ux_host_class_entry_function(&class_command); + + /* Return result of activation. */ + return(status); + } + + /* It the PID/VID did not work, we continue looking for the Class\Subclass\Protocol match. */ + class_command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_QUERY; + class_command.ux_host_class_command_container = (VOID *) device; + class_command.ux_host_class_command_usage = UX_HOST_CLASS_COMMAND_USAGE_CSP; + + class = _ux_host_stack_class_call(&class_command); + + /* On return, either we have found a class or the device is still an orphan. */ + if (class != UX_NULL) + { + + device -> ux_device_class = class; + class_command.ux_host_class_command_class_ptr = class; + class_command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_ACTIVATE; + status = device -> ux_device_class -> ux_host_class_entry_function(&class_command); + + /* Return result of activation. */ + return(status); + } + + /* Return an error. */ + return(UX_NO_CLASS_MATCH); +} + diff --git a/common/core/src/ux_host_stack_class_get.c b/common/core/src/ux_host_stack_class_get.c new file mode 100644 index 0000000..121d48b --- /dev/null +++ b/common/core/src/ux_host_stack_class_get.c @@ -0,0 +1,124 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a pointer to the class container. A class */ +/* needs to obtain its container from the USBX stack to search for */ +/* instances when a driver or an application wants to open a device. */ +/* */ +/* Note: The C string of class_name must be NULL-terminated and the */ +/* length of it (without the NULL-terminator itself) must be no larger */ +/* than UX_MAX_CLASS_NAME_LENGTH. */ +/* */ +/* INPUT */ +/* */ +/* class_name Name of class */ +/* class Class pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check C string and return its */ +/* length if null-terminated */ +/* _ux_utility_memory_compare Compare memory blocks */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_get(UCHAR *class_name, UX_HOST_CLASS **host_class) +{ + +UX_HOST_CLASS *class_ptr; +ULONG class_index; +UINT status; +UINT class_name_length = 0; + + + /* Get the length of the class name (exclude null-terminator). */ + status = _ux_utility_string_length_check(class_name, &class_name_length, UX_MAX_CLASS_NAME_LENGTH); + if (status) + return(status); + + /* We need to parse the class driver table. */ + class_ptr = _ux_system_host -> ux_system_host_class_array; + for (class_index = 0; class_index < _ux_system_host -> ux_system_host_max_class; class_index++) + { + + /* Check if this class is already being used. */ + if (class_ptr -> ux_host_class_status == UX_USED) + { + + /* We have found a container. Check if this is the one we need (compare including null-terminator). */ + if (_ux_utility_memory_compare(class_ptr -> ux_host_class_name, class_name, class_name_length + 1) == UX_SUCCESS) + { + + /* The class container was found. Update the pointer to the class container for the caller. */ + *host_class = class_ptr; + + /* Return success. */ + return(UX_SUCCESS); + } + } + + /* Move to the next class. */ + class_ptr++; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_UNKNOWN, class_name, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* This class does not exist, return an error. */ + return(UX_HOST_CLASS_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_class_instance_create.c b/common/core/src/ux_host_stack_class_instance_create.c new file mode 100644 index 0000000..2784ca0 --- /dev/null +++ b/common/core/src/ux_host_stack_class_instance_create.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_instance_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a new class instance for a class container. */ +/* The instance of a class is not contained in the class code to */ +/* reduce the class driver complexity. Rather, each class instance is */ +/* attached to class container. */ +/* */ +/* INPUT */ +/* */ +/* class Pointer to class */ +/* class_instance Pointer to class instance */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_instance_create(UX_HOST_CLASS *host_class, VOID *class_instance) +{ + +VOID **current_class_instance; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_CLASS_INSTANCE_CREATE, host_class, class_instance, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_CLASS_INSTANCE, class_instance, 0, 0, 0) + + /* Start with the first class instance attached to the class container. */ + current_class_instance = host_class -> ux_host_class_first_instance; + + /* Check if there are any instances attached. */ + if (current_class_instance == UX_NULL) + { + + /* Since it is the first class, attach it to the class container. */ + host_class -> ux_host_class_first_instance = class_instance; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + + /* Traverse the list of the class instances until we find the last class. */ + while (*current_class_instance != UX_NULL) + { + + /* Point to the next class instance. */ + current_class_instance = *current_class_instance; + } + + /* We have reached the last class, hook the new class to the end. This way, we preserve + the chronological order of the class instances. */ + *current_class_instance = class_instance; + + /* Return successful completion to caller. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_class_instance_destroy.c b/common/core/src/ux_host_stack_class_instance_destroy.c new file mode 100644 index 0000000..0ab3709 --- /dev/null +++ b/common/core/src/ux_host_stack_class_instance_destroy.c @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_instance_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function destroys a class instance for a class container. */ +/* */ +/* INPUT */ +/* */ +/* class Pointer to class */ +/* class_instance Pointer to class instance */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_instance_destroy(UX_HOST_CLASS *host_class, VOID *class_instance) +{ + +VOID **current_class_instance; +VOID **next_class_instance; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_CLASS_INSTANCE_DESTROY, host_class, class_instance, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(class_instance); + + /* Get the pointer to the instance pointed by the instance to destroy. */ + next_class_instance = class_instance; + next_class_instance = *next_class_instance; + + /* Start with the first class instance attached to the class container. */ + current_class_instance = host_class -> ux_host_class_first_instance; + + /* Check if there are any instances attached. */ + if (current_class_instance == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, class_instance, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* The first instance is a special case because it is attached to the class + container. */ + if (current_class_instance == class_instance) + { + + /* Point to next class instance. */ + host_class -> ux_host_class_first_instance = next_class_instance; + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Traverse the list of the class instances until we found the right one. */ + while (*current_class_instance != UX_NULL) + { + + /* Check to see if this class is the one we need to destroy. */ + if(*current_class_instance == class_instance) + { + + /* Point to next class instance. */ + *current_class_instance = next_class_instance; + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Points to the next class instance. */ + current_class_instance = *current_class_instance; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, class_instance, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error to caller. */ + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_class_instance_get.c b/common/core/src/ux_host_stack_class_instance_get.c new file mode 100644 index 0000000..8259858 --- /dev/null +++ b/common/core/src/ux_host_stack_class_instance_get.c @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_instance_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a class instance pointer for a specific */ +/* class. The instance of a class is not contained in the class code */ +/* to reduce the class complexity. Rather, each class instance is */ +/* attached to class container. */ +/* */ +/* INPUT */ +/* */ +/* class Pointer to class */ +/* class_index Index of class */ +/* class_instance Destination of class instance */ +/* pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_instance_get(UX_HOST_CLASS *host_class, UINT class_index, VOID **class_instance) +{ + +VOID **current_class_instance; + + + /* Start with the first class instance attached to the class container. */ + current_class_instance = host_class -> ux_host_class_first_instance; + + /* Check if there are any instances attached. */ + if(current_class_instance == UX_NULL) + { + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Traverse the list of the class instances until we found the right one. */ + while (class_index-- != 0) + { + + /* Points to the next class instance. */ + current_class_instance = *current_class_instance; + + /* Check if we have reached the end of the list of the class instances. */ + if (current_class_instance == UX_NULL) + { + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + } + + /* Update the class instance pointer from the caller. */ + *class_instance = current_class_instance; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_class_instance_verify.c b/common/core/src/ux_host_stack_class_instance_verify.c new file mode 100644 index 0000000..41aa28c --- /dev/null +++ b/common/core/src/ux_host_stack_class_instance_verify.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_instance_verify PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function ensures that a given instance exists. An application */ +/* is not responsible for keeping the instance valid pointer. The */ +/* class is responsible for the instance checks if the instance is */ +/* still valid. */ +/* */ +/* INPUT */ +/* */ +/* class_name Name of class */ +/* class_instance Pointer to class instance */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check C string and return its */ +/* length if null-terminated */ +/* _ux_utility_memory_compare Compare blocks of memory */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_instance_verify(UCHAR *class_name, VOID *class_instance) +{ + +UX_HOST_CLASS *class; +ULONG class_index; +VOID **current_class_instance; +UINT status; +UINT class_name_length = 0; + + + /* Get the length of the class name (exclude null-terminator). */ + status = _ux_utility_string_length_check(class_name, &class_name_length, UX_MAX_CLASS_NAME_LENGTH); + if (status) + return(status); + + /* We need to parse the class table. */ + class = _ux_system_host -> ux_system_host_class_array; + for(class_index = 0; class_index < _ux_system_host -> ux_system_host_max_class; class_index++) + { + + /* Check if this class is already used. */ + if (class -> ux_host_class_status == UX_USED) + { + + /* Start with the first class instance attached to the class container. */ + current_class_instance = class -> ux_host_class_first_instance; + + /* Traverse the list of the class instances until we find the correct instance. */ + while (current_class_instance != UX_NULL) + { + + /* Check the class instance attached to the container with the caller's + instance. */ + if (current_class_instance == class_instance) + { + + /* We have found the class container. Check if this is the one we need (compare including null-terminator). */ + if (_ux_utility_memory_compare(class -> ux_host_class_name, class_name, class_name_length + 1) == UX_SUCCESS) + return(UX_SUCCESS); + } + + /* Points to the next class instance. */ + current_class_instance = *current_class_instance; + } + } + + /* Move to the next class. */ + class++; + } + + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, class_instance, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* This class does not exist. */ + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_class_interface_scan.c b/common/core/src/ux_host_stack_class_interface_scan.c new file mode 100644 index 0000000..d202d77 --- /dev/null +++ b/common/core/src/ux_host_stack_class_interface_scan.c @@ -0,0 +1,196 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_interface_scan PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will scan all default interfaces for a single */ +/* configuration and call the registered class with the */ +/* Class/SubClass/Protocol of the interface. */ +/* */ +/* If the device has multiple configurations (like the Apple iPod), */ +/* the first configuration is treated as the default configuration. */ +/* If a device which has multiple configurations wants to control the */ +/* configuration selection, it must ensure that the PID/VID based */ +/* class at the device level claims the entire device. */ +/* */ +/* */ +/* For the interface, there is no reason to use the PID/VID has a */ +/* binding element as classes that trigger on PID/VID will be called */ +/* by the device descriptor scanning process. */ +/* */ +/* INPUT */ +/* */ +/* device Device pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Result of operation */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_call Call class command */ +/* _ux_host_stack_device_configuration_select */ +/* Select configuration */ +/* (ux_host_stack_class_call) Call class from host stack */ +/* (ux_host_class_entry_function) Class entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_interface_scan(UX_DEVICE *device) +{ + +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UINT nb_class_owners; +UX_HOST_CLASS *class; +UX_HOST_CLASS_COMMAND class_command; +UINT status; + + /* Initialize class owners to 0. */ + nb_class_owners = 0; + + /* Get the 1st and only configuration. If the device has multiple + configurations, we simply use the first one as default. */ + configuration = device -> ux_device_first_configuration; + if (configuration == UX_NULL) + return(UX_ERROR); + + /* Get the first interface container for this configuration. */ + interface = configuration -> ux_configuration_first_interface; + + /* We now scan all the alternate settings 0 for each of the interfaces. */ + while (interface != UX_NULL) + { + + /* Is there a default interface? */ + if(interface -> ux_interface_descriptor.bAlternateSetting == 0) + { + + /* We have a default interface for this configuration. Call each class + with the class\subclass\protocol. We include the IAD for the cdc classes. */ + class_command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_QUERY; + class_command.ux_host_class_command_container = (VOID *)interface; + class_command.ux_host_class_command_usage = UX_HOST_CLASS_COMMAND_USAGE_CSP; + class_command.ux_host_class_command_class = interface -> ux_interface_descriptor.bInterfaceClass; + class_command.ux_host_class_command_subclass = interface -> ux_interface_descriptor.bInterfaceSubClass; + class_command.ux_host_class_command_protocol = interface -> ux_interface_descriptor.bInterfaceProtocol; + class_command.ux_host_class_command_iad_class = interface -> ux_interface_iad_class ; + class_command.ux_host_class_command_iad_subclass = interface -> ux_interface_iad_subclass; + class_command.ux_host_class_command_iad_protocol = interface -> ux_interface_iad_protocol; + + class = _ux_host_stack_class_call(&class_command); + + /* On return, either we have found a class or the interface is still an orphan. */ + if (class != UX_NULL) + { + + /* There is a class. */ + nb_class_owners++; + interface -> ux_interface_class = class; + } + } + + /* point to the next interface until end of the list. */ + interface = interface -> ux_interface_next_interface; + } + + /* Assume no classes. */ + status = UX_NO_CLASS_MATCH; + + /* Check the number of class owner found. */ + if (nb_class_owners != 0) + { + + /* If we have found one or more classes for any of the interfaces, + we can safely do a SET_CONFIGURATION of the device. */ + status = _ux_host_stack_device_configuration_select(configuration); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* The device is in the CONFIGURED state, we have to call each of the classes + again with an ACTIVATE signal. */ + interface = configuration -> ux_configuration_first_interface; + + while (interface != UX_NULL) + { + + /* Is there a default interface? */ + if (interface -> ux_interface_descriptor.bAlternateSetting == 0) + { + + /* We have found the default interface. If this interface is owned, + activate its class. */ + class_command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_ACTIVATE; + class_command.ux_host_class_command_container = (VOID *) interface; + + if (interface -> ux_interface_class != UX_NULL) + { + + /* Save the class in the command container */ + class_command.ux_host_class_command_class_ptr = interface -> ux_interface_class; + + /* Send the ACTIVATE command to the class */ + status = interface -> ux_interface_class -> ux_host_class_entry_function(&class_command); + + } + } + + /* Point to the next interface until end of the list. */ + interface = interface -> ux_interface_next_interface; + } + } + } + + /* Return operation result. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_class_register.c b/common/core/src/ux_host_stack_class_register.c new file mode 100644 index 0000000..5628401 --- /dev/null +++ b/common/core/src/ux_host_stack_class_register.c @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_class_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a USB class to the USB stack. The Class */ +/* must specify an entry point for the USB stack to send commands */ +/* such as: */ +/* */ +/* UX_HOST_CLASS_COMMAND_QUERY */ +/* UX_HOST_CLASS_COMMAND_ACTIVATE */ +/* UX_HOST_CLASS_COMMAND_DESTROY */ +/* */ +/* Note: The C string of class_name must be NULL-terminated and the */ +/* length of it (without the NULL-terminator itself) must be no larger */ +/* than UX_MAX_CLASS_NAME_LENGTH. */ +/* */ +/* INPUT */ +/* */ +/* class_name Name of class */ +/* class_entry_function Entry function of the class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check C string and return */ +/* length if null-terminated */ +/* _ux_utility_memory_copy Copy memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_class_register(UCHAR *class_name, + UINT (*class_entry_function)(struct UX_HOST_CLASS_COMMAND_STRUCT *)) +{ + +UX_HOST_CLASS *class; +ULONG class_index; +UINT status; +UINT class_name_length = 0; + + + /* Get the length of the class name (exclude null-terminator). */ + status = _ux_utility_string_length_check(class_name, &class_name_length, UX_MAX_CLASS_NAME_LENGTH); + if (status) + return(status); + + /* We need to parse the class table to find an empty spot. */ + class = _ux_system_host -> ux_system_host_class_array; + for (class_index = 0; class_index < _ux_system_host -> ux_system_host_max_class; class_index++) + { + + /* Check if this class is already used. */ + if (class -> ux_host_class_status == UX_UNUSED) + { + + /* We have found a free container for the class. Copy the name (with null-terminator). */ + _ux_utility_memory_copy(class -> ux_host_class_name, class_name, class_name_length + 1); + + /* Memorize the entry function of this class. */ + class -> ux_host_class_entry_function = class_entry_function; + + /* Mark it as used. */ + class -> ux_host_class_status = UX_USED; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + + /* Do a sanity check to make sure the class is not already installed by + mistake. To verify this, we simple check for the class entry point. */ + else + { + + /* Check for an already installed class entry function. */ + if(class -> ux_host_class_entry_function == class_entry_function) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_INIT, UX_HOST_CLASS_ALREADY_INSTALLED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_ALREADY_INSTALLED, class_name, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Yes, return an error. */ + return(UX_HOST_CLASS_ALREADY_INSTALLED); + } + } + + /* Move to the next class. */ + class++; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_ARRAY_FULL, class_name, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_INIT, UX_MEMORY_ARRAY_FULL); + + /* No more entries in the class table. */ + return(UX_MEMORY_ARRAY_FULL); +} + diff --git a/common/core/src/ux_host_stack_configuration_descriptor_parse.c b/common/core/src/ux_host_stack_configuration_descriptor_parse.c new file mode 100644 index 0000000..4677860 --- /dev/null +++ b/common/core/src/ux_host_stack_configuration_descriptor_parse.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_configuration_descriptor_parse PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the entire configuration descriptor and */ +/* enumerates the interfaces, binds the interface to a class driver... */ +/* if the device has multiple configurations, we read all the */ +/* configurations but do not instantiate any configuration. Rather we */ +/* let a class driver do the work. */ +/* */ +/* INPUT */ +/* */ +/* device Pointer to device */ +/* configuration Pointer to configuration */ +/* configuration_index Index of configuration */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interfaces_scan Scan host interfaces */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate block of memory */ +/* _ux_utility_memory_free Free block of memory */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_configuration_descriptor_parse(UX_DEVICE *device, UX_CONFIGURATION *configuration, + UINT configuration_index) +{ + +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *descriptor; +UX_ENDPOINT *control_endpoint; +ULONG total_configuration_length; + + + /* Retrieve the pointer to the control endpoint and its transfer_request. */ + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Retrieve the size of all the configuration descriptor. */ + total_configuration_length = configuration -> ux_configuration_descriptor.wTotalLength; + + /* Allocate enough memory to read all descriptors attached to this configuration. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, total_configuration_length); + + /* Determine if the memory was allocated. */ + if (descriptor == UX_NULL) + { + + /* No, return an error. */ + return(UX_MEMORY_INSUFFICIENT); + } + else + { + + /* Create a transfer_request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = total_configuration_length; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = configuration_index | (UINT)(UX_CONFIGURATION_DESCRIPTOR_ITEM << 8); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == total_configuration_length)) + { + + /* The entire descriptor now contains the configuration descriptor, + the interface(s) descriptors, all alternate settings, endpoints + and descriptor specific to the class. The descriptor is parsed for all interfaces. */ + status = _ux_host_stack_interfaces_scan(configuration, descriptor); + } + } + + /* Free all used resources. */ + _ux_utility_memory_free(descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_configuration_enumerate.c b/common/core/src/ux_host_stack_configuration_enumerate.c new file mode 100644 index 0000000..5b7158d --- /dev/null +++ b/common/core/src/ux_host_stack_configuration_enumerate.c @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_configuration_enumerate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the configuration descriptor, creates the */ +/* configuration container(s) for the device, and enumerates all found */ +/* configurations. */ +/* */ +/* At this stage, only the containers for each subcomponents are */ +/* linked. No configuration, interface or endpoints are active unless */ +/* a class issues a SET_CONFIGURATION. */ +/* */ +/* INPUT */ +/* */ +/* device Pointer to device */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_descriptor_parse */ +/* Parse configuration descriptor*/ +/* _ux_host_stack_configuration_instance_delete */ +/* Delete configuration instance */ +/* _ux_host_stack_new_configuration_create */ +/* Create new configuration */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate block of memory */ +/* _ux_utility_memory_free Free block of memory */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_configuration_enumerate(UX_DEVICE *device) +{ + +UX_TRANSFER *transfer_request; +UINT status = UX_ERROR; +UCHAR * descriptor; +UX_ENDPOINT *control_endpoint; +UX_CONFIGURATION *configuration; +ULONG nb_configurations; +ULONG configuration_index; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_CONFIGURATION_ENUMERATE, device, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Retrieve the pointer to the control endpoint and its transfer_request. */ + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the configuration descriptor the first time we read + only the configuration descriptor when we have the configuration descriptor, we have + the length of the entire configuration\interface\endpoint descriptors. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* There maybe multiple configurations for this device. */ + nb_configurations = device -> ux_device_descriptor.bNumConfigurations; + + /* Parse all the configurations attached to the device. We start with the first index. + The index and the actual configuration value may be different according to the USB specification! */ + for (configuration_index = 0; configuration_index < nb_configurations; configuration_index++) + { + + /* Create a transfer_request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_CONFIGURATION_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = configuration_index | (UINT)(UX_CONFIGURATION_DESCRIPTOR_ITEM << 8); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH)) + { + + /* Allocate some memory for the container of this descriptor. */ + configuration = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_CONFIGURATION)); + + /* Check to see if the block was allocated. */ + if (configuration != UX_NULL) + { + + /* This configuration must be linked to the device. */ + _ux_host_stack_new_configuration_create(device, configuration); + + /* The descriptor is in a packed format, parse it locally. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration -> ux_configuration_descriptor); + + /* Parse the device descriptor so that we can retrieve the length + of the entire configuration. */ + status = _ux_host_stack_configuration_descriptor_parse(device, configuration, configuration_index); + + /* Check the completion status. */ + if (status != UX_SUCCESS) + { + /* Error, delete the configuration instance. */ + _ux_host_stack_configuration_instance_delete(configuration); + } + } + else + { + + /* Cannot allocate configuration memory. Abort enumeration */ + status = UX_MEMORY_INSUFFICIENT; + + break; + } + } + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* The device descriptor does not contain the right amount of data. Maybe corruption. */ + status = UX_DESCRIPTOR_CORRUPTED; + + break; + } + + } + + /* Free all used resources. */ + _ux_utility_memory_free(descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_configuration_instance_create.c b/common/core/src/ux_host_stack_configuration_instance_create.c new file mode 100644 index 0000000..4c2aaa7 --- /dev/null +++ b/common/core/src/ux_host_stack_configuration_instance_create.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_configuration_instance_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create a configuration instance. It scan all the */ +/* interfaces hooked to the configuration and enable each interface */ +/* with the alternate setting 0. */ +/* */ +/* INPUT */ +/* */ +/* configuration Pointer to configuration */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_instance_create Create interface instance */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_configuration_instance_create(UX_CONFIGURATION *configuration) +{ + +UX_INTERFACE *interface; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_CONFIGURATION_INSTANCE_CREATE, configuration, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Obtain the first interface for this configuration. */ + interface = configuration -> ux_configuration_first_interface; + + /* Each selected alternate setting 0 for each interface must be created. */ + while (interface != UX_NULL) + { + + /* Check if we are dealing with the first alternate setting. */ + if (interface -> ux_interface_descriptor.bAlternateSetting == 0) + { + /* Create the interface. */ + status = _ux_host_stack_interface_instance_create(interface); + + /* Check status, the controller may have refused the endpoint creation. */ + if (status != UX_SUCCESS) + + /* An error occurred. The interface cannot be mounted. */ + return(status); + + } + + /* Next interface. */ + interface = interface -> ux_interface_next_interface; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_configuration_instance_delete.c b/common/core/src/ux_host_stack_configuration_instance_delete.c new file mode 100644 index 0000000..e537523 --- /dev/null +++ b/common/core/src/ux_host_stack_configuration_instance_delete.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_configuration_instance_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will delete a configuration instance. It does not */ +/* delete configuration container but it deletes all the alternate */ +/* current alternate settings for each interface it owns. */ +/* */ +/* INPUT */ +/* */ +/* configuration Pointer to configuration */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_instance_delete Delete interface instance */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_configuration_instance_delete(UX_CONFIGURATION *configuration) +{ + +UX_INTERFACE *interface; +ULONG current_alternate_setting; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_CONFIGURATION_INSTANCE_DELETE, configuration, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Obtain the first interface for this configuration. */ + interface = configuration -> ux_configuration_first_interface; + + /* In order to keep the compiler happy, we reset the alternate setting. */ + current_alternate_setting = 0; + + /* Each selected alternate setting for each interface must be deleted. */ + while (interface != UX_NULL) + { + + /* If this is the first alternate setting, the current alternate setting is maintained here. */ + if (interface -> ux_interface_descriptor.bAlternateSetting == 0) + { + + current_alternate_setting = interface -> ux_interface_current_alternate_setting; + } + + if (interface -> ux_interface_descriptor.bAlternateSetting == current_alternate_setting) + { + _ux_host_stack_interface_instance_delete(interface); + } + + interface = interface -> ux_interface_next_interface; + } + + return; +} + diff --git a/common/core/src/ux_host_stack_configuration_interface_get.c b/common/core/src/ux_host_stack_configuration_interface_get.c new file mode 100644 index 0000000..6c6362e --- /dev/null +++ b/common/core/src/ux_host_stack_configuration_interface_get.c @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_configuration_interface_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns an interface container based on a */ +/* configuration handle, an interface index and an alternate setting */ +/* index. */ +/* */ +/* INPUT */ +/* */ +/* configuration Pointer to configuration */ +/* interface_index Index of interface */ +/* alternate_setting_index Index of alternate setting */ +/* interface Destination of interface */ +/* pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_configuration_interface_get(UX_CONFIGURATION *configuration, + UINT interface_index, UINT alternate_setting_index, + UX_INTERFACE **interface) +{ + +UINT current_interface_number; +UINT container_index; +UX_INTERFACE *current_interface; + + + /* Do a sanity check on the configuration handle. */ + if (configuration -> ux_configuration_handle != (ULONG) (ALIGN_TYPE) configuration) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, configuration, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Start with the interface attached to the configuration. */ + current_interface = configuration -> ux_configuration_first_interface; + + /* The first interface has the index 0 */ + container_index = 0; + + /* Reset the interface number */ + current_interface_number = 0; + + /* Traverse the list of the interfaces until we found the right one */ + while (current_interface != UX_NULL) + { + + /* Check if the interface index matches the current one. */ + if (interface_index == container_index) + { + + /* We have found the correct interface, now search for the alternate setting. */ + current_interface_number = current_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* The first alternate setting has the index 0. */ + container_index = 0; + + /* Loop on all the alternate settings for this interface. */ + while (current_interface != UX_NULL) + { + + /* Check if the index is matched */ + if (alternate_setting_index == container_index) + { + + /* We have found the right interface/alternate setting combination. Set the + interface return pointer. */ + *interface = current_interface; + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Move to next alternate setting index. */ + container_index++; + + /* Move to the next alternate setting. */ + current_interface = current_interface -> ux_interface_next_interface; + + + /* Check new interface pointer, might be the end. */ + if (current_interface != UX_NULL) + { + + /* And verify that we are still in the same interface. */ + if (current_interface -> ux_interface_descriptor.bInterfaceNumber != current_interface_number) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_INTERFACE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_INTERFACE_HANDLE_UNKNOWN); + } + } + } + } + + /* Check the current interface, we may already be at the end ... */ + if (current_interface != UX_NULL) + { + + /* Move to the next interface. */ + current_interface = current_interface -> ux_interface_next_interface; + + /* Move to the next interface index. */ + container_index++; + } + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_INTERFACE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Didn't find the right interface/alternate setting, return an error! */ + return(UX_INTERFACE_HANDLE_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_configuration_set.c b/common/core/src/ux_host_stack_configuration_set.c new file mode 100644 index 0000000..bda29c4 --- /dev/null +++ b/common/core/src/ux_host_stack_configuration_set.c @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_configuration_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a setting of a device configuration. */ +/* If the host is OTG capable and the device has an OTG descriptor */ +/* that supports HNP we perform a SET_FEATURE with b_hnp_support. */ +/* */ +/* INPUT */ +/* */ +/* configuration Pointer to configuration */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_configuration_set(UX_CONFIGURATION *configuration) +{ + +UX_DEVICE *device; +UX_TRANSFER *transfer_request; +UINT status; +UX_ENDPOINT *control_endpoint; +#ifdef UX_OTG_SUPPORT +UX_HCD *hcd; +#endif + + + /* A configuration is selected. Retrieve the pointer to the control endpoint + and its transfer request. */ + device = configuration -> ux_configuration_device; + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + +#ifdef UX_OTG_SUPPORT + /* Check if the configuration has an OTG device with HNP feature. */ + if (configuration -> ux_configuration_otg_capabilities & UX_OTG_HNP_SUPPORT) + { + + /* For HNP to work the device has to be connected directly to the Root Hub and not + a down stream hub. If the parent is NULL, the device is on the root hub. */ + if (device -> ux_device_parent == UX_NULL) + { + + /* With the device we have the pointer to the HCD. */ + hcd = device -> ux_device_hcd; + + /* Check the HCD to ensure we have an OTG host controller. */ + if (hcd -> ux_hcd_otg_capabilities & UX_HCD_OTG_CAPABLE) + { + + /* The Host controller is OTG aware. Perform a SET_FEATURE with b_hnp_support. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_SET_FEATURE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT| UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_OTG_FEATURE_A_HNP_SUPPORT; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the device fails this command we turn off its OTG capabilities. */ + if (status != UX_SUCCESS) + + /* Reset the OTG capabilities of the device. */ + configuration -> ux_configuration_otg_capabilities = 0; + + } + } + } +#endif + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_CONFIGURATION_SET, configuration, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Create a transfer_request for the SET_CONFIGURATION request. No data for this request. */ + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_SET_CONFIGURATION; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (USHORT) configuration -> ux_configuration_descriptor.bConfigurationValue; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check completion status. */ + if(status == UX_SUCCESS) + { + + /* Change the device state to configured. */ + device -> ux_device_state = UX_DEVICE_CONFIGURED; + + /* Store the new configuration value in the device container. */ + device -> ux_device_current_configuration = configuration -> ux_configuration_descriptor.bConfigurationValue; + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_delay_ms.c b/common/core/src/ux_host_stack_delay_ms.c new file mode 100644 index 0000000..fa96985 --- /dev/null +++ b/common/core/src/ux_host_stack_delay_ms.c @@ -0,0 +1,74 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_delay_ms PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is now obsolete. */ +/* */ +/* INPUT */ +/* */ +/* time Number of milliseconds to */ +/* wait for */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_hcd_entry_function) HCD entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_delay_ms(ULONG time) +{ + UX_PARAMETER_NOT_USED(time); +} + diff --git a/common/core/src/ux_host_stack_device_address_set.c b/common/core/src/ux_host_stack_device_address_set.c new file mode 100644 index 0000000..436d0e6 --- /dev/null +++ b/common/core/src/ux_host_stack_device_address_set.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_address_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the device address to the new device. */ +/* */ +/* INPUT */ +/* */ +/* device Pointer to device */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_delay_ms Thread sleep */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_address_set(UX_DEVICE *device) +{ + +UX_TRANSFER *transfer_request; +UINT status; +UX_ENDPOINT *control_endpoint; +UX_HCD *hcd; +UINT address_byte_index; +UINT address_bit_index; +UCHAR device_address_byte; +USHORT device_address; + + /* Retrieve the pointer to the control endpoint. */ + control_endpoint = &device -> ux_device_control_endpoint; + + /* Retrieve the transfer request pointer. */ + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* We need the HCD pointer as well. */ + hcd = device -> ux_device_hcd; + + /* Calculate the new address of this device. We start with address 1. */ + device_address = 1; + for (address_byte_index = 0; address_byte_index < 16; address_byte_index++) + { + + /* Get the address mask byte. */ + device_address_byte = hcd -> ux_hcd_address[address_byte_index]; + + /* Scan each bit for an empty spot. */ + for (address_bit_index = 0; address_bit_index < 8; address_bit_index++) + { + + if ((device_address_byte & (1 << address_bit_index)) == 0) + { + + /* We have found an empty spot. Reserve this address. */ + device_address_byte = (UCHAR)((UCHAR)device_address_byte | (UCHAR)(1 << address_bit_index)); + + /* Store the address mask byte. */ + hcd -> ux_hcd_address[address_byte_index] = device_address_byte; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_ADDRESS_SET, device, device_address, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Create a transfer request for the SET_ADDRESS request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_SET_ADDRESS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = device_address; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Now, this address will be the one used in future transfers. The transfer may have failed and therefore + all the device resources including the new address will be free.*/ + device -> ux_device_address = (ULONG) device_address; + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* Some devices need some time to accept this address. */ + _ux_utility_delay_ms(UX_DEVICE_ADDRESS_SET_WAIT); + + /* Return successful status. */ + return(status); + } + else + { + + /* We have an error at the first device transaction. This is mostly + due to the device having failed on the reset after power up. + we will try again either at the root hub or regular hub. */ + return(status); + } + } + + /* This address was already taken, increment to the next address. */ + device_address++; + } + } + + /* We should never get here! */ + return(UX_ERROR); +} + diff --git a/common/core/src/ux_host_stack_device_configuration_get.c b/common/core/src/ux_host_stack_device_configuration_get.c new file mode 100644 index 0000000..bdd772c --- /dev/null +++ b/common/core/src/ux_host_stack_device_configuration_get.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_configuration_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a configuration container based on a device */ +/* handle and a configuration index. */ +/* */ +/* INPUT */ +/* */ +/* device Pointer to device */ +/* configuration_index Index of configuration */ +/* configuration Pointer to configuration */ +/* destination */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_configuration_get(UX_DEVICE *device, UINT configuration_index, + UX_CONFIGURATION **configuration) +{ + +UINT current_configuration_index; +UX_CONFIGURATION *current_configuration; + + /* Do a sanity check on the device handle. */ + if (device -> ux_device_handle != (ULONG) (ALIGN_TYPE) device) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DEVICE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DEVICE_HANDLE_UNKNOWN); + } + + /* Start with the configuration attached to the device. */ + current_configuration = device -> ux_device_first_configuration; + + /* The first configuration has the index 0. */ + current_configuration_index = 0; + + /* Traverse the list of the configurations until we found the right one. */ + while (current_configuration != UX_NULL) + { + + /* Check if the configuration index matches the current one. */ + if (configuration_index == current_configuration_index) + { + + /* Return the configuration pointer. */ + *configuration = current_configuration; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_CONFIGURATION_GET, device, current_configuration, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Return successful completion. */ + return(UX_SUCCESS); + } + + /* Move to the next configuration. */ + current_configuration = current_configuration -> ux_configuration_next_configuration; + + /* Move to the next index. */ + current_configuration_index++; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, configuration, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_device_configuration_reset.c b/common/core/src/ux_host_stack_device_configuration_reset.c new file mode 100644 index 0000000..91f3f50 --- /dev/null +++ b/common/core/src/ux_host_stack_device_configuration_reset.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_configuration_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function resets the configuration of the device to zero. */ +/* */ +/* INPUT */ +/* */ +/* configuration Pointer to configuration */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_instance_delete */ +/* Delete configuration instance*/ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_configuration_reset(UX_DEVICE *device) +{ + +UX_TRANSFER *transfer_request; +UX_ENDPOINT *control_endpoint; +UX_CONFIGURATION *current_configuration; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_CONFIGURATION_SELECT, device, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* A configuration is selected. Retrieve the pointer to the control endpoint + and its transfer request. */ + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Check for the state of the device . If the device is already configured, + we need to cancel the existing configuration before resetting it. */ + if (device -> ux_device_state == UX_DEVICE_CONFIGURED) + { + + /* The device is configured. Get the first configuration pointer. */ + current_configuration = device -> ux_device_first_configuration; + + /* Traverse the configuration list until we find the right one. */ + while (current_configuration -> ux_configuration_descriptor.bConfigurationValue != + device -> ux_device_current_configuration) + { + + current_configuration = current_configuration -> ux_configuration_next_configuration; + } + + /* Deselect this instance */ + _ux_host_stack_configuration_instance_delete(current_configuration); + } + + /* Set state of device to ATTACHED. */ + device -> ux_device_state = UX_DEVICE_ATTACHED; + + /* Create a transfer_request for the SET_CONFIGURATION request. No data for this request. */ + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_SET_CONFIGURATION; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_device_configuration_select.c b/common/core/src/ux_host_stack_device_configuration_select.c new file mode 100644 index 0000000..7d66915 --- /dev/null +++ b/common/core/src/ux_host_stack_device_configuration_select.c @@ -0,0 +1,160 @@ + + + + + + + + +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_configuration_select PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function selects a specific configuration for a device. */ +/* When this configuration is set to the device, by default all the */ +/* device interface and their associated alternate setting 0 is */ +/* activated on the device. If the device/interface class driver */ +/* wishes to change the setting of a particular interface, it needs */ +/* to issue a select interface setting function. */ +/* */ +/* INPUT */ +/* */ +/* configuration Pointer to configuration */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_instance_create Create configuration */ +/* instance */ +/* _ux_host_stack_configuration_instance_delete Delete configuration */ +/* instance */ +/* _ux_host_stack_configuration_set Set configuration */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_configuration_select(UX_CONFIGURATION *configuration) +{ + +UX_DEVICE *device; +UX_CONFIGURATION *current_configuration; +UINT status; + + /* Check for validity of the configuration handle. */ + if (configuration -> ux_configuration_handle != (ULONG) (ALIGN_TYPE) configuration) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, configuration, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Get the device container for this configuration. */ + device = configuration -> ux_configuration_device; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_CONFIGURATION_SELECT, device, configuration, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Check for the state of the device . If the device is already configured, + we need to cancel the existing configuration before enabling this one. */ + if (device -> ux_device_state == UX_DEVICE_CONFIGURED) + { + + /* The device is configured. Get the first configuration pointer. */ + current_configuration = device -> ux_device_first_configuration; + + /* Traverse the configuration list until we find the right one. */ + while (current_configuration -> ux_configuration_descriptor.bConfigurationValue != + device -> ux_device_current_configuration) + { + + current_configuration = current_configuration -> ux_configuration_next_configuration; + } + + /* Deselect this instance */ + _ux_host_stack_configuration_instance_delete(current_configuration); + } + + /* The device is now in the unconfigured state. We need to deal + with the amount of power the device is consuming before allowing + it to be configured. Otherwise we may run the risk of an over + current fault. */ + if (configuration -> ux_configuration_descriptor.MaxPower > device -> ux_device_max_power) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_OVER_CURRENT_CONDITION); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_OVER_CURRENT_CONDITION, configuration, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_OVER_CURRENT_CONDITION); + } + + /* The device can now be configured. */ + status = _ux_host_stack_configuration_set(configuration); + if (status != UX_SUCCESS) + return(status); + + /* Create the configuration instance. */ + status = _ux_host_stack_configuration_instance_create(configuration); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_device_descriptor_read.c b/common/core/src/ux_host_stack_device_descriptor_read.c new file mode 100644 index 0000000..968155e --- /dev/null +++ b/common/core/src/ux_host_stack_device_descriptor_read.c @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_descriptor_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the device descriptor. */ +/* */ +/* INPUT */ +/* */ +/* device Pointer to device */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_descriptor_read(UX_DEVICE *device) +{ + +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * descriptor; +UX_ENDPOINT *control_endpoint; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_DESCRIPTOR_READ, device, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Retrieve the pointer to the control endpoint. */ + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_DEVICE_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer_request for the GET_DESCRIPTOR request. The first transfer_request asks + for the first 8 bytes only. This way we will know the real MaxPacketSize + value for the control endpoint. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = 8; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_DEVICE_DESCRIPTOR_ITEM << 8; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 8)) + { + + /* Parse the device descriptor and create the local descriptor. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_device_descriptor_structure, UX_DEVICE_DESCRIPTOR_ENTRIES, + (UCHAR *) &device -> ux_device_descriptor); + } + else + { + + /* Free all used resources. */ + _ux_utility_memory_free(descriptor); + + /* Return completion status. */ + return(status); + } + + /* Update the max packet size value for the endpoint. */ + control_endpoint -> ux_endpoint_descriptor.wMaxPacketSize = device -> ux_device_descriptor.bMaxPacketSize0; + + /* Create a transfer_request for the GET_DESCRIPTOR request. This time, we have the complete length */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_DEVICE_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_DEVICE_DESCRIPTOR_ITEM << 8; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_DEVICE_DESCRIPTOR_LENGTH)) + { + + /* Parse the device descriptor and create the local descriptor. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_device_descriptor_structure, UX_DEVICE_DESCRIPTOR_ENTRIES, + (UCHAR *) &device -> ux_device_descriptor); + } + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* The device descriptor does not contain the right amount of data. Maybe corruption. */ + status = UX_DESCRIPTOR_CORRUPTED; + } + + /* Free all used resources. */ + _ux_utility_memory_free(descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_device_get.c b/common/core/src/ux_host_stack_device_get.c new file mode 100644 index 0000000..d323ff5 --- /dev/null +++ b/common/core/src/ux_host_stack_device_get.c @@ -0,0 +1,133 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a device container based on its index. The */ +/* device index start with device 0. Note that the index is a ULONG */ +/* because we could have several controllers and a byte index might */ +/* not be enough. */ +/* */ +/* INPUT */ +/* */ +/* device_index Index of device */ +/* device Destination for device pointer*/ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_get(ULONG device_index, UX_DEVICE **device) +{ + +UX_DEVICE *current_device; +ULONG current_device_index; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_GET, device_index, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Check if the device index is still within the limits. */ + if (device_index >= _ux_system_host -> ux_system_host_max_devices) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DEVICE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DEVICE_HANDLE_UNKNOWN); + } + + /* Start with the first device. */ + current_device = _ux_system_host -> ux_system_host_device_array; + current_device_index = 0; + + /* Search the list until the end. */ + while (current_device_index < _ux_system_host -> ux_system_host_max_devices) + { + + /* Check to see if this device is existing. */ + if (current_device -> ux_device_handle != UX_UNUSED) + { + + /* Have we reached the index we are looking for? */ + if (device_index == current_device_index) + { + + /* Yes, return the device pointer. */ + *device = current_device; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + + /* Move to next device index. */ + current_device_index++; + + /* Move to next device. */ + current_device++; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error. */ + return(UX_DEVICE_HANDLE_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_device_remove.c b/common/core/src/ux_host_stack_device_remove.c new file mode 100644 index 0000000..f82e923 --- /dev/null +++ b/common/core/src/ux_host_stack_device_remove.c @@ -0,0 +1,192 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_remove PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will remove a USB device from the bus. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to the HCD */ +/* parent The parent device address */ +/* port_index Index of the port on which the*/ +/* change of status occurred */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_device_resources_free Free all device resources */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_remove(UX_HCD *hcd, UX_DEVICE *parent, UINT port_index) +{ + +ULONG container_index; +UX_DEVICE *device; +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UX_HOST_CLASS_COMMAND command; + + /* We need to find the device descriptor for the removed device. We can find it + with the parent device and the port it was attached to. Start with the first device. */ + device = _ux_system_host -> ux_system_host_device_array; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_REMOVE, hcd, parent, port_index, device, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* If trace is enabled, unregister this object. */ + UX_TRACE_OBJECT_UNREGISTER(device); + + /* Start at the beginning of the list. */ + container_index = 0; + + /* Search the list until the end. */ + while (container_index++ < _ux_system_host -> ux_system_host_max_devices) + { + + /* Until we have found a used entry. */ + if (device -> ux_device_handle != UX_UNUSED) + { + + /* Check for the parent device and the port location and the controller. */ + if((device -> ux_device_parent == parent) && (device -> ux_device_port_location == port_index) && + (device -> ux_device_hcd == hcd)) + { + + /* We have found the device to be removed. */ + device -> ux_device_state = UX_DEVICE_REMOVED; + + /* We have found the device to be removed. Initialize the class + command with the generic parameters. */ + command.ux_host_class_command_request = UX_HOST_CLASS_COMMAND_DEACTIVATE; + + /* The device may have a class associated with the device container or its interfaces. */ + if (device -> ux_device_class_instance != UX_NULL) + { + + /* We need to stop the class instance for the device. */ + command.ux_host_class_command_instance = device -> ux_device_class_instance; + + /* Call the class. */ + device -> ux_device_class -> ux_host_class_entry_function(&command); + } + else + { + + /* Search for the active configuration. */ + configuration = device -> ux_device_first_configuration; + + /* Parse the interface(s) for this device in search of the classes + who own this device. */ + while (configuration != UX_NULL) + { + + /* Is this the correct configuration? */ + if (configuration -> ux_configuration_descriptor.bConfigurationValue == + device -> ux_device_current_configuration) + { + + /* We have the correct configuration, search the interface(s). */ + interface = configuration -> ux_configuration_first_interface; + + /* Loop to perform the search. */ + while (interface != UX_NULL) + { + + /* Check if an instance of the interface is present. */ + if (interface -> ux_interface_class_instance != UX_NULL) + { + + /* We need to stop the class instance for the device. */ + command.ux_host_class_command_instance = interface -> ux_interface_class_instance; + + /* Call the class. */ + interface -> ux_interface_class -> ux_host_class_entry_function(&command); + } + + /* Move to next interface. */ + interface = interface -> ux_interface_next_interface; + } + } + + /* Move to next configuration in the list. */ + configuration = configuration -> ux_configuration_next_configuration; + } + } + + /* Now all the resources for this device must be free. */ + _ux_host_stack_device_resources_free(device); + + /* Decrement the number of devices on this bus. */ + hcd -> ux_hcd_nb_devices--; + + /* We are done with this device removal. */ + return(UX_SUCCESS); + } + } + + /* Move to the next device entry. */ + device++; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DEVICE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We get here when we could not find the device. */ + return(UX_DEVICE_HANDLE_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_device_resources_free.c b/common/core/src/ux_host_stack_device_resources_free.c new file mode 100644 index 0000000..050f970 --- /dev/null +++ b/common/core/src/ux_host_stack_device_resources_free.c @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_device_resources_free PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will free all the device resources allocated. */ +/* */ +/* INPUT */ +/* */ +/* device Device pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* _ux_host_stack_endpoint_instance_delete */ +/* Delete endpoint instance */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_memory_set Set memory with a value */ +/* _ux_utility_semaphore_delete Semaphore delete */ +/* _ux_utility_thread_schedule_other Sleep thread to let others */ +/* run */ +/* (ux_hcd_entry_function) HCD entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_device_resources_free(UX_DEVICE *device) +{ + +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UX_ENDPOINT *endpoint; +VOID *container; +ULONG current_alternate_setting; +UX_HCD *hcd; +UINT device_address_byte_index; +UINT device_address_bit_index; +UCHAR device_address_byte; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_RESOURCE_FREE, device, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Set the alternate setting to zero. */ + current_alternate_setting = 0; + + /* Get the first configuration registered to the device. */ + configuration = device -> ux_device_first_configuration; + + /* Parse all the configurations, remove all resources for the possible configuration. */ + while (configuration != UX_NULL) + { + + /* We have the correct configuration, search the interface(s). */ + interface = configuration -> ux_configuration_first_interface; + + /* Parse all the interfaces. */ + while (interface != UX_NULL) + { + + /* The alternate setting 0 has the selected alternate setting value. */ + if (interface -> ux_interface_descriptor.bAlternateSetting == 0) + current_alternate_setting = interface -> ux_interface_current_alternate_setting; + + /* If this is the selected interface, we need to free all the endpoints + attached to the alternate setting for this interface. */ + endpoint = interface -> ux_interface_first_endpoint; + + /* Parse all the endpoints. */ + while (endpoint != UX_NULL) + { + + /* Check if this is the selected interface. */ + if (interface -> ux_interface_descriptor.bAlternateSetting == current_alternate_setting) + { + + /* Delete the endpoint instance first. */ + _ux_host_stack_endpoint_instance_delete(endpoint); + } + + /* Memorize the endpoint container address. */ + container = (VOID *) endpoint; + + /* Get the next endpoint. */ + endpoint = endpoint -> ux_endpoint_next_endpoint; + + /* Delete the endpoint container. */ + _ux_utility_memory_free(container); + } + + + /* Memorize the interface container address. */ + container = (VOID *) interface; + + /* Get the next interface. */ + interface = interface -> ux_interface_next_interface; + + /* Delete the interface container. */ + _ux_utility_memory_free(container); + } + + /* Memorize this configuration address before we free it. */ + container = (VOID *) configuration; + + /* Move to the next configuration in the list. */ + configuration = configuration -> ux_configuration_next_configuration; + + /* Free the configuration. */ + _ux_utility_memory_free(container); + } + + /* We need the HCD address for the control endpoint removal and to free + the device address. */ + hcd = device -> ux_device_hcd; + + /* Was the control endpoint already created ? */ + if (device -> ux_device_control_endpoint.ux_endpoint_state != 0) + { + + /* There may be pending transactions on the control endpoint. They need to be aborted. */ + _ux_host_stack_endpoint_transfer_abort(&device -> ux_device_control_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + the control endpoint to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* The control endpoint should be destroyed at the HCD level. */ + hcd -> ux_hcd_entry_function(hcd, UX_HCD_DESTROY_ENDPOINT, (VOID *) &device -> ux_device_control_endpoint); + } + + /* The semaphore attached to the control endpoint must be destroyed. */ + _ux_utility_semaphore_delete(&device -> ux_device_control_endpoint.ux_endpoint_transfer_request.ux_transfer_request_semaphore); + + /* Check if the device had an assigned address. */ + if (device -> ux_device_address != 0) + { + + /* The USB address of this device can now be returned to the pool + We need the HCD pointer for this operation. */ + + /* Calculate in which byte index the device address belongs. */ + device_address_byte_index = (UINT) (device -> ux_device_address-1)/8; + + /* Now calculate the amount left in the byte index in bit. */ + device_address_bit_index = (UINT) (device -> ux_device_address-1)%8; + + /* Build the mask for the address. */ + device_address_byte = (UCHAR)(1 << device_address_bit_index); + + /* Free the address. */ + hcd -> ux_hcd_address[device_address_byte_index] &= (UCHAR)~device_address_byte; + } + + /* The semaphore for endpoint 0 protection must be destroyed. */ + _ux_utility_semaphore_delete(&device -> ux_device_protection_semaphore); + + /* Now this device can be free and its container return to the pool. */ + _ux_utility_memory_set(device, 0, sizeof(UX_DEVICE)); + + /* Mark the device handle as unused. */ + device -> ux_device_handle = UX_UNUSED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_endpoint_instance_create.c b/common/core/src/ux_host_stack_endpoint_instance_create.c new file mode 100644 index 0000000..f24a8c4 --- /dev/null +++ b/common/core/src/ux_host_stack_endpoint_instance_create.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_endpoint_instance_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an endpoint instance. The HCD layer is */ +/* invoked to create each endpoint and the bandwidth claimed by each */ +/* endpoint is allocated. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Endpoint to delete */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_bandwidth_check Check bandwidth */ +/* _ux_host_stack_bandwidth_claim Claim bandwidth */ +/* _ux_utility_semaphore_create Semaphore create */ +/* (ux_hcd_entry_function) HCD entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_endpoint_instance_create(UX_ENDPOINT *endpoint) +{ + +UX_HCD *hcd; +UX_DEVICE *device; +UINT status; +UCHAR endpoint_type; + + + /* Obtain the HCD for this endpoint. */ + device = endpoint -> ux_endpoint_device; + hcd = device -> ux_device_hcd; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_ENDPOINT_INSTANCE_CREATE, device, endpoint, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + + /* If the endpoint needs guaranteed bandwidth, check if we have enough */ + endpoint_type = (endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE; + switch (endpoint_type) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + + break; + + default: + + /* Check the bandwidth for this endpoint */ + if (_ux_host_stack_bandwidth_check(hcd, endpoint) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_NO_BANDWIDTH_AVAILABLE); + + return(UX_NO_BANDWIDTH_AVAILABLE); + } + + + break; + } + + /* Create this endpoint. */ + status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_CREATE_ENDPOINT, (VOID *) endpoint); + + /* Check status. */ + if (status != UX_SUCCESS) + { + + /* Return completion status. */ + return(status); + } + + /* Claim bandwidth if needed. */ + if ((endpoint_type == UX_INTERRUPT_ENDPOINT) || (endpoint_type == UX_ISOCHRONOUS_ENDPOINT)) + { + + /* Claim its bandwidth */ + _ux_host_stack_bandwidth_claim(hcd, endpoint); + } + + /* Create a semaphore for this endpoint to be attached to its transfer request. */ + status = _ux_utility_semaphore_create(&endpoint -> ux_endpoint_transfer_request.ux_transfer_request_semaphore, + "ux_transfer_request_semaphore", 0); + + /* Check status. */ + if (status == UX_SUCCESS) + { + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_ENDPOINT, endpoint, 0, 0, 0) + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_endpoint_instance_delete.c b/common/core/src/ux_host_stack_endpoint_instance_delete.c new file mode 100644 index 0000000..66e69a9 --- /dev/null +++ b/common/core/src/ux_host_stack_endpoint_instance_delete.c @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_endpoint_instance_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will delete an endpoint instance. It does not delete */ +/* the endpoint container but it removes the HCD endpoint and reclaims */ +/* the bandwidth. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Endpoint to delete */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_bandwidth_release Release bandwidth */ +/* _ux_utility_semaphore_delete Semaphore delete */ +/* (ux_hcd_entry_function) HCD entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_endpoint_instance_delete(UX_ENDPOINT *endpoint) +{ + +UX_HCD *hcd; +UX_DEVICE *device; + + + /* Obtain the HCD for this endpoint. */ + device = endpoint -> ux_endpoint_device; + hcd = device -> ux_device_hcd; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_ENDPOINT_INSTANCE_DELETE, device, endpoint, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Ensure the endpoint had its physical ED allocated. */ + if (endpoint -> ux_endpoint_ed != UX_NULL) + { + + /* Destroy this endpoint. */ + hcd -> ux_hcd_entry_function(hcd, UX_HCD_DESTROY_ENDPOINT, (VOID *) endpoint); + + /* Free the semaphore previously attached to the transfer_request of this endpoint. */ + _ux_utility_semaphore_delete(&endpoint -> ux_endpoint_transfer_request.ux_transfer_request_semaphore); + } + + /* If the endpoint requested guaranteed bandwidth, free it now. */ + switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + + break; + + default: + + /* Reclaim its bandwidth. */ + _ux_host_stack_bandwidth_release(hcd, endpoint); + } + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(endpoint); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_host_stack_endpoint_reset.c b/common/core/src/ux_host_stack_endpoint_reset.c new file mode 100644 index 0000000..78753de --- /dev/null +++ b/common/core/src/ux_host_stack_endpoint_reset.c @@ -0,0 +1,118 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_endpoint_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function resets an endpoint after a stall or other error */ +/* condition. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Endpoint to abort transfer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Send transfer request */ +/* (ux_hcd_entry_function) HCD entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_endpoint_reset(UX_ENDPOINT *endpoint) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UX_DEVICE *device; +UX_HCD *hcd; +UINT status; + + + /* Get the device container from the endpoint */ + device = endpoint -> ux_endpoint_device; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_ENDPOINT_RESET, device, endpoint, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Get the control endpoint attached to the device. */ + control_endpoint = &device -> ux_device_control_endpoint; + + /* Retrieve the transfer_request pointer. */ + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Create a transfer_request for the CLEAR_FEATURE request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_CLEAR_FEATURE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT| UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_ENDPOINT; + transfer_request -> ux_transfer_request_value = UX_ENDPOINT_HALT; + transfer_request -> ux_transfer_request_index = endpoint -> ux_endpoint_descriptor.bEndpointAddress; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Reset the endpoint at the HCD level. */ + if (status == UX_SUCCESS) + { + + /* Pickup HCD pointer. */ + hcd = device -> ux_device_hcd; + + /* Call HCD entry function. */ + status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_RESET_ENDPOINT, endpoint); + } + + /* And return the completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_endpoint_transfer_abort.c b/common/core/src/ux_host_stack_endpoint_transfer_abort.c new file mode 100644 index 0000000..f0b7ed5 --- /dev/null +++ b/common/core/src/ux_host_stack_endpoint_transfer_abort.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cancel all the transfer_requests attached to an */ +/* endpoint. The endpoint is not reset and its toggle state is left */ +/* the same. */ +/* */ +/* In this version of USBX, there can only be one transfer request */ +/* pending for an endpoint. It is not known at this stage if having */ +/* multiple transfer request is a benefit for USBX but this function */ +/* may be changed one day to add such functionality. */ +/* */ +/* INPUT */ +/* */ +/* endpoint Endpoint to abort transfer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request_abort Transfer request abort */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_endpoint_transfer_abort(UX_ENDPOINT *endpoint) +{ + +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_ENDPOINT_TRANSFER_ABORT, endpoint, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Since we only have one transfer_request per endpoint, use the regular + abort transfer request function. */ + status = _ux_host_stack_transfer_request_abort(&endpoint -> ux_endpoint_transfer_request); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_enum_thread_entry.c b/common/core/src/ux_host_stack_enum_thread_entry.c new file mode 100644 index 0000000..29d6e8a --- /dev/null +++ b/common/core/src/ux_host_stack_enum_thread_entry.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_enum_thread_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains the enum thread for USBX. It is in charge of */ +/* the topology changes either from device insertion\extraction on */ +/* the root hub or on a regular hub. */ +/* */ +/* This thread ensures we never have more that 2 instances trying to */ +/* perform a change to the topology (mostly enumeration) for fear that */ +/* more than one device could answer to address 0. */ +/* */ +/* This function is the entry point of the topology thread. It waits */ +/* until one of the HCDs or a hub sets the semaphore to indicate */ +/* there has been a change in the USB topology which could be either */ +/* a insertion or extraction or eventually a hub downstream port */ +/* signal. */ +/* */ +/* INPUT */ +/* */ +/* input Not used input */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_rh_change_process Root hub processing */ +/* _ux_utility_semaphore_get Get signal semaphore */ +/* (ux_system_host_enum_hub_function) HUB enum processing function */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_enum_thread_entry(ULONG input) +{ + + UX_PARAMETER_NOT_USED(input); + + /* Loop forever waiting for changes signaled through the semaphore. */ + while (1) + { + + /* Wait for the semaphore to be put by the root hub or a regular hub. */ + _ux_utility_semaphore_get(&_ux_system_host -> ux_system_host_enum_semaphore, UX_WAIT_FOREVER); + + /* We try the hub first. For this we look into the USBX project + structure to see if there is at least one hub. */ + if (_ux_system_host -> ux_system_host_enum_hub_function != UX_NULL) + { + + /* Yes, there is a HUB function, call it! */ + _ux_system_host -> ux_system_host_enum_hub_function(); + } + + /* The signal may be also coming from the root hub, call the root hub handler. */ + _ux_host_stack_rh_change_process(); + } +} + diff --git a/common/core/src/ux_host_stack_hcd_register.c b/common/core/src/ux_host_stack_hcd_register.c new file mode 100644 index 0000000..5aa1b3e --- /dev/null +++ b/common/core/src/ux_host_stack_hcd_register.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_hcd_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a USB controller driver with the USBX stack */ +/* and invokes the HCD driver's initialization function. */ +/* */ +/* Note: The C string of hcd_name must be NULL-terminated and the */ +/* length of it (without the NULL-terminator itself) must be no larger */ +/* than UX_MAX_HCD_NAME_LENGTH. */ +/* */ +/* INPUT */ +/* */ +/* hcd_name Name of HCD to register */ +/* hcd_entry_function Entry function of HCD driver */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check and return C string */ +/* length if no error */ +/* _ux_utility_memory_copy Copy name into HCD structure */ +/* (hcd_init_function) Init function of HCD driver */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_hcd_register(UCHAR *hcd_name, + UINT (*hcd_init_function)(struct UX_HCD_STRUCT *), ULONG hcd_param1, ULONG hcd_param2) +{ + +UX_HCD *hcd; +ULONG hcd_index; +UINT status; +UINT hcd_name_length = 0; + + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_HCD_REGISTER, hcd_name, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Get the length of the class name (exclude null-terminator). */ + status = _ux_utility_string_length_check(hcd_name, &hcd_name_length, UX_MAX_HCD_NAME_LENGTH); + if (status) + return(status); + + /* We need to parse the controller driver table to find an empty spot. */ + hcd = _ux_system_host -> ux_system_host_hcd_array; + for(hcd_index = 0; hcd_index < _ux_system_host -> ux_system_host_max_hcd; hcd_index++) + { + + /* Is this slot available? */ + if(hcd -> ux_hcd_status == UX_UNUSED) + { + + /* Yes, setup the new HCD entry. */ + + /* Initialize the array of the new controller with its name (include null-terminator). */ + _ux_utility_memory_copy(hcd -> ux_hcd_name, hcd_name, hcd_name_length + 1); + + /* Store the hardware resources of the controller */ + hcd -> ux_hcd_io = hcd_param1; + hcd -> ux_hcd_irq = hcd_param2; + + /* This controller is now used */ + hcd -> ux_hcd_status = UX_USED; + + /* And we have one new controller registered. */ + _ux_system_host -> ux_system_host_registered_hcd++; + + /* We are now calling the HCD driver initialization. */ + status = hcd_init_function(hcd); + + /* Return the completion status to the caller. */ + return(status); + } + + /* Try the next HCD structure */ + hcd++; + } + + /* We have exhausted the array of the HCDs, return an error. */ + return(UX_MEMORY_INSUFFICIENT); +} + diff --git a/common/core/src/ux_host_stack_hcd_thread_entry.c b/common/core/src/ux_host_stack_hcd_thread_entry.c new file mode 100644 index 0000000..cc01793 --- /dev/null +++ b/common/core/src/ux_host_stack_hcd_thread_entry.c @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_hcd_thread_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the host controller thread. */ +/* The HCD thread is initialized at the system level and the thread */ +/* entry routine is invoked right away. This thread suspends until */ +/* one of the HCD resumes it due to HCD activities. */ +/* */ +/* INPUT */ +/* */ +/* input Not used input */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_get Get signal semaphore */ +/* (ux_hcd_entry_function) HCD's entry function */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_hcd_thread_entry(ULONG input) +{ + +UINT hcd_index; +UX_HCD *hcd; +UX_INT_SAVE_AREA + + UX_PARAMETER_NOT_USED(input); + + /* Loop forever on the semaphore. The semaphore is used to signal that + there is work for one or more HCDs. */ + while (1) + { + + /* Get the semaphore that signals something is available for this + thread to process. */ + _ux_utility_semaphore_get(&_ux_system_host -> ux_system_host_hcd_semaphore, UX_WAIT_FOREVER); + + /* This thread was awaken by one or more HCD controllers. Check each of the HCDs + to see who posted work to do. */ + for(hcd_index = 0; hcd_index < _ux_system_host -> ux_system_host_registered_hcd; hcd_index++) + { + + /* Pickup HCD pointer. */ + hcd = &_ux_system_host -> ux_system_host_hcd_array[hcd_index]; + + /* Is there work to do for this HCD? */ + if((hcd -> ux_hcd_status == UX_HCD_STATUS_OPERATIONAL) && (hcd -> ux_hcd_thread_signal !=0)) + { + + /* Yes, call the HCD function to process the work. */ + hcd -> ux_hcd_entry_function(hcd, UX_HCD_PROCESS_DONE_QUEUE, UX_NULL); + UX_DISABLE_INTS + hcd -> ux_hcd_thread_signal--; + UX_RESTORE_INTS + } + } + } +} + diff --git a/common/core/src/ux_host_stack_hcd_transfer_request.c b/common/core/src/ux_host_stack_hcd_transfer_request.c new file mode 100644 index 0000000..02bfe6a --- /dev/null +++ b/common/core/src/ux_host_stack_hcd_transfer_request.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_hcd_transfer_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will route the transfer_request to the appropriate */ +/* HCD for transfer. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_hcd_entry_function) HCD's entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_hcd_transfer_request(UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_DEVICE *device; +UINT status; +UX_HCD *hcd; + + /* Get the endpoint container from the transfer request. */ + endpoint = transfer_request -> ux_transfer_request_endpoint; + + /* Obtain the HCD for this endpoint. */ + device = endpoint -> ux_endpoint_device; + hcd = device -> ux_device_hcd; + + /* Call the HCD entry function to process the transfer request. */ + status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_TRANSFER_REQUEST, (VOID *) transfer_request); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_hnp_polling_thread_entry.c b/common/core/src/ux_host_stack_hnp_polling_thread_entry.c new file mode 100644 index 0000000..d01d2da --- /dev/null +++ b/common/core/src/ux_host_stack_hnp_polling_thread_entry.c @@ -0,0 +1,232 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_hnp_polling_thread_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is awaken every 2 seconds to check if there is a */ +/* OTG device on the bus and if so perform a GET_STATUS as the device */ +/* may request a change of role. */ +/* */ +/* INPUT */ +/* */ +/* none */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_thread_sleep Sleep thread */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_host_stack_role_swap Swapping role */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_hnp_polling_thread_entry(ULONG argument) +{ + +UINT hcd_index; +UX_DEVICE *device; +UX_CONFIGURATION *configuration; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UX_HCD *hcd; +UCHAR *otg_status; +ULONG port_index; +ULONG port_status; +ULONG container_index; +UINT status; + + UX_PARAMETER_NOT_USED(argument); + + /* This thread goes on forever once started. */ + while(1) + { + + /* We need to wake every 2 seconds or so. */ + _ux_utility_thread_sleep(UX_OTG_HNP_THREAD_SLEEP_TIME); + + /* We need to parse the controller driver table to find all controllers that registered + as OTG. */ + for (hcd_index = 0; hcd_index < _ux_system_host -> ux_system_host_registered_hcd; hcd_index++) + { + + /* Pickup HCD pointer. */ + hcd = &_ux_system_host -> ux_system_host_hcd_array[hcd_index]; + + /* Check type of controller. Is it OTG capable ? Must be operational too. */ + if ((hcd -> ux_hcd_otg_capabilities & UX_HCD_OTG_CAPABLE) && + (hcd -> ux_hcd_status == UX_HCD_STATUS_OPERATIONAL)) + { + + /* Yes, we can parse the root hub and see if any devices attached to it. */ + for (port_index = 0; port_index < hcd -> ux_hcd_nb_root_hubs; port_index++) + { + + /* Call HCD for port status. */ + port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_GET_PORT_STATUS, (VOID *)((ALIGN_TYPE)port_index)); + + /* Check return status. */ + if (port_status != UX_PORT_INDEX_UNKNOWN) + { + + /* the port_status value is valid and will tell us if there is + a device attached\detached on the downstream port and if the port is powered. */ + if ((port_status & UX_PS_CCS) && (port_status & UX_PS_PPS)) + { + + /* There is a device attached to one of the root hub port. Parse the device + to find out which one it is. */ + device = _ux_system_host -> ux_system_host_device_array; + + /* Start at the beginning of the list. */ + container_index = 0; + + /* Search the list until the end. */ + while (container_index++ < _ux_system_host -> ux_system_host_max_devices) + { + + /* Until we have found a used entry. */ + if (device -> ux_device_handle != UX_UNUSED) + { + + /* Check for the parent device and the port location and the controller. */ + if((device -> ux_device_port_location == port_index) && + (device -> ux_device_hcd == hcd)) + { + + /* We have a device on a OTG port. But is it a OTG HNP capable device ? + We need to parse the configuration until we find the one current. */ + configuration = device -> ux_device_first_configuration; + + /* Are we at the of the configuration list ? */ + while (configuration != UX_NULL) + { + + /* Is this the current configuration ? */ + if (configuration -> ux_configuration_descriptor.bConfigurationValue == + device -> ux_device_current_configuration) + { + + /* Check for OTG HNP support. */ + if (configuration -> ux_configuration_otg_capabilities & UX_OTG_HNP_SUPPORT) + { + + /* Allocate memory for the OTG status. */ + otg_status = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 16); + + /* Check for status. */ + if (otg_status == UX_NULL) + return; + + /* Retrieve the control endpoint and the transfer request associated with it. */ + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Perform a GET_STATUS on this device to see if it wants to become the host. */ + /* Create a transfer_request for the SET_CONFIGURATION request. No data for this request. */ + transfer_request -> ux_transfer_request_data_pointer = otg_status; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = UX_GET_STATUS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = UX_OTG_STATUS_SELECTOR; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check completion status. */ + if(status == UX_SUCCESS && transfer_request -> ux_transfer_request_actual_length == 1) + { + + /* We have an answer from the device. Check the HNP flag. */ + if (*otg_status & UX_OTG_HOST_REQUEST_FLAG) + { + + /* The device has requested a Host swap. Initiate the command and perform the + stopping of the host. */ + _ux_host_stack_role_swap(device); + } + + } + + /* Free all used resources. */ + _ux_utility_memory_free(otg_status); + + } + } + + /* Move to next configuration in the list. */ + configuration = configuration -> ux_configuration_next_configuration; + + } + } + } + + /* Move to the next device entry. */ + device++; + + } + } + } + } + } + } + } +} + diff --git a/common/core/src/ux_host_stack_initialize.c b/common/core/src/ux_host_stack_initialize.c new file mode 100644 index 0000000..f8b6072 --- /dev/null +++ b/common/core/src/ux_host_stack_initialize.c @@ -0,0 +1,345 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + +UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(sizeof(UX_HCD), UX_MAX_HCD), UX_MAX_HCD_mul_ovf) +UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(sizeof(UX_HOST_CLASS), UX_MAX_CLASS_DRIVER), UX_MAX_CLASS_DRIVER_mul_ovf) +UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(sizeof(UX_DEVICE), UX_MAX_DEVICES), UX_MAX_DEVICES_mul_ovf) + +#ifndef UX_HOST_HNP_POLLING_THREAD_STACK_SIZE +#define UX_HOST_HNP_POLLING_THREAD_STACK_SIZE UX_THREAD_STACK_SIZE +#endif + +/* Defined USBX host variables. */ + +UX_SYSTEM_HOST *_ux_system_host; + +/* Define table of periodic tree entries, properly indexed. */ + +UINT _ux_system_host_hcd_periodic_tree_entries[32] = { + 0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c, + 0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e, + 0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d, + 0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f}; + +/* Define the names of all the USB Classes of USBX. */ + +UCHAR _ux_system_host_class_hub_name[] = "ux_host_class_hub"; +UCHAR _ux_system_host_class_printer_name[] = "ux_host_class_printer"; +UCHAR _ux_system_host_class_storage_name[] = "ux_host_class_storage"; +UCHAR _ux_system_host_class_hid_name[] = "ux_host_class_hid"; +UCHAR _ux_system_host_class_audio_name[] = "ux_host_class_audio"; +UCHAR _ux_system_host_class_cdc_acm_name[] = "ux_host_class_cdc_acm"; +UCHAR _ux_system_host_class_cdc_dlc_name[] = "ux_host_class_cdc_dlc"; +UCHAR _ux_system_host_class_cdc_ecm_name[] = "ux_host_class_cdc_ecm"; +UCHAR _ux_system_host_class_prolific_name[] = "ux_host_class_prolific"; +UCHAR _ux_system_host_class_pima_name[] = "ux_host_class_pima"; +UCHAR _ux_system_host_class_dpump_name[] = "ux_host_class_dpump"; +UCHAR _ux_system_host_class_asix_name[] = "ux_host_class_asix"; +UCHAR _ux_system_host_class_swar_name[] = "ux_host_class_sierra_wireless"; +UCHAR _ux_system_host_class_gser_name[] = "ux_host_class_generic_serial"; +UCHAR _ux_system_host_class_hid_client_remote_control_name[] = "ux_host_class_hid_client_remote_control"; +UCHAR _ux_system_host_class_hid_client_mouse_name[] = "ux_host_class_hid_client_mouse"; +UCHAR _ux_system_host_class_hid_client_keyboard_name[] = "ux_host_class_hid_client_keyboard"; + +/* Define the name of all the USB Host Controllers of USBX. */ + +UCHAR _ux_system_host_hcd_ohci_name[] = "ux_hcd_ohci"; +UCHAR _ux_system_host_hcd_ehci_name[] = "ux_hcd_ehci"; +UCHAR _ux_system_host_hcd_isp1161_name[] = "ux_hcd_isp1161"; +UCHAR _ux_system_host_hcd_isp1362_name[] = "ux_hcd_isp1362"; +UCHAR _ux_system_host_hcd_sh2_name[] = "ux_hcd_rx"; +UCHAR _ux_system_host_hcd_rx_name[] = "ux_hcd_sh2"; +UCHAR _ux_system_host_hcd_pic32_name[] = "ux_hcd_pic32"; +UCHAR _ux_system_host_hcd_stm32_name[] = "ux_hcd_stm32"; +UCHAR _ux_system_host_hcd_musb_name[] = "ux_hcd_musb"; +UCHAR _ux_system_host_hcd_atm7_name[] = "ux_hcd_atm7"; +UCHAR _ux_system_host_hcd_simulator_name[] = "ux_hcd_simulator"; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes all the host code for USBX to work on a */ +/* specific platform. */ +/* */ +/* INPUT */ +/* */ +/* (ux_system_host_change_function) Function pointer to the */ +/* callback function for a */ +/* device change */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate host memory */ +/* _ux_utility_semaphore_create Create host semaphore */ +/* _ux_utility_mutex_create Create host mutex */ +/* _ux_utility_thread_create Create host thread */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_initialize(UINT (*ux_system_host_change_function)(ULONG, UX_HOST_CLASS *, VOID *)) +{ + +UINT status; +UCHAR *memory; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_INITIALIZE, 0, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Initialize some of the global so that we don't have to recompile the + core code when one item is adjusted. */ + _ux_system_host -> ux_system_host_max_class = UX_MAX_CLASS_DRIVER; + _ux_system_host -> ux_system_host_max_hcd = UX_MAX_HCD; + _ux_system_host -> ux_system_host_max_devices = UX_MAX_DEVICES; + _ux_system_host -> ux_system_host_max_ed = UX_MAX_ED; + _ux_system_host -> ux_system_host_max_td = UX_MAX_TD; + _ux_system_host -> ux_system_host_max_iso_td = UX_MAX_ISO_TD; + + /* Set the change device function address. */ + _ux_system_host -> ux_system_host_change_function = ux_system_host_change_function; + + /* Allocate memory for the HCDs. + * sizeof(UX_HCD)*UX_MAX_HCD overflow is checked outside of the function. + */ + memory = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HCD)*UX_MAX_HCD); + + /* Check for successful allocation. */ + if (memory == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set to success by default. */ + status = UX_SUCCESS; + + /* Store memory in system structure. */ + _ux_system_host -> ux_system_host_hcd_array = (UX_HCD *) memory; + + /* Allocate memory for the classes. + * sizeof(UX_HOST_CLASS)*UX_MAX_CLASS_DRIVER overflow is checked outside of the function. + */ + memory = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS)*UX_MAX_CLASS_DRIVER); + + /* Check for successful allocation. */ + if (memory == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + + /* Store memory in system structure. */ + _ux_system_host -> ux_system_host_class_array = (UX_HOST_CLASS *) memory; + + /* Allocate memory for the device containers. + * sizeof(UX_DEVICE)*UX_MAX_DEVICES overflow is checked outside of the function. + */ + if (status == UX_SUCCESS) + { + memory = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_DEVICE)*UX_MAX_DEVICES); + + /* Check for successful allocation. */ + if(memory == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + + /* Store memory in system structure. */ + _ux_system_host -> ux_system_host_device_array = (UX_DEVICE *) memory; + } + + /* Obtain enough stack for the two USBX host threads. */ + if (status == UX_SUCCESS) + { + _ux_system_host -> ux_system_host_enum_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + UX_HOST_ENUM_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (_ux_system_host -> ux_system_host_enum_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Allocate another stack area. */ + if (status == UX_SUCCESS) + { + _ux_system_host -> ux_system_host_hcd_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + UX_HOST_HCD_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (_ux_system_host -> ux_system_host_hcd_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Create the semaphores used by the hub and root hub to awake the enumeration thread. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&_ux_system_host -> ux_system_host_enum_semaphore, "ux_system_host_enum_semaphore", 0); + if(status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Create the semaphores used by the HCD to perform the completion phase of transfer_requests. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&_ux_system_host -> ux_system_host_hcd_semaphore, "ux_system_host_hcd_semaphore", 0); + if(status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Create the enumeration thread of USBX. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&_ux_system_host -> ux_system_host_enum_thread, "ux_system_host_enum_thread", _ux_host_stack_enum_thread_entry, + 0, _ux_system_host -> ux_system_host_enum_thread_stack, + UX_HOST_ENUM_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_ENUM, + UX_THREAD_PRIORITY_ENUM, UX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check the completion status. */ + if(status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + + /* Create the HCD thread of USBX. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&_ux_system_host -> ux_system_host_hcd_thread, "ux_host_stack_hcd_thread", _ux_host_stack_hcd_thread_entry, + 0, _ux_system_host -> ux_system_host_hcd_thread_stack, + UX_HOST_HCD_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_HCD, + UX_THREAD_PRIORITY_HCD, UX_NO_TIME_SLICE,TX_AUTO_START); + + /* Check the completion status. */ + if(status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + +#ifdef UX_OTG_SUPPORT + /* Allocate another stack area for the HNP polling thread. */ + if (status == UX_SUCCESS) + { + _ux_system_host -> ux_system_host_hnp_polling_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + UX_HOST_HNP_POLLING_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (_ux_system_host -> ux_system_host_hnp_polling_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Create the HNP polling thread of USBX. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&_ux_system_host -> ux_system_host_hnp_polling_thread, "ux_host_stack_hnp_polling_thread", _ux_host_stack_hnp_polling_thread_entry, + 0, _ux_system_host -> ux_system_host_hnp_polling_thread_stack, + UX_HOST_HNP_POLLING_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_ENUM, + UX_THREAD_PRIORITY_ENUM, UX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check the completion status. */ + if (status != UX_SUCCESS) + status = UX_THREAD_ERROR; + + /* Return success (SUCCESS). */ + else + return(status); + } + + /* Free up resources on error cases. */ + + /* Last resource, _ux_system_host -> ux_system_host_hnp_polling_thread is not created or created error, + * no need to delete it. */ + + /* Free _ux_system_host -> ux_system_host_hnp_polling_thread_stack. */ + if (_ux_system_host -> ux_system_host_hnp_polling_thread_stack) + _ux_utility_memory_free(_ux_system_host -> ux_system_host_hnp_polling_thread_stack); + + /* Delete _ux_system_host -> ux_system_host_hcd_thread. */ + if (_ux_system_host -> ux_system_host_hcd_thread.tx_thread_id != 0) + _ux_utility_thread_delete(&_ux_system_host -> ux_system_host_hcd_thread); +#else + + /* Return completion status to caller if success. */ + if (status == UX_SUCCESS) + return(status); + + /* Free up resources on error cases. */ + + /* Last resource, _ux_system_host -> ux_system_host_hcd_thread is not created or created error, + * no need to delete it. */ +#endif + + /* Delete _ux_system_host -> ux_system_host_enum_thread. */ + if (_ux_system_host -> ux_system_host_enum_thread.tx_thread_id != 0) + _ux_utility_thread_delete(&_ux_system_host -> ux_system_host_enum_thread); + + /* Delete _ux_system_host -> ux_system_host_hcd_semaphore. */ + if (_ux_system_host -> ux_system_host_hcd_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&_ux_system_host -> ux_system_host_hcd_semaphore); + + /* Delete _ux_system_host -> ux_system_host_enum_semaphore. */ + if (_ux_system_host -> ux_system_host_enum_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&_ux_system_host -> ux_system_host_enum_semaphore); + + /* Free _ux_system_host -> ux_system_host_hcd_thread_stack. */ + if (_ux_system_host -> ux_system_host_hcd_thread_stack) + _ux_utility_memory_free(_ux_system_host -> ux_system_host_hcd_thread_stack); + + /* Free _ux_system_host -> ux_system_host_enum_thread_stack. */ + if (_ux_system_host -> ux_system_host_enum_thread_stack) + _ux_utility_memory_free(_ux_system_host -> ux_system_host_enum_thread_stack); + + /* Free _ux_system_host -> ux_system_host_device_array. */ + if (_ux_system_host -> ux_system_host_device_array) + _ux_utility_memory_free(_ux_system_host -> ux_system_host_device_array); + + /* Free _ux_system_host -> ux_system_host_class_array. */ + if (_ux_system_host -> ux_system_host_class_array) + _ux_utility_memory_free(_ux_system_host -> ux_system_host_class_array); + + /* Free _ux_system_host -> ux_system_host_hcd_array. */ + _ux_utility_memory_free(_ux_system_host -> ux_system_host_hcd_array); + + /* Return completion status to caller. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_interface_endpoint_get.c b/common/core/src/ux_host_stack_interface_endpoint_get.c new file mode 100644 index 0000000..549b79b --- /dev/null +++ b/common/core/src/ux_host_stack_interface_endpoint_get.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_interface_endpoint_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns an endpoint container based on the interface */ +/* handle and an endpoint index. */ +/* */ +/* INPUT */ +/* */ +/* interface Pointer to interface */ +/* endpoint_index Index of endpoint to get */ +/* endpoint Destination for endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_interface_endpoint_get(UX_INTERFACE *interface, UINT endpoint_index, UX_ENDPOINT **endpoint) +{ + +UINT current_endpoint_index; +UX_ENDPOINT *current_endpoint; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_INTERFACE_ENDPOINT_GET, interface, endpoint_index, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Do a sanity check on the interface handle. */ + if (interface -> ux_interface_handle != (ULONG) (ALIGN_TYPE) interface) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_INTERFACE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_INTERFACE_HANDLE_UNKNOWN); + } + + /* Start with the endpoint attached to the interface. */ + current_endpoint = interface -> ux_interface_first_endpoint; + + /* The first endpoint has the index 0. */ + current_endpoint_index = 0; + + /* Traverse the list of the endpoints until we found the right one. */ + while (current_endpoint != UX_NULL) + { + + /* Check if the endpoint index matches the current one. */ + if (endpoint_index == current_endpoint_index) + { + + /* Setup the return endpoint pointer. */ + *endpoint=current_endpoint; + + /* Return success to the caller. */ + return(UX_SUCCESS); + } + + /* Move to the next endpoint. */ + current_endpoint = current_endpoint -> ux_endpoint_next_endpoint; + + /* Move to the next index. */ + current_endpoint_index++; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error! */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); +} + diff --git a/common/core/src/ux_host_stack_interface_instance_create.c b/common/core/src/ux_host_stack_interface_instance_create.c new file mode 100644 index 0000000..79482fc --- /dev/null +++ b/common/core/src/ux_host_stack_interface_instance_create.c @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_interface_instance_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an interface instance. It creates each */ +/* endpoint associated with the interface. */ +/* */ +/* INPUT */ +/* */ +/* interface Pointer to interface */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_instance_create Create instance endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_interface_instance_create(UX_INTERFACE *interface) +{ + +UX_ENDPOINT *endpoint; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_INTERFACE_INSTANCE_CREATE, interface, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Obtain the first endpoint for this alternate setting. */ + endpoint = interface -> ux_interface_first_endpoint; + + /* Loop to create each endpoint. */ + while (endpoint != UX_NULL) + { + + /* Create an endpoint for the instance. */ + status = _ux_host_stack_endpoint_instance_create(endpoint); + + /* Check status, the controller may have refused the endpoint creation. */ + if (status != UX_SUCCESS) + + /* An error occurred at the controller level. */ + return(status); + + /* Move to next endpoint. */ + endpoint = endpoint -> ux_endpoint_next_endpoint; + } + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, interface, 0, 0, 0); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_interface_instance_delete.c b/common/core/src/ux_host_stack_interface_instance_delete.c new file mode 100644 index 0000000..f12b1f5 --- /dev/null +++ b/common/core/src/ux_host_stack_interface_instance_delete.c @@ -0,0 +1,99 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_interface_instance_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will delete an interface instance. It does not delete */ +/* the interface container, but it removes all the endpoints */ +/* associated with alternate setting. */ +/* */ +/* INPUT */ +/* */ +/* interface Pointer to interface */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_instance_delete Delete endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_interface_instance_delete(UX_INTERFACE *interface) +{ + +UX_ENDPOINT *endpoint; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_INTERFACE_INSTANCE_DELETE, interface, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(interface); + + /* Obtain the first endpoint for this alternate setting. */ + endpoint = interface -> ux_interface_first_endpoint; + + /* Loop to delete each endpoint. */ + while (endpoint != UX_NULL) + { + + /* Delete endpoint. */ + _ux_host_stack_endpoint_instance_delete(endpoint); + + /* Move to next endpoint. */ + endpoint = endpoint -> ux_endpoint_next_endpoint; + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_host_stack_interface_set.c b/common/core/src/ux_host_stack_interface_set.c new file mode 100644 index 0000000..dd62866 --- /dev/null +++ b/common/core/src/ux_host_stack_interface_set.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_interface_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a setting of an alternate setting for a */ +/* specific interface. */ +/* */ +/* INPUT */ +/* */ +/* interface Pointer to interface */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_hcd_transfer_request HCD transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_interface_set(UX_INTERFACE *interface) +{ + +UX_DEVICE *device; +UX_CONFIGURATION *configuration; +UX_TRANSFER *transfer_request; +UINT status; +UX_ENDPOINT *control_endpoint; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_INTERFACE_SET, interface, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Retrieve the pointer to the control endpoint and its transfer_request. + From the interface we go back to the configuration, then the device. + The device contains the default control endpoint container. */ + configuration = interface -> ux_interface_configuration; + device = configuration -> ux_configuration_device; + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Create a transfer_request for the SET_INTERFACE request. No data for this request */ + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_SET_INTERFACE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_index = (USHORT) interface -> ux_interface_descriptor.bInterfaceNumber; + transfer_request -> ux_transfer_request_value = (USHORT) interface -> ux_interface_descriptor.bAlternateSetting; + + /* Send request to HCD layer. */ + status = _ux_host_stack_hcd_transfer_request(transfer_request); + + /* Return status completion. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_interface_setting_select.c b/common/core/src/ux_host_stack_interface_setting_select.c new file mode 100644 index 0000000..5fb24a7 --- /dev/null +++ b/common/core/src/ux_host_stack_interface_setting_select.c @@ -0,0 +1,203 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_interface_setting_select PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function selects a specific alternate setting for a given */ +/* interface belonging to the selected configuration. */ +/* */ +/* INPUT */ +/* */ +/* interface Pointer to interface */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_instance_create Create interface instance */ +/* _ux_host_stack_interface_instance_delete Delete interface instance */ +/* _ux_host_stack_interface_set Set interface instance */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_interface_setting_select(UX_INTERFACE *interface) +{ + +UX_CONFIGURATION *configuration; +UX_INTERFACE *current_interface; +UX_INTERFACE *previous_interface; +UX_INTERFACE *main_interface; +UINT current_interface_number; +UINT current_alternate_setting; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_INTERFACE_SETTING_SELECT, interface, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Check this alternate setting container. It must be valid before + we continue. */ + if (interface -> ux_interface_handle != (ULONG) (ALIGN_TYPE) interface) + { + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_INTERFACE_HANDLE_UNKNOWN); + } + + /* From the interface, get the configuration container and the first + interface hooked to this configuration. */ + configuration = interface -> ux_interface_configuration; + current_interface_number = interface -> ux_interface_descriptor.bInterfaceNumber; + current_interface = configuration -> ux_configuration_first_interface; + + /* Remember the main interface to store the next alternate setting. We set the main interface + to the first interface to keep the compiler happy. */ + main_interface = current_interface; + + /* In order to keep the compiler happy, we reset the alternate setting. */ + current_alternate_setting = 0; + previous_interface = UX_NULL; + + /* Search for the current alternate setting for this interface. + All its endpoints will need to be destroyed. + Since interfaces in the parent configuration should be linked together + in correct way, just find it in while loop. + */ + while (1) + { + + /* Try to locate the first interface container in the interface chain which + has the same interface number as the one supplied by the caller. */ + if (current_interface -> ux_interface_descriptor.bInterfaceNumber == current_interface_number) + { + + /* The alternate setting 0 of this interface has the current selected + alternate setting. */ + if (current_interface -> ux_interface_descriptor.bAlternateSetting == 0) + { + + /* Set the alternate setting. */ + current_alternate_setting = current_interface -> ux_interface_current_alternate_setting; + + /* Remember the main interface to store the next alternate setting. */ + main_interface = current_interface; + + } + + /* See if the current alternate setting matches that of the interface alternate setting. */ + if (current_alternate_setting == current_interface -> ux_interface_descriptor.bAlternateSetting) + { + + /* Yes, save the current alternate setting. */ + previous_interface = current_interface; + + /* Then delete the current interface. */ + _ux_host_stack_interface_instance_delete(current_interface); + + /* We are done in this loop. */ + break; + + } + } + + /* Move to the next interface. */ + current_interface = current_interface -> ux_interface_next_interface; + } + + /* Remember the new alternate setting. */ + main_interface -> ux_interface_current_alternate_setting = interface -> ux_interface_descriptor.bAlternateSetting; + + /* Now, the interface must be created with the new alternate setting. */ + status = _ux_host_stack_interface_instance_create(interface); + + /* If we could not create it, we return to the default one. */ + if (status != UX_SUCCESS) + { + + /* Then delete the failed interface. */ + _ux_host_stack_interface_instance_delete(interface); + + /* Error, reset the main interface alternate setting to the default. */ + main_interface -> ux_interface_current_alternate_setting = current_alternate_setting; + + /* Re-create the previous interface with the old alternate setting. */ + _ux_host_stack_interface_instance_create(previous_interface); + + /* Return error status. */ + return(status); + } + + /* Issue a SET_INTERFACE command to the target device. */ + status = _ux_host_stack_interface_set(interface); + + /* Check completion status. */ + if (status != UX_SUCCESS) + { + + /* Error, reset the main interface alternate setting to the default. */ + main_interface -> ux_interface_current_alternate_setting = current_alternate_setting; + + /* Delete the current interface. */ + _ux_host_stack_interface_instance_delete(interface); + + /* Re-create the previous interface with the old alternate setting. */ + _ux_host_stack_interface_instance_create(previous_interface); + + /* Return error status. */ + return(status); + } + + /* Return to caller. */ + return(status); +} + diff --git a/common/core/src/ux_host_stack_interfaces_scan.c b/common/core/src/ux_host_stack_interfaces_scan.c new file mode 100644 index 0000000..41cf9cd --- /dev/null +++ b/common/core/src/ux_host_stack_interfaces_scan.c @@ -0,0 +1,201 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_interfaces_scan PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function scans all the interfaces and alternate settings for */ +/* particular configuration. */ +/* */ +/* INPUT */ +/* */ +/* configuration Where the interface(s) will */ +/* be attached */ +/* descriptor Contains the entire descriptor*/ +/* for this configuration */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse interface descriptor */ +/* _ux_host_stack_new_interface_create Create new interface */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_interfaces_scan(UX_CONFIGURATION *configuration, UCHAR * descriptor) +{ + +ULONG total_configuration_length; +UINT descriptor_length; +UINT descriptor_type; +ULONG status; +ULONG interface_association_descriptor_present; +ULONG interface_in_iad_count; +UX_INTERFACE_ASSOCIATION_DESCRIPTOR interface_association; + + /* Retrieve the size of all the configuration descriptor. */ + total_configuration_length = configuration -> ux_configuration_descriptor.wTotalLength; + + /* Set the IAD to false. */ + interface_association_descriptor_present = UX_FALSE; + + /* Set the IAD interface count to zero. */ + interface_in_iad_count = 0; + + /* Scan the entire descriptor and search for interfaces. We should also ensure that + the descriptor is valid by verifying the length of each descriptor scanned. */ + while (total_configuration_length) + { + + /* Gather the length and type of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Check the type for an interface association descriptor. */ + if (descriptor_type == UX_INTERFACE_ASSOCIATION_DESCRIPTOR_ITEM) + { + + /* Parse the interface association descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, + _ux_system_interface_association_descriptor_structure, + UX_INTERFACE_ASSOCIATION_DESCRIPTOR_ENTRIES, + (UCHAR *) &interface_association); + + /* Retrieve the CLASS/SUBCLASS from descriptor and store it in the configuration instance. */ + configuration -> ux_configuration_iad_class = interface_association.bFunctionClass; + configuration -> ux_configuration_iad_subclass = interface_association.bFunctionSubClass; + configuration -> ux_configuration_iad_protocol = interface_association.bFunctionProtocol; + + /* We have an IAD. */ + interface_association_descriptor_present = UX_TRUE; + + /* Memorize the number of interfaces attached to this IAD. */ + interface_in_iad_count = interface_association.bInterfaceCount; + } + + /* Check the type for an interface descriptor. */ + if (descriptor_type == UX_INTERFACE_DESCRIPTOR_ITEM) + { + + /* We have found an interface descriptor. This descriptor contains at least + the default alternate setting (with value 0) and may have others. */ + status = _ux_host_stack_new_interface_create(configuration, descriptor, total_configuration_length); + + /* Are we within an IAD ? */ + if (interface_association_descriptor_present == UX_TRUE) + { + + /* Decrement the number of interfaces attached here. */ + interface_in_iad_count--; + + /* Are we at the end of the interface count ? */ + if (interface_in_iad_count == 0) + { + + /* Set the IAD to false now. */ + interface_association_descriptor_present = UX_FALSE; + + /* Reset the IAD Class/Subclass/Protocol. */ + configuration -> ux_configuration_iad_class = 0; + configuration -> ux_configuration_iad_subclass = 0; + configuration -> ux_configuration_iad_protocol = 0; + + } + } + + /* Check return status. */ + if(status != UX_SUCCESS) + return(status); + } + + /* Check the type for an OTG descriptor. */ + if (descriptor_type == UX_OTG_DESCRIPTOR_ITEM) + + /* Retrieve the bmAttributes for SRP/HNP support. */ + configuration -> ux_configuration_otg_capabilities = (ULONG) *(descriptor + UX_OTG_BM_ATTRIBUTES); + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_configuration_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_configuration_length -= descriptor_length; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_new_configuration_create.c b/common/core/src/ux_host_stack_new_configuration_create.c new file mode 100644 index 0000000..e982289 --- /dev/null +++ b/common/core/src/ux_host_stack_new_configuration_create.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_new_configuration_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a new configuration for the current device */ +/* a device can have multiple configurations. */ +/* */ +/* INPUT */ +/* */ +/* device Pointer to the descriptor */ +/* for the device */ +/* configuration_descriptor Configuration descriptor */ +/* previously parsed */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_new_configuration_create(UX_DEVICE *device, UX_CONFIGURATION *configuration) +{ + +UX_CONFIGURATION *list_configuration; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_NEW_CONFIGURATION_CREATE, device, configuration, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* The device that owns this configuration is memorized in the + configuration container itself, easier for back chaining. */ + configuration -> ux_configuration_device = device; + + /* Save the configuration handle in the container, this is for ensuring the + configuration container is not corrupted. */ + configuration -> ux_configuration_handle = (ULONG) (ALIGN_TYPE) configuration; + + /* There is 2 cases for the creation of the configuration descriptor + if this is the first one, the configuration descriptor is hooked + to the device. If it is not the first one, the configuration is + hooked to the end of the chain of configurations. */ + if (device -> ux_device_first_configuration == UX_NULL) + { + device -> ux_device_first_configuration = configuration; + } + else + { + + /* Get the pointer to the first configuration. */ + list_configuration = device -> ux_device_first_configuration; + + /* And traverse until we have reached the end of the configuration list. */ + while (list_configuration -> ux_configuration_next_configuration != UX_NULL) + { + list_configuration = list_configuration -> ux_configuration_next_configuration; + } + + /* Hook the new configuration. */ + list_configuration -> ux_configuration_next_configuration = configuration; + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_host_stack_new_device_create.c b/common/core/src/ux_host_stack_new_device_create.c new file mode 100644 index 0000000..387cba9 --- /dev/null +++ b/common/core/src/ux_host_stack_new_device_create.c @@ -0,0 +1,244 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_new_device_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a new device on the USB. It may be called */ +/* either by a hub or by the root hub. */ +/* */ +/* INPUT */ +/* */ +/* HCD HCD that owns that device */ +/* device_owner Either a root hub instance */ +/* or a hub instance */ +/* port_index Port the new device is mounted*/ +/* device_speed Speed at which the device is */ +/* running (low, full, high) */ +/* port_power_available Power available on the root */ +/* or hub port. This value is */ +/* used to ensure that the */ +/* device can be configured */ +/* without creating an */ +/* OVER_CURRENT condition on */ +/* the bus */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* An Error code could be an indication that the device could not be */ +/* created in which case the error is fatal to the enumeration and */ +/* will not be retried. The error code can also indicate a failure */ +/* for the device to respond to the GET_DEVICE_DESCRIPTOR. This error */ +/* is not fatal and the caller should retry the enumeration process */ +/* after the port to which the device is attached has been reset. */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_device_scan Scan class devices */ +/* _ux_host_stack_class_interface_scan Scan class interfaces */ +/* _ux_host_stack_device_address_set Set device address */ +/* _ux_host_stack_device_descriptor_read Read device descriptor */ +/* _ux_host_stack_configuration_enumerate */ +/* Enumerate device config */ +/* _ux_host_stack_new_device_get Get new device */ +/* _ux_utility_semaphore_create Create a semaphore */ +/* (ux_hcd_entry_function) HCD entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_new_device_create(UX_HCD *hcd, UX_DEVICE *device_owner, + UINT port_index, UINT device_speed, + UINT port_max_power) +{ + +UX_DEVICE *device; +UINT status; +UX_ENDPOINT *control_endpoint; + + + /* Verify the number of devices attached to the HCD already. Normally a HCD + can have up to 127 devices but that can be tailored. */ + if (hcd -> ux_hcd_nb_devices > UX_MAX_USB_DEVICES) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_TOO_MANY_DEVICES); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TOO_MANY_DEVICES, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_TOO_MANY_DEVICES); + } + + /* Get a new device container to store this new device. */ + device = _ux_host_stack_new_device_get(); + if (device == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_TOO_MANY_DEVICES); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TOO_MANY_DEVICES, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_TOO_MANY_DEVICES); + } + + /* Increment the number of devices on this bus. */ + hcd -> ux_hcd_nb_devices++; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_NEW_DEVICE_CREATE, hcd, device_owner, port_index, device, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* At this stage the device is attached but not configured. + we don't have to worry about power consumption yet. + Initialize the device structure. */ + device -> ux_device_handle = (ULONG) (ALIGN_TYPE) device; + device -> ux_device_state = UX_DEVICE_ATTACHED; + device -> ux_device_parent = device_owner; + device -> ux_device_hcd = hcd; + device -> ux_device_speed = device_speed; + device -> ux_device_port_location = port_index; + device -> ux_device_max_power = port_max_power; + + /* Create a semaphore for the device. This is to protect endpoint 0 mostly for OTG HNP polling. The initial count is 1 as + a mutex mechanism. */ + status = _ux_utility_semaphore_create(&device -> ux_device_protection_semaphore, "ux_host_endpoint0_semaphore", 1); + + /* Check semaphore creation. */ + if (status != UX_SUCCESS) + { + + /* Return error. Device resources that have been allocated until this + point should be freed by the caller via _ux_host_stack_device_resources_free. */ + return(UX_SEMAPHORE_ERROR); + } + + /* Initialize the default control endpoint permanently attached + to the device. */ + control_endpoint = &device -> ux_device_control_endpoint; + control_endpoint -> ux_endpoint = (ULONG) (ALIGN_TYPE) control_endpoint; + control_endpoint -> ux_endpoint_next_endpoint = UX_NULL; + control_endpoint -> ux_endpoint_interface = UX_NULL; + control_endpoint -> ux_endpoint_device = device; + control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_endpoint = control_endpoint; + + /* Create a semaphore for this endpoint to be attached to its transfer request. */ + status = _ux_utility_semaphore_create(&control_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_semaphore, "ux_host_transfer_request_semaphore", 0); + + /* Check semaphore creation. */ + if (status != UX_SUCCESS) + { + + /* Return error. Device resources that have been allocated until this + point should be freed by the caller via _ux_host_stack_device_resources_free. */ + return(UX_SEMAPHORE_ERROR); + } + + /* If the device is running in high speed the default max packet size for the control endpoint is 64. + All other speeds the size is 8. */ + if (device_speed == UX_HIGH_SPEED_DEVICE) + control_endpoint -> ux_endpoint_descriptor.wMaxPacketSize = UX_DEFAULT_HS_MPS; + else + control_endpoint -> ux_endpoint_descriptor.wMaxPacketSize = UX_DEFAULT_MPS; + + /* Create the default control endpoint at the HCD level. */ + status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_CREATE_ENDPOINT, (VOID *) control_endpoint); + if (status == UX_SUCCESS) + { + + /* Now control endpoint is ready, set state to running. */ + control_endpoint -> ux_endpoint_state = UX_ENDPOINT_RUNNING; + + /* Set the address of the device. The first time a USB device is + accessed, it responds to the address 0. We need to change the address + to a free device address between 1 and 127 ASAP. */ + status = _ux_host_stack_device_address_set(device); + if (status == UX_SUCCESS) + { + + /* Get the device descriptor. */ + status = _ux_host_stack_device_descriptor_read(device); + if (status == UX_SUCCESS) + { + + /* Get the configuration descriptor(s) for the device + and parse all the configuration, interface, endpoints... */ + status = _ux_host_stack_configuration_enumerate(device); + } + } + } + + /* Check the status of the previous operations. If there was an + error during any of the phases, the device resources must be + freed based on if we want to retry. */ + if (status == UX_SUCCESS) + { + + /* The device, configuration(s), interface(s), endpoint(s) are + now in order for this device to work. No configuration is set + yet. First we need to find a class driver that wants to own + it. There is no need to have an orphan device in a configured state. */ + status = _ux_host_stack_class_device_scan(device); + if (status == UX_NO_CLASS_MATCH) + { + + status = _ux_host_stack_class_interface_scan(device); + } + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_DEVICE, hcd, device_owner, port_index, 0); + } + + /* Return status. If there's an error, device resources that have been + allocated until this point should be freed by the caller via _ux_host_stack_device_resources_free. */ + return(status); +} diff --git a/common/core/src/ux_host_stack_new_device_get.c b/common/core/src/ux_host_stack_new_device_get.c new file mode 100644 index 0000000..88fbb2f --- /dev/null +++ b/common/core/src/ux_host_stack_new_device_get.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_new_device_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free device container for the new device. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* UX_DEVICE pointer */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory to a value */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_DEVICE *_ux_host_stack_new_device_get(VOID) +{ + +ULONG container_index; +UX_DEVICE *device; + + + /* Start with the first device. */ + device = _ux_system_host -> ux_system_host_device_array; + + /* Reset the container index. */ + container_index = 0; + + /* Search the list until the end. */ + while (container_index++ < _ux_system_host -> ux_system_host_max_devices) + { + + /* Until we have found an unused entry. */ + if (device -> ux_device_handle == UX_UNUSED) + { + + /* Reset the entire entry. */ + _ux_utility_memory_set(device, 0, sizeof(UX_DEVICE)); + + /* This entry is now used. */ + device -> ux_device_handle = UX_USED; + + /* Return the device pointer. */ + return(device); + } + + /* Move to the next device entry. */ + device++; + } + + /* No unused devices, return NULL. */ + return(UX_NULL); +} + diff --git a/common/core/src/ux_host_stack_new_endpoint_create.c b/common/core/src/ux_host_stack_new_endpoint_create.c new file mode 100644 index 0000000..abadc00 --- /dev/null +++ b/common/core/src/ux_host_stack_new_endpoint_create.c @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_new_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a new endpoint for the current interface */ +/* scanned. The endpoint is hooked to the interface that owns it. */ +/* It is not active yet until either the default interface for the */ +/* configuration is selected by a SET_CONFIGURATION or when an */ +/* alternate setting for this interface is set. */ +/* */ +/* INPUT */ +/* */ +/* interface Interface container that owns */ +/* this endpoint */ +/* endpoint_pointer Pointer to a unparsed */ +/* endpoint descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse the descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_new_endpoint_create(UX_INTERFACE *interface, + UCHAR * interface_endpoint) +{ + +UX_ENDPOINT *endpoint; +UX_ENDPOINT *list_endpoint; + + /* Obtain memory for storing this new endpoint. */ + endpoint = (UX_ENDPOINT *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_ENDPOINT)); + if (endpoint == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_NEW_ENDPOINT_CREATE, interface, endpoint, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Save the endpoint handle in the container, this is for ensuring the + endpoint container is not corrupted. */ + endpoint -> ux_endpoint = (ULONG) (ALIGN_TYPE) endpoint; + + /* The endpoint container has a built in transfer_request. + The transfer_request needs to point to the endpoint as well. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_endpoint = endpoint; + + /* Save the pointer to the device. This is useful for the HCD layer. */ + endpoint -> ux_endpoint_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(interface_endpoint, + _ux_system_endpoint_descriptor_structure, + UX_ENDPOINT_DESCRIPTOR_ENTRIES, + (UCHAR *) &endpoint -> ux_endpoint_descriptor); + + /* The interface that owns this endpoint is memorized in the + endpoint container itself, easier for back chaining. */ + endpoint -> ux_endpoint_interface = interface; + + /* There is 2 cases for the creation of the endpoint descriptor + if this is the first one, the endpoint descriptor is hooked + to the interface. + If it is not the first one, the endpoint is hooked to the + end of the chain of endpoints. */ + if (interface -> ux_interface_first_endpoint == UX_NULL) + { + + interface -> ux_interface_first_endpoint = endpoint; + } + else + { + + list_endpoint = interface -> ux_interface_first_endpoint; + + /* Traverse the list until the end. */ + while (list_endpoint -> ux_endpoint_next_endpoint != UX_NULL) + list_endpoint = list_endpoint -> ux_endpoint_next_endpoint; + + /* Hook the endpoint. */ + list_endpoint -> ux_endpoint_next_endpoint = endpoint; + } + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_new_interface_create.c b/common/core/src/ux_host_stack_new_interface_create.c new file mode 100644 index 0000000..14c9c5a --- /dev/null +++ b/common/core/src/ux_host_stack_new_interface_create.c @@ -0,0 +1,207 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_new_interface_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a new interface for the current configuration */ +/* scanned. A device has at least 1 alternate setting per interface */ +/* which is the default one. */ +/* */ +/* The interface is hooked to the configuration that owns it. */ +/* */ +/* From the interface descriptor, all the endpoints are hooked but */ +/* not activated. */ +/* */ +/* INPUT */ +/* */ +/* configuration Configuration container that */ +/* owns this interface */ +/* interface_pointer Pointer to a unparsed */ +/* interface descriptor */ +/* length Length remaining in this */ +/* descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_new_endpoint_create Create new endpoint */ +/* _ux_utility_descriptor_parse Parse the descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_new_interface_create(UX_CONFIGURATION *configuration, + UCHAR * descriptor, ULONG length) +{ + +UX_INTERFACE *list_interface; +UX_INTERFACE *interface; +UINT number_endpoints; +UINT descriptor_length; +UINT descriptor_type; +UINT status; +UCHAR *this_interface_descriptor; + + /* Obtain memory for storing this new interface. */ + interface = (UX_INTERFACE *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_INTERFACE)); + + /* If no memory left, exit with error. */ + if (interface == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save the interface handle in the container, this is for ensuring the + interface container is not corrupted. */ + interface -> ux_interface_handle = (ULONG) (ALIGN_TYPE) interface; + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, + _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, + (UCHAR *) &interface -> ux_interface_descriptor); + + /* The configuration that owns this interface is memorized in the + interface container itself, easier for back chaining. */ + interface -> ux_interface_configuration = configuration; + + /* If the interface belongs to an IAD, remember the IAD Class/SubClass/Protocol. */ + interface -> ux_interface_iad_class = configuration -> ux_configuration_iad_class; + interface -> ux_interface_iad_subclass = configuration -> ux_configuration_iad_subclass; + interface -> ux_interface_iad_protocol = configuration -> ux_configuration_iad_protocol; + + /* There is 2 cases for the creation of the interface descriptor + if this is the first one, the interface descriptor is hooked + to the configuration. If it is not the first one, the interface + is hooked to the end of the chain of interfaces. */ + if (configuration -> ux_configuration_first_interface == UX_NULL) + { + configuration -> ux_configuration_first_interface = interface; + } + else + { + + list_interface = configuration -> ux_configuration_first_interface; + + /* Traverse the list until we reach the end */ + while (list_interface -> ux_interface_next_interface != UX_NULL) + { + + list_interface = list_interface -> ux_interface_next_interface; + } + + /* Hook the interface. */ + list_interface -> ux_interface_next_interface = interface; + } + + /* Traverse the interface in search of all endpoints that belong to it. + We need the length remaining in the descriptor and the number of endpoints + reported for this interface. */ + number_endpoints = interface -> ux_interface_descriptor.bNumEndpoints; + + this_interface_descriptor = descriptor; + + while (length && (number_endpoints != 0)) + { + + /* Gather the length and type of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor+1); + + /* make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Check the type for an interface descriptor. */ + if (descriptor_type == UX_ENDPOINT_DESCRIPTOR_ITEM) + { + + /* We have found an endpoint descriptor for this interface. */ + status = _ux_host_stack_new_endpoint_create(interface, descriptor); + + /* Check return status. */ + if(status != UX_SUCCESS) + return(status); + + number_endpoints--; + } + + /* Verify if the descriptor is still valid, or we moved to next interface. */ + if ((descriptor_length > length) || (descriptor_type == UX_INTERFACE_DESCRIPTOR_ITEM && descriptor != this_interface_descriptor)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + length -= descriptor_length; + } + + /* Return success! */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_rh_change_process.c b/common/core/src/ux_host_stack_rh_change_process.c new file mode 100644 index 0000000..91bc167 --- /dev/null +++ b/common/core/src/ux_host_stack_rh_change_process.c @@ -0,0 +1,173 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_rh_change_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for changes in topology in each of the */ +/* installed HCDs. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_rh_device_insertion Process device insertion */ +/* _ux_host_stack_rh_device_extraction Process device extraction */ +/* (ux_hcd_entry_function) HCD entry function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_stack_rh_change_process(VOID) +{ + +UX_HCD *hcd; +UINT hcd_index; +ULONG port_status; +UINT port_index; +UX_INT_SAVE_AREA + + /* This thread was maybe awaken by one or more HCD controllers. Check each + of the HCD to see where there has been a change of topology. */ + for(hcd_index = 0; hcd_index < _ux_system_host -> ux_system_host_registered_hcd; hcd_index++) + { + + /* Pickup HCD pointer. */ + hcd = &_ux_system_host -> ux_system_host_hcd_array[hcd_index]; + + /* Is this HCD operational? */ + if (hcd -> ux_hcd_status == UX_HCD_STATUS_OPERATIONAL) + { + + /* On each port of the root hub of the controller, get the port status */ + for (port_index = 0; port_index < hcd -> ux_hcd_nb_root_hubs; port_index++) + { + + /* Was there any activity on this port ? */ + if (hcd -> ux_hcd_root_hub_signal[port_index] != 0) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_RH_CHANGE_PROCESS, port_index, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Reset that port activity signal. */ + UX_DISABLE_INTS + hcd -> ux_hcd_root_hub_signal[port_index]--; + UX_RESTORE_INTS + + /* Call HCD for port status. */ + port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_GET_PORT_STATUS, (VOID *)((ALIGN_TYPE)port_index)); + + /* Check return status. */ + if (port_status != UX_PORT_INDEX_UNKNOWN) + { + + /* The port_status value is valid and will tell us if there is + a device attached\detached on the downstream port. */ + if (port_status & UX_PS_CCS) + { + + /* There is a device attached to this port. Check if we know + about it. If not, this is a device insertion signal. */ + if ((hcd -> ux_hcd_rh_device_connection & (ULONG)(1 << port_index)) == 0) + { + + /* We have a simple device insertion, we have not lost any signals. + the root hub and the stack enumeration module are in synch. */ + _ux_host_stack_rh_device_insertion(hcd,port_index); + } + else + { + /* We can get here when there has been multiple events on the hardware root hub + but we may have missed them of they were too close or the stack got too busy. + We check the number of events in the root hub signal. If it is not zero + we are out of synch, meaning we got a disconnection followed very quickly + by a insertion. */ + + if (hcd -> ux_hcd_root_hub_signal[port_index] != 0) + { + + /* We need to get back in synch now. */ + UX_DISABLE_INTS + hcd -> ux_hcd_root_hub_signal[port_index] = 0; + UX_RESTORE_INTS + + /* First extract the device. */ + _ux_host_stack_rh_device_extraction(hcd,port_index); + + /* Now, insert it again. */ + _ux_host_stack_rh_device_insertion(hcd,port_index); + + + } + + } + } + else + { + + /* There is no device attached to this port. Check if we know + about it. If not, this is a device removal signal. */ + if ((hcd -> ux_hcd_rh_device_connection & (ULONG)(1 << port_index)) !=0) + { + _ux_host_stack_rh_device_extraction(hcd,port_index); + } + } + } + } + } + } + } +} + diff --git a/common/core/src/ux_host_stack_rh_device_extraction.c b/common/core/src/ux_host_stack_rh_device_extraction.c new file mode 100644 index 0000000..90d8b80 --- /dev/null +++ b/common/core/src/ux_host_stack_rh_device_extraction.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_rh_device_extraction PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles a device extraction on a downstream port */ +/* of the root hub pointed by HCD. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD structure */ +/* port_index Port index of insertion */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_device_remove Remove device */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_rh_device_extraction(UX_HCD *hcd, UINT port_index) +{ + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_RH_DEVICE_EXTRACTION, hcd, port_index, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Ask the stack to remove the device, pass the value 0 as the parent root hub. */ + _ux_host_stack_device_remove(hcd, 0, port_index); + + /* The device has been removed, so the port is free again. */ + hcd -> ux_hcd_rh_device_connection &= (ULONG)~(1 << port_index); + + /* That command should never fail! */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_rh_device_insertion.c b/common/core/src/ux_host_stack_rh_device_insertion.c new file mode 100644 index 0000000..fe08260 --- /dev/null +++ b/common/core/src/ux_host_stack_rh_device_insertion.c @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_rh_device_insertion PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles a device insertion on a downstream port of */ +/* the root hub pointed by HCD. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD structure */ +/* port_index Port index of insertion */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_delay_ms Thread sleep */ +/* _ux_host_stack_new_device_create New device create */ +/* _ux_host_stack_device_remove Device remove */ +/* (hcd_entry_function) Entry function of HCD driver */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_rh_device_insertion(UX_HCD *hcd, UINT port_index) +{ + +UINT index_loop; +UINT device_speed; +ULONG port_status; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_RH_DEVICE_INSERTION, hcd, port_index, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* Perform a PORT_ENABLE command. */ + port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_ENABLE_PORT, (VOID *)((ALIGN_TYPE)port_index)); + + /* Check return status. */ + if (port_status == UX_PORT_INDEX_UNKNOWN) + return(port_status); + + /* A debounce interval with a minimum duration of 100 ms on attach. */ + _ux_utility_delay_ms(100); + + /* The first attempts to do a device enumeration may fail. + Typically, after the port is reset and the first command is sent to + the device, there is no answer. In this case, we reset the port again + and retry. Usually that does the trick! */ + for (index_loop = 0; index_loop < UX_RH_ENUMERATION_RETRY; index_loop++) + { + + /* Now we have to do a PORT_RESET command. */ + port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_RESET_PORT, (VOID *)((ALIGN_TYPE)port_index)); + if (port_status == UX_SUCCESS) + { + + /* The port reset phase was successful. Before we invoke the device enumeration function, + we need to know the speed of the device. */ + port_status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_GET_PORT_STATUS, (VOID *)((ALIGN_TYPE)port_index)); + + /* Check return status. */ + if (port_status == UX_PORT_INDEX_UNKNOWN) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ROOT_HUB, UX_DEVICE_ENUMERATION_FAILURE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_ENUMERATION_FAILURE, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DEVICE_ENUMERATION_FAILURE); + } + + /* Set the device speed. */ + device_speed = port_status >> UX_PS_DS; + + /* Ask the USB stack to enumerate this device. A root hub is considered self + powered. */ + status = _ux_host_stack_new_device_create(hcd, UX_NULL, port_index, device_speed, UX_MAX_SELF_POWER); + + /* Check return status. */ + if (status == UX_SUCCESS) + { + + /* Successful device create. */ + + /* The device has been mounted properly, we have to remember this + so when the device is removed, we have to invoke the enumeration + function again */ + hcd -> ux_hcd_rh_device_connection |= (ULONG)(1 << port_index); + + /* Return success to the caller. */ + return(UX_SUCCESS); + } + else if (index_loop < UX_RH_ENUMERATION_RETRY - 1) + + /* Simulate remove to free allocated resources before retry. */ + _ux_host_stack_device_remove(hcd, UX_NULL, port_index); + } + + /* We get here if something did not go well. Either the port did not respond + well to the ENABLE\RESET phases or the device did not enumerate well + so we try again ! */ + _ux_utility_delay_ms(UX_RH_ENUMERATION_RETRY_DELAY); + } + + /* If we get here, the device did not enumerate completely. The device is still attached to the root + hub and therefore there is a physical connection with a unenumerated device. */ + hcd -> ux_hcd_rh_device_connection |= (ULONG)(1 << port_index); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ROOT_HUB, UX_DEVICE_ENUMERATION_FAILURE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_ENUMERATION_FAILURE, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return a failed enumeration. */ + return(UX_DEVICE_ENUMERATION_FAILURE); +} + diff --git a/common/core/src/ux_host_stack_role_swap.c b/common/core/src/ux_host_stack_role_swap.c new file mode 100644 index 0000000..f28b32a --- /dev/null +++ b/common/core/src/ux_host_stack_role_swap.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_role_swap PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when the host or the device demand a role */ +/* swap. This function may be called by an application or by the HNP */ +/* thread. */ +/* */ +/* INPUT */ +/* */ +/* hcd Pointer to HCD */ +/* device Device pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_host_stack_transfer_request Transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application of HNP thread. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_role_swap(UX_DEVICE *device) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; + + /* Retrieve the control endpoint and the transfer request associated with it. */ + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Perform a SET_FEATURE on this device to inform the it we are ready to change role. */ + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_SET_FEATURE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_OTG_FEATURE_B_HNP_ENABLE; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the status fails, simply ignore the command but do not proceed. */ + if (status != UX_SUCCESS) + + /* We have an error. Do not proceed. */ + return(status); + + /* Keep track of the event for HNP synchronization. */ + _ux_system_otg -> ux_system_otg_change_mode_event = UX_OTG_HOST_TO_SLAVE; + + /* Call the OTG function that will perform the swap. */ + _ux_system_otg -> ux_system_otg_function(UX_OTG_HOST_TO_SLAVE); + + /* Reset the event. */ + _ux_system_otg -> ux_system_otg_change_mode_event = 0; + + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_host_stack_transfer_request.c b/common/core/src/ux_host_stack_transfer_request.c new file mode 100644 index 0000000..8c093be --- /dev/null +++ b/common/core/src/ux_host_stack_transfer_request.c @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_transfer_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a USB transaction. On entry the transfer */ +/* request gives the endpoint pipe selected for this transaction and */ +/* the parameters associated with the transfer (data payload, length */ +/* of transaction) */ +/* */ +/* For Control pipe, the transaction is blocking and will only return */ +/* when the 3 phases of the control transfer have been completed or if */ +/* there is a previous error. For other pipes, the USB stack will */ +/* schedule the transaction on the USB but will not wait for its */ +/* completion. Each request for non blocking pipes has to specify a */ +/* completion routine. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Transfer request structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status If UX_SUCCESS, transfer was */ +/* successfully started */ +/* */ +/* CALLS */ +/* */ +/* HCD Entry Function */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_transfer_request(UX_TRANSFER *transfer_request) +{ + +TX_INTERRUPT_SAVE_AREA + +UX_ENDPOINT *endpoint; +UX_DEVICE *device; +UX_HCD *hcd; +UINT status; + + + /* Get the endpoint container from the transfer_request */ + endpoint = transfer_request -> ux_transfer_request_endpoint; + + /* Get the device container from the endpoint. */ + device = endpoint -> ux_endpoint_device; + + /* Ensure we are not preempted by the enum thread while we check the device + state and set the transfer status. */ + TX_DISABLE + + /* We can only transfer when the device is ATTACHED, ADDRESSED OR CONFIGURED. */ + if ((device -> ux_device_state == UX_DEVICE_ATTACHED) || (device -> ux_device_state == UX_DEVICE_ADDRESSED) + || (device -> ux_device_state == UX_DEVICE_CONFIGURED)) + { + + /* Set the transfer to pending. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STATUS_PENDING; + + /* Save the thread making this transfer. If we're under interrupt, this + will be null. */ + transfer_request -> ux_transfer_request_thread_pending = _ux_utility_thread_identify(); + } + else + { + + /* The device is in an invalid state. Restore interrupts and return error. */ + TX_RESTORE + + /* Check if this is endpoint 0. */ + if ((endpoint -> ux_endpoint_descriptor.bEndpointAddress & (UINT)~UX_ENDPOINT_DIRECTION) == 0) + { + + /* Check if the class has already protected it. */ + if (device -> ux_device_protection_semaphore.tx_semaphore_count == 0) + { + + /* Class is using endpoint 0. Unprotect semaphore. */ + _ux_utility_semaphore_put(&device -> ux_device_protection_semaphore); + } + } + + return(UX_TRANSFER_NOT_READY); + } + + /* Restore interrupts. */ + TX_RESTORE + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_TRANSFER_REQUEST, device, endpoint, transfer_request, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* With the device we have the pointer to the HCD. */ + hcd = device -> ux_device_hcd; + + /* If this is endpoint 0, we protect the endpoint from a possible re-entry. */ + if ((endpoint -> ux_endpoint_descriptor.bEndpointAddress & (UINT)~UX_ENDPOINT_DIRECTION) == 0) + { + + /* Check if the class has already protected it. */ + if (device -> ux_device_protection_semaphore.tx_semaphore_count != 0) + { + + /* We are using endpoint 0. Protect with semaphore. */ + status = _ux_utility_semaphore_get(&device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + } + } + + /* Send the command to the controller. */ + status = hcd -> ux_hcd_entry_function(hcd, UX_HCD_TRANSFER_REQUEST, transfer_request); + + /* If this is endpoint 0, we unprotect the endpoint. */ + if ((endpoint -> ux_endpoint_descriptor.bEndpointAddress & (UINT)~UX_ENDPOINT_DIRECTION) == 0) + + /* We are using endpoint 0. Unprotect with semaphore. */ + _ux_utility_semaphore_put(&device -> ux_device_protection_semaphore); + + /* And return the status. */ + return(status); +} diff --git a/common/core/src/ux_host_stack_transfer_request_abort.c b/common/core/src/ux_host_stack_transfer_request_abort.c new file mode 100644 index 0000000..cbdaa19 --- /dev/null +++ b/common/core/src/ux_host_stack_transfer_request_abort.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_stack_transfer_request_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function aborts a pending transfer request that has been */ +/* previously submitted. This function only cancels the specific */ +/* transfer request. */ +/* */ +/* The call back to the function will have the */ +/* UX_TRANSFER_STATUS_ABORT status. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Transfer request structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status If UX_SUCCESS, transfer was */ +/* successfully aborted */ +/* */ +/* CALLS */ +/* */ +/* HCD Entry Function */ +/* Transfer Completion Function */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_stack_transfer_request_abort(UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_DEVICE *device; +UX_HCD *hcd; +ULONG completion_code; + + + /* Get the endpoint container from the transfer request. */ + endpoint = transfer_request -> ux_transfer_request_endpoint; + + /* Get the device container from the endpoint. */ + device = endpoint -> ux_endpoint_device; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_TRANSFER_REQUEST_ABORT, device, endpoint, transfer_request, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) + + /* With the device we have the pointer to the HCD. */ + hcd = device -> ux_device_hcd; + + /* Check pending transaction. */ + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + { + + /* Send the abort command to the controller. */ + hcd -> ux_hcd_entry_function(hcd, UX_HCD_TRANSFER_ABORT, transfer_request); + + /* Save the completion code since we're about to set it to ABORT. The + reason we can't just assume its value is PENDING is that in between + the completion code check and this line, it's possible that the transfer + completed, which would've changed the completion code to SUCCESS. + Such a case is valid, and we want to make sure we don't put() the + transfer request's semaphore again. */ + completion_code = transfer_request -> ux_transfer_request_completion_code; + + /* Set the transfer_request status to abort. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STATUS_ABORT; + + /* We need to inform the class that owns this transfer_request of the + abort if there is a call back mechanism. */ + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* Is a thread waiting on the semaphore? */ + if (/* Is the transfer pending? */ + completion_code == UX_TRANSFER_STATUS_PENDING && + /* Is the thread waiting not this one? (clearly we're not waiting!) */ + transfer_request -> ux_transfer_request_thread_pending != _ux_utility_thread_identify() && + /* Does the transfer request not have a completion function? */ + transfer_request -> ux_transfer_request_completion_function == UX_NULL) + + /* Wake up the semaphore for this request. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + } + + /* This function never fails! */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_system_error_handler.c b/common/core/src/ux_system_error_handler.c new file mode 100644 index 0000000..530c925 --- /dev/null +++ b/common/core/src/ux_system_error_handler.c @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_system_error_handler PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the last critical error from USBX functions. */ +/* It is mainly used for debugging purpose to trap where error occurred*/ +/* */ +/* */ +/* INPUT */ +/* */ +/* error_code */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_system_error_handler(UINT system_level, UINT system_context, UINT error_code) +{ + + /* Save the last system error code. */ + _ux_system -> ux_system_last_error = error_code; + + /* Increment the total number of system errors. */ + _ux_system -> ux_system_error_count++; + + /* Is there an application call back function to call ? */ + if (_ux_system -> ux_system_error_callback_function != UX_NULL) + { + + /* The callback function is defined, call it. */ + _ux_system -> ux_system_error_callback_function(system_level, system_context, error_code); + } +} diff --git a/common/core/src/ux_system_initialize.c b/common/core/src/ux_system_initialize.c new file mode 100644 index 0000000..b232f8d --- /dev/null +++ b/common/core/src/ux_system_initialize.c @@ -0,0 +1,241 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Define UX_SYSTEM_INIT to bring in the USBX version ID string. */ + +#define UX_SYSTEM_INIT + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_system.h" + +/* Define the USBX system data structure. */ + +UX_SYSTEM *_ux_system; +UX_SYSTEM_OTG *_ux_system_otg; + +/* Define names of all the packed descriptors in USBX. */ + +UCHAR _ux_system_endpoint_descriptor_structure[] = {1,1,1,1,2,1 }; +UCHAR _ux_system_device_descriptor_structure[] = {1,1,2,1,1,1,1,2,2,2,1,1,1,1}; +UCHAR _ux_system_configuration_descriptor_structure[] = {1,1,2,1,1,1,1,1}; +UCHAR _ux_system_interface_descriptor_structure[] = {1,1,1,1,1,1,1,1,1}; +UCHAR _ux_system_interface_association_descriptor_structure[] = {1,1,1,1,1,1,1,1}; +UCHAR _ux_system_string_descriptor_structure[] = {1,1,2}; +UCHAR _ux_system_dfu_functional_descriptor_structure[] = {1,1,1,2,2,2}; +UCHAR _ux_system_class_audio_interface_descriptor_structure[] = {1,1,1,1,1,1,1,1}; +UCHAR _ux_system_class_audio_input_terminal_descriptor_structure[] = {1,1,1,1,2,1,1,2,1,1}; +UCHAR _ux_system_class_audio_output_terminal_descriptor_structure[] = {1,1,1,1,2,1,1,1}; +UCHAR _ux_system_class_audio_feature_unit_descriptor_structure[] = {1,1,1,1,1,1,1}; +UCHAR _ux_system_class_audio_streaming_interface_descriptor_structure[] = {1,1,1,1,1,1}; +UCHAR _ux_system_class_audio_streaming_endpoint_descriptor_structure[] = {1,1,1,1,1,1}; +UCHAR _ux_system_hub_descriptor_structure[] = {1,1,1,2,1,1,1,1}; +UCHAR _ux_system_hid_descriptor_structure[] = {1,1,2,1,1,1,2}; +UCHAR _ux_system_class_pima_storage_structure[] = {2,2,2,4,4,4,4,4}; +UCHAR _ux_system_class_pima_object_structure[] = {4,2,2,4,2,4,4,4,4,4,4,4,2,4,4}; +UCHAR _ux_system_ecm_interface_descriptor_structure[] = {1,1,1,1,4,2,2,1}; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_system_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the various control data structures for */ +/* the USBX system. */ +/* */ +/* INPUT */ +/* */ +/* regular_memory_pool_start Start of non cached memory pool */ +/* regular_memory_size Size of non cached memory pool */ +/* cache_safe_memory_pool_start Start of cached memory pool */ +/* cache_safe_memory_size Size of cached memory pool */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_mutex_create Create mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_system_initialize(VOID *regular_memory_pool_start, ULONG regular_memory_size, + VOID *cache_safe_memory_pool_start, ULONG cache_safe_memory_size) +{ + +UX_MEMORY_BLOCK *memory_block; +ALIGN_TYPE int_memory_pool_start; +VOID *regular_memory_pool_end; +UINT status; +ULONG memory_pool_offset; + + /* Reset memory block */ + _ux_utility_memory_set(regular_memory_pool_start, 0, regular_memory_size); + + /* Set the _ux_system structure at the start of our regular memory */ + _ux_system = (UX_SYSTEM *) regular_memory_pool_start; + + /* Add to the memory offset the size of the allocated block. */ + memory_pool_offset = sizeof(UX_SYSTEM); + +#ifndef UX_DEVICE_SIDE_ONLY + + /* Set the _ux_system_host structure. */ + _ux_system_host = (UX_SYSTEM_HOST *) (((UCHAR *) regular_memory_pool_start) + memory_pool_offset); + + /* Add to the memory offset the size of the allocated block. */ + memory_pool_offset += (ULONG)sizeof(UX_SYSTEM_HOST); + +#endif + +#ifndef UX_HOST_SIDE_ONLY + + /* Set the _ux_system_slave structure. */ + _ux_system_slave = (UX_SYSTEM_SLAVE *) (((UCHAR *) regular_memory_pool_start) + memory_pool_offset); + + /* Add to the memory offset the size of the allocated block. */ + memory_pool_offset += (ULONG)sizeof(UX_SYSTEM_SLAVE); + +#endif + + +#ifdef UX_OTG_SUPPORT + + /* Set the _ux_system_otg structure. */ + _ux_system_otg = (UX_SYSTEM_OTG *) (((UCHAR *) regular_memory_pool_start) + memory_pool_offset); + + /* Add to the memory offset the size of the allocated block. */ + memory_pool_offset += (ULONG)sizeof(UX_SYSTEM_OTG); +#endif + + + /* Set the cache safe memory for the dynamic pool */ + _ux_system -> ux_system_regular_memory_pool_start = (UX_MEMORY_BLOCK *) (((UCHAR *) regular_memory_pool_start) + + memory_pool_offset); + + /* Make sure the regular memory pool is aligned properly */ + int_memory_pool_start = (ALIGN_TYPE) _ux_system -> ux_system_regular_memory_pool_start; + int_memory_pool_start += UX_ALIGN_MIN; + int_memory_pool_start &= ~((ALIGN_TYPE)UX_ALIGN_MIN); + + /* Set the end of the regular memory pool. */ + regular_memory_pool_end = (void *) (((UCHAR *) regular_memory_pool_start) + regular_memory_size); + + /* Check if we have memory available. */ + if (int_memory_pool_start >= (ALIGN_TYPE)regular_memory_pool_end) + { + + /* No memory available. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Now, we have a project structure allocated, save the regular memory allocation details */ + _ux_system -> ux_system_regular_memory_pool_size = (ULONG) (((ALIGN_TYPE) regular_memory_pool_end) - int_memory_pool_start); + _ux_system -> ux_system_regular_memory_pool_free = _ux_system -> ux_system_regular_memory_pool_size; + _ux_system -> ux_system_regular_memory_pool_start = (UX_MEMORY_BLOCK *) int_memory_pool_start; + + /* Build the first free memory block */ + memory_block = _ux_system -> ux_system_regular_memory_pool_start; + memory_block -> ux_memory_block_size = _ux_system -> ux_system_regular_memory_pool_size - (ULONG)sizeof(UX_MEMORY_BLOCK); + memory_block -> ux_memory_block_status = UX_MEMORY_UNUSED; + + /* Check the definition of the cache safe pool. If the application or controller do not require any cache safe memory, + define the cached safe memory region as the regular memory region. */ + if (cache_safe_memory_pool_start == UX_NULL) + { + + /* Cache safe memory is the same as regular memory. */ + _ux_system -> ux_system_cache_safe_memory_pool_size = _ux_system -> ux_system_regular_memory_pool_size; + _ux_system -> ux_system_cache_safe_memory_pool_free = _ux_system -> ux_system_regular_memory_pool_free; + _ux_system -> ux_system_cache_safe_memory_pool_start = _ux_system -> ux_system_regular_memory_pool_start; + } + else + { + + /* Make sure the cache safe memory pool is aligned properly */ + int_memory_pool_start = (ALIGN_TYPE) cache_safe_memory_pool_start; + int_memory_pool_start += UX_ALIGN_MIN; + int_memory_pool_start &= ~((ALIGN_TYPE)UX_ALIGN_MIN); + + /* Save the cache safe memory allocation details */ + _ux_system -> ux_system_cache_safe_memory_pool_size = cache_safe_memory_size - UX_ALIGN_MIN; + _ux_system -> ux_system_cache_safe_memory_pool_free = _ux_system -> ux_system_cache_safe_memory_pool_size; + _ux_system -> ux_system_cache_safe_memory_pool_start = (UX_MEMORY_BLOCK *) int_memory_pool_start; + + /* Reset this memory block */ + _ux_utility_memory_set(_ux_system -> ux_system_cache_safe_memory_pool_start, 0, _ux_system -> ux_system_cache_safe_memory_pool_size); + + /* Build the first free memory block */ + memory_block = _ux_system -> ux_system_cache_safe_memory_pool_start; + memory_block -> ux_memory_block_size = _ux_system -> ux_system_cache_safe_memory_pool_size - (ULONG)sizeof(UX_MEMORY_BLOCK); + memory_block -> ux_memory_block_status = UX_MEMORY_UNUSED; + } + +#ifdef UX_ENABLE_DEBUG_LOG + + /* Obtain memory for storing the debug log. */ + _ux_system -> ux_system_debug_log_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_DEBUG_LOG_SIZE); + if (_ux_system -> ux_system_debug_log_buffer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Setup the head and tail pointers. */ + _ux_system -> ux_system_debug_log_head = _ux_system -> ux_system_debug_log_buffer; + _ux_system -> ux_system_debug_log_tail = _ux_system -> ux_system_debug_log_buffer; + + /* Keep the size in system structure variable. */ + _ux_system -> ux_system_debug_log_size = UX_DEBUG_LOG_SIZE; + +#endif + + /* Create the Mutex object used by USBX to control critical sections. */ + status = _ux_utility_mutex_create(&_ux_system -> ux_system_mutex, "ux_system_mutex"); + if(status != UX_SUCCESS) + return(UX_MUTEX_ERROR); + + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_system_uninitialize.c b/common/core/src/ux_system_uninitialize.c new file mode 100644 index 0000000..c00a36a --- /dev/null +++ b/common/core/src/ux_system_uninitialize.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** System */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_system.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_system_uninitialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function uninitializes the various control data structures for */ +/* the USBX system. */ +/* */ +/* INPUT */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_mutex_delete ThreadX delete mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_system_uninitialize(VOID) +{ + + /* Delete the Mutex object used by USBX to control critical sections. */ + _ux_utility_mutex_delete(&_ux_system -> ux_system_mutex); + + return(UX_SUCCESS); +} + + diff --git a/common/core/src/ux_trace_event_insert.c b/common/core/src/ux_trace_event_insert.c new file mode 100644 index 0000000..33ae201 --- /dev/null +++ b/common/core/src/ux_trace_event_insert.c @@ -0,0 +1,140 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef UX_SOURCE_CODE +#define UX_SOURCE_CODE +#endif + + +/* Include necessary system files. */ + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_trace_event_insert PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a USBX event into the current trace buffer. */ +/* */ +/* INPUT */ +/* */ +/* event_id User Event ID */ +/* info_field_1 First information field */ +/* info_field_2 First information field */ +/* info_field_3 First information field */ +/* info_field_4 First information field */ +/* current_event Current event pointer for */ +/* post event update */ +/* current_timestamp Timestamp for post event */ +/* update */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Internal USBX Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +#ifdef UX_TRACE_INSERT_MACROS +VOID _ux_trace_event_insert(ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4, + ULONG filter, TX_TRACE_BUFFER_ENTRY **current_event, ULONG *current_timestamp) +{ + +TX_INTERRUPT_SAVE_AREA + +TX_TRACE_BUFFER_ENTRY *event; +ULONG timestamp; + + + /* Disable interrupts. */ + TX_DISABLE + + /* Pickup the current event. */ + event = _tx_trace_buffer_current_ptr; + + /* Insert this event into the trace buffer. */ + TX_TRACE_IN_LINE_INSERT(event_id, info_field_1, info_field_2, info_field_3, info_field_4, filter) + + /* Initialize the timestamp to 0. */ + timestamp = 0; + + /* Determine if the event was inserted. */ + if (event) + { + + /* Was the event inserted? */ + if (event -> tx_trace_buffer_entry_event_id == event_id) + { + + /* Yes, the event was inserted in the event trace so pickup the timestamp. */ + timestamp = event -> tx_trace_buffer_entry_time_stamp; + } + else + { + + /* Event was not inserted, simply set the event pointer to NULL. */ + event = UX_NULL; + } + } + + /* Now determine if the caller requested the current event. */ + if (current_event) + { + + /* Yes, return the event pointer of potential subsequent update. */ + *current_event = event; + } + + /* Now determine if the current timestamp was requested. */ + if (current_timestamp) + { + + /* Yes, return the current timestamp. */ + *current_timestamp = timestamp; + } + + /* Restore interrupts. */ + TX_RESTORE +} +#endif + diff --git a/common/core/src/ux_trace_event_update.c b/common/core/src/ux_trace_event_update.c new file mode 100644 index 0000000..516f783 --- /dev/null +++ b/common/core/src/ux_trace_event_update.c @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef UX_SOURCE_CODE +#define UX_SOURCE_CODE +#endif + + +/* Include necessary system files. */ + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_trace_event_update PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a USBX event into the current trace buffer. */ +/* */ +/* INPUT */ +/* */ +/* event Event pointer */ +/* timestamp Timestamp of the event */ +/* event_id User Event ID */ +/* info_field_1 First information field */ +/* info_field_2 First information field */ +/* info_field_3 First information field */ +/* info_field_4 First information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Internal USBX Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +#ifdef UX_TRACE_INSERT_MACROS +VOID _ux_trace_event_update(TX_TRACE_BUFFER_ENTRY *event, ULONG timestamp, ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Disable interrupts. */ + TX_DISABLE + + /* Determine if the event exists and is still the event originally inserted into the trace. */ + if ((event) && (event -> tx_trace_buffer_entry_event_id == event_id) && (event -> tx_trace_buffer_entry_time_stamp == timestamp)) + { + + /* Yes, update this trace entry based on the info input parameters. */ + + /* Check for info field 1 update. */ + if (info_field_1) + { + + /* Yes, update info field 1. */ + event -> tx_trace_buffer_entry_information_field_1 = info_field_1; + } + + /* Check for info field 2 update. */ + if (info_field_2) + { + + /* Yes, update info field 2. */ + event -> tx_trace_buffer_entry_information_field_2 = info_field_2; + } + + /* Check for info field 3 update. */ + if (info_field_3) + { + + /* Yes, update info field 3. */ + event -> tx_trace_buffer_entry_information_field_3 = info_field_3; + } + + /* Check for info field 4 update. */ + if (info_field_4) + { + + /* Yes, update info field 4. */ + event -> tx_trace_buffer_entry_information_field_4 = info_field_4; + } + } + /* Restore interrupts. */ + TX_RESTORE +} +#endif + diff --git a/common/core/src/ux_trace_object_register.c b/common/core/src/ux_trace_object_register.c new file mode 100644 index 0000000..8a1758f --- /dev/null +++ b/common/core/src/ux_trace_object_register.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef UX_SOURCE_CODE +#define UX_SOURCE_CODE +#endif + + +/* Include necessary system files. */ + +#include "ux_api.h" + +#ifdef UX_TRACE_INSERT_MACROS +extern VOID _tx_trace_object_register(UCHAR , VOID *, CHAR *, ULONG , ULONG ); +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_trace_object_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a USBX object in the trace registry. */ +/* */ +/* INPUT */ +/* */ +/* object_type Type of system object */ +/* object_ptr Address of system object */ +/* object_name Name of system object */ +/* parameter_1 Supplemental parameter 1 */ +/* parameter_2 Supplemental parameter 2 */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_trace_object_register Actual register function */ +/* */ +/* CALLED BY */ +/* */ +/* Application Initialization */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_trace_object_register(UCHAR object_type, VOID *object_ptr, CHAR *object_name, ULONG parameter_1, ULONG parameter_2) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Disable interrupts. */ + TX_DISABLE + + /* Call actual object register function. */ + _tx_trace_object_register(object_type, object_ptr, object_name, parameter_1, parameter_2); + + /* Restore interrupts. */ + TX_RESTORE +} +#endif + diff --git a/common/core/src/ux_trace_object_unregister.c b/common/core/src/ux_trace_object_unregister.c new file mode 100644 index 0000000..88417db --- /dev/null +++ b/common/core/src/ux_trace_object_unregister.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Trace */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef UX_SOURCE_CODE +#define UX_SOURCE_CODE +#endif + + +/* Include necessary system files. */ + +#include "ux_api.h" + +#ifdef UX_TRACE_INSERT_MACROS +extern VOID _tx_trace_object_unregister(VOID *); +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_trace_object_unregister PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a USBX object in the trace registry. */ +/* */ +/* INPUT */ +/* */ +/* object_pointer Address of system object */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_trace_object_unregister Actual unregister function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX delete functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_trace_object_unregister(VOID *object_ptr) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Disable interrupts. */ + TX_DISABLE + + /* Call actual object unregister function. */ + _tx_trace_object_unregister(object_ptr); + + /* Restore interrupts. */ + TX_RESTORE +} +#endif diff --git a/common/core/src/ux_utility_debug_callback_register.c b/common/core/src/ux_utility_debug_callback_register.c new file mode 100644 index 0000000..59b35c4 --- /dev/null +++ b/common/core/src/ux_utility_debug_callback_register.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +#ifdef UX_ENABLE_DEBUG_LOG + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_debug_callback_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a callback for sending formatted debug */ +/* messages to the application. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* debug_callback */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_debug_callback_register(VOID (*debug_callback)(UCHAR *, ULONG)) + +{ + + /* The callback function is defined. */ + _ux_system -> ux_system_debug_callback_function = debug_callback; + + /* We are done here. No return codes. */ + return; +} + +#endif diff --git a/common/core/src/ux_utility_debug_log.c b/common/core/src/ux_utility_debug_log.c new file mode 100644 index 0000000..a7fdddf --- /dev/null +++ b/common/core/src/ux_utility_debug_log.c @@ -0,0 +1,310 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +#ifdef UX_ENABLE_DEBUG_LOG + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_debug_log PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function logs a debug msg in a circular queue. The queue */ +/* must be initialized during the init of USBX. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* debug_location C string to locate the debug, */ +/* for example source file name. */ +/* debug_message C string of message */ +/* debug_code Debug code */ +/* debug_parameter_1 First parameter */ +/* debug_parameter_2 Second parameter */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check C string and return */ +/* its length if null-terminated */ +/* _tx_time_get Return system clock time */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_debug_log(UCHAR *debug_location, UCHAR *debug_message, ULONG debug_code, + ULONG debug_parameter_1, ULONG debug_parameter_2) +{ + +UINT debug_location_string_length; +UINT debug_message_string_length; +ULONG total_debug_message_length; +ULONG parameter_length; +ULONG parameter_shift; +UCHAR parameter_hexa; +ULONG local_parameter_value; +ULONG current_time; +TX_INTERRUPT_SAVE_AREA + + /* Is USBX system completely initialized ? */ + if (_ux_system -> ux_system_debug_log_size == 0) + + /* Not yet. */ + return; + + /* Entering critical area. Disable interrupts. */ + TX_DISABLE + + /* Store the debug value as the last debug value recorded. */ + _ux_system -> ux_system_debug_code = debug_code; + + /* Increment the debug value code count. */ + _ux_system -> ux_system_debug_count++; + + /* Calculate the string length. */ + debug_location_string_length = UX_DEBUG_LOG_SIZE; + _ux_utility_string_length_check(source, &debug_location_string_length, UX_DEBUG_LOG_SIZE); + debug_message_string_length = UX_DEBUG_LOG_SIZE; + _ux_utility_string_length_check(source, &debug_message_string_length, UX_DEBUG_LOG_SIZE); + + /* Calculate the length of the entire message string. 1 fixed string, then 1 hexa + decimal, then 2 strings then 2 hexa decimal numbers in the format 0x00000000 + separated by commas and . at the end, zero terminated. */ + total_debug_message_length = debug_location_string_length + debug_message_string_length + 10 + 10 + 10 + 10 + 5; + + /* Can we accommodate this debug value message at the current location ? */ + if (_ux_system -> ux_system_debug_log_head + total_debug_message_length > _ux_system -> ux_system_debug_log_buffer + _ux_system -> ux_system_debug_log_size) + { + + /* The debug value log to insert goes beyond the end of the log buffer, rewind to the beginning. */ + _ux_system -> ux_system_debug_log_head = _ux_system -> ux_system_debug_log_buffer; + } + + /* Copy the time strings and parameters in the log buffer. */ + _ux_utility_memory_copy(_ux_system -> ux_system_debug_log_head, "At time : ", 10); + _ux_system -> ux_system_debug_log_head += 10; + + /* Get the time value from TX. */ + current_time = _tx_time_get(); + + /* Reset the value of the length.*/ + parameter_length = 0; + + /* Init the shift value. */ + parameter_shift = 32 - 4; + + /* We parse the hexa value parameter and build the hexa value one byte at a type. */ + while(parameter_length < 8) + { + + /* Shift the 4 bit value we are interested in. We keep the lowest nibble. */ + local_parameter_value = (current_time >> parameter_shift) & 0x0f; + + /* See if this value is from 0-9 or A to F. */ + if (local_parameter_value <= 9) + + /* We have a digit. */ + parameter_hexa = (UCHAR) (local_parameter_value + '0'); + + else + + /* We have 'A' to 'F' value. */ + parameter_hexa = (UCHAR) (local_parameter_value - 10 + 'A'); + + /* Store the converted hexa value. */ + *_ux_system -> ux_system_debug_log_head = parameter_hexa; + + /* Next position. */ + _ux_system -> ux_system_debug_log_head++; + + /* Update length. */ + parameter_length++; + + /* Continue shifting by one nibble. */ + parameter_shift = parameter_shift - 4; + } + + /* Add the comma after the time. */ + *_ux_system -> ux_system_debug_log_head = ','; + _ux_system -> ux_system_debug_log_head++; + + /* Copy the strings and parameters in the log buffer. */ + _ux_utility_memory_copy(_ux_system -> ux_system_debug_log_head, debug_location, debug_location_string_length); + _ux_system -> ux_system_debug_log_head += debug_location_string_length; + *_ux_system -> ux_system_debug_log_head = ','; + _ux_system -> ux_system_debug_log_head++; + _ux_utility_memory_copy(_ux_system -> ux_system_debug_log_head, debug_message, debug_message_string_length); + _ux_system -> ux_system_debug_log_head += debug_message_string_length; + *_ux_system -> ux_system_debug_log_head = ','; + _ux_system -> ux_system_debug_log_head++; + + /* Create the hexa string for parameter 1. */ + *_ux_system -> ux_system_debug_log_head = '0'; + _ux_system -> ux_system_debug_log_head++; + *_ux_system -> ux_system_debug_log_head = 'x'; + _ux_system -> ux_system_debug_log_head++; + + /* Reset the value of the length.*/ + parameter_length = 0; + + /* Init the shift value. */ + parameter_shift = 32 - 4; + + /* We parse the hexa value parameter and build the hexa value one byte at a type. */ + while(parameter_length < 8) + { + + /* Shift the 4 bit value we are interested in. We keep the lowest nibble. */ + local_parameter_value = (debug_parameter_1 >> parameter_shift) & 0x0f; + + /* See if this value is from 0-9 or A to F. */ + if (local_parameter_value <= 9) + + /* We have a digit. */ + parameter_hexa = (UCHAR) (local_parameter_value + '0'); + + else + + /* We have 'A' to 'F' value. */ + parameter_hexa = (UCHAR) (local_parameter_value - 10 + 'A'); + + /* Store the converted hexa value. */ + *_ux_system -> ux_system_debug_log_head = parameter_hexa; + + /* Next position. */ + _ux_system -> ux_system_debug_log_head++; + + /* Update length. */ + parameter_length++; + + /* Continue shifting by one nibble. */ + parameter_shift = parameter_shift - 4; + } + + /* Add the comma between the 2 hexa values. */ + *_ux_system -> ux_system_debug_log_head = ','; + _ux_system -> ux_system_debug_log_head++; + + /* Create the hexa string for parameter 2. */ + *_ux_system -> ux_system_debug_log_head = '0'; + _ux_system -> ux_system_debug_log_head++; + *_ux_system -> ux_system_debug_log_head = 'x'; + _ux_system -> ux_system_debug_log_head++; + + /* Reset the value of the length.*/ + parameter_length = 0; + + /* Init the shift value. */ + parameter_shift = 32 - 4; + + /* We parse the hexa value parameter and build the hexa value one byte at a type. */ + while(parameter_length < 8) + { + + /* Shift the 4 bit value we are interested in. We keep the lowest nibble. */ + local_parameter_value = (debug_parameter_2 >> parameter_shift) & 0x0f; + + /* See if this value is from 0-9 or A to F. */ + if (local_parameter_value <= 9) + + /* We have a digit. */ + parameter_hexa = (UCHAR) (local_parameter_value + '0'); + + else + + /* We have 'A' to 'F' value. */ + parameter_hexa = (UCHAR) (local_parameter_value - 10 + 'A'); + + /* Store the converted hexa value. */ + *_ux_system -> ux_system_debug_log_head = parameter_hexa; + + /* Next position. */ + _ux_system -> ux_system_debug_log_head++; + + /* Update length. */ + parameter_length++; + + /* Continue shifting by one nibble. */ + parameter_shift = parameter_shift - 4; + } + + /* Add the termination dot at the end. */ + *_ux_system -> ux_system_debug_log_head = '.'; + _ux_system -> ux_system_debug_log_head++; + + /* Add the CR/LF end of the string. */ + *_ux_system -> ux_system_debug_log_head = 0x0d; + _ux_system -> ux_system_debug_log_head++; + *_ux_system -> ux_system_debug_log_head = 0x0a; + _ux_system -> ux_system_debug_log_head++; + + /* Add the zero end of the string. */ + *_ux_system -> ux_system_debug_log_head = 0x00; + _ux_system -> ux_system_debug_log_head++; + + /* The log string is put into the log buffer. It can stay here until + a break into debugger by the developer or be passed to a callback registered + by the application. */ + if (_ux_system -> ux_system_debug_callback_function != UX_NULL) + { + + /* The callback function is defined, call it. */ + _ux_system -> ux_system_debug_callback_function(_ux_system -> ux_system_debug_log_tail, debug_code); + + /* Update the tail. */ + _ux_system -> ux_system_debug_log_tail += total_debug_message_length; + + } + + /* Restore interrupts. */ + TX_RESTORE + + /* We are done here. No return codes. */ + return; +} + +#endif diff --git a/common/core/src/ux_utility_delay_ms.c b/common/core/src/ux_utility_delay_ms.c new file mode 100644 index 0000000..78c1dc8 --- /dev/null +++ b/common/core/src/ux_utility_delay_ms.c @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_delay_ms PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function causes the calling thread to sleep for the */ +/* specified number of milliseconds */ +/* */ +/* INPUT */ +/* */ +/* ms_wait Number of milliseconds to */ +/* wait for */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_sleep ThreadX sleep function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_delay_ms(ULONG ms_wait) +{ + +ULONG ticks; + + /* translate ms into ticks. */ + ticks = (ms_wait * UX_PERIODIC_RATE) / 1000; + + /* For safety add 1 to ticks. */ + ticks++; + + /* Call ThreadX sleep function. */ + tx_thread_sleep(ticks); + + /* Return completion status. */ + return; +} + diff --git a/common/core/src/ux_utility_descriptor_pack.c b/common/core/src/ux_utility_descriptor_pack.c new file mode 100644 index 0000000..6db1b61 --- /dev/null +++ b/common/core/src/ux_utility_descriptor_pack.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_descriptor_pack PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will pack an application structure into a USB */ +/* descriptor. */ +/* */ +/* INPUT */ +/* */ +/* descriptor Pointer to the unpacked */ +/* descriptor */ +/* descriptor_structure Components of the descriptor */ +/* descriptor_entries Number of entries in the */ +/* descriptor */ +/* raw_descriptor Pointer to packed descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_descriptor_pack(UCHAR * descriptor, UCHAR * descriptor_structure, + UINT descriptor_entries, UCHAR * raw_descriptor) +{ + + /* Loop on all the entries in this descriptor. */ + while(descriptor_entries--) + { + + /* Get the length of that component. */ + switch(*descriptor_structure++) + { + + /* Check the size then build the component from the source and + insert it into the target descriptor. */ + case 4: + + _ux_utility_long_put(raw_descriptor, *((ULONG *) descriptor)); + raw_descriptor += 4; + break; + + case 2: + + _ux_utility_short_put(raw_descriptor, (USHORT)*((ULONG *) descriptor)); + raw_descriptor += 2; + break; + + default: + + *raw_descriptor = (UCHAR) *((ULONG *) descriptor); + raw_descriptor++; + } + + /* Add the size of the component to the destination. */ + descriptor += 4; + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_descriptor_parse.c b/common/core/src/ux_utility_descriptor_parse.c new file mode 100644 index 0000000..496a406 --- /dev/null +++ b/common/core/src/ux_utility_descriptor_parse.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_descriptor_parse PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will unpack a USB descriptor from the bus into a */ +/* memory aligned structure. */ +/* */ +/* INPUT */ +/* */ +/* raw_descriptor Pointer to packed descriptor */ +/* descriptor_structure Components of the descriptor */ +/* descriptor_entries Number of entries in the */ +/* descriptor */ +/* descriptor Pointer to the unpacked */ +/* descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_short_get Get 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_descriptor_parse(UCHAR * raw_descriptor, UCHAR * descriptor_structure, + UINT descriptor_entries, UCHAR * descriptor) +{ + + /* Loop on all the entries in this descriptor. */ + while(descriptor_entries--) + { + + /* Get the length of that component. */ + switch(*descriptor_structure++) + { + + /* Check the size then build the component from the source and + insert it into the target descriptor. */ + case 4: + + *((ULONG *) descriptor) = _ux_utility_long_get(raw_descriptor); + raw_descriptor += 4; + break; + + case 2: + + *((ULONG *) descriptor) = (ULONG) _ux_utility_short_get(raw_descriptor); + raw_descriptor += 2; + break; + + default: + + *((ULONG *) descriptor) = (ULONG) *raw_descriptor; + raw_descriptor++; + } + + /* Add the size of the component to the destination. */ + descriptor += 4; + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_error_callback_register.c b/common/core/src/ux_utility_error_callback_register.c new file mode 100644 index 0000000..cf77f0f --- /dev/null +++ b/common/core/src/ux_utility_error_callback_register.c @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_error_callback_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a callback for sending formatted errors to */ +/* the application. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* error_callback */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_error_callback_register(VOID (*error_callback)(UINT system_level, UINT system_context, UINT error_code)) + +{ + + /* The callback function is defined. */ + _ux_system -> ux_system_error_callback_function = error_callback; + + /* We are done here. No return codes. */ + return; +} diff --git a/common/core/src/ux_utility_event_flags_create.c b/common/core/src/ux_utility_event_flags_create.c new file mode 100644 index 0000000..8d43c58 --- /dev/null +++ b/common/core/src/ux_utility_event_flags_create.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_event_flags_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a threadx group of flags */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Event flag control group */ +/* name Pointer to thread name string */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_event_flags_create ThreadX create event flag */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_event_flags_create(TX_EVENT_FLAGS_GROUP *group_ptr, CHAR *name) +{ + +UINT status; + + /* Call ThreadX to create the event flags. */ + status = tx_event_flags_create(group_ptr, name); + + /* Check for status. */ + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, UX_EVENT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_EVENT_ERROR, group_ptr, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_event_flags_delete.c b/common/core/src/ux_utility_event_flags_delete.c new file mode 100644 index 0000000..e6ed879 --- /dev/null +++ b/common/core/src/ux_utility_event_flags_delete.c @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_event_flags_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a threadx group of flags */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Event flag control group */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_event_flags_delete ThreadX delete event flag */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr) +{ + +UINT status; + + /* Call ThreadX to delete the event flags. */ + status = tx_event_flags_delete(group_ptr); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_event_flags_get.c b/common/core/src/ux_utility_event_flags_get.c new file mode 100644 index 0000000..ee634a4 --- /dev/null +++ b/common/core/src/ux_utility_event_flags_get.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_event_flags_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function get event flags from event flag group */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Event flag control group */ +/* requested_flags 32 bits variable event flags */ +/* get_option AND/OR/CLEAR ... options */ +/* actual_flag_ptr where the flags are placed */ +/* wait_option waiting option */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_event_flags_get ThreadX get event flag */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_event_flags_get(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG requested_flags, + UINT get_option, ULONG *actual_flags_ptr, ULONG wait_option) +{ + +UINT status; +ULONG local_actual_flags_ptr; + + /* Call ThreadX to get the event flags. */ + status = tx_event_flags_get(group_ptr, requested_flags, get_option, &local_actual_flags_ptr, wait_option); + + /* Update the actual flags. */ + *actual_flags_ptr = local_actual_flags_ptr; + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_event_flags_set.c b/common/core/src/ux_utility_event_flags_set.c new file mode 100644 index 0000000..4c01799 --- /dev/null +++ b/common/core/src/ux_utility_event_flags_set.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_event_flags_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function set event flags from event flag group */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Event flag control group */ +/* flags_to_set 32 bits variable event flags */ +/* set_option set option */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_event_flags_set ThreadX set event flag */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_event_flags_set(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG flags_to_set, + UINT set_option) +{ + +UINT status; + + /* Call ThreadX to set the event flags. */ + status = tx_event_flags_set(group_ptr, flags_to_set, set_option); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_long_get.c b/common/core/src/ux_utility_long_get.c new file mode 100644 index 0000000..46b4053 --- /dev/null +++ b/common/core/src/ux_utility_long_get.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_long_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a 32-bit value. */ +/* */ +/* INPUT */ +/* */ +/* address Source address */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_utility_long_get(UCHAR * address) +{ + +ULONG value; + + + /* In order to make this function endian agnostic and memory alignment + independent, we read a byte at a time from the address. */ + value = (ULONG) *address++; + value |= (ULONG)*address++ << 8; + value |= (ULONG)*address++ << 16; + value |= (ULONG)*address << 24; + + /* Return 32-bit value. */ + return(value); +} + diff --git a/common/core/src/ux_utility_long_get_big_endian.c b/common/core/src/ux_utility_long_get_big_endian.c new file mode 100644 index 0000000..96b59fc --- /dev/null +++ b/common/core/src/ux_utility_long_get_big_endian.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_long_get_big_endian PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a 32-bit value in big endian format. */ +/* */ +/* INPUT */ +/* */ +/* address Source address */ +/* */ +/* OUTPUT */ +/* */ +/* 16-bit value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_utility_long_get_big_endian(UCHAR * address) +{ + +ULONG value; + + + /* We read a byte at a time from the address. */ + value = (ULONG) ((*address++) << 24); + value |= (ULONG) ((*address++) << 16); + value |= (ULONG) ((*address++) << 8); + value |= (ULONG) *address; + + /* Return 32-bit value. */ + return(value); +} + diff --git a/common/core/src/ux_utility_long_put.c b/common/core/src/ux_utility_long_put.c new file mode 100644 index 0000000..b68b441 --- /dev/null +++ b/common/core/src/ux_utility_long_put.c @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_long_put PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a 32-bit value. */ +/* */ +/* INPUT */ +/* */ +/* address Destination address */ +/* value 32-bit value */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_long_put(UCHAR * address, ULONG value) +{ + + /* In order to make this function endian agnostic and memory alignment + independent, we write a byte at a time from the address. */ + *address++ = (UCHAR) (value & 0xff); + *address++ = (UCHAR) ((value >> 8) & 0xff); + *address++ = (UCHAR) ((value >> 16) & 0xff); + *address = (UCHAR) ((value >> 24) & 0xff); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_long_put_big_endian.c b/common/core/src/ux_utility_long_put_big_endian.c new file mode 100644 index 0000000..a7e6244 --- /dev/null +++ b/common/core/src/ux_utility_long_put_big_endian.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_long_put_big_endian PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a 32-bit value in big endian format. */ +/* */ +/* INPUT */ +/* */ +/* address Destination address */ +/* value 32-bit value */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_long_put_big_endian(UCHAR * address, ULONG value) +{ + +ULONG low_word_value; +ULONG high_word_value; + + /* First we swap the value words. */ + low_word_value = value >> 16; + high_word_value = value << 16; + value = high_word_value | low_word_value; + + /* In order to make this function endian agnostic and memory alignment + independent, we write a byte at a time from the address. */ + *address++ = (UCHAR) ((value >> 8) & 0xff); + *address++ = (UCHAR) (value & 0xff); + *address++ = (UCHAR) ((value >> 24 ) & 0xff); + *address = (UCHAR) ((value >> 16) & 0xff); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_memory_allocate.c b/common/core/src/ux_utility_memory_allocate.c new file mode 100644 index 0000000..c874fb6 --- /dev/null +++ b/common/core/src/ux_utility_memory_allocate.c @@ -0,0 +1,354 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_allocate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function allocates a block of memory for the specified size */ +/* and alignment. */ +/* */ +/* INPUT */ +/* */ +/* memory_alignment Memory alignment required */ +/* memory_cache_flag Memory pool source */ +/* memory_size_requested Number of bytes required */ +/* */ +/* OUTPUT */ +/* */ +/* Pointer to block of memory */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_free_block_best_get Get best fit block of memory */ +/* _ux_utility_memory_set Set block of memory */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID *_ux_utility_memory_allocate(ULONG memory_alignment, ULONG memory_cache_flag, + ULONG memory_size_requested) +{ + +UX_MEMORY_BLOCK *memory_block; +UX_MEMORY_BLOCK *new_memory_block; +UX_MEMORY_BLOCK *leftover_memory_block; +ULONG memory_for_alignment; +ULONG memory_removed_from_pool; +ULONG leftover; +UCHAR *memory_buffer; +ALIGN_TYPE int_memory_buffer; + + + /* Get the mutex as this is a critical section. */ + _ux_utility_mutex_on(&_ux_system -> ux_system_mutex); + +#ifdef UX_ENFORCE_SAFE_ALIGNMENT + + /* Check if safe alignment requested, in this case switch to UX_NO_ALIGN. */ + if (memory_alignment == UX_SAFE_ALIGN) + { + + /* We will use the memory_size_requested for the alignment. + But we check to see if we have a minimum or maximum alignment. */ + if (memory_size_requested < UX_ALIGN_MIN) + + /* No need to bother about alignment for small packets sizes. */ + memory_alignment = UX_NO_ALIGN; + + else + { + + /* Check if we are over the maximum. */ + if (memory_size_requested > UX_MAX_SCATTER_GATHER_ALIGNMENT) + + /* We are over the max alignment required. Use the maximum instead. */ + memory_alignment = UX_MAX_SCATTER_GATHER_ALIGNMENT - 1; + + else + { + /* We are not over the maximum, so approximate the alignment according to the size of the memory. + Check range for alignment on 4096 bytes. */ + if (memory_size_requested >= UX_ALIGN_2048 + 1) + memory_alignment = UX_ALIGN_4096; + + else + { + + /* Check range for alignment on 2048 bytes. */ + if (memory_size_requested >= UX_ALIGN_1024 + 1) + memory_alignment = UX_ALIGN_2048; + + else + { + + /* Check range for alignment on 1024 bytes. */ + if (memory_size_requested >= UX_ALIGN_512 + 1) + memory_alignment = UX_ALIGN_1024; + + else + { + + /* Check range for alignment on 512 bytes. */ + if (memory_size_requested >= UX_ALIGN_256 + 1) + memory_alignment = UX_ALIGN_512; + + else + { + + /* Check range for alignment on 256 bytes. */ + if (memory_size_requested >= UX_ALIGN_128 + 1) + memory_alignment = UX_ALIGN_256; + + else + { + + /* Check range for alignment on 128 bytes. */ + if (memory_size_requested >= UX_ALIGN_64 + 1) + memory_alignment = UX_ALIGN_128; + + else + { + + /* Check range for alignment on 128 bytes. */ + if (memory_size_requested >= UX_ALIGN_64 + 1) + memory_alignment = UX_ALIGN_128; + + else + { + + /* Check range for alignment on 64 bytes. */ + if (memory_size_requested >= UX_ALIGN_32 + 1) + memory_alignment = UX_ALIGN_64; + + else + { + + /* Check range for alignment on 32 bytes. */ + if (memory_size_requested >= UX_ALIGN_16 + 1) + memory_alignment = UX_ALIGN_32; + + else + memory_alignment = UX_ALIGN_MIN; + + + } + } + } + } + } + } + } + } + } + } + } + +#else + + /* Check if safe alignment requested, in this case switch to UX_NO_ALIGN. */ + if (memory_alignment == UX_SAFE_ALIGN) + memory_alignment = UX_NO_ALIGN; + +#endif + + /* Ensure the alignment meats the minimum. */ + if (memory_alignment < UX_ALIGN_MIN) + memory_alignment = UX_ALIGN_MIN; + + /* Adjust the memory alignment since our macros are one minus the desired alignment. + Also determine the amount of extra memory we need for the alignment, which is one + minus the actual alignment. */ + memory_for_alignment = memory_alignment; + memory_alignment++; + + /* We need to make sure that the next memory block buffer is 16-byte aligned too. We + do this by first adjusting the requested memory to be 16-byte aligned. One problem + now is that the memory block might not be a size that is a multiple of 16, so we need + to add the amount of memory required such that the memory buffer after the block has + the correct alignment. For example, if the memory block has a size of 24, then we need + to make sure it is placed on an 8-byte alignment that is after a 16-byte alignment so + that the memory right after the memory block is 16-byte aligned (8 + 24 = 32). */ + memory_size_requested = (memory_size_requested + UX_ALIGN_MIN) & (~(ULONG)UX_ALIGN_MIN); + memory_size_requested += (((ULONG)sizeof(UX_MEMORY_BLOCK) + UX_ALIGN_MIN) & (~(ULONG)UX_ALIGN_MIN)) - (ULONG)sizeof(UX_MEMORY_BLOCK); + + /* Try to find the best block for this memory by requesting the maximum amount of + memory we'll need which is calculated as follows: the amount memory requested by + the caller plus the maximum amount of memory wasted due to alignment plus 2 memory + blocks structs - one for the new memory block we'll create for the user block and one + that we might create if there is extra memory after doing the alignment. */ + memory_block = _ux_utility_memory_free_block_best_get(memory_cache_flag, memory_size_requested + memory_for_alignment + (ULONG)sizeof(UX_MEMORY_BLOCK)); + + /* If the block returned is NULL, there is no free memory in the pool + for that size. */ + if (memory_block == UX_NULL) + { + + /* Release the protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, memory_size_requested, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, UX_MEMORY_INSUFFICIENT); + + /* Return NULL to indicate no block was found. */ + return(UX_NULL); + } + + /* Get the memory buffer for this block. */ + int_memory_buffer = (ALIGN_TYPE) ((UCHAR *) memory_block + sizeof(UX_MEMORY_BLOCK)); + + /* Are we already aligned? */ + if ((int_memory_buffer & (memory_alignment - 1)) == 0) + { + + /* Setup the new memory block. */ + new_memory_block = (UX_MEMORY_BLOCK *) ((UCHAR *) memory_block + sizeof(UX_MEMORY_BLOCK) + memory_size_requested); + new_memory_block -> ux_memory_block_next = memory_block -> ux_memory_block_next; + new_memory_block -> ux_memory_block_previous = memory_block; + new_memory_block -> ux_memory_block_size = memory_block -> ux_memory_block_size - memory_size_requested - (ULONG)sizeof(UX_MEMORY_BLOCK); + new_memory_block -> ux_memory_block_status = UX_MEMORY_UNUSED; + + /* Update the current memory block. */ + memory_block -> ux_memory_block_size = memory_size_requested; + memory_block -> ux_memory_block_next = new_memory_block; + memory_block -> ux_memory_block_status = UX_MEMORY_USED; + + /* Declare how much memory we removed from the pool. */ + memory_removed_from_pool = memory_block -> ux_memory_block_size + (ULONG)sizeof(UX_MEMORY_BLOCK); + } + else + { + + /* Align the buffer. The first thing we do is increment by the size of a + memory block because we have to make sure we have enough memory for at + least that. */ + int_memory_buffer += (ULONG)sizeof(UX_MEMORY_BLOCK); + int_memory_buffer += memory_alignment - 1; + int_memory_buffer &= ~(((ALIGN_TYPE) memory_alignment) - 1); + + /* Setup the new memory block. Note that its size is updated again later. */ + new_memory_block = (UX_MEMORY_BLOCK *) (int_memory_buffer - (ULONG)sizeof(UX_MEMORY_BLOCK)); + new_memory_block -> ux_memory_block_previous = memory_block; + new_memory_block -> ux_memory_block_next = memory_block -> ux_memory_block_next; + new_memory_block -> ux_memory_block_size = memory_block -> ux_memory_block_size; + new_memory_block -> ux_memory_block_status = UX_MEMORY_USED; + + /* Update the current memory block. */ + int_memory_buffer = (ALIGN_TYPE) ((UCHAR *) memory_block + sizeof(UX_MEMORY_BLOCK)); + memory_block -> ux_memory_block_next = new_memory_block; + memory_block -> ux_memory_block_size = (ULONG) ((ALIGN_TYPE) new_memory_block - int_memory_buffer); + + /* Update the new memory block's size. */ + new_memory_block -> ux_memory_block_size -= (memory_block -> ux_memory_block_size + (ULONG)sizeof(UX_MEMORY_BLOCK)); + + /* Calculate how much memory is leftover in the new memory block after doing + the alignment. */ + leftover = new_memory_block -> ux_memory_block_size - memory_size_requested; + + /* Can we fit another block after the new block? */ + if (leftover > sizeof(UX_MEMORY_BLOCK)) + { + + /* Setup the leftover memory block. */ + leftover_memory_block = (UX_MEMORY_BLOCK *) ((ALIGN_TYPE) new_memory_block + sizeof(UX_MEMORY_BLOCK) + memory_size_requested); + leftover_memory_block -> ux_memory_block_next = new_memory_block -> ux_memory_block_next; + leftover_memory_block -> ux_memory_block_previous = new_memory_block; + leftover_memory_block -> ux_memory_block_size = leftover - (ULONG)sizeof(UX_MEMORY_BLOCK); + leftover_memory_block -> ux_memory_block_status = UX_MEMORY_UNUSED; + + new_memory_block -> ux_memory_block_next = leftover_memory_block; + new_memory_block -> ux_memory_block_size -= leftover; + } + + /* Declare how much memory we removed from the pool. */ + memory_removed_from_pool = new_memory_block -> ux_memory_block_size + (ULONG)sizeof(UX_MEMORY_BLOCK); + + /* The new memory block is the one we give to the user. */ + memory_block = new_memory_block; + } + + /* The memory to be returned is after the block header. */ + memory_buffer = ((UCHAR *) memory_block) + sizeof(UX_MEMORY_BLOCK); + + /* Clear the memory block. */ + _ux_utility_memory_set(memory_buffer, 0, memory_size_requested); + + /* Update the memory free in the pool. */ + if (_ux_system -> ux_system_cache_safe_memory_pool_start == _ux_system -> ux_system_regular_memory_pool_start) + { + + /* There is only one memory pool. */ + _ux_system -> ux_system_regular_memory_pool_free -= memory_removed_from_pool; + } + else + { + + switch (memory_cache_flag) + { + + case UX_CACHE_SAFE_MEMORY: + /* Update the amount of free memory in the cache safe memory pool. */ + _ux_system -> ux_system_cache_safe_memory_pool_free -= memory_removed_from_pool; + break; + + default: + /* Update the amount of free memory in the regular memory pool. */ + _ux_system -> ux_system_regular_memory_pool_free -= memory_removed_from_pool; + break; + + } + } + + /* Release the protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* The memory block pointer contains a memory area properly + aligned. */ + return(memory_buffer); +} diff --git a/common/core/src/ux_utility_memory_allocate_add_safe.c b/common/core/src/ux_utility_memory_allocate_add_safe.c new file mode 100644 index 0000000..9a972d5 --- /dev/null +++ b/common/core/src/ux_utility_memory_allocate_add_safe.c @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_allocate_add_safe PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function allocates a block of memory for the specified added */ +/* size and alignment. */ +/* */ +/* Note if adding result overflow, no memory is allocated. */ +/* */ +/* INPUT */ +/* */ +/* align Memory alignment required */ +/* cache Memory pool source */ +/* size_add_a Number of bytes a to add */ +/* size_add_b Number of bytes b to add */ +/* */ +/* OUTPUT */ +/* */ +/* Pointer to block of memory */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate block of memory */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID* _ux_utility_memory_allocate_add_safe(ULONG align,ULONG cache,ULONG size_add_a,ULONG size_add_b) +{ + return UX_UTILITY_MEMORY_ALLOCATE_ADD_SAFE(align, cache, size_add_a, size_add_b); +} diff --git a/common/core/src/ux_utility_memory_allocate_mulc_safe.c b/common/core/src/ux_utility_memory_allocate_mulc_safe.c new file mode 100644 index 0000000..000a465 --- /dev/null +++ b/common/core/src/ux_utility_memory_allocate_mulc_safe.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_allocate_mulc_safe PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function allocates a block of memory for the specified */ +/* multiplied size and alignment. */ +/* */ +/* Note if multiplying result overflow, no memory is allocated. */ +/* Note it's assumed at least the last factor to multiply is const for */ +/* possible code optimization. */ +/* */ +/* INPUT */ +/* */ +/* align Memory alignment required */ +/* cache Memory pool source */ +/* size_mul_v Number of bytes variable to */ +/* multiply */ +/* size_mul_c Number of bytes const to */ +/* multiply */ +/* */ +/* OUTPUT */ +/* */ +/* Pointer to block of memory */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate block of memory */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID* _ux_utility_memory_allocate_mulc_safe(ULONG align,ULONG cache,ULONG size_mul_v,ULONG size_mul_c) +{ + return UX_UTILITY_MEMORY_ALLOCATE_MULC_SAFE(align, cache, size_mul_v, size_mul_c); +} diff --git a/common/core/src/ux_utility_memory_allocate_mulv_safe.c b/common/core/src/ux_utility_memory_allocate_mulv_safe.c new file mode 100644 index 0000000..6054ef7 --- /dev/null +++ b/common/core/src/ux_utility_memory_allocate_mulv_safe.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_allocate_mulv_safe PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function allocates a block of memory for the specified */ +/* multiplied size and alignment. */ +/* */ +/* Note if multiplying result overflow, no memory is allocated. */ +/* Note it's assumed all factors to multiply is variables for */ +/* possible code optimization. */ +/* */ +/* INPUT */ +/* */ +/* align Memory alignment required */ +/* cache Memory pool source */ +/* size_mul_v0 Number of bytes variable to */ +/* multiply */ +/* size_mul_v1 Number of bytes variable to */ +/* multiply */ +/* */ +/* OUTPUT */ +/* */ +/* Pointer to block of memory */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate block of memory */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID* _ux_utility_memory_allocate_mulv_safe(ULONG align,ULONG cache,ULONG size_mul_v0,ULONG size_mul_v1) +{ + return UX_UTILITY_MEMORY_ALLOCATE_MULV_SAFE(align, cache, size_mul_v0, size_mul_v1); +} diff --git a/common/core/src/ux_utility_memory_compare.c b/common/core/src/ux_utility_memory_compare.c new file mode 100644 index 0000000..713e11e --- /dev/null +++ b/common/core/src/ux_utility_memory_compare.c @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_compare PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function compares two memory blocks. */ +/* */ +/* INPUT */ +/* */ +/* memory_source Pointer to source */ +/* memory_destination Pointer to destination */ +/* length Number of bytes to compare */ +/* */ +/* OUTPUT */ +/* */ +/* UX_SUCCESS If blocks are equal */ +/* UX_ERROR If blocks are not equal */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_memory_compare(VOID *memory_source, VOID *memory_destination, ULONG length) +{ + +UCHAR * source; +UCHAR * destination; + + + /* Setup source and destination byte oriented pointers. */ + source = (UCHAR *) memory_source; + destination = (UCHAR *) memory_destination; + + /* Loop to compare blocks. */ + while(length--) + { + + /* Compare a single byte. */ + if(*destination++ != *source++) + { + + /* Not equal, return an error. */ + return(UX_ERROR); + } + } + + /* Blocks are equal, return success. */ + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_utility_memory_copy.c b/common/core/src/ux_utility_memory_copy.c new file mode 100644 index 0000000..0bb83d0 --- /dev/null +++ b/common/core/src/ux_utility_memory_copy.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_copy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function copies a block of memory from a source to a */ +/* destination. */ +/* */ +/* INPUT */ +/* */ +/* memory_destination Pointer to destination */ +/* memory_source Pointer to source */ +/* length Number of bytes to copy */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_memory_copy(VOID *memory_destination, VOID *memory_source, ULONG length) +{ + +UCHAR * source; +UCHAR * destination; + + /* Setup byte oriented source and destination pointers. */ + source = (UCHAR *) memory_source; + destination = (UCHAR *) memory_destination; + + /* Loop to perform the copy. */ + while(length--) + { + + /* Copy one byte. */ + *destination++ = *source++; + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_memory_free.c b/common/core/src/ux_utility_memory_free.c new file mode 100644 index 0000000..02d28f6 --- /dev/null +++ b/common/core/src/ux_utility_memory_free.c @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_free PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function frees a previously allocated memory block. */ +/* */ +/* INPUT */ +/* */ +/* memory Pointer to memory block */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_mutex_on Start system protection */ +/* _ux_utility_mutex_off End system protection */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_memory_free(VOID *memory) +{ + +UX_MEMORY_BLOCK *memory_block; +UX_MEMORY_BLOCK *next_block; +ULONG memory_size_returned; +UCHAR *memory_address; + + /* Get the mutex as this is a critical section. */ + _ux_utility_mutex_on(&_ux_system -> ux_system_mutex); + + /* The memory block for this memory pointer is located right before the + memory. */ + memory_block = (UX_MEMORY_BLOCK *) (((UCHAR *) memory) - sizeof(UX_MEMORY_BLOCK)); + + /* Keep track of the memory returned to the pool. */ + memory_size_returned = memory_block -> ux_memory_block_size + (ULONG)sizeof(UX_MEMORY_BLOCK); + + /* Check this memory block to see if it valid. */ + if (memory_block -> ux_memory_block_status != UX_MEMORY_USED) + { + + /* Not valid. Release the protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, UX_MEMORY_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_CORRUPTED, memory, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return to caller. */ + return; + } + + /* We mark this memory block as being unused. */ + memory_block -> ux_memory_block_status = UX_MEMORY_UNUSED; + + /* Now we must concatenate as many free blocks as possible, + that include the blocks before and the blocks after the current + block. Scan memory backwards. */ + + while (memory_block -> ux_memory_block_previous !=UX_NULL) + { + + /* Check if the block is free. */ + if (memory_block -> ux_memory_block_previous -> ux_memory_block_status == UX_MEMORY_UNUSED) + + /* The memory block before is free. This will be our starting point to + concatenate memory. */ + memory_block = memory_block -> ux_memory_block_previous; + + else + + /* The previous memory block is not free. */ + break; + } + + /* The pointer to the memory block is now our first free block. We use this + starting address to concatenate all the contiguous memory block. */ + next_block = memory_block -> ux_memory_block_next; + while (next_block != UX_NULL) + { + + /* Determine if the memory block is used. */ + if (next_block -> ux_memory_block_status == UX_MEMORY_USED) + { + + /* Yes, move to next block. */ + memory_block -> ux_memory_block_next = next_block; + next_block -> ux_memory_block_previous = memory_block; + break; + } + + memory_block -> ux_memory_block_next = next_block -> ux_memory_block_next; + memory_block -> ux_memory_block_size += next_block -> ux_memory_block_size + (ULONG)sizeof(UX_MEMORY_BLOCK); + next_block = next_block -> ux_memory_block_next; + } + + /* Update the memory free in the appropriate pool. We need to know if this + block is in regular memory or cache safe memory. */ + if(_ux_system -> ux_system_cache_safe_memory_pool_start == _ux_system -> ux_system_regular_memory_pool_start) + { + + /* There is only one regular memory pool. */ + _ux_system -> ux_system_regular_memory_pool_free += memory_size_returned; + + } + else + { + + /* Which pool is this memory in ? */ + memory_address = (UCHAR *) _ux_system -> ux_system_regular_memory_pool_start; + + /* If the memory address is in this range, we are in the regular memory pool. */ + if ((UCHAR *) memory_block >= memory_address && (UCHAR *) memory_block < (memory_address + _ux_system -> ux_system_regular_memory_pool_size)) + + /* Update the regular memory pool. */ + _ux_system -> ux_system_regular_memory_pool_free += memory_size_returned; + + else + + /* Update the cache safe memory pool. */ + _ux_system -> ux_system_cache_safe_memory_pool_free += memory_size_returned; + + } + + /* Release the protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_memory_free_block_best_get.c b/common/core/src/ux_utility_memory_free_block_best_get.c new file mode 100644 index 0000000..b6294bb --- /dev/null +++ b/common/core/src/ux_utility_memory_free_block_best_get.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** USBX main stack */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_free_block_best_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the best free memory block. */ +/* */ +/* INPUT */ +/* */ +/* memory_cache_flag Memory pool source */ +/* memory_size_requested Size of memory requested */ +/* */ +/* OUTPUT */ +/* */ +/* Pointer to best free block */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_MEMORY_BLOCK *_ux_utility_memory_free_block_best_get(ULONG memory_cache_flag, + ULONG memory_size_requested) +{ + +UX_MEMORY_BLOCK *memory_block; +UX_MEMORY_BLOCK *best_memory_block; + + + /* Reset the free memory block. */ + best_memory_block = UX_NULL; + + /* Check the type of memory we need. */ + switch (memory_cache_flag) + { + + case UX_REGULAR_MEMORY : + + /* Start at the beginning of the regular memory pool. */ + memory_block = _ux_system -> ux_system_regular_memory_pool_start; + break; + + case UX_CACHE_SAFE_MEMORY : + + /* Start at the beginning of the cache safe memory pool. */ + memory_block = _ux_system -> ux_system_cache_safe_memory_pool_start; + break; + + default : + + /* Wrong memory type. */ + return(UX_NULL); + + } + + /* Loop on all memory blocks from the beginning. */ + while (memory_block != UX_NULL) + { + + /* Check the memory block status. */ + if (memory_block -> ux_memory_block_status == UX_MEMORY_UNUSED) + { + + /* Check the size of this free block and see if it will + fit the memory requirement. */ + if (memory_block -> ux_memory_block_size > memory_size_requested) + { + + /* This memory block will do. Now see if it is the best. + The best memory block is the one whose memory is closest + to the memory requested. */ + if (best_memory_block == UX_NULL) + + /* Initialize the best block with the first free one. */ + best_memory_block = memory_block; + else + { + + if (memory_block -> ux_memory_block_size < best_memory_block -> ux_memory_block_size) + + /* We have discovered a better fit block. */ + best_memory_block = memory_block; + } + } + } + + /* Search the next free block until the end. */ + memory_block = memory_block -> ux_memory_block_next; + } + + /* If no free memory block was found, the return value will be NULL. */ + return(best_memory_block); +} + diff --git a/common/core/src/ux_utility_memory_set.c b/common/core/src/ux_utility_memory_set.c new file mode 100644 index 0000000..5cf7a7f --- /dev/null +++ b/common/core/src/ux_utility_memory_set.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_memory_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets a memory block with a specific value. */ +/* */ +/* INPUT */ +/* */ +/* destination Destination address */ +/* value 8-bit value */ +/* length Size of memory to set */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_memory_set(VOID *destination, UCHAR value, ULONG length) +{ + +UCHAR * work_ptr; + + + /* Setup the working pointer */ + work_ptr = (UCHAR *) destination; + + /* Loop to set the memory. */ + while(length--) + { + + /* Set a byte. */ + *work_ptr++ = value; + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_mutex_create.c b/common/core/src/ux_utility_mutex_create.c new file mode 100644 index 0000000..b8bb410 --- /dev/null +++ b/common/core/src/ux_utility_mutex_create.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_mutex_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a protection mutex. */ +/* */ +/* INPUT */ +/* */ +/* mutex Pointer to mutex */ +/* mutex_name Name of mutex */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_mutex_create ThreadX mutex create */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_mutex_create(TX_MUTEX *mutex, CHAR *mutex_name) +{ + +UINT status; + + + /* Call ThreadX to create the Mutex object. */ + status = tx_mutex_create(mutex, (CHAR *) mutex_name, TX_NO_INHERIT); + + /* Check for status. */ + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MUTEX_ERROR, mutex, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_mutex_delete.c b/common/core/src/ux_utility_mutex_delete.c new file mode 100644 index 0000000..caed65c --- /dev/null +++ b/common/core/src/ux_utility_mutex_delete.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_mutex_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deletes a protection mutex. */ +/* */ +/* INPUT */ +/* */ +/* mutex Pointer to mutex */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_mutex_create ThreadX mutex create */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_mutex_delete(TX_MUTEX *mutex) +{ + +UINT status; + + + /* Call ThreadX to delete the Mutex object. */ + status = tx_mutex_delete(mutex); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_mutex_off.c b/common/core/src/ux_utility_mutex_off.c new file mode 100644 index 0000000..03ccec2 --- /dev/null +++ b/common/core/src/ux_utility_mutex_off.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_mutex_off PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases system protection. */ +/* */ +/* INPUT */ +/* */ +/* Mutex */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* tx_mutex_put ThreadX mutex put */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_mutex_off(TX_MUTEX *mutex) +{ + + /* Call ThreadX to release protection. */ + tx_mutex_put(mutex); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_mutex_on.c b/common/core/src/ux_utility_mutex_on.c new file mode 100644 index 0000000..14a4aa2 --- /dev/null +++ b/common/core/src/ux_utility_mutex_on.c @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_mutex_on PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets system protection. */ +/* */ +/* INPUT */ +/* */ +/* Mutex */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* tx_mutex_get ThreadX mutex get */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_mutex_on(TX_MUTEX *mutex) +{ + +UINT status; + + /* Call ThreadX to get system mutex. */ + status = tx_mutex_get(mutex, TX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, status); + } + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_pci_class_scan.c b/common/core/src/ux_utility_pci_class_scan.c new file mode 100644 index 0000000..672fada --- /dev/null +++ b/common/core/src/ux_utility_pci_class_scan.c @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_pci_class_scan PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function scans the PCI bus from a certain position for a */ +/* specific PCI class. */ +/* */ +/* INPUT */ +/* */ +/* pci_class PCI class requested */ +/* bus_number PCI bus number */ +/* device_number Device number */ +/* function_number Function number */ +/* current_bus_number Current bus number */ +/* current_device_number Current device number */ +/* current_function_number Current function number */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit value */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_pci_read PCI read utility */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_utility_pci_class_scan(ULONG pci_class, ULONG bus_number, ULONG device_number, + ULONG function_number, ULONG *current_bus_number, + ULONG *current_device_number, ULONG *current_function_number) +{ + +ULONG bus_number_index; +ULONG device_number_index; +ULONG function_number_index; +ULONG value; +ULONG current_pci_class; + + + /* Scan all bus. */ + for (bus_number_index = bus_number; bus_number_index <= UX_PCI_NB_BUS; bus_number_index++) + { + + /* Scan all devices. */ + for(device_number_index = device_number;device_number_index <= UX_PCI_NB_DEVICE; device_number_index++) + { + + /* Scan all functions. */ + for(function_number_index = function_number; function_number_index <= UX_PCI_NB_FUNCTIONS; function_number_index++) + { + + /* Reset all PCI address for next loop. */ + function_number = 0; + device_number = 0; + bus_number = 0; + + /* Read the PCI class bus/device/function. */ + value = _ux_utility_pci_read(bus_number_index, device_number_index, function_number_index, + UX_PCI_CFG_REVISION,32); + + /* Isolate the class code which is in the upper 3 bytes. */ + current_pci_class = (value >> 8) & 0x00ffffff; + + /* Do we have a match with the demanded class? */ + if(current_pci_class == pci_class) + { + + /* Return the position of this device on the PCI */ + *current_bus_number = bus_number_index; + *current_device_number = device_number_index; + *current_function_number = function_number_index; + + /* Return success! */ + return(UX_SUCCESS); + } + } + } + } + + /* Return an error since we didn't find anything. */ + return(UX_ERROR); +} + diff --git a/common/core/src/ux_utility_pci_read.c b/common/core/src/ux_utility_pci_read.c new file mode 100644 index 0000000..22cd473 --- /dev/null +++ b/common/core/src/ux_utility_pci_read.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_pci_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a 32/16/8 bit value from a specific PCI bus */ +/* at a certain offset. */ +/* */ +/* INPUT */ +/* */ +/* bus_number PCI bus number */ +/* device_number Device number */ +/* function_number Function number */ +/* offset Offset */ +/* read_size Size of read */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit value */ +/* */ +/* CALLS */ +/* */ +/* inpl PCI input long */ +/* inpw PCI input word */ +/* inpb PCI input byte */ +/* outpl PCI output function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_utility_pci_read(ULONG bus_number, ULONG device_number, ULONG function_number, + ULONG offset, UINT read_size) +{ + +ULONG destination_address; +ULONG cfg_ctrl; + + + /* Calculate the destination address. */ + destination_address = (((bus_number << 16) & 0x00ff0000) | ((device_number << 11) & 0x0000f800) | + ((function_number << 8) & 0x00000700)); + + /* Calculate the configure control value. */ + cfg_ctrl = destination_address | offset | 0x80000000; + + /* Read based on the size requested. */ + switch(read_size) + { + + case 32: + + /* Write the address we need to read from. */ + outpl(UX_PCI_CFG_CTRL_ADDRESS, cfg_ctrl); + + /* Return the 32 bit content of this address. */ + return(inpl(UX_PCI_CFG_DATA_ADDRESS)); + + case 16: + + /* Write the address we need to read from. */ + outpl(UX_PCI_CFG_CTRL_ADDRESS, cfg_ctrl); + + /* Return the 16 bit content of this address. */ + return((USHORT)(inpw(UX_PCI_CFG_DATA_ADDRESS))); + + case 8: + + /* Write the address we need to read from. */ + outpl(UX_PCI_CFG_CTRL_ADDRESS, cfg_ctrl); + + /* Return the 8 bit content of this address */ + return((ULONG)(inpb(UX_PCI_CFG_DATA_ADDRESS))); + + default: + + return(0); + } +} diff --git a/common/core/src/ux_utility_pci_write.c b/common/core/src/ux_utility_pci_write.c new file mode 100644 index 0000000..a17655d --- /dev/null +++ b/common/core/src/ux_utility_pci_write.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_pci_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a 32/16/8 bit value to a specific PCI bus */ +/* at a certain offset. */ +/* */ +/* INPUT */ +/* */ +/* bus_number PCI bus number */ +/* device_number Device number */ +/* function_number Function number */ +/* offset Offset */ +/* value Value to write */ +/* write_size Size of write */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* outpl PCI output long function */ +/* outpw PCI output word function */ +/* outpb PCI output byte function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_pci_write(ULONG bus_number, ULONG device_number, ULONG function_number, + ULONG offset, ULONG value, UINT write_size) +{ + +ULONG destination_address; +ULONG cfg_ctrl; + + + /* Calculate the destination address. */ + destination_address = (((bus_number << 16) & 0x00ff0000) | ((device_number << 11) & 0x0000f800) | + ((function_number << 8) & 0x00000700)); + + /* Calculate the configure control value. */ + cfg_ctrl = destination_address | offset | 0x80000000; + + /* Process relative to write size. */ + switch(write_size) + { + + case 32: + + /* Write the address we need to write to. */ + outpl(UX_PCI_CFG_CTRL_ADDRESS, cfg_ctrl); + + /* Write the 32 bit content of this address. */ + outpl(UX_PCI_CFG_DATA_ADDRESS, value); + break; + + case 16: + + /* Write the address we need to write to. */ + outpl(UX_PCI_CFG_CTRL_ADDRESS, cfg_ctrl); + + /* Write the 16 bit content of this address. */ + outpw(UX_PCI_CFG_DATA_ADDRESS + (offset & 2), (USHORT) value); + break; + + case 8: + + /* Write the address we need to write to. */ + outpl(UX_PCI_CFG_CTRL_ADDRESS, cfg_ctrl); + + /* Write the 8 bit content of this address */ + outpb(UX_PCI_CFG_DATA_ADDRESS + (offset & 3), (UCHAR) value); + break; + + default: + + break; + } +} + diff --git a/common/core/src/ux_utility_physical_address.c b/common/core/src/ux_utility_physical_address.c new file mode 100644 index 0000000..8709155 --- /dev/null +++ b/common/core/src/ux_utility_physical_address.c @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_physical_address PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a physical address given the supplied */ +/* virtual address. */ +/* */ +/* INPUT */ +/* */ +/* virtual_address Physical address */ +/* */ +/* OUTPUT */ +/* */ +/* physical_address Virtual address */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID *_ux_utility_physical_address(VOID *virtual_address) +{ + +VOID *physical_address; + + /* Any code to translate the virtual address into a physical address + will be below. If there is no translation, the physical address= + the virtual address. */ + + physical_address = virtual_address; + + return(physical_address); +} + diff --git a/common/core/src/ux_utility_semaphore_create.c b/common/core/src/ux_utility_semaphore_create.c new file mode 100644 index 0000000..ec411fa --- /dev/null +++ b/common/core/src/ux_utility_semaphore_create.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_semaphore_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a semaphore. */ +/* */ +/* INPUT */ +/* */ +/* semaphore Semaphore to create */ +/* semaphore_name Semaphore name */ +/* initial_count Initial semaphore count */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_semaphore_create ThreadX semaphore create */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_semaphore_create(TX_SEMAPHORE *semaphore, CHAR *semaphore_name, UINT initial_count) +{ + +UINT status; + + /* Call ThreadX to create the semaphore. */ + status = tx_semaphore_create(semaphore, (CHAR *) semaphore_name, initial_count); + + /* Check for status. */ + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_SEMAPHORE_ERROR, semaphore, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_semaphore_delete.c b/common/core/src/ux_utility_semaphore_delete.c new file mode 100644 index 0000000..d9d3ea5 --- /dev/null +++ b/common/core/src/ux_utility_semaphore_delete.c @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_semaphore_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deletes the specified semaphore. */ +/* */ +/* INPUT */ +/* */ +/* semaphore Semaphore to delete */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_semaphore_delete ThreadX semaphore delete */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_semaphore_delete(TX_SEMAPHORE *semaphore) +{ + +UINT status; + + /* Call ThreadX Semaphore delete function. */ + status = tx_semaphore_delete(semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_semaphore_get.c b/common/core/src/ux_utility_semaphore_get.c new file mode 100644 index 0000000..8627b5d --- /dev/null +++ b/common/core/src/ux_utility_semaphore_get.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_semaphore_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets a semaphore signal. */ +/* */ +/* INPUT */ +/* */ +/* semaphore Semaphore to get signal from */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_identify ThreadX identify thread */ +/* tx_thread_info_get ThreadX get thread info */ +/* tx_semaphore_get ThreadX semaphore get */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_semaphore_get(TX_SEMAPHORE *semaphore, ULONG semaphore_signal) +{ + +UINT status; +TX_THREAD *my_thread; +CHAR *name; +UINT state; +ULONG run_count; +UINT priority; +UINT preemption_threshold; +ULONG time_slice; +TX_THREAD *next_thread; +TX_THREAD *suspended_thread; + + /* Call TX to know my own tread. */ + my_thread = tx_thread_identify(); + + /* Retrieve information about the previously created thread "my_thread." */ + tx_thread_info_get(my_thread, &name, &state, &run_count, + &priority, &preemption_threshold, + &time_slice, &next_thread,&suspended_thread); + + /* Is this the lowest priority thread in the system trying to use TX services ? */ + if (priority > _ux_system -> ux_system_thread_lowest_priority) + { + + /* We need to remember this thread priority. */ + _ux_system -> ux_system_thread_lowest_priority = priority; + + } + + /* Get ThreadX semaphore instance. */ + status = tx_semaphore_get(semaphore, semaphore_signal); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_semaphore_put.c b/common/core/src/ux_utility_semaphore_put.c new file mode 100644 index 0000000..ba1df05 --- /dev/null +++ b/common/core/src/ux_utility_semaphore_put.c @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_semaphore_put PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets a semaphore signal. */ +/* */ +/* INPUT */ +/* */ +/* semaphore Semaphore to signal */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_semaphore_put ThreadX semaphore put */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_semaphore_put(TX_SEMAPHORE *semaphore) +{ + +UINT status; + + /* Put a ThreadX semaphore. */ + status = tx_semaphore_put(semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_set_interrupt_handler.c b/common/core/src/ux_utility_set_interrupt_handler.c new file mode 100644 index 0000000..fd30c78 --- /dev/null +++ b/common/core/src/ux_utility_set_interrupt_handler.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_set_interrupt_handler PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function installs an interrupt handler for USBX. */ +/* */ +/* INPUT */ +/* */ +/* irq Interrupt to install */ +/* interrupt_handler Pointer to interrupt handler */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_set_interrupt_handler(UINT irq, VOID (*interrupt_handler)(VOID)) +{ + + UX_PARAMETER_NOT_USED(interrupt_handler); + UX_PARAMETER_NOT_USED(irq); + + /* Unimplemented, just return. */ + return; +} diff --git a/common/core/src/ux_utility_short_get.c b/common/core/src/ux_utility_short_get.c new file mode 100644 index 0000000..99b43fd --- /dev/null +++ b/common/core/src/ux_utility_short_get.c @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_short_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a 16-bit value. */ +/* */ +/* INPUT */ +/* */ +/* address Source address */ +/* */ +/* OUTPUT */ +/* */ +/* 16-bit value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_utility_short_get(UCHAR * address) +{ + +USHORT value; + + + /* In order to make this function endian agnostic and memory alignment + independent, we read a byte at a time from the address. */ + value = (USHORT) *address++; + value |= (USHORT)(*address << 8); + + /* Return to caller. */ + return((ULONG) value); +} + diff --git a/common/core/src/ux_utility_short_get_big_endian.c b/common/core/src/ux_utility_short_get_big_endian.c new file mode 100644 index 0000000..ac3cd35 --- /dev/null +++ b/common/core/src/ux_utility_short_get_big_endian.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_short_get_big_endian PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a 16-bit value in big endian format. */ +/* */ +/* INPUT */ +/* */ +/* address Source address */ +/* */ +/* OUTPUT */ +/* */ +/* 16-bit value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_utility_short_get_big_endian(UCHAR * address) +{ + +USHORT value; + + /* We read a byte at a time from the address. */ + value = (USHORT)((*address++) << 8); + value = (USHORT)(value | *address); + + /* Return 16-bit value. */ + return((ULONG) value); +} + diff --git a/common/core/src/ux_utility_short_put.c b/common/core/src/ux_utility_short_put.c new file mode 100644 index 0000000..3655e5b --- /dev/null +++ b/common/core/src/ux_utility_short_put.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_short_put PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a 16-bit value. */ +/* */ +/* INPUT */ +/* */ +/* address Destination address */ +/* value 16-bit value */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_short_put(UCHAR * address, USHORT value) +{ + + /* In order to make this function endian agnostic and memory alignment + independent, we write a byte at a time from the address */ + *address++ = (UCHAR) (value & 0xff); + *address = (UCHAR) ((value >> 8) & 0xff); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_short_put_big_endian.c b/common/core/src/ux_utility_short_put_big_endian.c new file mode 100644 index 0000000..f382255 --- /dev/null +++ b/common/core/src/ux_utility_short_put_big_endian.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_short_put_big_endian PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a 16-bit value in big endian format. */ +/* */ +/* INPUT */ +/* */ +/* address Destination address */ +/* value 16-bit value */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_short_put_big_endian(UCHAR * address, USHORT value) +{ + +USHORT low_byte_value; +USHORT high_byte_value; + + + /* First we swap the value bytes. */ + low_byte_value = value >> 8; + high_byte_value = (USHORT)(value<< 8); + value = high_byte_value | low_byte_value; + + /* In order to make this function endian agnostic and memory alignment + independent, we write a byte at a time from the address. */ + *address++ = (UCHAR) (value & 0xff); + *address= (UCHAR) ((value >> 8) & 0xff); + + /* Return to caller. */ + return; +} + diff --git a/common/core/src/ux_utility_string_length_check.c b/common/core/src/ux_utility_string_length_check.c new file mode 100644 index 0000000..4ecc3cb --- /dev/null +++ b/common/core/src/ux_utility_string_length_check.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_string_length_check PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if a NULL-terminated C string reaches its end */ +/* before specified length exceeds. */ +/* */ +/* On success the actual length of C string is written back to UINT */ +/* variable pointed by string_length_ptr (if not NULL). */ +/* Otherwise the variable keeps untouched. */ +/* */ +/* INPUT */ +/* */ +/* string Pointer to string */ +/* string_length_ptr Pointer to UINT to receive */ +/* the string length */ +/* max_string_length Max string length */ +/* */ +/* OUTPUT */ +/* */ +/* returns success if the string length was less than the max length, */ +/* else it returns error */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_string_length_check(UCHAR *string, UINT *string_length_ptr, UINT max_string_length) +{ + +UINT string_length; + + + if (string == UX_NULL) + return(UX_ERROR); + + string_length = 0; + + while (1) + { + + if (string[string_length] == '\0') + break; + + string_length++; + if (string_length > max_string_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, UX_ERROR); + + return(UX_ERROR); + } + } + + if (string_length_ptr) + *string_length_ptr = string_length; + + return(UX_SUCCESS); +} + diff --git a/common/core/src/ux_utility_string_length_get.c b/common/core/src/ux_utility_string_length_get.c new file mode 100644 index 0000000..6522612 --- /dev/null +++ b/common/core/src/ux_utility_string_length_get.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_string_length_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function derives the length of a NULL-terminated string. */ +/* */ +/* This function is deprecated, for the possible issue of operating on */ +/* a buffer that is not NULL-terminated. As a replacement, */ +/* _ux_utility_string_length_check should be used, where the length of */ +/* the buffer is introduced to validate the string by checking for the */ +/* NULL-terminator within the buffer length. */ +/* */ +/* INPUT */ +/* */ +/* string Pointer to string */ +/* */ +/* OUTPUT */ +/* */ +/* length of string */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_utility_string_length_get(UCHAR *string) +{ + +ULONG length = 0; + + /* Loop to find the length of the string. */ + length = 0; + while (string[length]) + { + + /* Move to next position. */ + length++; + } + + /* Return length to caller. */ + return(length); +} + diff --git a/common/core/src/ux_utility_string_to_unicode.c b/common/core/src/ux_utility_string_to_unicode.c new file mode 100644 index 0000000..6ec0b47 --- /dev/null +++ b/common/core/src/ux_utility_string_to_unicode.c @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_string_to_unicode PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function converts a ascii string to a unicode string. */ +/* */ +/* Note: */ +/* The unicode string length (including NULL-terminator) is limited by */ +/* length in a byte, so max ascii string length must be no more than */ +/* 254 (NULL-terminator excluded). Only first 254 characters */ +/* are converted if the string is too long. */ +/* The buffer of destination must have enough space for result, at */ +/* least 1 + (strlen(source) + 1) * 2 bytes. */ +/* */ +/* INPUT */ +/* */ +/* source Ascii String */ +/* destination Unicode String */ +/* */ +/* OUTPUT */ +/* */ +/* none */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_string_length_check Check and return C string */ +/* length */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_string_to_unicode(UCHAR *source, UCHAR *destination) +{ + +UINT string_length; + + /* Get the ascii string length, when there is error length is not modified so max length is used. */ + string_length = 254; + _ux_utility_string_length_check(source, &string_length, 254); + + /* Set the length of the string as the first byte of the unicode string. + The length is casted as a byte since Unicode strings cannot be more than 255 chars. */ + *destination++ = (UCHAR)(string_length + 1); + + while(string_length--) + { + /* First character is from the source. */ + *destination++ = *source++; + + /* Second character of unicode word is 0. */ + *destination++ = 0; + } + + /* Finish with a 0. */ + *destination++ = 0; + + /* Finish with a 0. */ + *destination++ = 0; + + /* We are done. */ + return; +} + diff --git a/common/core/src/ux_utility_thread_create.c b/common/core/src/ux_utility_thread_create.c new file mode 100644 index 0000000..0a5d969 --- /dev/null +++ b/common/core/src/ux_utility_thread_create.c @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a thread for USBX. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Thread control block pointer */ +/* name Pointer to thread name string */ +/* entry_function Entry function of the thread */ +/* entry_input 32-bit input value to thread */ +/* stack_start Pointer to start of stack */ +/* stack_size Stack size in bytes */ +/* priority Priority of thread (0-31) */ +/* preempt_threshold Preemption threshold */ +/* time_slice Thread time-slice value */ +/* auto_start Automatic start selection */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_create ThreadX create thread function*/ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_thread_create(TX_THREAD *thread_ptr, CHAR *name, + VOID (*entry_function)(ULONG), ULONG entry_input, + VOID *stack_start, ULONG stack_size, + UINT priority, UINT preempt_threshold, + ULONG time_slice, UINT auto_start) +{ + +UINT status; + + + /* Call ThreadX to create USBX thread. */ + status = tx_thread_create(thread_ptr,name,entry_function,entry_input, + stack_start,stack_size, priority,preempt_threshold,time_slice,auto_start); + + /* Check for status. */ + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_THREAD_ERROR, thread_ptr, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_thread_delete.c b/common/core/src/ux_utility_thread_delete.c new file mode 100644 index 0000000..dd9aad5 --- /dev/null +++ b/common/core/src/ux_utility_thread_delete.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deletes a thread for USBX. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Thread control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_terminate ThreadX terminate thread */ +/* tx_thread_delete ThreadX delete thread service */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_thread_delete(TX_THREAD *thread_ptr) +{ + +UINT status; + + + /* Call ThreadX to terminate the USBX thread. */ + tx_thread_terminate(thread_ptr); + + /* Call ThreadX to delete the USBX thread. */ + status = tx_thread_delete(thread_ptr); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_thread_identify.c b/common/core/src/ux_utility_thread_identify.c new file mode 100644 index 0000000..02f640d --- /dev/null +++ b/common/core/src/ux_utility_thread_identify.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_identify PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function return a pointer to the calling thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_identify ThreadX identify function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +TX_THREAD *_ux_utility_thread_identify(VOID) +{ + + + /* If we're under interrupt, the thread returned by tx_thread_identify + is the thread running prior to the ISR. Instead, we set it to null. */ + return(TX_THREAD_GET_SYSTEM_STATE() ? UX_NULL : tx_thread_identify()); +} + diff --git a/common/core/src/ux_utility_thread_relinquish.c b/common/core/src/ux_utility_thread_relinquish.c new file mode 100644 index 0000000..d7f687a --- /dev/null +++ b/common/core/src/ux_utility_thread_relinquish.c @@ -0,0 +1,73 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_relinquish PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function helps the thread relinquish its control. */ +/* */ +/* INPUT */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_relinquish ThreadX relinquish thread */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_thread_relinquish(VOID) +{ + + /* Call ThreadX to relinquish a USBX thread. */ + tx_thread_relinquish(); + +} + diff --git a/common/core/src/ux_utility_thread_resume.c b/common/core/src/ux_utility_thread_resume.c new file mode 100644 index 0000000..2bb13a6 --- /dev/null +++ b/common/core/src/ux_utility_thread_resume.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_resume PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function resumes a thread for USBX. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Thread control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_resume ThreadX resume thread function*/ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_thread_resume(TX_THREAD *thread_ptr) +{ + +UINT status; + + + /* Call ThreadX to resume USBX thread. */ + status = tx_thread_resume(thread_ptr); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_thread_schedule_other.c b/common/core/src/ux_utility_thread_schedule_other.c new file mode 100644 index 0000000..388031e --- /dev/null +++ b/common/core/src/ux_utility_thread_schedule_other.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_priority_change PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function force the scheduling of all other threads. */ +/* */ +/* INPUT */ +/* */ +/* caller_priority Priority to restore. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_identify ThreadX identify */ +/* tx_thread_priority_change ThreadX priority change */ +/* tx_thread_relinquish ThreadX relinquish */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_thread_schedule_other(UINT caller_priority) +{ + +UINT status; +UINT old_priority; +TX_THREAD *my_thread; + + UX_PARAMETER_NOT_USED(caller_priority); + + /* Call TX to know my own tread. */ + my_thread = tx_thread_identify(); + + /* Call ThreadX to change thread priority . */ + status = tx_thread_priority_change(my_thread, _ux_system -> ux_system_thread_lowest_priority, &old_priority); + + /* Check for error. */ + if (status == TX_SUCCESS) + { + + /* Wait until all other threads passed into the scheduler. */ + _ux_utility_thread_relinquish(); + + /* And now return the priority of the thread to normal. */ + status = tx_thread_priority_change(my_thread, old_priority, &old_priority); + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_thread_sleep.c b/common/core/src/ux_utility_thread_sleep.c new file mode 100644 index 0000000..cfa494e --- /dev/null +++ b/common/core/src/ux_utility_thread_sleep.c @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_sleep PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function causes the calling thread to sleep for the */ +/* specified number of ticks. */ +/* */ +/* INPUT */ +/* */ +/* ticks Number of ticks to sleep */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_sleep ThreadX sleep function */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_thread_sleep(ULONG ticks) +{ + +UINT status; + + /* Call ThreadX sleep function. */ + status = tx_thread_sleep(ticks); + + return(status); +} + diff --git a/common/core/src/ux_utility_thread_suspend.c b/common/core/src/ux_utility_thread_suspend.c new file mode 100644 index 0000000..304ce18 --- /dev/null +++ b/common/core/src/ux_utility_thread_suspend.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_thread_suspend PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function suspends thread for USBX. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Thread control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_thread_suspend ThreadX suspend thread service*/ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_thread_suspend(TX_THREAD *thread_ptr) +{ + +UINT status; + + + /* Call ThreadX to suspend USBX thread. */ + status = tx_thread_suspend(thread_ptr); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_timer_create.c b/common/core/src/ux_utility_timer_create.c new file mode 100644 index 0000000..04c3c69 --- /dev/null +++ b/common/core/src/ux_utility_timer_create.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_timer_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates a timer. */ +/* */ +/* INPUT */ +/* */ +/* timer Pointer to timer */ +/* timer_name Name of timer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* tx_timer_create ThreadX timer create */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_utility_timer_create(TX_TIMER *timer, CHAR *timer_name, VOID (*expiration_function) (ULONG), + ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks, + UINT activation_flag) +{ + +UINT status; + + + /* Call ThreadX to create the timer object. */ + status = tx_timer_create(timer, (CHAR *) timer_name, expiration_function, expiration_input, + initial_ticks, reschedule_ticks, activation_flag); + + /* Check status. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_UTILITY, status); + + /* Return completion status. */ + return(status); +} + diff --git a/common/core/src/ux_utility_unicode_to_string.c b/common/core/src/ux_utility_unicode_to_string.c new file mode 100644 index 0000000..aeb1564 --- /dev/null +++ b/common/core/src/ux_utility_unicode_to_string.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_unicode_to_string PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function converts a unicode string to a zero terminated */ +/* ascii string. */ +/* */ +/* INPUT */ +/* */ +/* source Unicode String */ +/* destination Ascii String */ +/* */ +/* OUTPUT */ +/* */ +/* none */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_utility_unicode_to_string(UCHAR *source, UCHAR *destination) +{ +ULONG string_length; + + /* Obtain the length of the unicode string. This is the first byte*/ + string_length = (ULONG) *source++; + + /* Parse the unicode string. First byte is always 0, second byte is the + ASCII character. */ + while(string_length--) + { + /* First character is from the source. */ + *destination++ = *source++; + + /* Second character of unicode word is 0. */ + source++; + } + + /* We are done with the ascii string. Insert a zero at the end. */ + *destination = 0; + + /* Finished. */ + return; +} + diff --git a/common/core/src/ux_utility_virtual_address.c b/common/core/src/ux_utility_virtual_address.c new file mode 100644 index 0000000..5bb4e95 --- /dev/null +++ b/common/core/src/ux_utility_virtual_address.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Utility */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_utility_virtual_address PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a virtual address given the supplied */ +/* physical address. */ +/* */ +/* INPUT */ +/* */ +/* physical_address Virtual address */ +/* */ +/* OUTPUT */ +/* */ +/* virtual_address Physical address */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID *_ux_utility_virtual_address(VOID *physical_address) +{ + +VOID *virtual_address; + + /* Any code to translate the physical address into a virtual address + will be below. If there is no translation, the physical address= + the virtual address. */ + + virtual_address = physical_address; + return(virtual_address); +} + diff --git a/common/usbx_device_classes/CMakeLists.txt b/common/usbx_device_classes/CMakeLists.txt new file mode 100644 index 0000000..10f9996 --- /dev/null +++ b/common/usbx_device_classes/CMakeLists.txt @@ -0,0 +1,151 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio10_control_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio20_control_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_frame_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_read_frame_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_read_frame_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_read_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read16.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read24.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read32.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_sample_read8.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_stream_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_transmission_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_unitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_write_frame_commit.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_write_frame_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_audio_write_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_bulkin_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_bulkout_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_unitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_acm_write_with_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_bulkin_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_bulkout_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_change.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_cdc_ecm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_dfu_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_descriptor_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_event_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_event_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_report_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_report_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_hid_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_data.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_info_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_prop_desc_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_prop_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_prop_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_device_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_event_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_event_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_data_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_handles_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_info_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_prop_desc_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_prop_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_prop_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_props_supported_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_references_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_object_references_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_objects_number_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_partial_object_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_response_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_storage_format.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_storage_id_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_storage_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_pima_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_bulkin_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_bulkout_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_interrupt_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_keep_alive.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_query.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_msg_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_rndis_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_control_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_csw_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_format.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_get_configuration.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_get_performance.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_get_status_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_inquiry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_mode_select.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_mode_sense.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_prevent_allow_media_removal.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_capacity.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_disk_information.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_dvd_structure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_format_capacity.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_read_toc.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_report_key.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_request_sense.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_start_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_synchronize_cache.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_test_ready.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_uninitialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_verify.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_device_class_storage_write.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/common/usbx_device_classes/inc/ux_device_class_audio.h b/common/usbx_device_classes/inc/ux_device_class_audio.h new file mode 100644 index 0000000..2ed7cfb --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_audio.h @@ -0,0 +1,421 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_audio.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX audio class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_AUDIO_H +#define UX_DEVICE_CLASS_AUDIO_H + +/* Define Audio Class function (AF) constants. */ + +#define UX_DEVICE_CLASS_AUDIO_FUNCTION_CLASS 1 +#define UX_DEVICE_CLASS_AUDIO_FUNCTION_SUBCLASS_UNDEFINED 0 +#define UX_DEVICE_CLASS_AUDIO_FUNCTION_PROTOCOL_UNDEFINED 0 +#define UX_DEVICE_CLASS_AUDIO_FUNCTION_PROTOCOL_VERSION_02_00 0x20 + +/* Define Audio Class interface constants. */ + +#define UX_DEVICE_CLASS_AUDIO_CLASS 1 + +#define UX_DEVICE_CLASS_AUDIO_SUBCLASS_UNDEFINED 0 +#define UX_DEVICE_CLASS_AUDIO_SUBCLASS_CONTROL 1 +#define UX_DEVICE_CLASS_AUDIO_SUBCLASS_AUDIOSTREAMING 2 +#define UX_DEVICE_CLASS_AUDIO_SUBCLASS_MIDISTREAMING 3 + +#define UX_DEVICE_CLASS_AUDIO_PROTOCOL_UNDEFINED 0 +#define UX_DEVICE_CLASS_AUTIO_PROTOCOL_VERSION_02_00 0x20 + + +/* Define Audio Class-specific (CS) descriptor types. */ + +#define UX_DEVICE_CLASS_AUDIO_CS_UNDEFINED 0x20 +#define UX_DEVICE_CLASS_AUDIO_CS_DEVICE 0x21 +#define UX_DEVICE_CLASS_AUDIO_CS_CONFIGURATION 0x22 +#define UX_DEVICE_CLASS_AUDIO_CS_STRING 0x23 +#define UX_DEVICE_CLASS_AUDIO_CS_INTERFACE 0x24 +#define UX_DEVICE_CLASS_AUDIO_CS_ENDPOINT 0x25 + + +/* Define Audio Class specific AC interface descriptor subclasses. */ + +#define UX_DEVICE_CLASS_AUDIO_AC_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO_AC_HEADER 0x01 +#define UX_DEVICE_CLASS_AUDIO_AC_INPUT_TERMINAL 0x02 +#define UX_DEVICE_CLASS_AUDIO_AC_OUTPUT_TERMINAL 0x03 +#define UX_DEVICE_CLASS_AUDIO_AC_FEATURE_UNIT 0x06 + + +/* Define Audio Class specific AS interface descriptor subclasses. */ + +#define UX_DEVICE_CLASS_AUDIO_AS_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO_AS_GENERAL 0x01 +#define UX_DEVICE_CLASS_AUDIO_AS_FORMAT_TYPE 0x02 + + +/* Define Audio Class data endpoint descriptor attributes. */ + +#define UX_DEVICE_CLASS_AUDIO_EP_TRANSFER_TYPE_MASK (0x3u<<0) +#define UX_DEVICE_CLASS_AUDIO_EP_TRANSFER_TYPE_ISOCHRONOUS (0x1u<<0) + +#define UX_DEVICE_CLASS_AUDIO_EP_SYNCHRONIZATION_TYPE_MASK (0x3u<<2) +#define UX_DEVICE_CLASS_AUDIO_EP_SYNCHRONIZATION_TYPE_ASYNCHRONOUS (0x1u<<2) +#define UX_DEVICE_CLASS_AUDIO_EP_SYNCHRONIZATION_TYPE_ADAPTIVE (0x2u<<2) +#define UX_DEVICE_CLASS_AUDIO_EP_SYNCHRONIZATION_TYPE_SYNCHRONOUS (0x3u<<2) + +#define UX_DEVICE_CLASS_AUDIO_EP_USAGE_TYPE_MASK (0x3u<<4) +#define UX_DEVICE_CLASS_AUDIO_EP_USAGE_TYPE_DATA (0x0u<<4) +#define UX_DEVICE_CLASS_AUDIO_EP_USAGE_TYPE_IMPLICIT_FEEDBACK (0x2u<<4) + + +/* Define Audio Class specific endpoint descriptor subtypes. */ + +#define UX_DEVICE_CLASS_AUDIO_EP_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO_EP_GENERAL 0x01 + + +/* Define Audio Class specific request codes. */ + +#define UX_DEVICE_CLASS_AUDIO_REQUEST_CODE_UNDEFINED 0x00 + +#define UX_DEVICE_CLASS_AUDIO_bmRequestType_GET_INTERFACE 0xA1 +#define UX_DEVICE_CLASS_AUDIO_bmRequestType_SET_INTERFACE 0x21 +#define UX_DEVICE_CLASS_AUDIO_bmRequestType_GET_ENDPOINT 0xA2 +#define UX_DEVICE_CLASS_AUDIO_bmRequestType_SET_ENDPOINT 0x22 + +#define UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST_TYPE 0 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST 1 + +#define UX_DEVICE_CLASS_AUDIO_REQUEST_VALUE_LOW 2 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_CHANNEL_NUMBER 2 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_CN 2 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_MIXER_CONTROL_NUMBER 2 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_MCN 2 + +#define UX_DEVICE_CLASS_AUDIO_REQUEST_VALUE_HIGH 3 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_CONTROL_SELECTOR 3 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_CS 3 + +#define UX_DEVICE_CLASS_AUDIO_REQUEST_INDEX_LOW 4 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_ENDPOINT 4 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_INTERFACE 4 + +#define UX_DEVICE_CLASS_AUDIO_REQUEST_INDEX_HIGH 5 +#define UX_DEVICE_CLASS_AUDIO_REQUEST_ENEITY_ID 5 + +#define UX_DEVICE_CLASS_AUDIO_REQUEST_LENGTH 6 + + +/* Define Audio Class terminal types. */ + +#define UX_DEVICE_CLASS_AUDIO_UNDEFINED 0x0100 +#define UX_DEVICE_CLASS_AUDIO_USB_STREAMING 0x0101 +#define UX_DEVICE_CLASS_AUDIO_USB_VENDOR_SPECIFIC 0x01FF + + +/* Define Audio Class input terminal types. */ + +#define UX_DEVICE_CLASS_AUDIO_INPUT 0x0200 +#define UX_DEVICE_CLASS_AUDIO_MICROPHONE 0x0201 +#define UX_DEVICE_CLASS_AUDIO_DESKTOP_MICROPHONE 0x0202 +#define UX_DEVICE_CLASS_AUDIO_PERSONAL_MICROPHONE 0x0203 +#define UX_DEVICE_CLASS_AUDIO_OMNI_DIRECTIONAL_MICROPHONE 0x0204 +#define UX_DEVICE_CLASS_AUDIO_MICROPHONE_ARRAY 0x0205 +#define UX_DEVICE_CLASS_AUDIO_PROCESSING_MICROPHONE_ARRAY 0x0206 + + +/* Define Audio Class output terminal types. */ + +#define UX_DEVICE_CLASS_AUDIO_OUTPUT 0x0300 +#define UX_DEVICE_CLASS_AUDIO_SPEAKER 0x0301 +#define UX_DEVICE_CLASS_AUDIO_HEADPHONES 0x0302 +#define UX_DEVICE_CLASS_AUDIO_HEAD_MOUNTED_DISPLAY 0x0303 +#define UX_DEVICE_CLASS_AUDIO_DESKTOP_SPEAKER 0x0304 +#define UX_DEVICE_CLASS_AUDIO_ROOM_SPEAKER 0x0305 +#define UX_DEVICE_CLASS_AUDIO_COMMUNICATION_SPEAKER 0x0306 +#define UX_DEVICE_CLASS_AUDIO_LOW_FREQUENCY_SPEAKER 0x0307 + + +/* Define Audio Class bidirectional terminal types. */ + +#define UX_DEVICE_CLASS_AUDIO_BIDIRECTIONAL_UNDEFINED 0x0400 +#define UX_DEVICE_CLASS_AUDIO_HANDSET 0x0401 +#define UX_DEVICE_CLASS_AUDIO_HEADSET 0x0402 +#define UX_DEVICE_CLASS_AUDIO_SPEAKERPHONE 0x0403 +#define UX_DEVICE_CLASS_AUDIO_ECHO_SUPRESS_SPEAKERPHONE 0x0404 +#define UX_DEVICE_CLASS_AUDIO_ECHO_CANCEL_SPEAKERPHONE 0x0405 + + +/* Define Audio Class telephony terminal types. */ + +#define UX_DEVICE_CLASS_AUDIO_TELEPHONTY_UNDEFINED 0x0500 +#define UX_DEVICE_CLASS_AUDIO_PHONE_LINE 0x0501 +#define UX_DEVICE_CLASS_AUDIO_TELEPHONE 0x0502 +#define UX_DEVICE_CLASS_AUDIO_DOWN_LINE_PHONE 0x0503 + + +/* Define Audio Class external terminal types. */ + +#define UX_DEVICE_CLASS_AUDIO_EXTERNAL_UNDEFINED 0x0600 +#define UX_DEVICE_CLASS_AUDIO_ANALOG_CONNECTOR 0x0601 +#define UX_DEVICE_CLASS_AUDIO_DIGITAL_AUDIO_INTERFACE 0x0602 +#define UX_DEVICE_CLASS_AUDIO_LINE_CONNECTOR 0x0603 +#define UX_DEVICE_CLASS_AUDIO_LEGACY_AUDIO_CONNECTOR 0x0604 +#define UX_DEVICE_CLASS_AUDIO_S_PDIF_INTERFACE 0x0605 +#define UX_DEVICE_CLASS_AUDIO_1394_DA_STREAM 0x0606 +#define UX_DEVICE_CLASS_AUDIO_1394_DV_STREAM_SOUNDTRACK 0x0607 +#define UX_DEVICE_CLASS_AUDIO_ADAT_LIGHTPIPE 0x0608 +#define UX_DEVICE_CLASS_AUDIO_TDIF 0x0609 +#define UX_DEVICE_CLASS_AUDIO_MADI 0x060A + + +/* Define Audio Class embedded function terminal types. */ + +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_UNDEFINED 0x0700 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_LEVEL_CALIB_NOISE_SRC 0x0701 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_EQUALIZATION_NOISE 0x0702 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_CD_PLAYER 0x0703 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_DAT 0x0704 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_DCC 0x0705 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_MINIDISK 0x0706 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_ANALOG_TAPE 0x0707 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_PHONOGRAPH 0x0708 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_VCR_AUDIO 0x0709 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_VIDEO_DISC_AUDIO 0x070A +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_DVD_AUDIO 0x070B +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_TV_TUNER_AUDIO 0x070C +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_SATELLITE_RECEIVER_AUDIO 0x070D +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_CABLE_TUNER_AUDIO 0x070E +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_DSS_AUDIO 0x070F +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_RADIO_RECEIVER 0x0710 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_RADIO_TRANSMITTER 0x0711 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_MULTI_TRACK_RECORDER 0x0712 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_SYNTHESIZER 0x0713 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_PIANO 0x0714 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_GUITAR 0x0715 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_DRUMS_RHYTHM 0x0716 +#define UX_DEVICE_CLASS_AUDIO_EMBEDDED_OTHER 0x0717 + + +/* Define Audio Class encoding format types. */ + +#define UX_DEVICE_CLASS_AUDIO_FORMAT_TYPE_UNDEFINED 0 +#define UX_DEVICE_CLASS_AUDIO_FORMAT_TYPE_I 1 +#define UX_DEVICE_CLASS_AUDIO_FORMAT_TYPE_II 2 +#define UX_DEVICE_CLASS_AUDIO_FORMAT_TYPE_III 3 +#define UX_DEVICE_CLASS_AUDIO_FORMAT_TYPE_IV 4 +#define UX_DEVICE_CLASS_AUDIO_EXT_FORMAT_TYPE_I 0x81 +#define UX_DEVICE_CLASS_AUDIO_EXT_FORMAT_TYPE_II 0x82 +#define UX_DEVICE_CLASS_AUDIO_EXT_FORMAT_TYPE_III 0x83 + + +/* Define channels. */ + +#define UX_DEVICE_CLASS_AUDIO_MASTER_CHANNEL 0 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_1 1 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_2 2 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_3 3 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_4 4 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_5 5 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_6 6 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_7 7 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_8 8 +#define UX_DEVICE_CLASS_AUDIO_CHANNEL_9 9 + + +/* Define IOCTL code. + ux_device_class_audio_ioctl(audio, IOCTL_CODE, parameter). + */ + +#define UX_DEVICE_CLASS_AUDIO_IOCTL_GET_ARG 1 + + +/* Define Audio Class callback structure. */ + +struct UX_DEVICE_CLASS_AUDIO_STREAM_STRUCT; +struct UX_DEVICE_CLASS_AUDIO_STRUCT; + +typedef struct UX_DEVICE_CLASS_AUDIO_CALLBACKS_STRUCT +{ + + VOID (*ux_slave_class_audio_instance_activate)(VOID *); + VOID (*ux_slave_class_audio_instance_deactivate)(VOID *); + UINT (*ux_device_class_audio_control_process)(struct UX_DEVICE_CLASS_AUDIO_STRUCT *, UX_SLAVE_TRANSFER *); + VOID *ux_device_class_audio_arg; +} UX_DEVICE_CLASS_AUDIO_CALLBACKS; + +typedef struct UX_DEVICE_CLASS_AUDIO_STREAM_CALLBACKS_STRUCT +{ + VOID (*ux_device_class_audio_stream_change)(struct UX_DEVICE_CLASS_AUDIO_STREAM_STRUCT *, ULONG); + VOID (*ux_device_class_audio_stream_frame_done)(struct UX_DEVICE_CLASS_AUDIO_STREAM_STRUCT *, ULONG); +} UX_DEVICE_CLASS_AUDIO_STREAM_CALLBACKS; + + +/* Define Audio Class Calling Parameter structure */ + +typedef struct UX_DEVICE_CLASS_AUDIO_STREAM_PARAMETER_STRUCT +{ + ULONG ux_device_class_audio_stream_parameter_thread_stack_size; + VOID (*ux_device_class_audio_stream_parameter_thread_entry)(ULONG id); + UX_DEVICE_CLASS_AUDIO_STREAM_CALLBACKS ux_device_class_audio_stream_parameter_callbacks; + + ULONG ux_device_class_audio_stream_parameter_max_frame_buffer_size; + ULONG ux_device_class_audio_stream_parameter_max_frame_buffer_nb; +} UX_DEVICE_CLASS_AUDIO_STREAM_PARAMETER; + +typedef struct UX_DEVICE_CLASS_AUDIO_PARAMETER_STRUCT +{ + ULONG ux_device_class_audio_parameter_master_interface; + UX_DEVICE_CLASS_AUDIO_CALLBACKS ux_device_class_audio_parameter_callbacks; + + ULONG ux_device_class_audio_parameter_streams_nb; + UX_DEVICE_CLASS_AUDIO_STREAM_PARAMETER *ux_device_class_audio_parameter_streams; +} UX_DEVICE_CLASS_AUDIO_PARAMETER; + + +/* Define Audio Class instance structure. */ + +typedef struct UX_DEVICE_CLASS_AUDIO_FRAME_STRUCT +{ + + ULONG ux_device_class_audio_frame_length; + ULONG ux_device_class_audio_frame_pos; + UCHAR ux_device_class_audio_frame_data[4]; +} UX_DEVICE_CLASS_AUDIO_FRAME; + +typedef struct UX_DEVICE_CLASS_AUDIO_STREAM_STRUCT +{ + + struct UX_DEVICE_CLASS_AUDIO_STRUCT *ux_device_class_audio_stream_audio; + UX_SLAVE_INTERFACE *ux_device_class_audio_stream_interface; + UX_SLAVE_ENDPOINT *ux_device_class_audio_stream_endpoint; + + UX_DEVICE_CLASS_AUDIO_STREAM_CALLBACKS ux_device_class_audio_stream_callbacks; + + UCHAR *ux_device_class_audio_stream_thread_stack; + TX_THREAD ux_device_class_audio_stream_thread; + + UCHAR *ux_device_class_audio_stream_buffer; + ULONG ux_device_class_audio_stream_buffer_size; + ULONG ux_device_class_audio_stream_frame_buffer_size; + + UX_DEVICE_CLASS_AUDIO_FRAME *ux_device_class_audio_stream_transfer_pos; + UX_DEVICE_CLASS_AUDIO_FRAME *ux_device_class_audio_stream_access_pos; +} UX_DEVICE_CLASS_AUDIO_STREAM; + +typedef struct UX_DEVICE_CLASS_AUDIO_STRUCT +{ + + UX_SLAVE_CLASS *ux_device_class_audio_class; + UX_SLAVE_DEVICE *ux_device_class_audio_device; + UX_SLAVE_INTERFACE *ux_device_class_audio_interface; + + UX_DEVICE_CLASS_AUDIO_CALLBACKS ux_device_class_audio_callbacks; + + ULONG ux_device_class_audio_streams_nb; + UX_DEVICE_CLASS_AUDIO_STREAM *ux_device_class_audio_streams; +} UX_DEVICE_CLASS_AUDIO; + + +/* Define Audio Class function prototypes. */ + +UINT _ux_device_class_audio_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_audio_uninitialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_audio_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_audio_change(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_audio_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_audio_control_request(UX_SLAVE_CLASS_COMMAND *command); + +UINT _ux_device_class_audio_entry(UX_SLAVE_CLASS_COMMAND *command); + +UINT _ux_device_class_audio_ioctl(UX_DEVICE_CLASS_AUDIO *audio, ULONG ioctl_function, + VOID *parameter); + +UINT _ux_device_class_audio_stream_get(UX_DEVICE_CLASS_AUDIO *audio, ULONG stream_index, UX_DEVICE_CLASS_AUDIO_STREAM **stream); + +VOID _ux_device_class_audio_write_thread_entry(ULONG audio_stream); +VOID _ux_device_class_audio_read_thread_entry(ULONG audio_stream); + +UINT _ux_device_class_audio_reception_start(UX_DEVICE_CLASS_AUDIO_STREAM *audio); +UINT _ux_device_class_audio_sample_read8(UX_DEVICE_CLASS_AUDIO_STREAM *audio, UCHAR *sample); +UINT _ux_device_class_audio_sample_read16(UX_DEVICE_CLASS_AUDIO_STREAM *audio, USHORT *sample); +UINT _ux_device_class_audio_sample_read24(UX_DEVICE_CLASS_AUDIO_STREAM *audio, ULONG *sample); +UINT _ux_device_class_audio_sample_read32(UX_DEVICE_CLASS_AUDIO_STREAM *audio, ULONG *sample); + +UINT _ux_device_class_audio_read_frame_get(UX_DEVICE_CLASS_AUDIO_STREAM *audio, UCHAR **frame_data, ULONG *frame_length); +UINT _ux_device_class_audio_read_frame_free(UX_DEVICE_CLASS_AUDIO_STREAM *audio); + +UINT _ux_device_class_audio_transmission_start(UX_DEVICE_CLASS_AUDIO_STREAM *audio); +UINT _ux_device_class_audio_frame_write(UX_DEVICE_CLASS_AUDIO_STREAM *audio, UCHAR *frame, ULONG length); + +UINT _ux_device_class_audio_write_frame_get(UX_DEVICE_CLASS_AUDIO_STREAM *audio, UCHAR **buffer, ULONG *max_length); +UINT _ux_device_class_audio_write_frame_commit(UX_DEVICE_CLASS_AUDIO_STREAM *audio, ULONG length); + + +/* Define Device Class Audio API prototypes. */ + +#define ux_device_class_audio_entry _ux_device_class_audio_entry + +#define ux_device_class_audio_read_thread_entry _ux_device_class_audio_read_thread_entry +#define ux_device_class_audio_write_thread_entry _ux_device_class_audio_write_thread_entry + +#define ux_device_class_audio_stream_get _ux_device_class_audio_stream_get + +#define ux_device_class_audio_reception_start _ux_device_class_audio_reception_start +#define ux_device_class_audio_sample_read8 _ux_device_class_audio_sample_read8 +#define ux_device_class_audio_sample_read16 _ux_device_class_audio_sample_read16 +#define ux_device_class_audio_sample_read24 _ux_device_class_audio_sample_read24 +#define ux_device_class_audio_sample_read32 _ux_device_class_audio_sample_read32 + +#define ux_device_class_audio_read_frame_get _ux_device_class_audio_read_frame_get +#define ux_device_class_audio_read_frame_free _ux_device_class_audio_read_frame_free + +#define ux_device_class_audio_transmission_start _ux_device_class_audio_transmission_start +#define ux_device_class_audio_frame_write _ux_device_class_audio_frame_write + +#define ux_device_class_audio_write_frame_get _ux_device_class_audio_write_frame_get +#define ux_device_class_audio_write_frame_commit _ux_device_class_audio_write_frame_commit + +#define ux_device_class_audio_ioctl _ux_device_class_audio_ioctl + +#endif /* ifndef UX_DEVICE_CLASS_AUDIO_H */ diff --git a/common/usbx_device_classes/inc/ux_device_class_audio10.h b/common/usbx_device_classes/inc/ux_device_class_audio10.h new file mode 100644 index 0000000..4d3572e --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_audio10.h @@ -0,0 +1,360 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_audio10.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX audio class version 1.0. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_AUDIO10_H +#define UX_DEVICE_CLASS_AUDIO10_H + + +/* Define Audio Class specific AC interface descriptor subclasses. */ + +#define UX_DEVICE_CLASS_AUDIO10_AC_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO10_AC_HEADER 0x01 +#define UX_DEVICE_CLASS_AUDIO10_AC_INPUT_TERMINAL 0x02 +#define UX_DEVICE_CLASS_AUDIO10_AC_OUTPUT_TERMINAL 0x03 +#define UX_DEVICE_CLASS_AUDIO10_AC_MIXER_UNIT 0x04 +#define UX_DEVICE_CLASS_AUDIO10_AC_SELECTOR_UNIT 0x05 +#define UX_DEVICE_CLASS_AUDIO10_AC_FEATURE_UNIT 0x06 +#define UX_DEVICE_CLASS_AUDIO10_AC_PROCESSING_UNIT 0x07 +#define UX_DEVICE_CLASS_AUDIO10_AC_EXTENSION_UNIT 0x08 + + +/* Define Audio Class specific AS interface descriptor subclasses. */ + +#define UX_DEVICE_CLASS_AUDIO10_AS_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO10_AS_GENERAL 0x01 +#define UX_DEVICE_CLASS_AUDIO10_AS_FORMAT_TYPE 0x02 +#define UX_DEVICE_CLASS_AUDIO10_AS_FORMAT_SPECIFIC 0x03 + + +/* Define Audio Class specific endpoint descriptor subtypes. */ + +#define UX_DEVICE_CLASS_AUDIO10_EP_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO10_EP_GENERAL 0x01 + + +/* Define Audio Class specific request codes. */ + +#define UX_DEVICE_CLASS_AUDIO10_REQUEST_CODE_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO10_SET_CUR 0x01 +#define UX_DEVICE_CLASS_AUDIO10_GET_CUR 0x81 +#define UX_DEVICE_CLASS_AUDIO10_SET_MIN 0x02 +#define UX_DEVICE_CLASS_AUDIO10_GET_MIN 0x82 +#define UX_DEVICE_CLASS_AUDIO10_SET_MAX 0x03 +#define UX_DEVICE_CLASS_AUDIO10_GET_MAX 0x83 +#define UX_DEVICE_CLASS_AUDIO10_SET_RES 0x04 +#define UX_DEVICE_CLASS_AUDIO10_GET_RES 0x84 +#define UX_DEVICE_CLASS_AUDIO10_SET_MEM 0x05 +#define UX_DEVICE_CLASS_AUDIO10_GET_MEM 0x85 +#define UX_DEVICE_CLASS_AUDIO10_GET_STAT 0xFF + + +/* Define Audio Class specific terminal control selectors. */ + +#define UX_DEVICE_CLASS_AUDIO10_TE_CONTROL_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO10_TE_COPY_PROTECT_CONTROL 0x01 + + +/* Define Audio Class specific feature unit control selectors. */ + +#define UX_DEVICE_CLASS_AUDIO10_FU_CONTROL_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO10_FU_MUTE_CONTROL 0x01 +#define UX_DEVICE_CLASS_AUDIO10_FU_VOLUME_CONTROL 0x02 +#define UX_DEVICE_CLASS_AUDIO10_FU_BASS_CONTROL 0x03 +#define UX_DEVICE_CLASS_AUDIO10_FU_MID_CONTROL 0x04 +#define UX_DEVICE_CLASS_AUDIO10_FU_TREBLE_CONTROL 0x05 +#define UX_DEVICE_CLASS_AUDIO10_FU_GRAPHIC_EQUALIZER_CONTROL 0x06 +#define UX_DEVICE_CLASS_AUDIO10_FU_AUTOMATIC_GAIN_CONTROL 0x07 +#define UX_DEVICE_CLASS_AUDIO10_FU_DELAY_CONTROL 0x08 +#define UX_DEVICE_CLASS_AUDIO10_FU_BASS_BOOST_CONTROL 0x09 +#define UX_DEVICE_CLASS_AUDIO10_FU_LOUNDNESS_CONTROL 0x0A + + +/* Define Audio Class encoding format types. */ + +#define UX_DEVICE_CLASS_AUDIO10_FORMAT_PCM 1 +#define UX_DEVICE_CLASS_AUDIO10_FORMAT_PCM8 2 +#define UX_DEVICE_CLASS_AUDIO10_FORMAT_IEEE_FLOAT 3 +#define UX_DEVICE_CLASS_AUDIO10_FORMAT_ALAW 4 +#define UX_DEVICE_CLASS_AUDIO10_FORMAT_MULAW 5 + + +/* Audio Class Control interface structures. */ + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_HEADER1_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bcdADC; + ULONG wTotalLength; + ULONG bInCollection; + ULONG baInterfaceNr1; +} UX_DEVICE_CLASS_AUDIO10_AC_HEADER1_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_HEADER2_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bcdADC; + ULONG wTotalLength; + ULONG bInCollection; + ULONG baInterfaceNr1; + ULONG baInterfaceNr2; +} UX_DEVICE_CLASS_AUDIO10_AC_HEADER2_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_HEADER3_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bcdADC; + ULONG wTotalLength; + ULONG bInCollection; + ULONG baInterfaceNr1; + ULONG baInterfaceNr2; + ULONG baInterfaceNr3; +} UX_DEVICE_CLASS_AUDIO10_AC_HEADER3_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_HEADER6_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bcdADC; + ULONG wTotalLength; + ULONG bInCollection; + ULONG baInterfaceNr1; + ULONG baInterfaceNr2; + ULONG baInterfaceNr3; + ULONG baInterfaceNr4; + ULONG baInterfaceNr5; + ULONG baInterfaceNr6; +} UX_DEVICE_CLASS_AUDIO10_AC_HEADER6_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_HEADER7_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bcdADC; + ULONG wTotalLength; + ULONG bInCollection; + ULONG baInterfaceNr1; + ULONG baInterfaceNr2; + ULONG baInterfaceNr3; + ULONG baInterfaceNr4; + ULONG baInterfaceNr5; + ULONG baInterfaceNr6; + ULONG baInterfaceNr7; +} UX_DEVICE_CLASS_AUDIO10_AC_HEADER7_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_HEADER8_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bcdADC; + ULONG wTotalLength; + ULONG bInCollection; + ULONG baInterfaceNr1; + ULONG baInterfaceNr2; + ULONG baInterfaceNr3; + ULONG baInterfaceNr4; + ULONG baInterfaceNr5; + ULONG baInterfaceNr6; + ULONG baInterfaceNr7; + ULONG baInterfaceNr8; +} UX_DEVICE_CLASS_AUDIO10_AC_HEADER8_DESCRIPTOR; + + +/* Define Audio Class specific input terminal interface descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_INPUT_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG bNrChannels; + ULONG wChannelConfig; + ULONG iChannelNames; + ULONG iTerminal; +} UX_DEVICE_CLASS_AUDIO10_AC_INPUT_TERMINAL_DESCRIPTOR; + + +/* Define Audio Class specific output terminal interface descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_OUTPUT_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG bSourceID; + ULONG iTerminal; +} UX_DEVICE_CLASS_AUDIO10_AC_OUTPUT_TERMINAL_DESCRIPTOR; + + +/* Define Audio Class specific feature unit descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO10_AC_FEATURE_UNIT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bControlSize; + ULONG bmaControls; +} UX_DEVICE_CLASS_AUDIO10_AC_FEATURE_UNIT_DESCRIPTOR; + + +/* Define Audio Class streaming interface descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO10_AS_INTERFACE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bTerminalLink; + ULONG bDelay; + ULONG wFormatTag; +} UX_DEVICE_CLASS_AUDIO10_AS_INTERFACE_DESCRIPTOR; + + +/* Define Audio Class type I format type descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO10_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bFormatType; + ULONG bNrChannels; + ULONG bSubframeSize; + ULONG bBitResolution; + ULONG bSamFreqType; + ULONG SamplingFrequency; +} UX_DEVICE_CLASS_AUDIO10_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR; + +#define UX_DEVICE_CLASS_AUDIO_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_FORMAT_TYPE 3 +#define UX_DEVICE_CLASS_AUDIO_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_NR_CHANNELS 4 +#define UX_DEVICE_CLASS_AUDIO_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_SUBFRAME_SIZE 5 +#define UX_DEVICE_CLASS_AUDIO_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_BIT_RESOLUTION 6 +#define UX_DEVICE_CLASS_AUDIO_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_SAM_FREQ_TYPE 7 +#define UX_DEVICE_CLASS_AUDIO_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_SAM_FREQ_TABLE 8 + + +/* Define Audio Class specific streaming endpoint descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO10_AS_ENDPOINT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bmAttributes; + ULONG wMaxPacketSize; + ULONG bInterval; + ULONG bRefresh; + ULONG bSynchAddress; +} UX_DEVICE_CLASS_AUDIO10_AS_ENDPOINT_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO10_AS_DATA_ENDPOINT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bmAttributes; + ULONG bLockDelayUnits; + ULONG wLockDelay; +} UX_DEVICE_CLASS_AUDIO10_AS_DATA_ENDPOINT_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO10_CONTROL_STRUCT +{ + ULONG ux_device_class_audio10_control_fu_id; + + ULONG ux_device_class_audio10_control_changed; + + USHORT ux_device_class_audio10_control_mute[1]; + SHORT ux_device_class_audio10_control_volume_min[1]; + SHORT ux_device_class_audio10_control_volume_max[1]; + SHORT ux_device_class_audio10_control_volume[1]; +} UX_DEVICE_CLASS_AUDIO10_CONTROL; + +#define UX_DEVICE_CLASS_AUDIO10_CONTROL_MUTE_CHANGED 1 +#define UX_DEVICE_CLASS_AUDIO10_CONTROL_VOLUME_CHANGED 2 + +typedef struct UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP_STRUCT +{ + ULONG ux_device_class_audio10_control_group_controls_nb; + UX_DEVICE_CLASS_AUDIO10_CONTROL *ux_device_class_audio10_control_group_controls; +} UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP; + +UINT _ux_device_class_audio10_control_process(UX_DEVICE_CLASS_AUDIO *audio, + UX_SLAVE_TRANSFER *transfer_request, + UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP *group); + +#define ux_device_class_audio10_control_process _ux_device_class_audio10_control_process + +#endif /* ifndef UX_DEVICE_CLASS_AUDIO10_H */ diff --git a/common/usbx_device_classes/inc/ux_device_class_audio20.h b/common/usbx_device_classes/inc/ux_device_class_audio20.h new file mode 100644 index 0000000..6da1b1b --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_audio20.h @@ -0,0 +1,418 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_audio20.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX audio class version 2.0. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_AUDIO20_H +#define UX_DEVICE_CLASS_AUDIO20_H + + +/* Define Audio Class function category codes. */ + +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_DESKTOP_SPEAKER 0x01 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_HOME_THEATER 0x02 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_MICROPHONE 0x03 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_HEADSET 0x04 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_TELEPHONE 0x05 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_CONVERTER 0x06 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_VOICE_SOUND_RECORDER 0x07 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_I_O_BOX 0x08 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_MUSICAL_INSTRUMENT 0x09 +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_PRO_AUDIO 0x0A +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_AUDIO_VIDEO 0x0B +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_CONTROL_PANEL 0x0C +#define UX_DEVICE_CLASS_AUDIO20_CATEGORY_OTHER 0xFF + + +/* Define Audio Class specific AC interface descriptor subclasses. */ + +#define UX_DEVICE_CLASS_AUDIO20_AC_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_AC_HEADER 0x01 +#define UX_DEVICE_CLASS_AUDIO20_AC_INPUT_TERMINAL 0x02 +#define UX_DEVICE_CLASS_AUDIO20_AC_OUTPUT_TERMINAL 0x03 +#define UX_DEVICE_CLASS_AUDIO20_AC_MIXER_UNIT 0x04 +#define UX_DEVICE_CLASS_AUDIO20_AC_SELECTOR_UNIT 0x05 +#define UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT 0x06 +#define UX_DEVICE_CLASS_AUDIO20_AC_EFFECT_UNIT 0x07 +#define UX_DEVICE_CLASS_AUDIO20_AC_PROCESSING_UNIT 0x08 +#define UX_DEVICE_CLASS_AUDIO20_AC_EXTENSION_UNIT 0x09 +#define UX_DEVICE_CLASS_AUDIO20_AC_CLOCK_SOURCE 0x0A +#define UX_DEVICE_CLASS_AUDIO20_AC_CLOCK_SELECTOR 0x0B +#define UX_DEVICE_CLASS_AUDIO20_AC_CLOCK_MULTIPLIER 0x0C +#define UX_DEVICE_CLASS_AUDIO20_AC_SAMPLE_RATE_CONVERTER 0x0D + + +/* Define Audio Class specific AS interface descriptor subclasses. */ + +#define UX_DEVICE_CLASS_AUDIO20_AS_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_AS_GENERAL 0x01 +#define UX_DEVICE_CLASS_AUDIO20_AS_FORMAT_TYPE 0x02 +#define UX_DEVICE_CLASS_AUDIO20_AS_ENCODER 0x03 +#define UX_DEVICE_CLASS_AUDIO20_AS_DECODER 0x04 + + +/* Define Audio Class specific endpoint descriptor subtypes. */ + +#define UX_DEVICE_CLASS_AUDIO20_EP_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_EP_GENERAL 0x01 + + +/* Define Audio Class specific request codes. */ + +#define UX_DEVICE_CLASS_AUDIO20_REQUEST_CODE_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_CUR 0x01 +#define UX_DEVICE_CLASS_AUDIO20_RANGE 0x02 +#define UX_DEVICE_CLASS_AUDIO20_MEM 0x03 + + +/* Define Audio Class specific clock source control selectors. */ + +#define UX_DEVICE_CLASS_AUDIO20_CS_CONTROL_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL 0x01 +#define UX_DEVICE_CLASS_AUDIO20_CS_CLOCK_VALID_CONTROL 0x02 + + +/* Define Audio Class specific clock selector control selectors. */ + + +#define UX_DEVICE_CLASS_AUDIO20_CX_CONTROL_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_CX_CLOCK_SELECTOR_CONTROL 0x01 + +/* Define Audio Class specific terminal control selectors. */ + +#define UX_DEVICE_CLASS_AUDIO20_TE_CONTROL_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_TE_COPY_PROTECT_CONTROL 0x01 +#define UX_DEVICE_CLASS_AUDIO20_TE_CONNECTOR_CONTROL 0x02 +#define UX_DEVICE_CLASS_AUDIO20_TE_OVERLOAD_CONTROL 0x03 +#define UX_DEVICE_CLASS_AUDIO20_TE_CLUSTER_CONTROL 0x04 +#define UX_DEVICE_CLASS_AUDIO20_TE_UNDERFLOW_CONTROL 0x05 +#define UX_DEVICE_CLASS_AUDIO20_TE_OVERFLOW_CONTROL 0x06 +#define UX_DEVICE_CLASS_AUDIO20_TE_LATENCY_CONTROL 0x07 + + +/* Define Audio Class specific feature unit control selectors. */ + +#define UX_DEVICE_CLASS_AUDIO20_FU_CONTROL_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_AUDIO20_FU_MUTE_CONTROL 0x01 +#define UX_DEVICE_CLASS_AUDIO20_FU_VOLUME_CONTROL 0x02 +#define UX_DEVICE_CLASS_AUDIO20_FU_BASS_CONTROL 0x03 +#define UX_DEVICE_CLASS_AUDIO20_FU_MID_CONTROL 0x04 +#define UX_DEVICE_CLASS_AUDIO20_FU_TREBLE_CONTROL 0x05 +#define UX_DEVICE_CLASS_AUDIO20_FU_GRAPHIC_EQUALIZER_CONTROL 0x06 +#define UX_DEVICE_CLASS_AUDIO20_FU_AUTOMATIC_GAIN_CONTROL 0x07 +#define UX_DEVICE_CLASS_AUDIO20_FU_DELAY_CONTROL 0x08 +#define UX_DEVICE_CLASS_AUDIO20_FU_BASS_BOOST_CONTROL 0x09 +#define UX_DEVICE_CLASS_AUDIO20_FU_LOUNDNESS_CONTROL 0x0A +#define UX_DEVICE_CLASS_AUDIO20_FU_INPUT_GAIN_CONTROL 0x0B +#define UX_DEVICE_CLASS_AUDIO20_FU_INPUT_GAIN_PAD_CONTROL 0x0C +#define UX_DEVICE_CLASS_AUDIO20_FU_PHASE_INVERTER_CONTROL 0x0D +#define UX_DEVICE_CLASS_AUDIO20_FU_UNDERFLOW_CONTROL 0x0E +#define UX_DEVICE_CLASS_AUDIO20_FU_OVERFLOW_CONTROL 0x0F +#define UX_DEVICE_CLASS_AUDIO20_FU_LATENCY_CONTROL 0x10 + + +/* Define Audio Class encoding format type bit allocations. */ + +#define UX_DEVICE_CLASS_AUDIO20_FORMAT_PCM (1u << 0) +#define UX_DEVICE_CLASS_AUDIO20_FORMAT_PCM8 (1u << 1) +#define UX_DEVICE_CLASS_AUDIO20_FORMAT_IEEE_FLOAT (1u << 2) +#define UX_DEVICE_CLASS_AUDIO20_FORMAT_ALAW (1u << 3) +#define UX_DEVICE_CLASS_AUDIO20_FORMAT_MULAW (1u << 4) +#define UX_DEVICE_CLASS_AUDIO20_FORMAT_RAW (1u << 31) + + +/* Audio Class Control header descriptor structures. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_HEADER_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bcdADC; + ULONG bCategory; + ULONG wTotalLength; + ULONG bmControls; +} UX_DEVICE_CLASS_AUDIO20_AC_HEADER_DESCRIPTOR; + + +/* Define Audio Class specific clock source descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_CLOCK_SOURCE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bClockID; + ULONG bmAttributes; + ULONG bmControls; + ULONG bAssocTerminal; + ULONG iClockSource; +} UX_DEVICE_CLASS_AUDIO20_AC_CLOCK_SOURCE_DESCRIPTOR; + + +/* Define Audio Class specific input terminal interface descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_INPUT_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG bCSourceID; + ULONG bNrChannels; + ULONG bmChannelConfig; + ULONG iChannelNames; + ULONG bmControls; + ULONG iTerminal; +} UX_DEVICE_CLASS_AUDIO20_AC_INPUT_TERMINAL_DESCRIPTOR; + + +/* Define Audio Class specific output terminal interface descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_OUTPUT_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG bSourceID; + ULONG bCSourceID; + ULONG bmControls; + ULONG iTerminal; +} UX_DEVICE_CLASS_AUDIO20_AC_OUTPUT_TERMINAL_DESCRIPTOR; + + +/* Define Audio Class specific feature unit descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bmaControls; +} UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT1_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bmaControls0; + ULONG bmaControls1; +} UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT1_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT2_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bmaControls0; + ULONG bmaControls1; + ULONG bmaControls2; +} UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT2_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT3_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bmaControls0; + ULONG bmaControls1; + ULONG bmaControls2; + ULONG bmaControls3; +} UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT3_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT6_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bmaControls0; + ULONG bmaControls1; + ULONG bmaControls2; + ULONG bmaControls3; + ULONG bmaControls4; + ULONG bmaControls5; + ULONG bmaControls6; +} UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT6_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT7_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bmaControls0; + ULONG bmaControls1; + ULONG bmaControls2; + ULONG bmaControls3; + ULONG bmaControls4; + ULONG bmaControls5; + ULONG bmaControls6; + ULONG bmaControls7; +} UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT7_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT8_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bmaControls0; + ULONG bmaControls1; + ULONG bmaControls2; + ULONG bmaControls3; + ULONG bmaControls4; + ULONG bmaControls5; + ULONG bmaControls6; + ULONG bmaControls7; + ULONG bmaControls8; +} UX_DEVICE_CLASS_AUDIO20_AC_FEATURE_UNIT8_DESCRIPTOR; + + +/* Define Audio Class streaming interface descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AS_INTERFACE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bTerminalLink; + ULONG bmControls; + ULONG bFormatType; + ULONG bmFormats; + ULONG bNrChannels; + ULONG bmChannelConfig; + ULONG iChannelNames; +} UX_DEVICE_CLASS_AUDIO20_AS_INTERFACE_DESCRIPTOR; + + +/* Define Audio Class type I format type descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bFormatType; + ULONG bSubslotSize; + ULONG bBitResolution; +} UX_DEVICE_CLASS_AUDIO20_AS_TYPE_I_FORMAT_TYPE_DESCRIPTOR; + + +/* Define Audio Class specific streaming endpoint descriptor. */ + +typedef struct UX_DEVICE_CLASS_AUDIO20_AS_DATA_ENDPOINT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bmAttributes; + ULONG bmControls; + ULONG bLockDelayUnits; + ULONG wLockDelay; +} UX_DEVICE_CLASS_AUDIO20_AS_DATA_ENDPOINT_DESCRIPTOR; + +typedef struct UX_DEVICE_CLASS_AUDIO20_CONTROL_STRUCT +{ + ULONG ux_device_class_audio20_control_cs_id; + ULONG ux_device_class_audio20_control_fu_id; + ULONG ux_device_class_audio20_control_sampling_frequency; + + ULONG ux_device_class_audio20_control_changed; + + USHORT ux_device_class_audio20_control_mute[1]; + SHORT ux_device_class_audio20_control_volume_min[1]; + SHORT ux_device_class_audio20_control_volume_max[1]; + SHORT ux_device_class_audio20_control_volume[1]; +} UX_DEVICE_CLASS_AUDIO20_CONTROL; + +#define UX_DEVICE_CLASS_AUDIO20_CONTROL_MUTE_CHANGED 1 +#define UX_DEVICE_CLASS_AUDIO20_CONTROL_VOLUME_CHANGED 2 + +typedef struct UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP_STRUCT +{ + ULONG ux_device_class_audio20_control_group_controls_nb; + UX_DEVICE_CLASS_AUDIO20_CONTROL *ux_device_class_audio20_control_group_controls; +} UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP; + + +UINT _ux_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO *audio, + UX_SLAVE_TRANSFER *transfer, + UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP *group); + +#define ux_device_class_audio20_control_process _ux_device_class_audio20_control_process + +#endif /* ifndef UX_DEVICE_CLASS_AUDIO20_H */ diff --git a/common/usbx_device_classes/inc/ux_device_class_cdc_acm.h b/common/usbx_device_classes/inc/ux_device_class_cdc_acm.h new file mode 100644 index 0000000..43a6703 --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_cdc_acm.h @@ -0,0 +1,259 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_cdc_acm.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the equivalences for the USBX Device Class CDC */ +/* ACM component. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_CDC_ACM_H +#define UX_DEVICE_CLASS_CDC_ACM_H + + +/* Define CDC Class USB Class constants. */ +#define UX_SLAVE_CLASS_CDC_ACM_CLASS 10 + +/* Device CDC Requests */ +#define UX_SLAVE_CLASS_CDC_ACM_SEND_ENCAPSULATED_COMMAND 0x00 +#define UX_SLAVE_CLASS_CDC_ACM_GET_ENCAPSULATED_RESPONSE 0x01 +#define UX_SLAVE_CLASS_CDC_ACM_SET_COMM_FEATURE 0x02 +#define UX_SLAVE_CLASS_CDC_ACM_GET_COMM_FEATURE 0x03 +#define UX_SLAVE_CLASS_CDC_ACM_CLEAR_COMM_FEATURE 0x04 +#define UX_SLAVE_CLASS_CDC_ACM_SET_AUX_LINE_STATE 0x10 +#define UX_SLAVE_CLASS_CDC_ACM_SET_HOOK_STATE 0x11 +#define UX_SLAVE_CLASS_CDC_ACM_PULSE_SETUP 0x12 +#define UX_SLAVE_CLASS_CDC_ACM_SEND_PULSE 0x13 +#define UX_SLAVE_CLASS_CDC_ACM_SET_PULSE_TIME 0x14 +#define UX_SLAVE_CLASS_CDC_ACM_RING_AUX_JACK 0x15 +#define UX_SLAVE_CLASS_CDC_ACM_SET_LINE_CODING 0x20 +#define UX_SLAVE_CLASS_CDC_ACM_GET_LINE_CODING 0x21 +#define UX_SLAVE_CLASS_CDC_ACM_SET_CONTROL_LINE_STATE 0x22 +#define UX_SLAVE_CLASS_CDC_ACM_SEND_BREAK 0x23 +#define UX_SLAVE_CLASS_CDC_ACM_SET_RINGER_PARMS 0x30 +#define UX_SLAVE_CLASS_CDC_ACM_GET_RINGER_PARMS 0x31 +#define UX_SLAVE_CLASS_CDC_ACM_SET_OPERATION_PARMS 0x32 +#define UX_SLAVE_CLASS_CDC_ACM_GET_OPERATION_PARMS 0x33 +#define UX_SLAVE_CLASS_CDC_ACM_SET_LINE_PARMS 0x34 +#define UX_SLAVE_CLASS_CDC_ACM_GET_LINE_PARMS 0x35 +#define UX_SLAVE_CLASS_CDC_ACM_DIAL_DIGITS 0x36 +#define UX_SLAVE_CLASS_CDC_ACM_SET_UNIT_PARAMETER 0x37 +#define UX_SLAVE_CLASS_CDC_ACM_GET_UNIT_PARAMETER 0x38 +#define UX_SLAVE_CLASS_CDC_ACM_CLEAR_UNIT_PARAMETER 0x39 +#define UX_SLAVE_CLASS_CDC_ACM_GET_PROFILE 0x3A +#define UX_SLAVE_CLASS_CDC_ACM_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define UX_SLAVE_CLASS_CDC_ACM_SET_ETHERNET_POWER_MANAGEMENT_PATTERN 0x41 +#define UX_SLAVE_CLASS_CDC_ACM_GET_ETHERNET_POWER_MANAGEMENT_PATTERN 0x42 +#define UX_SLAVE_CLASS_CDC_ACM_SET_ETHERNET_PACKET_FILTER 0x43 +#define UX_SLAVE_CLASS_CDC_ACM_GET_ETHERNET_STATISTIC 0x44 +#define UX_SLAVE_CLASS_CDC_ACM_SET_ATM_DATA_FORMAT 0x50 +#define UX_SLAVE_CLASS_CDC_ACM_GET_ATM_DEVICE_STATISTICS 0x51 +#define UX_SLAVE_CLASS_CDC_ACM_SET_ATM_DEFAULT_VC 0x52 +#define UX_SLAVE_CLASS_CDC_ACM_GET_ATM_VC_STATISTICS 0x53 + +/* Default line coding values. */ +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_BAUDRATE 115200 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_STOP_BIT 1 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARITY 0 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_DATA_BIT 8 + +/* Define line coding structure. */ +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_BAUDRATE_STRUCT 0 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_STRUCT 4 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARITY_STRUCT 5 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_DATA_BIT_STRUCT 6 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_RESPONSE_SIZE 7 + +/* Define line state bits. */ +#define UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_DTR 1 +#define UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_RTS 2 + +/* Define Transfer direction bits. */ +#define UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_XMIT 1 +#define UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_RCV 2 + +/* Define IOCTL functions. */ +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING 1 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING 2 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_STATE 3 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_ABORT_PIPE 4 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE 5 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START 6 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_STOP 7 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_READ_TIMEOUT 8 +#define UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_WRITE_TIMEOUT 9 + +/* Define event group flag. */ +#define UX_DEVICE_CLASS_CDC_ACM_WRITE_EVENT 1 + +/* Define Slave CDC Class Calling Parameter structure */ + +typedef struct UX_SLAVE_CLASS_CDC_ACM_PARAMETER_STRUCT +{ + VOID (*ux_slave_class_cdc_acm_instance_activate)(VOID *); + VOID (*ux_slave_class_cdc_acm_instance_deactivate)(VOID *); + VOID (*ux_slave_class_cdc_acm_parameter_change)(VOID *); + +} UX_SLAVE_CLASS_CDC_ACM_PARAMETER; + +/* Define CDC Class structure. */ + +typedef struct UX_SLAVE_CLASS_CDC_ACM_STRUCT +{ + UX_SLAVE_INTERFACE *ux_slave_class_cdc_acm_interface; + UX_SLAVE_CLASS_CDC_ACM_PARAMETER ux_slave_class_cdc_acm_parameter; + TX_MUTEX ux_slave_class_cdc_acm_endpoint_in_mutex; + TX_MUTEX ux_slave_class_cdc_acm_endpoint_out_mutex; + ULONG ux_slave_class_cdc_acm_baudrate; + UCHAR ux_slave_class_cdc_acm_stop_bit; + UCHAR ux_slave_class_cdc_acm_parity; + UCHAR ux_slave_class_cdc_acm_data_bit; + UCHAR ux_slave_class_cdc_acm_data_dtr_state; + UCHAR ux_slave_class_cdc_acm_data_rts_state; + TX_THREAD ux_slave_class_cdc_acm_bulkin_thread; + TX_THREAD ux_slave_class_cdc_acm_bulkout_thread; + UCHAR *ux_slave_class_cdc_acm_bulkin_thread_stack; + UCHAR *ux_slave_class_cdc_acm_bulkout_thread_stack; + UINT (*ux_device_class_cdc_acm_write_callback)(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, ULONG length); + UINT (*ux_device_class_cdc_acm_read_callback)(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, UCHAR *data_pointer, ULONG length); + TX_EVENT_FLAGS_GROUP ux_slave_class_cdc_acm_event_flags_group; + ULONG ux_slave_class_cdc_acm_transmission_status; + ULONG ux_slave_class_cdc_acm_scheduled_write; + ULONG ux_slave_class_cdc_acm_callback_total_length; + UCHAR *ux_slave_class_cdc_acm_callback_data_pointer; + UCHAR *ux_slave_class_cdc_acm_callback_current_data_pointer; + +} UX_SLAVE_CLASS_CDC_ACM; + +/* Define some CDC Class structures */ + +typedef struct UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER_STRUCT +{ + ULONG ux_slave_class_cdc_acm_parameter_baudrate; + UCHAR ux_slave_class_cdc_acm_parameter_stop_bit; + UCHAR ux_slave_class_cdc_acm_parameter_parity; + UCHAR ux_slave_class_cdc_acm_parameter_data_bit; + +} UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER; + +typedef struct UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER_STRUCT +{ + UCHAR ux_slave_class_cdc_acm_parameter_rts; + UCHAR ux_slave_class_cdc_acm_parameter_dtr; + +} UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER; + +typedef struct UX_SLAVE_CLASS_CDC_ACM_CALLBACK_PARAMETER_STRUCT +{ + UINT (*ux_device_class_cdc_acm_parameter_write_callback)(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, ULONG length); + UINT (*ux_device_class_cdc_acm_parameter_read_callback)(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, UCHAR *data_pointer, ULONG length); + +} UX_SLAVE_CLASS_CDC_ACM_CALLBACK_PARAMETER; + + + +/* Requests - Ethernet Networking Control Model */ + +#define UX_SLAVE_CLASS_CDC_ACM_SEND_ENCAPSULATED_COMMAND 0x00 + /* Issues a command in the format of the supported control + protocol. The intent of this mechanism is to support + networking devices (e.g., host-based cable modems) + that require an additional vendor-defined interface for + media specific hardware configuration and + management. */ +#define UX_SLAVE_CLASS_CDC_ACM_GET_ENCAPSULATED_RESPONSE 0x01 + /* Requests a response in the format of the supported + control protocol. */ +#define UX_SLAVE_CLASS_CDC_ACM_SET_ETHERNET_MULTICAST_FILTERS 0x40 + /* As applications are loaded and unloaded on the host, + the networking transport will instruct the device's MAC + driver to change settings of the Networking device's + multicast filters. */ +#define UX_SLAVE_CLASS_CDC_ACM_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x41 + /* Some hosts are able to conserve energy and stay quiet + in a 'sleeping' state while not being used. USB + Networking devices may provide special pattern filtering + hardware that enables it to wake up the attached host + on demand when something is attempting to contact the + host (e.g., an incoming web browser connection). + Primitives are needed in management plane to negotiate + the setting of these special filters */ +#define UX_SLAVE_CLASS_CDC_ACM_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER 0x42 + /* Retrieves the status of the above power management + pattern filter setting */ +#define UX_SLAVE_CLASS_CDC_ACM_SET_ETHERNET_PACKET_FILTER 0x43 + /* Sets device filter for running a network analyzer + application on the host machine */ +#define UX_SLAVE_CLASS_CDC_ACM_GET_ETHERNET_STATISTIC 0x44 + /* Retrieves Ethernet device statistics such as frames + transmitted, frames received, and bad frames received. */ + +/* Define buffer length for IN/OUT pipes. */ + +#define UX_SLAVE_CLASS_CDC_ACM_BUFFER_SIZE 4096 + + +/* Define Device CDC Class prototypes. */ + +UINT _ux_device_class_cdc_acm_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_acm_control_request(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_acm_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_acm_entry(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_acm_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_acm_uninitialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_acm_write(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_device_class_cdc_acm_read(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_device_class_cdc_acm_ioctl(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, ULONG ioctl_function, + VOID *parameter); +VOID _ux_device_class_cdc_acm_bulkin_thread(ULONG class_pointer); +VOID _ux_device_class_cdc_acm_bulkout_thread(ULONG class_pointer); +UINT _ux_device_class_cdc_acm_write_with_callback(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, + ULONG requested_length); + + +/* Define Device CDC Class API prototypes. */ + +#define ux_device_class_cdc_acm_entry _ux_device_class_cdc_acm_entry +#define ux_device_class_cdc_acm_read _ux_device_class_cdc_acm_read +#define ux_device_class_cdc_acm_write _ux_device_class_cdc_acm_write +#define ux_device_class_cdc_acm_ioctl _ux_device_class_cdc_acm_ioctl +#define ux_device_class_cdc_acm_write_with_callback _ux_device_class_cdc_acm_write_with_callback + +#endif /* UX_DEVICE_CLASS_CDC_ACM_H */ diff --git a/common/usbx_device_classes/inc/ux_device_class_cdc_ecm.h b/common/usbx_device_classes/inc/ux_device_class_cdc_ecm.h new file mode 100644 index 0000000..df6dee3 --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_cdc_ecm.h @@ -0,0 +1,337 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_cdc_ecm.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the equivalences for the USBX Device Class */ +/* CDC_ECM component. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_CDC_ECM_H +#define UX_DEVICE_CLASS_CDC_ECM_H + +#include "nx_api.h" +#include "ux_network_driver.h" + +/* Define generic CDC_ECM equivalences. */ +#define UX_DEVICE_CLASS_CDC_ECM_CLASS_COMMUNICATION_CONTROL 0x02 +#define UX_DEVICE_CLASS_CDC_ECM_SUBCLASS_COMMUNICATION_CONTROL 0x06 +#define UX_DEVICE_CLASS_CDC_ECM_CLASS_COMMUNICATION_DATA 0x0A +#define UX_DEVICE_CLASS_CDC_ECM_NEW_INTERRUPT_EVENT 0x01 +#define UX_DEVICE_CLASS_CDC_ECM_NEW_BULKOUT_EVENT 0x02 +#define UX_DEVICE_CLASS_CDC_ECM_NEW_BULKIN_EVENT 0x04 +#define UX_DEVICE_CLASS_CDC_ECM_NEW_DEVICE_STATE_CHANGE_EVENT 0x08 +#define UX_DEVICE_CLASS_CDC_ECM_NETWORK_NOTIFICATION_EVENT 0x10 +#define UX_DEVICE_CLASS_CDC_ECM_INTERRUPT_RESPONSE_LENGTH 8 +#define UX_DEVICE_CLASS_CDC_ECM_MAX_CONTROL_RESPONSE_LENGTH 256 +#define UX_DEVICE_CLASS_CDC_ECM_INTERRUPT_RESPONSE_AVAILABLE_FLAG 1 +#define UX_DEVICE_CLASS_CDC_ECM_BASE_IP_ADDRESS 0xC0A80001 +#define UX_DEVICE_CLASS_CDC_ECM_BASE_IP_MASK 0xFFFFFF00 +#define UX_DEVICE_CLASS_CDC_ECM_MAX_MTU 1518 +#define UX_DEVICE_CLASS_CDC_ECM_ETHERNET_IP 0x0800 +#define UX_DEVICE_CLASS_CDC_ECM_ETHERNET_ARP 0x0806 +#define UX_DEVICE_CLASS_CDC_ECM_ETHERNET_RARP 0x8035 +#define UX_DEVICE_CLASS_CDC_ECM_ETHERNET_PACKET_SIZE 1536 +#define UX_DEVICE_CLASS_CDC_ECM_NX_ALIGN_PADDING 2 +#ifndef UX_DEVICE_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES +#define UX_DEVICE_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES 16 +#endif + +#define UX_DEVICE_CLASS_CDC_ECM_NX_PACKET_SIZE sizeof(NX_PACKET) + +#define UX_DEVICE_CLASS_CDC_ECM_NX_PAYLOAD_SIZE_ASSERT UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG(UX_DEVICE_CLASS_CDC_ECM_ETHERNET_PACKET_SIZE, UX_DEVICE_CLASS_CDC_ECM_NX_ALIGN_PADDING), UX_DEVICE_CLASS_CDC_ECM_NX_PAYLOAD_SIZE_calc_ovf) +#define UX_DEVICE_CLASS_CDC_ECM_NX_PAYLOAD_SIZE (UX_DEVICE_CLASS_CDC_ECM_ETHERNET_PACKET_SIZE + UX_DEVICE_CLASS_CDC_ECM_NX_ALIGN_PADDING) + +#define UX_DEVICE_CLASS_CDC_ECM_NX_BUFF_SIZE_ASSERT \ + UX_DEVICE_CLASS_CDC_ECM_NX_PAYLOAD_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_DEVICE_CLASS_CDC_ECM_NX_PAYLOAD_SIZE, \ + UX_DEVICE_CLASS_CDC_ECM_NX_PACKET_SIZE), \ + UX_DEVICE_CLASS_CDC_ECM_NX_BUFF_SIZE_calc_ovf) +#define UX_DEVICE_CLASS_CDC_ECM_NX_BUFF_SIZE (UX_DEVICE_CLASS_CDC_ECM_NX_PAYLOAD_SIZE + UX_DEVICE_CLASS_CDC_ECM_NX_PACKET_SIZE) + +#define UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT \ + UX_DEVICE_CLASS_CDC_ECM_NX_BUFF_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG( \ + UX_DEVICE_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES, \ + UX_DEVICE_CLASS_CDC_ECM_NX_BUFF_SIZE), \ + UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_calc1_ovf) \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_DEVICE_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES * \ + UX_DEVICE_CLASS_CDC_ECM_NX_BUFF_SIZE, \ + 32), UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_calc2_ovf) +#define UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE (UX_DEVICE_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES * UX_DEVICE_CLASS_CDC_ECM_NX_BUFF_SIZE + 32) + +#define UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE 14 +#define UX_DEVICE_CLASS_CDC_ECM_NODE_ID_LENGTH 6 +#define UX_DEVICE_CLASS_CDC_ECM_VENDOR_DESCRIPTION_MAX_LENGTH 64 +#define UX_DEVICE_CLASS_CDC_ECM_MAC_OPTIONS 8 +#define UX_DEVICE_CLASS_CDC_ECM_PACKET_HEADER_MSG 1 + +/* Device CDC_ECM Requests */ +#define UX_DEVICE_CLASS_CDC_ECM_SEND_ENCAPSULATED_COMMAND 0x00 +#define UX_DEVICE_CLASS_CDC_ECM_GET_ENCAPSULATED_RESPONSE 0x01 +#define UX_DEVICE_CLASS_CDC_ECM_SET_ETHERNET_MULTICAST_FILTER 0x40 +#define UX_DEVICE_CLASS_CDC_ECM_SET_ETHERNET_POWER_MANAGEMENT_FILTER 0x41 +#define UX_DEVICE_CLASS_CDC_ECM_GET_ETHERNET_POWER_MANAGEMENT_FILTER 0x42 +#define UX_DEVICE_CLASS_CDC_ECM_SET_ETHERNET_PACKET_FILTER 0x43 + +/* Define CDC_ECM Versions. Set to 1.0 here. */ +#define UX_DEVICE_CLASS_CDC_ECM_VERSION_MAJOR 0x00000001 +#define UX_DEVICE_CLASS_CDC_ECM_VERSION_MINOR 0x00000000 + +/* Define CDC_ECM Conection type supported. Set to conectionless. */ +#define UX_DEVICE_CLASS_CDC_ECM_DF_CONNECTIONLESS 0x00000001 +#define UX_DEVICE_CLASS_CDC_ECM_DF_CONNECTION_ORIENTED 0x00000002 +#define UX_DEVICE_CLASS_CDC_ECM_DF_CONNECTION_SUPPORTED UX_DEVICE_CLASS_CDC_ECM_DF_CONNECTIONLESS + +/* Define CDC_ECM Medium supported by the device. */ +#define UX_DEVICE_CLASS_CDC_ECM_MEDIUM_SUPPORTED 0x00000000 + +/* Define CDC_ECM Packet size and types supported. */ +#define UX_DEVICE_CLASS_CDC_ECM_MAX_PACKET_PER_TRANSFER 0x00000001 +#define UX_DEVICE_CLASS_CDC_ECM_MAX_PACKET_TRANSFER_SIZE 0x00000640 +#define UX_DEVICE_CLASS_CDC_ECM_PACKET_ALIGNEMENT_FACTOR 0x00000003 +#define UX_DEVICE_CLASS_CDC_ECM_MAX_FRAME_SIZE 0x000005DC +#define UX_DEVICE_CLASS_CDC_ECM_MAX_PACKET_LENGTH 0x000005EA + +/* Define LINK speeds. */ +#define UX_DEVICE_CLASS_CDC_ECM_LINK_SPEED_FS 0x0001D4C0 + +/* Define LINK statess. */ +#define UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_DOWN 0 +#define UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP 1 +#define UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_PENDING_UP 2 +#define UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_PENDING_DOWN 3 + +/* Define media connection values. */ +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_CONNECTED 0x00000000 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_DISCONNECTED 0x00000001 + +/* Define media supported values. */ +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_802_3 0x00000000 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_802_5 0x00000001 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_FDDI 0x00000002 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_WAN 0x00000003 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_LOCAL_TALK 0x00000004 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_DIX 0x00000005 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_ARCNET_RAW 0x00000006 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_ARCNET_878_2 0x00000007 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_ATM 0x00000008 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_WIRELESS_WAN 0x00000009 +#define UX_DEVICE_CLASS_CDC_ECM_MEDIA_IRDA 0x0000000A + +/* Define CDC_ECM status values. */ +#define UX_DEVICE_CLASS_CDC_ECM_STATUS_SUCCESS 0x00000000 +#define UX_DEVICE_CLASS_CDC_ECM_STATUS_FAILURE 0xC0000001 +#define UX_DEVICE_CLASS_CDC_ECM_STATUS_INVALID_DATA 0xC0010015 +#define UX_DEVICE_CLASS_CDC_ECM_STATUS_NOT_SUPPORTED 0xC00000BB +#define UX_DEVICE_CLASS_CDC_ECM_STATUS_MEDIA_CONNECTED 0x4001000B +#define UX_DEVICE_CLASS_CDC_ECM_STATUS_MEDIA_DISCONNECT 0x4001000C + +/* Define CDC_ECM Control Messages values. */ + +/* Define CDC_ECM State machine. */ +#define UX_DEVICE_CLASS_CDC_ECM_STATE_UNINITIALIZED 0x00000000 +#define UX_DEVICE_CLASS_CDC_ECM_STATE_INITIALIZED 0x00000001 +#define UX_DEVICE_CLASS_CDC_ECM_STATE_DATA_INITIALIZED 0x00000002 + +/* Define NetX errors inside the CDC_ECM class. */ +#define UX_DEVICE_CLASS_CDC_ECM_NX_SUCCESS 0x00 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NO_PACKET 0x01 +#define UX_DEVICE_CLASS_CDC_ECM_NX_UNDERFLOW 0x02 +#define UX_DEVICE_CLASS_CDC_ECM_NX_OVERFLOW 0x03 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NO_MAPPING 0x04 +#define UX_DEVICE_CLASS_CDC_ECM_NX_DELETED 0x05 +#define UX_DEVICE_CLASS_CDC_ECM_NX_POOL_ERROR 0x06 +#define UX_DEVICE_CLASS_CDC_ECM_NX_PTR_ERROR 0x07 +#define UX_DEVICE_CLASS_CDC_ECM_NX_WAIT_ERROR 0x08 +#define UX_DEVICE_CLASS_CDC_ECM_NX_SIZE_ERROR 0x09 +#define UX_DEVICE_CLASS_CDC_ECM_NX_OPTION_ERROR 0x0a +#define UX_DEVICE_CLASS_CDC_ECM_NX_DELETE_ERROR 0x10 +#define UX_DEVICE_CLASS_CDC_ECM_NX_CALLER_ERROR 0x11 +#define UX_DEVICE_CLASS_CDC_ECM_NX_INVALID_PACKET 0x12 +#define UX_DEVICE_CLASS_CDC_ECM_NX_INVALID_SOCKET 0x13 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_ENABLED 0x14 +#define UX_DEVICE_CLASS_CDC_ECM_NX_ALREADY_ENABLED 0x15 +#define UX_DEVICE_CLASS_CDC_ECM_NX_ENTRY_NOT_FOUND 0x16 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NO_MORE_ENTRIES 0x17 +#define UX_DEVICE_CLASS_CDC_ECM_NX_ARP_TIMER_ERROR 0x18 +#define UX_DEVICE_CLASS_CDC_ECM_NX_RESERVED_CODE0 0x19 +#define UX_DEVICE_CLASS_CDC_ECM_NX_WAIT_ABORTED 0x1A +#define UX_DEVICE_CLASS_CDC_ECM_NX_IP_INTERNAL_ERROR 0x20 +#define UX_DEVICE_CLASS_CDC_ECM_NX_IP_ADDRESS_ERROR 0x21 +#define UX_DEVICE_CLASS_CDC_ECM_NX_ALREADY_BOUND 0x22 +#define UX_DEVICE_CLASS_CDC_ECM_NX_PORT_UNAVAILABLE 0x23 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_BOUND 0x24 +#define UX_DEVICE_CLASS_CDC_ECM_NX_RESERVED_CODE1 0x25 +#define UX_DEVICE_CLASS_CDC_ECM_NX_SOCKET_UNBOUND 0x26 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_CREATED 0x27 +#define UX_DEVICE_CLASS_CDC_ECM_NX_SOCKETS_BOUND 0x28 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NO_RESPONSE 0x29 +#define UX_DEVICE_CLASS_CDC_ECM_NX_POOL_DELETED 0x30 +#define UX_DEVICE_CLASS_CDC_ECM_NX_ALREADY_RELEASED 0x31 +#define UX_DEVICE_CLASS_CDC_ECM_NX_RESERVED_CODE2 0x32 +#define UX_DEVICE_CLASS_CDC_ECM_NX_MAX_LISTEN 0x33 +#define UX_DEVICE_CLASS_CDC_ECM_NX_DUPLICATE_LISTEN 0x34 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_CLOSED 0x35 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_LISTEN_STATE 0x36 +#define UX_DEVICE_CLASS_CDC_ECM_NX_IN_PROGRESS 0x37 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_CONNECTED 0x38 +#define UX_DEVICE_CLASS_CDC_ECM_NX_WINDOW_OVERFLOW 0x39 +#define UX_DEVICE_CLASS_CDC_ECM_NX_ALREADY_SUSPENDED 0x40 +#define UX_DEVICE_CLASS_CDC_ECM_NX_DISCONNECT_FAILED 0x41 +#define UX_DEVICE_CLASS_CDC_ECM_NX_STILL_BOUND 0x42 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_SUCCESSFUL 0x43 +#define UX_DEVICE_CLASS_CDC_ECM_NX_UNHANDLED_COMMAND 0x44 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NO_FREE_PORTS 0x45 +#define UX_DEVICE_CLASS_CDC_ECM_NX_INVALID_PORT 0x46 +#define UX_DEVICE_CLASS_CDC_ECM_NX_INVALID_RELISTEN 0x47 +#define UX_DEVICE_CLASS_CDC_ECM_NX_CONNECTION_PENDING 0x48 +#define UX_DEVICE_CLASS_CDC_ECM_NX_TX_QUEUE_DEPTH 0x49 +#define UX_DEVICE_CLASS_CDC_ECM_NX_NOT_IMPLEMENTED 0x80 + +/* Define timeout packet allocation value. */ +#ifndef UX_DEVICE_CLASS_CDC_ECM_PACKET_POOL_WAIT +#define UX_DEVICE_CLASS_CDC_ECM_PACKET_POOL_WAIT 1000 +#endif + +/* Define Slave CDC_ECM Class Calling Parameter structure */ + +typedef struct UX_SLAVE_CLASS_CDC_ECM_PARAMETER_STRUCT +{ + VOID (*ux_slave_class_cdc_ecm_instance_activate)(VOID *); + VOID (*ux_slave_class_cdc_ecm_instance_deactivate)(VOID *); + ULONG ux_slave_class_cdc_ecm_parameter_media; + ULONG ux_slave_class_cdc_ecm_parameter_vendor_id; + ULONG ux_slave_class_cdc_ecm_parameter_driver_version; + UCHAR ux_slave_class_cdc_ecm_parameter_vendor_description[UX_DEVICE_CLASS_CDC_ECM_VENDOR_DESCRIPTION_MAX_LENGTH]; + UCHAR ux_slave_class_cdc_ecm_parameter_local_node_id[UX_DEVICE_CLASS_CDC_ECM_NODE_ID_LENGTH]; + UCHAR ux_slave_class_cdc_ecm_parameter_remote_node_id[UX_DEVICE_CLASS_CDC_ECM_NODE_ID_LENGTH]; +} UX_SLAVE_CLASS_CDC_ECM_PARAMETER; + +/* Define CDC_ECM Class structure. */ + +typedef struct UX_SLAVE_CLASS_CDC_ECM_STRUCT +{ + UX_SLAVE_INTERFACE *ux_slave_class_cdc_ecm_interface; + UX_SLAVE_CLASS_CDC_ECM_PARAMETER ux_slave_class_cdc_ecm_parameter; + UX_SLAVE_ENDPOINT *ux_slave_class_cdc_ecm_bulkin_endpoint; + UX_SLAVE_ENDPOINT *ux_slave_class_cdc_ecm_bulkout_endpoint; + UX_SLAVE_ENDPOINT *ux_slave_class_cdc_ecm_interrupt_endpoint; + ULONG ux_slave_class_cdc_ecm_state; + ULONG ux_slave_class_cdc_ecm_current_alternate_setting; + ULONG ux_slave_class_cdc_ecm_max_transfer_size; + ULONG ux_slave_class_cdc_ecm_request_id; + ULONG ux_slave_class_cdc_ecm_statistics_xmit_ok; + ULONG ux_slave_class_cdc_ecm_statistics_rcv_ok; + ULONG ux_slave_class_cdc_ecm_statistics_xmit_error; + ULONG ux_slave_class_cdc_ecm_statistics_rcv_error; + ULONG ux_slave_class_cdc_ecm_statistics_rcv_no_buffer; + ULONG ux_slave_class_cdc_ecm_statistics_rcv_error_alignment; + ULONG ux_slave_class_cdc_ecm_statistics_xmit_one_collision; + ULONG ux_slave_class_cdc_ecm_statistics_xmit_more_collisions; + ULONG ux_slave_class_cdc_ecm_ethernet_multicast_filter; + ULONG ux_slave_class_cdc_ecm_ethernet_power_management_filter; + ULONG ux_slave_class_cdc_ecm_ethernet_packet_filter; + TX_EVENT_FLAGS_GROUP ux_slave_class_cdc_ecm_event_flags_group; + UCHAR ux_slave_class_cdc_ecm_local_node_id[UX_DEVICE_CLASS_CDC_ECM_NODE_ID_LENGTH]; + UCHAR ux_slave_class_cdc_ecm_remote_node_id[UX_DEVICE_CLASS_CDC_ECM_NODE_ID_LENGTH]; + NX_IP *ux_slave_class_cdc_ecm_nx_ip; + ULONG ux_slave_class_cdc_ecm_nx_ip_address; + ULONG ux_slave_class_cdc_ecm_nx_ip_network_mask; + NX_INTERFACE *ux_slave_class_cdc_ecm_nx_interface; + NX_PACKET *ux_slave_class_cdc_ecm_xmit_queue; + NX_PACKET *ux_slave_class_cdc_ecm_xmit_queue_tail; + NX_PACKET *ux_slave_class_cdc_ecm_receive_queue; + UCHAR *ux_slave_class_cdc_ecm_pool_memory; + NX_PACKET_POOL ux_slave_class_cdc_ecm_packet_pool; + TX_THREAD ux_slave_class_cdc_ecm_bulkin_thread; + TX_THREAD ux_slave_class_cdc_ecm_bulkout_thread; + TX_THREAD ux_slave_class_cdc_ecm_interrupt_thread; + UCHAR *ux_slave_class_cdc_ecm_bulkin_thread_stack; + UCHAR *ux_slave_class_cdc_ecm_bulkout_thread_stack; + UCHAR *ux_slave_class_cdc_ecm_interrupt_thread_stack; + ULONG ux_slave_class_cdc_ecm_link_state; + TX_MUTEX ux_slave_class_cdc_ecm_mutex; + VOID *ux_slave_class_cdc_ecm_network_handle; + +} UX_SLAVE_CLASS_CDC_ECM; + + +/* Requests - Ethernet Networking Control Model */ + +#define UX_DEVICE_CLASS_CDC_ECM_SEND_ENCAPSULATED_COMMAND 0x00 + /* Issues a command in the format of the supported control + protocol. The intent of this mechanism is to support + networking devices (e.g., host-based cable modems) + that require an additional vendor-defined interface for + media specific hardware configuration and + management. */ +#define UX_DEVICE_CLASS_CDC_ECM_GET_ENCAPSULATED_RESPONSE 0x01 + /* Requests a response in the format of the supported + control protocol. */ + + +/* Define buffer length for IN/OUT pipes. */ + +#define UX_DEVICE_CLASS_CDC_ECM_BUFFER_SIZE 4096 + + +/* Define Device CDC_ECM Class prototypes. */ + +UINT _ux_device_class_cdc_ecm_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_ecm_control_request(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_ecm_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_ecm_change(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_ecm_entry(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_ecm_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_ecm_uninitialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_cdc_ecm_write(VOID *cdc_ecm_class, NX_PACKET *packet); +VOID _ux_device_class_cdc_ecm_bulkin_thread(ULONG cdc_ecm_class); +VOID _ux_device_class_cdc_ecm_bulkout_thread(ULONG cdc_ecm_class); +VOID _ux_device_class_cdc_ecm_interrupt_thread(ULONG cdc_ecm_class); + + +/* Define Device CDC Class API prototypes. */ + +#define ux_device_class_cdc_ecm_entry _ux_device_class_cdc_ecm_entry +#define ux_device_class_cdc_ecm_read _ux_device_class_cdc_ecm_read +#define ux_device_class_cdc_ecm_write _ux_device_class_cdc_ecm_write + +#endif /* UX_DEVICE_CLASS_CDC_ECM_H */ diff --git a/common/usbx_device_classes/inc/ux_device_class_dfu.h b/common/usbx_device_classes/inc/ux_device_class_dfu.h new file mode 100644 index 0000000..815aec0 --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_dfu.h @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** DFU Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_dfu.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the equivalences for the USBX Device Class DFU */ +/* ACM component. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_DFU_H +#define UX_DEVICE_CLASS_DFU_H + +/* Define DFU class descriptor capabilities. */ +#define UX_SLAVE_CLASS_DFU_CAPABILITY_WILL_DETACH 0x08 +#define UX_SLAVE_CLASS_DFU_CAPABILITY_MANIFESTATION_TOLERANT 0x04 +#define UX_SLAVE_CLASS_DFU_CAPABILITY_CAN_UPLOAD 0x02 +#define UX_SLAVE_CLASS_DFU_CAPABILITY_CAN_DOWNLOAD 0x01 + +/* Define DFU Class USB Class constants. */ +#define UX_SLAVE_CLASS_DFU_CLASS 0xFE +#define UX_SLAVE_CLASS_DFU_SUBCLASS 0x01 +#define UX_SLAVE_CLASS_DFU_PROTOCOL_RUNTIME 0x01 +#define UX_SLAVE_CLASS_DFU_PROTOCOL_DFU_MODE 0x02 + +/* Define DFU MODES signals. */ +#define UX_DEVICE_CLASS_DFU_MODE_RUNTIME 1 +#define UX_DEVICE_CLASS_DFU_MODE_DFU 2 + + + + +/* Device DFU Requests */ +#define UX_SLAVE_CLASS_DFU_COMMAND_DETACH 0 +#define UX_SLAVE_CLASS_DFU_COMMAND_DOWNLOAD 1 +#define UX_SLAVE_CLASS_DFU_COMMAND_UPLOAD 2 +#define UX_SLAVE_CLASS_DFU_COMMAND_GET_STATUS 3 +#define UX_SLAVE_CLASS_DFU_COMMAND_CLEAR_STATUS 4 +#define UX_SLAVE_CLASS_DFU_COMMAND_GET_STATE 5 +#define UX_SLAVE_CLASS_DFU_COMMAND_ABORT 6 + +/* Device DFU Status values */ +#define UX_SLAVE_CLASS_DFU_STATUS_OK 0x00 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_TARGET 0x01 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_FILE 0x02 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_WRITE 0x03 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_ERASE 0x04 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_CHECK_ERASED 0x05 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_PROG 0x06 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_VERIFY 0x07 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_ADDRESS 0x08 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_NOTDONE 0x09 +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_FIRMWARE 0x0A +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_VENDOR 0x0B +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_USBR 0x0C +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_POR 0x0D +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_UNKNOWN 0x0E +#define UX_SLAVE_CLASS_DFU_STATUS_ERROR_STALLEDPKT 0x0F + +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_APP_IDLE 0 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_APP_DETACH 1 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_IDLE 2 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_DNLOAD_SYNC 3 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_DNBUSY 4 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_DNLOAD_IDLE 5 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_MANIFEST_SYNC 6 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_MANIFEST 7 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_MANIFEST_WAIT_RESET 8 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_UPLOAD_IDLE 9 +#define UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_ERROR 10 + +/* Define DFU class GET_STATUS command response. */ +#define UX_SLAVE_CLASS_DFU_GET_STATUS_STATUS 0 +#define UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT 1 +#define UX_SLAVE_CLASS_DFU_GET_STATUS_STATE 4 +#define UX_SLAVE_CLASS_DFU_GET_STATUS_STRING 5 +#define UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH 6 + +/* Define DFU class GET_STATE command response. */ +#define UX_SLAVE_CLASS_DFU_GET_STATE_STATE 0 +#define UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH 1 + +/* Define DFU application notification signals. */ +#define UX_SLAVE_CLASS_DFU_NOTIFICATION_BEGIN_DOWNLOAD 1 +#define UX_SLAVE_CLASS_DFU_NOTIFICATION_END_DOWNLOAD 2 +#define UX_SLAVE_CLASS_DFU_NOTIFICATION_ABORT_DOWNLOAD 3 + +/* Define DFU application notification signals. */ +#define UX_SLAVE_CLASS_DFU_MEDIA_STATUS_OK 0 +#define UX_SLAVE_CLASS_DFU_MEDIA_STATUS_BUSY 1 +#define UX_SLAVE_CLASS_DFU_MEDIA_STATUS_ERROR 2 + +/* Define DFU thread event signals. */ +#define UX_DEVICE_CLASS_DFU_THREAD_EVENT_DISCONNECT 1 +#define UX_DEVICE_CLASS_DFU_THREAD_EVENT_WAIT_RESET 2 + +/* Define Slave DFU Class Calling Parameter structure */ + +typedef struct UX_SLAVE_CLASS_DFU_PARAMETER_STRUCT +{ + + ULONG ux_slave_class_dfu_parameter_will_detach; + ULONG ux_slave_class_dfu_parameter_capabilities; + VOID (*ux_slave_class_dfu_parameter_instance_activate)(VOID *); + VOID (*ux_slave_class_dfu_parameter_instance_deactivate)(VOID *); + UINT (*ux_slave_class_dfu_parameter_read)(VOID *dfu, ULONG block_number, UCHAR * data_pointer, ULONG length, ULONG *media_status); + UINT (*ux_slave_class_dfu_parameter_write)(VOID *dfu, ULONG block_number, UCHAR * data_pointer, ULONG length, ULONG *media_status); + UINT (*ux_slave_class_dfu_parameter_get_status)(VOID *dfu, ULONG *media_status); + UINT (*ux_slave_class_dfu_parameter_notify)(VOID *dfu, ULONG notification); + UCHAR *ux_slave_class_dfu_parameter_framework; + ULONG ux_slave_class_dfu_parameter_framework_length; + +} UX_SLAVE_CLASS_DFU_PARAMETER; + +/* Define DFU Class structure. */ + +typedef struct UX_SLAVE_CLASS_DFU_STRUCT +{ + UX_SLAVE_INTERFACE *ux_slave_class_dfu_interface; + ULONG ux_slave_class_dfu_status; + ULONG ux_slave_class_dfu_state; + VOID (*ux_slave_class_dfu_instance_activate)(VOID *); + VOID (*ux_slave_class_dfu_instance_deactivate)(VOID *); + UINT (*ux_slave_class_dfu_read)(VOID *dfu, ULONG block_number, UCHAR * data_pointer, ULONG length, ULONG *media_status); + UINT (*ux_slave_class_dfu_write)(VOID *dfu, ULONG block_number, UCHAR * data_pointer, ULONG length, ULONG *media_status); + UINT (*ux_slave_class_dfu_get_status)(VOID *dfu, ULONG *media_status); + UINT (*ux_slave_class_dfu_notify)(VOID *dfu, ULONG notification); + ULONG ux_slave_class_dfu_download_block_count; + ULONG ux_slave_class_dfu_upload_block_count; + TX_THREAD ux_slave_class_dfu_thread; + UCHAR *ux_slave_class_dfu_thread_stack; + TX_EVENT_FLAGS_GROUP ux_slave_class_dfu_event_flags_group; + +} UX_SLAVE_CLASS_DFU; + +/* Define Device DFU Class prototypes. */ + +UINT _ux_device_class_dfu_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dfu_control_request(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dfu_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dfu_entry(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_dfu_initialize(UX_SLAVE_CLASS_COMMAND *command); +VOID _ux_device_class_dfu_thread(ULONG dfu_class); + +/* Define Device DFU Class API prototypes. */ + +#define ux_device_class_dfu_entry _ux_device_class_dfu_entry + +#endif /* UX_DEVICE_CLASS_DFU_H */ diff --git a/common/usbx_device_classes/inc/ux_device_class_hid.h b/common/usbx_device_classes/inc/ux_device_class_hid.h new file mode 100644 index 0000000..f1b885b --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_hid.h @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_hid.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX HID class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_HID_H +#define UX_DEVICE_CLASS_HID_H + +/* Define HID Class constants. */ + +#define UX_DEVICE_CLASS_HID_CLASS 0x03 +#define UX_DEVICE_CLASS_HID_SUBCLASS 0X00 +#define UX_DEVICE_CLASS_HID_PROTOCOL 0X00 + +/* Define HID Class commands. */ + +#define UX_DEVICE_CLASS_HID_COMMAND_GET_REPORT 0x01 +#define UX_DEVICE_CLASS_HID_COMMAND_GET_IDLE 0x02 +#define UX_DEVICE_CLASS_HID_COMMAND_GET_PROTOCOL 0x03 +#define UX_DEVICE_CLASS_HID_COMMAND_SET_REPORT 0x09 +#define UX_DEVICE_CLASS_HID_COMMAND_SET_IDLE 0x0A +#define UX_DEVICE_CLASS_HID_COMMAND_SET_PROTOCOL 0x0B + +/* Define HID Class Descriptor types. */ + +#define UX_DEVICE_CLASS_HID_DESCRIPTOR_HID 0x21 +#define UX_DEVICE_CLASS_HID_DESCRIPTOR_REPORT 0x22 +#define UX_DEVICE_CLASS_HID_DESCRIPTOR_PHYSICAL 0x23 + +/* Define HID Report Types. */ + +#define UX_DEVICE_CLASS_HID_REPORT_TYPE_INPUT 0x1 +#define UX_DEVICE_CLASS_HID_REPORT_TYPE_OUTPUT 0x2 +#define UX_DEVICE_CLASS_HID_REPORT_TYPE_FEATURE 0x3 + +/* Define HID event info structure. */ + +#ifndef UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH +#define UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH 32 +#endif + +/* Ensure the event buffer can fit inside the control endpoint's data buffer. */ +#if UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH +#error "Error: the event buffer cannot fit inside the control endpoint's data buffer. Reduce UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH such that it is less than or equal to UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH." +#endif + +/* Ensure the event buffer can fit inside the interrupt endpoint's data buffer. */ +#if UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH > UX_SLAVE_REQUEST_DATA_MAX_LENGTH +#error "Error: the event buffer cannot fit inside the interrupt endpoint's data buffer. Reduce UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH such that it is less than or equal to UX_SLAVE_REQUEST_DATA_MAX_LENGTH." +#endif + +#ifndef UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE +#define UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE 16 +#endif + +#define UX_DEVICE_CLASS_HID_NEW_EVENT 1 +#define UX_DEVICE_CLASS_HID_NEW_IDLE_RATE 2 +#define UX_DEVICE_CLASS_HID_EVENTS_MASK 3 + +typedef struct UX_SLAVE_CLASS_HID_EVENT_STRUCT +{ + ULONG ux_device_class_hid_event_report_id; + ULONG ux_device_class_hid_event_report_type; + UCHAR ux_device_class_hid_event_buffer[UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH]; + ULONG ux_device_class_hid_event_length; + +} UX_SLAVE_CLASS_HID_EVENT; + +/* Define HID structure. */ + +typedef struct UX_SLAVE_CLASS_HID_STRUCT +{ + + UX_SLAVE_INTERFACE *ux_slave_class_hid_interface; + UX_SLAVE_ENDPOINT *ux_device_class_hid_interrupt_endpoint; + UINT ux_device_class_hid_state; + UINT (*ux_device_class_hid_callback)(struct UX_SLAVE_CLASS_HID_STRUCT *hid, UX_SLAVE_CLASS_HID_EVENT *); + UINT (*ux_device_class_hid_get_callback)(struct UX_SLAVE_CLASS_HID_STRUCT *hid, UX_SLAVE_CLASS_HID_EVENT *); + VOID (*ux_slave_class_hid_instance_activate)(VOID *); + VOID (*ux_slave_class_hid_instance_deactivate)(VOID *); + UCHAR *ux_device_class_hid_report_address; + ULONG ux_device_class_hid_report_id; + ULONG ux_device_class_hid_report_length; + TX_EVENT_FLAGS_GROUP ux_device_class_hid_event_flags_group; + ULONG ux_device_class_hid_event_idle_rate; + ULONG ux_device_class_hid_event_wait_timeout; + UX_SLAVE_CLASS_HID_EVENT *ux_device_class_hid_event_array; + UX_SLAVE_CLASS_HID_EVENT *ux_device_class_hid_event_array_head; + UX_SLAVE_CLASS_HID_EVENT *ux_device_class_hid_event_array_tail; + UX_SLAVE_CLASS_HID_EVENT *ux_device_class_hid_event_array_end; + +} UX_SLAVE_CLASS_HID; + +/* Define HID initialization command structure. */ + +typedef struct UX_SLAVE_CLASS_HID_PARAMETER_STRUCT +{ + + VOID (*ux_slave_class_hid_instance_activate)(VOID *); + VOID (*ux_slave_class_hid_instance_deactivate)(VOID *); + UCHAR *ux_device_class_hid_parameter_report_address; + ULONG ux_device_class_hid_parameter_report_id; + ULONG ux_device_class_hid_parameter_report_length; + UINT (*ux_device_class_hid_parameter_callback)(struct UX_SLAVE_CLASS_HID_STRUCT *hid, UX_SLAVE_CLASS_HID_EVENT *); + UINT (*ux_device_class_hid_parameter_get_callback)(struct UX_SLAVE_CLASS_HID_STRUCT *hid, UX_SLAVE_CLASS_HID_EVENT *); + +} UX_SLAVE_CLASS_HID_PARAMETER; + + +/* Define HID Class function prototypes. */ +UINT _ux_device_class_hid_descriptor_send(UX_SLAVE_CLASS_HID *hid, ULONG descriptor_type, + ULONG request_index, ULONG host_length); +UINT _ux_device_class_hid_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_hid_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_hid_control_request(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_hid_entry(UX_SLAVE_CLASS_COMMAND *command); +VOID _ux_device_class_hid_interrupt_thread(ULONG hid_class); +UINT _ux_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_hid_uninitialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_hid_event_set(UX_SLAVE_CLASS_HID *hid, + UX_SLAVE_CLASS_HID_EVENT *hid_event); +UINT _ux_device_class_hid_event_get(UX_SLAVE_CLASS_HID *hid, + UX_SLAVE_CLASS_HID_EVENT *hid_event); +UINT _ux_device_class_hid_report_set(UX_SLAVE_CLASS_HID *hid, ULONG descriptor_type, + ULONG request_index, ULONG host_length); +UINT _ux_device_class_hid_report_get(UX_SLAVE_CLASS_HID *hid, ULONG descriptor_type, + ULONG request_index, ULONG host_length); + + +/* Define Device HID Class API prototypes. */ + +#define ux_device_class_hid_entry _ux_device_class_hid_entry +#define ux_device_class_hid_event_set _ux_device_class_hid_event_set +#define ux_device_class_hid_event_get _ux_device_class_hid_event_get +#define ux_device_class_hid_report_set _ux_device_class_hid_report_set +#define ux_device_class_hid_report_get _ux_device_class_hid_report_get + + +#endif diff --git a/common/usbx_device_classes/inc/ux_device_class_pima.h b/common/usbx_device_classes/inc/ux_device_class_pima.h new file mode 100644 index 0000000..5506927 --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_pima.h @@ -0,0 +1,996 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_pima.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX PIMA class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_PIMA_H +#define UX_DEVICE_CLASS_PIMA_H + +/* Define PIMA Class constants. */ + +#define UX_DEVICE_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_DEVICE_CLASS_PIMA_CLASS 0x06 +#define UX_DEVICE_CLASS_PIMA_SUBCLASS 0X01 +#define UX_DEVICE_CLASS_PIMA_PROTOCOL 0X01 +#define UX_DEVICE_CLASS_PIMA_MAGIC_NUMBER 0x50494D41 +#define UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH 256 +#define UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH 64 +#define UX_DEVICE_CLASS_PIMA_MAX_PAYLOAD 1024 +#define UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE 1024 +#define UX_DEVICE_CLASS_PIMA_DEVICE_INFO_BUFFER_SIZE 1024 +#define UX_DEVICE_CLASS_PIMA_STORAGE_INFO_BUFFER_SIZE 1024 +#define UX_DEVICE_CLASS_PIMA_ARRAY_BUFFER_SIZE 1024 +#define UX_DEVICE_CLASS_PIMA_MAX_EVENTS_QUEUE 16 +#define UX_DEVICE_CLASS_PIMA_MAX_STORAGE_IDS 1 +#define UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH 256 +#define UX_DEVICE_CLASS_PIMA_ARRAY_MAX_LENGTH 256 +#define UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH 64 +#define UX_DEVICE_CLASS_PIMA_DEVICE_PROPERTIES_ARRAY_MAX_ITEMS 32 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTIES_ARRAY_MAX_ITEMS 128 +#define UX_DEVICE_CLASS_PIMA_DEVICE_PROP_VALUE_BUFFER_SIZE 1024 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_VALUE_BUFFER_SIZE 1024 +#define UX_DEVICE_CLASS_PIMA_PROP_VALUE_SIZE 256 +#define UX_DEVICE_CLASS_PIMA_MICROSOFT_VENDOR_COMMAND_CODE 0x54 + +/* Define PIMA versions. */ +#define UX_DEVICE_CLASS_PIMA_STANDARD_VERSION 100 +#define UX_DEVICE_CLASS_PIMA_VENDOR_EXTENSION_ID 6 +#define UX_DEVICE_CLASS_PIMA_EXTENSION_VERSION 100 +#define UX_DEVICE_CLASS_PIMA_STANDARD_MODE 0 + +/* Define PIMA data phases. */ + +#define UX_DEVICE_CLASS_PIMA_DATA_PHASE_NONE 0 +#define UX_DEVICE_CLASS_PIMA_DATA_PHASE_IN 1 +#define UX_DEVICE_CLASS_PIMA_DATA_PHASE_OUT 2 + +/* Define PIMA session states. */ + +#define UX_DEVICE_CLASS_PIMA_SESSION_STATE_CLOSED 0 +#define UX_DEVICE_CLASS_PIMA_SESSION_STATE_OPENED 1 + +/* Define PIMA object and thumb states. */ + +#define UX_DEVICE_CLASS_PIMA_OBJECT_STATE_CLOSED 0 +#define UX_DEVICE_CLASS_PIMA_OBJECT_STATE_OPENED 1 + +/* Define PIMA object and thumb transfer status. */ + +#define UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_STATUS_INACTIVE 0 +#define UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE 1 +#define UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED 2 +#define UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED 3 + +/* Define PIMA object and thumb transfer phase. */ + +#define UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_ACTIVE 0 +#define UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_COMPLETED 1 +#define UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_COMPLETED_ERROR 2 + + +/* Define PIMA Cancel Request equivalences. */ + +#define UX_DEVICE_CLASS_PIMA_REQUEST_CANCEL_COMMAND 0x64 +#define UX_DEVICE_CLASS_PIMA_REQUEST_CANCEL_DATA_LENGTH 0x06 +#define UX_DEVICE_CLASS_PIMA_REQUEST_CANCEL_CODE 0x04001 +#define UX_DEVICE_CLASS_PIMA_REQUEST_CANCEL_OFFSET_CODE 0x00 +#define UX_DEVICE_CLASS_PIMA_REQUEST_CANCEL_OFFSET_TRANSACTION_ID 0x02 + +/* Define PIMA Reset Request equivalences. */ + +#define UX_DEVICE_CLASS_PIMA_REQUEST_RESET_DEVICE 0x66 + +/* Define PIMA Status Request equivalences. */ + +#define UX_DEVICE_CLASS_PIMA_REQUEST_STATUS_COMMAND 0x67 +#define UX_DEVICE_CLASS_PIMA_REQUEST_STATUS_DATA_LENGTH 0x40 +#define UX_DEVICE_CLASS_PIMA_REQUEST_STATUS_OFFSET_LENGTH 0x00 +#define UX_DEVICE_CLASS_PIMA_REQUEST_STATUS_OFFSET_CODE 0x02 +#define UX_DEVICE_CLASS_PIMA_REQUEST_STATUS_COMMAND_COUNTER 16 +#define UX_DEVICE_CLASS_PIMA_REQUEST_STATUS_COMMAND_DELAY (1 * UX_PERIODIC_RATE) + +/* Define PIMA command container type. */ + +#define UX_DEVICE_CLASS_PIMA_CT_UNDEFINED 0x00 +#define UX_DEVICE_CLASS_PIMA_CT_COMMAND_BLOCK 0x01 +#define UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK 0x02 +#define UX_DEVICE_CLASS_PIMA_CT_RESPONSE_BLOCK 0x03 +#define UX_DEVICE_CLASS_PIMA_CT_EVENT_BLOCK 0x04 + +/* Define PIMA Extended Event Data Request payload Format. */ + +#define UX_DEVICE_CLASS_PIMA_EEDR_EVENT_CODE 0x00 +#define UX_DEVICE_CLASS_PIMA_EEDR_TRANSACTION_ID 0x02 +#define UX_DEVICE_CLASS_PIMA_EEDR_NUMBER_PARAMETERS 0x06 +#define UX_DEVICE_CLASS_PIMA_EEDR_SIZE_PARAMETER 0x08 + +/* Define PIMA Device Status Data Format. */ + +#define UX_DEVICE_CLASS_PIMA_DSD_LENGTH 0x00 +#define UX_DEVICE_CLASS_PIMA_DSD_CODE 0x02 +#define UX_DEVICE_CLASS_PIMA_DSD_PARAMETER 0x04 + +/* Define PIMA Command Header Format. */ + +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_LENGTH 0x00 +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_TYPE 0x04 +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_CODE 0x06 +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID 0x08 +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1 0x0C +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_2 0x10 +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_3 0x14 +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_4 0x18 +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_5 0x1C + +#define UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_SIZE 0x0C +#define UX_DEVICE_CLASS_PIMA_CONTAINER_SIZE 0x40 +#define UX_DEVICE_CLASS_PIMA_ALL_HEADER_SIZE 0x20 + +/* Define PIMA Data Header Format. */ + +#define UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH 0x00 +#define UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE 0x04 +#define UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE 0x06 +#define UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID 0x08 +#define UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE 0x0C + + +/* Define PIMA Response Header Format. */ + +#define UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_LENGTH 0x00 +#define UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_TYPE 0x04 +#define UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_CODE 0x06 +#define UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_TRANSACTION_ID 0x08 +#define UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_PARAMETERS 0x0C +#define UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_SIZE 0x0C + +/* Define PIMA Asynchronous Event Interrupt Data Format. */ + +#define UX_DEVICE_CLASS_PIMA_AEI_DATA_LENGTH 0x00 +#define UX_DEVICE_CLASS_PIMA_AEI_TYPE 0x04 +#define UX_DEVICE_CLASS_PIMA_AEI_EVENT_CODE 0x06 +#define UX_DEVICE_CLASS_PIMA_AEI_TRANSACTION_ID 0x08 +#define UX_DEVICE_CLASS_PIMA_AEI_PARAMETER_1 0x0C +#define UX_DEVICE_CLASS_PIMA_AEI_PARAMETER_2 0x10 +#define UX_DEVICE_CLASS_PIMA_AEI_PARAMETER_3 0x14 +#define UX_DEVICE_CLASS_PIMA_AEI_MAX_LENGTH 0x18 + +/* Define PIMA Operation Commands. */ + +#define UX_DEVICE_CLASS_PIMA_OC_UNDEFINED 0x1000 +#define UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_INFO 0x1001 +#define UX_DEVICE_CLASS_PIMA_OC_OPEN_SESSION 0x1002 +#define UX_DEVICE_CLASS_PIMA_OC_CLOSE_SESSION 0x1003 +#define UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_IDS 0x1004 +#define UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_INFO 0x1005 +#define UX_DEVICE_CLASS_PIMA_OC_GET_NUM_OBJECTS 0x1006 +#define UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_HANDLES 0x1007 +#define UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_INFO 0x1008 +#define UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT 0x1009 +#define UX_DEVICE_CLASS_PIMA_OC_GET_THUMB 0x100A +#define UX_DEVICE_CLASS_PIMA_OC_DELETE_OBJECT 0x100B +#define UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT_INFO 0x100C +#define UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT 0x100D +#define UX_DEVICE_CLASS_PIMA_OC_INITIATE_CAPTURE 0x100E +#define UX_DEVICE_CLASS_PIMA_OC_FORMAT_STORE 0x100F +#define UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE 0x1010 +#define UX_DEVICE_CLASS_PIMA_OC_SELF_TEST 0x1011 +#define UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_PROTECTION 0x1012 +#define UX_DEVICE_CLASS_PIMA_OC_POWER_DOWN 0x1013 +#define UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_DESC 0x1014 +#define UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_VALUE 0x1015 +#define UX_DEVICE_CLASS_PIMA_OC_SET_DEVICE_PROP_VALUE 0x1016 +#define UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE_PROP_VALUE 0x1017 +#define UX_DEVICE_CLASS_PIMA_OC_TERMINATE_OPEN_CAPTURE 0x1018 +#define UX_DEVICE_CLASS_PIMA_OC_MOVE_OBJECT 0x1019 +#define UX_DEVICE_CLASS_PIMA_OC_COPY_OBJECT 0x101A +#define UX_DEVICE_CLASS_PIMA_OC_GET_PARTIAL_OBJECT 0x101B +#define UX_DEVICE_CLASS_PIMA_OC_INITIATE_OPEN_CAPTURE 0x101C +#define UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROPS_SUPPORTED 0x9801 +#define UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_DESC 0x9802 +#define UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_VALUE 0x9803 +#define UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_PROP_VALUE 0x9804 +#define UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_REFERENCES 0x9810 +#define UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_REFERENCES 0x9811 + +/* Define PIMA Response Codes. */ + +#define UX_DEVICE_CLASS_PIMA_RC_UNDEFINED 0x2000 +#define UX_DEVICE_CLASS_PIMA_RC_OK 0x2001 +#define UX_DEVICE_CLASS_PIMA_RC_GENERAL_ERROR 0x2002 +#define UX_DEVICE_CLASS_PIMA_RC_SESSION_NOT_OPEN 0x2003 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_TRANSACTION_ID 0x2004 +#define UX_DEVICE_CLASS_PIMA_RC_OPERATION_NOT_SUPPORTED 0x2005 +#define UX_DEVICE_CLASS_PIMA_RC_PARAMETER_NOT_SUPPORTED 0x2006 +#define UX_DEVICE_CLASS_PIMA_RC_INCOMPLETE_TRANSFER 0x2007 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_STORAGE_ID 0x2008 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_HANDLE 0x2009 +#define UX_DEVICE_CLASS_PIMA_RC_DEVICE_PROP_NOT_SUPPORTED 0x200A +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_FORMAT_CODE 0x200B +#define UX_DEVICE_CLASS_PIMA_RC_STORE_FULL 0x200C +#define UX_DEVICE_CLASS_PIMA_RC_OBJECT_WRITE_PROTECTED 0x200D +#define UX_DEVICE_CLASS_PIMA_RC_STORE_READ_ONLY 0x200E +#define UX_DEVICE_CLASS_PIMA_RC_ACCESS_DENIED 0x200F +#define UX_DEVICE_CLASS_PIMA_RC_NO_THUMBNAIL_PRESENT 0x2010 +#define UX_DEVICE_CLASS_PIMA_RC_SELF_TEST_FAILED 0x2011 +#define UX_DEVICE_CLASS_PIMA_RC_PARTIAL_DELETION 0x2012 +#define UX_DEVICE_CLASS_PIMA_RC_STORE_NOT_AVAILABLE 0x2013 +#define UX_DEVICE_CLASS_PIMA_RC_FORMAT_UNSUPPORTED 0x2014 +#define UX_DEVICE_CLASS_PIMA_RC_NO_VALID_OBJECT_INFO 0x2015 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_CODE_FORMAT 0x2016 +#define UX_DEVICE_CLASS_PIMA_RC_UNKNOWN_VENDOR_CODE 0x2017 +#define UX_DEVICE_CLASS_PIMA_RC_CAPTURE_ALREADY_TERMINATED 0x2018 +#define UX_DEVICE_CLASS_PIMA_RC_DEVICE_BUSY 0x2019 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_PARENT_OBJECT 0x201A +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_DEVICE_PROP_FORMAT 0x201B +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_DEVICE_PROP_VALUE 0x201C +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER 0x201D +#define UX_DEVICE_CLASS_PIMA_RC_SESSION_ALREADY_OPENED 0x201E +#define UX_DEVICE_CLASS_PIMA_RC_TRANSACTION_CANCELED 0x201F +#define UX_DEVICE_CLASS_PIMA_RC_DESTINATION_UNSUPPORTED 0x2020 +#define UX_DEVICE_CLASS_PIMA_RC_OBJECT_ALREADY_OPENED 0x2021 +#define UX_DEVICE_CLASS_PIMA_RC_OBJECT_ALREADY_CLOSED 0x2022 +#define UX_DEVICE_CLASS_PIMA_RC_OBJECT_NOT_OPENED 0x2023 + +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_PROP_CODE 0xA801 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_PROP_FORMAT 0xA802 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_PROP_VALUE 0xA803 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_REFERENCE 0xA804 +#define UX_DEVICE_CLASS_PIMA_RC_INVALID_DATASET 0xA806 +#define UX_DEVICE_CLASS_PIMA_RC_SPECIFICATION_BY_GROUP_UNSUPPORTED 0xA807 +#define UX_DEVICE_CLASS_PIMA_RC_SPECIFICATION_BY_DEPTH_UNSUPPORTED 0xA808 +#define UX_DEVICE_CLASS_PIMA_RC_OBJECT_TOO_LARGE 0xA809 +#define UX_DEVICE_CLASS_PIMA_RC_OBJECT_PROP_NOT_SUPPORTED 0xA80A + +/* Define PIMA Event Codes. */ + +#define UX_DEVICE_CLASS_PIMA_EC_UNDEFINED 0x4000 +#define UX_DEVICE_CLASS_PIMA_EC_CANCEL_TRANSACTION 0x4001 +#define UX_DEVICE_CLASS_PIMA_EC_OBJECT_ADDED 0x4002 +#define UX_DEVICE_CLASS_PIMA_EC_OBJECT_REMOVED 0x4003 +#define UX_DEVICE_CLASS_PIMA_EC_STORE_ADDED 0x4004 +#define UX_DEVICE_CLASS_PIMA_EC_STORE_REMOVED 0x4005 +#define UX_DEVICE_CLASS_PIMA_EC_DEVICE_PROP_CHANGED 0x4006 +#define UX_DEVICE_CLASS_PIMA_EC_OBJECT_INFO_CHANGED 0x4007 +#define UX_DEVICE_CLASS_PIMA_EC_DEVICE_INFO_CHANGED 0x4008 +#define UX_DEVICE_CLASS_PIMA_EC_REQUEST_OBJECT_TRANSFER 0x4009 +#define UX_DEVICE_CLASS_PIMA_EC_STORE_FULL 0x400A +#define UX_DEVICE_CLASS_PIMA_EC_DEVICE_RESET 0x400B +#define UX_DEVICE_CLASS_PIMA_EC_STORAGE_INFO_CHANGED 0x400C +#define UX_DEVICE_CLASS_PIMA_EC_CAPTURE_COMPLETE 0x400D +#define UX_DEVICE_CLASS_PIMA_EC_UNREPORTED_STATUS 0x400E + +/* Define PIMA Object Format Codes. */ + +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED 0x3000 +#define UX_DEVICE_CLASS_PIMA_OFC_ASSOCIATION 0x3001 +#define UX_DEVICE_CLASS_PIMA_OFC_SCRIPT 0x3002 +#define UX_DEVICE_CLASS_PIMA_OFC_EXECUTABLE 0x3003 +#define UX_DEVICE_CLASS_PIMA_OFC_TEXT 0x3004 +#define UX_DEVICE_CLASS_PIMA_OFC_HTML 0x3005 +#define UX_DEVICE_CLASS_PIMA_OFC_DPOF 0x3006 +#define UX_DEVICE_CLASS_PIMA_OFC_AIFF 0x3007 +#define UX_DEVICE_CLASS_PIMA_OFC_WAV 0x3008 +#define UX_DEVICE_CLASS_PIMA_OFC_MP3 0x3009 +#define UX_DEVICE_CLASS_PIMA_OFC_AVI 0x300A +#define UX_DEVICE_CLASS_PIMA_OFC_MPEG 0x300B +#define UX_DEVICE_CLASS_PIMA_OFC_ASF 0x300C +#define UX_DEVICE_CLASS_PIMA_OFC_DEFINED 0x3800 +#define UX_DEVICE_CLASS_PIMA_OFC_EXIF_JPEG 0x3801 +#define UX_DEVICE_CLASS_PIMA_OFC_TIFF_EP 0x3802 +#define UX_DEVICE_CLASS_PIMA_OFC_FLASHPIX 0x3803 +#define UX_DEVICE_CLASS_PIMA_OFC_BMP 0x3804 +#define UX_DEVICE_CLASS_PIMA_OFC_CIFF 0x3805 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_2 0x3806 +#define UX_DEVICE_CLASS_PIMA_OFC_GIF 0x3807 +#define UX_DEVICE_CLASS_PIMA_OFC_JFIF 0x3808 +#define UX_DEVICE_CLASS_PIMA_OFC_CD 0x3809 +#define UX_DEVICE_CLASS_PIMA_OFC_PICT 0x380A +#define UX_DEVICE_CLASS_PIMA_OFC_PNG 0x380B +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_3 0x380C +#define UX_DEVICE_CLASS_PIMA_OFC_TIFF 0x380D +#define UX_DEVICE_CLASS_PIMA_OFC_TIFF_IT 0x380E +#define UX_DEVICE_CLASS_PIMA_OFC_JP2 0x380F +#define UX_DEVICE_CLASS_PIMA_OFC_JPX 0x3810 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_FIRMWARE 0xB802 +#define UX_DEVICE_CLASS_PIMA_OFC_WINDOWS_IMAGE_FORMAT 0xB881 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_AUDIO 0xB900 +#define UX_DEVICE_CLASS_PIMA_OFC_WMA 0xB901 +#define UX_DEVICE_CLASS_PIMA_OFC_OGG 0xB902 +#define UX_DEVICE_CLASS_PIMA_OFC_AAC 0xB903 +#define UX_DEVICE_CLASS_PIMA_OFC_AUDIBLE 0xB904 +#define UX_DEVICE_CLASS_PIMA_OFC_FLAC 0xB906 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_VIDEO 0xB980 +#define UX_DEVICE_CLASS_PIMA_OFC_WMV 0xB981 +#define UX_DEVICE_CLASS_PIMA_OFC_MP4_CONTAINER 0xB982 +#define UX_DEVICE_CLASS_PIMA_OFC_MP2 0xB983 +#define UX_DEVICE_CLASS_PIMA_OFC_3GP_CONTAINER 0xB984 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_COLLECTION 0xBA00 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MULTIMEDIA_ALBUM 0xBA01 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_IMAGE_ALBUM 0xBA02 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_AUDIO_ALBUM 0xBA03 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_VIDEO_ALBUM 0xBA04 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_AUDIO_AND_VIDEO_PLAYLIST 0xBA05 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_CONTACT_GROUP 0xBA06 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MESSAGE_FOLDER 0xBA07 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_CHAPTERED_PRODUCTION 0xBA08 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_AUDIO_PLAYLIST 0xBA09 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_VIDEO_PLAYLIST 0xBA0A +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MEDIACAST 0xBA0B +#define UX_DEVICE_CLASS_PIMA_OFC_WPL_PLAYLIST 0xBA10 +#define UX_DEVICE_CLASS_PIMA_OFC_M3U_PLAYLIST 0xBA11 +#define UX_DEVICE_CLASS_PIMA_OFC_MPL_PLAYLIST 0xBA12 +#define UX_DEVICE_CLASS_PIMA_OFC_ASX_PLAYLIST 0xBA13 +#define UX_DEVICE_CLASS_PIMA_OFC_PLS_PLAYLIST 0xBA14 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_DOCUMENT 0xBA80 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_DOCUMENT 0xBA81 +#define UX_DEVICE_CLASS_PIMA_OFC_XML_DOCUMENT 0xBA82 +#define UX_DEVICE_CLASS_PIMA_OFC_MICROSOFT_WORD_DOCUMENT 0xBA83 +#define UX_DEVICE_CLASS_PIMA_OFC_MHT_COMPILED_HTML_DOCUMENT 0xBA84 +#define UX_DEVICE_CLASS_PIMA_OFC_MICROSOFT_EXCEL_SPREADSHEET 0xBA85 +#define UX_DEVICE_CLASS_PIMA_OFC_MICROSOFT_POWERPOINT_PRESENTATION 0xBA86 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_MESSAGE 0xBB00 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MESSAGE 0xBB01 +#define UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_CONTACT 0xBB80 +#define UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_CONTACT 0xBB81 +#define UX_DEVICE_CLASS_PIMA_OFC_VCARD2 0xBB82 + +/* Define PIMA Device Format Codes. */ + +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_UNDEFINED 0x5000 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_BATTERY_LEVEL 0x5001 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_FUNCTIONAL_MODE 0x5002 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_IMAGE_SIZE 0x5003 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_COMPRESSION_SETTING 0x5004 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_WHITE_BALANCE 0x5005 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_RGB_GAIN 0x5006 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_F_NUMBER 0x5007 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCAL_LENGTH 0x5008 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCUS_DISTANCE 0x5009 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCUS_MODE 0x500A +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_METERING_MODE 0x500B +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_FLASH_MODE 0x500C +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_TIME 0x500D +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_PROGRAM_MODE 0x500E +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_INDEX 0x500F +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_BIAS_COMPENSATION 0x5010 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_DATE_TIME 0x5011 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_CAPTURE_DELAY 0x5012 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_STILL_CAPTURE_MODE 0x5013 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_CONTRAST 0x5014 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_SHARPNESS 0x5015 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_DIGITAL_ZOOM 0x5016 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_EFFECT_MODE 0x5017 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_BURST_NUMBER 0x5018 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_BURST_INTERVAL 0x5019 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_TIME_LAPSE_NUMBER 0x501A +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_TIME_LAPSE_INTERVAL 0x501B +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCUS_METERING_MODE 0x501C +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_UPLOAD_URL 0x501D +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_ARTIST 0x501E +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_COPYRIGHT_INFO 0x501F +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_SYNCHRONIZATION_PARTNER 0xD401 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_DEVICE_FRIENDLY_NAME 0xD402 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_VOLUME 0xD403 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_SUPPORTED_FORMATS_ORDERED 0xD404 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_DEVICE_ICON 0xD405 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_PLAYBACK_RATE 0xD410 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_PLAYBACK_OBJECT 0xD411 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_PLAYBACK_CONTAINER 0xD412 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_SESSION_INITIATOR_VERSION_INFO 0xD406 +#define UX_DEVICE_CLASS_PIMA_DEV_PROP_PERCEIVED_DEVICE_TYPE 0xD407 + +/* Define PIMA Object Format Codes. */ + +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_STORAGEID 0xDC01 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FORMAT 0xDC02 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PROTECTION_STATUS 0xDC03 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_SIZE 0xDC04 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ASSOCIATION_TYPE 0xDC05 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ASSOCIATION_DESC 0xDC06 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_FILE_NAME 0xDC07 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DATE_CREATED 0xDC08 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DATE_MODIFIED 0xDC09 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_KEYWORDS 0xDC0A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PARENT_OBJECT 0xDC0B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ALLOWED_FOLDER_CONTENTS 0xDC0C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_HIDDEN 0xDC0D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SYSTEM_OBJECT 0xDC0E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PERSISTENT_UNIQUE_OBJECT_IDENTIFIER 0xDC41 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SYNCID 0xDC42 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PROPERTY_BAG 0xDC43 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_NAME 0xDC44 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_CREATED_BY 0xDC45 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ARTIST 0xDC46 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DATE_AUTHORED 0xDC47 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DESCRIPTION 0xDC48 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_URL_REFERENCE 0xDC49 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_LANGUAGE_LOCALE 0xDC4A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_COPYRIGHT_INFORMATION 0xDC4B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SOURCE 0xDC4C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ORIGIN_LOCATION 0xDC4D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DATE_ADDED 0xDC4E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_NON_CONSUMABLE 0xDC4F +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_CORRUPT_UNPLAYABLE 0xDC50 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PRODUCER_SERIA_LNUMBER 0xDC51 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_REPRESENTATIVE_SAMPLE_FORMAT 0xDC81 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_REPRESENTATIVE_SAMPLE_SIZE 0xDC82 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_REPRESENTATIVE_SAMPLE_HEIGHT 0xDC83 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_REPRESENTATIVE_SAMPLE_WIDTH 0xDC84 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_REPRESENTATIVE_SAMPLE_DURATION 0xDC85 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_REPRESENTATIVE_SAMPLE_DATA 0xDC86 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_WIDTH 0xDC87 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_HEIGHT 0xDC88 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DURATION 0xDC89 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_RATING 0xDC8A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_TRACK 0xDC8B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_GENRE 0xDC8C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_CREDITS 0xDC8D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_LYRICS 0xDC8E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SUBSCRIPTION_CONTENT_ID 0xDC8F +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PRODUCED_BY 0xDC90 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_USE_COUNT 0xDC91 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SKIP_COUNT 0xDC92 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_LAST_ACCESSED 0xDC93 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PARENTAL_RATING 0xDC94 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_META_GENRE 0xDC95 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_COMPOSER 0xDC96 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EFFECTIVE_RATING 0xDC97 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SUBTITLE 0xDC98 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ORIGINAL_RELEASE_DATE 0xDC99 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ALBUM_NAME 0xDC9A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ALBUM_ARTIST 0xDC9B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MOOD 0xDC9C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DRM_STATUS 0xDC9D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SUB_DESCRIPTION 0xDC9E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_IS_CROPPED 0xDCD1 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_IS_COLOUR_CORRECTED 0xDCD2 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_IMAGE_BIT_DEPTH 0xDCD3 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_FNUMBER 0xDCD4 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EXPOSURE_TIME 0xDCD5 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EXPOSURE_INDEX 0xDCD6 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_TOTAL_BITRATE 0xDE91 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_BITRATE_TYPE 0xDE92 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SAMPLE_RATE 0xDE93 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_NUMBER_OF_CHANNELS 0xDE94 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_AUDIO_BITDEPTH 0xDE95 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SCAN_TYPE 0xDE97 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_AUDIO_WAVE_CODEC 0xDE99 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_AUDIO_BITRATE 0xDE9A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_VIDEO_FOURCC_CODEC 0xDE9B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_VIDEO_BITRATE 0xDE9C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_FRAMES_PER_THOUSAND_SECONDS 0xDE9D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_KEYFRAME_DISTANCE 0xDE9E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_BUFFER_SIZE 0xDE9F +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ENCODING_QUALITY 0xDEA0 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ENCODING_PROFILE 0xDEA1 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_DISPLAY_NAME 0xDCE0 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_BODY_TEXT 0xDCE1 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SUBJECT 0xDCE2 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PRIORITY 0xDCE3 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_GIVEN_NAME 0xDD00 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MIDDLE_NAMES 0xDD01 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_FAMILY_NAME 0xDD02 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PREFIX 0xDD03 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_SUFFIX 0xDD04 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONETIC_GIVEN_NAME 0xDD05 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONETIC_FAMILY_NAME 0xDD06 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EMAIL_PRIMARY 0xDD07 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EMAIL_PERSONAL_1 0xDD08 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EMAIL_PERSONAL_2 0xDD09 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EMAIL_BUSINESS_1 0xDD0A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EMAIL_BUSINESS_2 0xDD0B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EMAIL_OTHERS 0xDD0C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_PRIMARY 0xDD0D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_PERSONAL 0xDD0E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_PERSONAL_2 0xDD0F +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_BUSINESS 0xDD10 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_BUSINESS_2 0xDD11 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_MOBILE 0xDD12 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_MOBILE_2 0xDD13 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_FAX_NUMBER_PRIMARY 0xDD14 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_FAX_NUMBER_PERSONAL 0xDD15 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_FAX_NUMBER_BUSINESS 0xDD16 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PAGER_NUMBER 0xDD17 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONE_NUMBER_OTHERS 0xDD18 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PRIMARY_WEB_ADDRESS 0xDD19 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PERSONAL_WEB_ADDRESS 0xDD1A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_BUSINESS_WEB_ADDRESS 0xDD1B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_INSTANT_MESSENGER_ADDRESS 0xDD1C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_INSTANT_MESSENGER_ADDRESS_2 0xDD1D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_INSTANT_MESSENGER_ADDRESS_3 0xDD1E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_PERSONAL_FULL 0xDD1F +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_PERSONAL_LINE_1 0xDD20 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_PERSONAL_LINE_2 0xDD21 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_PERSONAL_CITY 0xDD22 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_PERSONAL_REGION 0xDD23 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE 0xDD24 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_PERSONAL_COUNTRY 0xDD25 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_BUSINESS_FULL 0xDD26 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_BUSINESS_LINE_1 0xDD27 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_BUSINESS_LINE_2 0xDD28 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_BUSINESS_CITY 0xDD29 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_BUSINESS_REGION 0xDD2A +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE 0xDD2B +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_BUSINESS_COUNTRY 0xDD2C +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_OTHER_FULL 0xDD2D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_OTHER_LINE_1 0xDD2E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_OTHER_LINE_2 0xDD2F +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_OTHER_CITY 0xDD30 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_OTHER_REGION 0xDD31 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_OTHER_POSTAL_CODE 0xDD32 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_POSTAL_ADDRESS_OTHER_COUNTRY 0xDD33 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ORGANIZATION_NAME 0xDD34 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_PHONETIC_ORGANIZATION_NAME 0xDD35 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ROLE 0xDD36 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_BIRTHDATE 0xDD37 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MESSAGE_TO 0xDD40 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MESSAGE_CC 0xDD41 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MESSAGE_BCC 0xDD42 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MESSAGE_READ 0xDD43 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MESSAGE_RECEIVED_TIME 0xDD44 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MESSAGE_SENDER 0xDD45 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ACTIVITY_BEGIN_TIME 0xDD50 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ACTIVITY_END_TIME 0xDD51 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ACTIVITY_LOCATION 0xDD52 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ACTIVITY_REQUIRED_ATTENDEES 0xDD54 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ACTIVITY_OPTIONAL_ATTENDEES 0xDD55 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ACTIVITY_RESOURCES 0xDD56 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_ACTIVITY_ACCEPTED 0xDD57 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OWNER 0xDD5D +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_EDITOR 0xDD5E +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_WEBMASTER 0xDD5F +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_URL_SOURCE 0xDD60 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_URL_DESTINATION 0xDD61 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_TIME_BOOKMARK 0xDD62 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_OBJECT_BOOKMARK 0xDD63 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_BYTE_BOOKMARK 0xDD64 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_LAST_BUILD_DATE 0xDD70 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_TIME_TO_LIVE 0xDD71 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROP_MEDIA_GUID 0xDD72 + +/* Define PIMA Object Protection Status Values. */ + +#define UX_DEVICE_CLASS_PIMA_OPS_NO_PROTECTION 0x0000 +#define UX_DEVICE_CLASS_PIMA_OPS_READ_ONLY 0x0001 + +/* Define PIMA Storage Types Codes. */ + +#define UX_DEVICE_CLASS_PIMA_STC_UNDEFINED 0x0000 +#define UX_DEVICE_CLASS_PIMA_STC_FIXED_ROM 0x0001 +#define UX_DEVICE_CLASS_PIMA_STC_REMOVABLE_ROM 0x0002 +#define UX_DEVICE_CLASS_PIMA_STC_FIXED_RAM 0x0003 +#define UX_DEVICE_CLASS_PIMA_STC_REMOVABLE_RAM 0x0004 + +/* Define PIMA File System Types Codes. */ + +#define UX_DEVICE_CLASS_PIMA_FSTC_UNDEFINED 0x0000 +#define UX_DEVICE_CLASS_PIMA_FSTC_GENERIC_FLAT 0x0001 +#define UX_DEVICE_CLASS_PIMA_FSTC_GENERIC_HIERARCHICAL 0x0002 +#define UX_DEVICE_CLASS_PIMA_FSTC_DCF 0x0003 + +/* Define PIMA File System Access Types Codes. */ + +#define UX_DEVICE_CLASS_PIMA_AC_READ_WRITE 0x0000 +#define UX_DEVICE_CLASS_PIMA_AC_RO_WITHOUT_OBJECT_DELETION 0x0001 +#define UX_DEVICE_CLASS_PIMA_AC_RO_WITH_OBJECT_DELETION 0x0002 + +/* Define PIMA types. */ +#define UX_DEVICE_CLASS_PIMA_TYPES_INT8 0x0001 +#define UX_DEVICE_CLASS_PIMA_TYPES_UINT8 0x0002 +#define UX_DEVICE_CLASS_PIMA_TYPES_INT16 0x0003 +#define UX_DEVICE_CLASS_PIMA_TYPES_UINT16 0x0004 +#define UX_DEVICE_CLASS_PIMA_TYPES_INT32 0x0005 +#define UX_DEVICE_CLASS_PIMA_TYPES_UINT32 0x0006 +#define UX_DEVICE_CLASS_PIMA_TYPES_INT64 0x0007 +#define UX_DEVICE_CLASS_PIMA_TYPES_UINT64 0x0008 +#define UX_DEVICE_CLASS_PIMA_TYPES_INT128 0x0009 +#define UX_DEVICE_CLASS_PIMA_TYPES_UINT128 0x000A +#define UX_DEVICE_CLASS_PIMA_TYPES_AINT8 0x4001 +#define UX_DEVICE_CLASS_PIMA_TYPES_AUINT8 0x4002 +#define UX_DEVICE_CLASS_PIMA_TYPES_AINT16 0x4003 +#define UX_DEVICE_CLASS_PIMA_TYPES_AUINT16 0x4004 +#define UX_DEVICE_CLASS_PIMA_TYPES_AINT32 0x4005 +#define UX_DEVICE_CLASS_PIMA_TYPES_AUINT32 0x4006 +#define UX_DEVICE_CLASS_PIMA_TYPES_AINT64 0x4007 +#define UX_DEVICE_CLASS_PIMA_TYPES_AUINT64 0x4008 +#define UX_DEVICE_CLASS_PIMA_TYPES_AINT128 0x4009 +#define UX_DEVICE_CLASS_PIMA_TYPES_AUINT128 0x400A +#define UX_DEVICE_CLASS_PIMA_TYPES_STR 0xFFFF + +/* Define PIMA Device Info fields. */ + +#define UX_DEVICE_CLASS_PIMA_DEVICE_INFO_STANDARD_VERSION (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 0x0000) +#define UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_ID (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 0x0002) +#define UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_VERSION (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 0x0006) +#define UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_DESC (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 0x0008) + +/* Define PIMA MTP OBJECT PROPERTY DATASET. */ +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_CODE 0x0000 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_DATATYPE 0x0002 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_GETSET 0x0004 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_VALUE 0x0005 + +/* Define PIMA Dataset equivalences. */ +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_VALUE_GET 0x00 +#define UX_DEVICE_CLASS_PIMA_OBJECT_PROPERTY_DATASET_VALUE_GETSET 0x01 + +/* Define PIMA event info structure. */ + +typedef struct UX_SLAVE_CLASS_PIMA_EVENT_STRUCT +{ + ULONG ux_device_class_pima_event_code; + ULONG ux_device_class_pima_event_session_id; + ULONG ux_device_class_pima_event_transaction_id; + ULONG ux_device_class_pima_event_parameter_1; + ULONG ux_device_class_pima_event_parameter_2; + ULONG ux_device_class_pima_event_parameter_3; + +} UX_SLAVE_CLASS_PIMA_EVENT; + +/* Define PIMA object info structure. */ + +typedef struct UX_SLAVE_CLASS_PIMA_OBJECT_STRUCT +{ + + ULONG ux_device_class_pima_object_storage_id; + ULONG ux_device_class_pima_object_format; + ULONG ux_device_class_pima_object_protection_status; + ULONG ux_device_class_pima_object_compressed_size; + ULONG ux_device_class_pima_object_thumb_format; + ULONG ux_device_class_pima_object_thumb_compressed_size; + ULONG ux_device_class_pima_object_thumb_pix_width; + ULONG ux_device_class_pima_object_thumb_pix_height; + ULONG ux_device_class_pima_object_image_pix_width; + ULONG ux_device_class_pima_object_image_pix_height; + ULONG ux_device_class_pima_object_image_bit_depth; + ULONG ux_device_class_pima_object_parent_object; + ULONG ux_device_class_pima_object_association_type; + ULONG ux_device_class_pima_object_association_desc; + ULONG ux_device_class_pima_object_sequence_number; + UCHAR ux_device_class_pima_object_filename[UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH]; + UCHAR ux_device_class_pima_object_capture_date[UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_device_class_pima_object_modification_date[UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_device_class_pima_object_keywords[UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH]; + ULONG ux_device_class_pima_object_state; + ULONG ux_device_class_pima_object_offset; + ULONG ux_device_class_pima_object_transfer_status; + ULONG ux_device_class_pima_object_handle_id; + ULONG ux_device_class_pima_object_length; + UCHAR *ux_device_class_pima_object_buffer; + +} UX_SLAVE_CLASS_PIMA_OBJECT; + +#define UX_SLAVE_CLASS_PIMA_OBJECT_DATA_LENGTH ((15 * sizeof(ULONG)) + \ + UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH + \ + UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH + \ + UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH + \ + UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH) + +/* Define PIMA session info structure. Not used in the device. Here for structure compatibility. */ + +typedef struct UX_SLAVE_CLASS_PIMA_SESSION_STRUCT +{ + + ULONG ux_device_class_pima_session_id; + +} UX_SLAVE_CLASS_PIMA_SESSION; + +/* Define PIMA device info structure. */ + +typedef struct UX_SLAVE_CLASS_PIMA_DEVICE_STRUCT +{ + + ULONG ux_device_class_pima_device_standard_version; + ULONG ux_device_class_pima_device_vendor_extension_id; + ULONG ux_device_class_pima_device_vendor_extension_version; + UCHAR ux_device_class_pima_device_vendor_extension_desc[UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH]; + ULONG ux_device_class_pima_device_functional_mode; + UCHAR ux_device_class_pima_device_operations_supported[UX_DEVICE_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_events_supported[UX_DEVICE_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_properties_supported[UX_DEVICE_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_capture_formats[UX_DEVICE_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_image_formats[UX_DEVICE_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_manufacturer[UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_model[UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_version[UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_device_class_pima_device_serial_number[UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH]; + +} UX_SLAVE_CLASS_PIMA_DEVICE; + +/* Define PIMA storage info structure. */ + +typedef struct UX_SLAVE_CLASS_PIMA_STORAGE_STRUCT +{ + + ULONG ux_device_class_pima_storage_type; + ULONG ux_device_class_pima_storage_file_system_type; + ULONG ux_device_class_pima_storage_access_capability; + ULONG ux_device_class_pima_storage_max_capacity_low; + ULONG ux_device_class_pima_storage_max_capacity_high; + ULONG ux_device_class_pima_storage_free_space_bytes_low; + ULONG ux_device_class_pima_storage_free_space_bytes_high; + ULONG ux_device_class_pima_storage_free_space_images; + UCHAR ux_device_class_pima_storage_description[UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH]; + UCHAR ux__class_pima_storage_volume_label[UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH]; + +} UX_SLAVE_CLASS_PIMA_STORAGE; + +/* Define PIMA structure. */ + +typedef struct UX_SLAVE_CLASS_PIMA_STRUCT +{ + + UX_SLAVE_INTERFACE *ux_slave_class_pima_interface; + UX_SLAVE_ENDPOINT *ux_device_class_pima_bulk_in_endpoint; + UX_SLAVE_ENDPOINT *ux_device_class_pima_bulk_out_endpoint; + UX_SLAVE_ENDPOINT *ux_device_class_pima_interrupt_endpoint; + UINT ux_device_class_pima_state; + ULONG ux_device_class_pima_session_id; + ULONG ux_device_class_pima_current_object_handle; + ULONG ux_device_class_pima_transaction_id; + UCHAR *ux_device_class_pima_manufacturer; + UCHAR *ux_device_class_pima_model; + UCHAR *ux_device_class_pima_device_version; + UCHAR *ux_device_class_pima_serial_number; + ULONG ux_device_class_pima_storage_id; + ULONG ux_device_class_pima_storage_type; + ULONG ux_device_class_pima_storage_file_system_type; + ULONG ux_device_class_pima_storage_access_capability; + ULONG ux_device_class_pima_storage_max_capacity_low; + ULONG ux_device_class_pima_storage_max_capacity_high; + ULONG ux_device_class_pima_storage_free_space_low; + ULONG ux_device_class_pima_storage_free_space_high; + ULONG ux_device_class_pima_storage_free_space_image; + UCHAR *ux_device_class_pima_storage_description; + UCHAR *ux_device_class_pima_storage_volume_label; + TX_SEMAPHORE ux_device_class_pima_semaphore; + TX_THREAD ux_device_class_pima_interrupt_thread; + UCHAR *ux_device_class_pima_interrupt_thread_stack; + TX_SEMAPHORE ux_device_class_pima_interrupt_thread_semaphore; + UX_SLAVE_CLASS_PIMA_EVENT + *ux_device_class_pima_event_array; + UX_SLAVE_CLASS_PIMA_EVENT + *ux_device_class_pima_event_array_head; + UX_SLAVE_CLASS_PIMA_EVENT + *ux_device_class_pima_event_array_tail; + UX_SLAVE_CLASS_PIMA_EVENT + *ux_device_class_pima_event_array_end; + USHORT *ux_device_class_pima_device_properties_list; + USHORT *ux_device_class_pima_supported_capture_formats_list; + USHORT *ux_device_class_pima_supported_image_formats_list; + USHORT *ux_device_class_pima_object_properties_list; + UINT (*ux_device_class_pima_device_reset)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima); + + UINT (*ux_device_class_pima_device_prop_desc_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG device_property, UCHAR **device_prop_dataset, ULONG *device_prop_dataset_length); + UINT (*ux_device_class_pima_device_prop_value_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG device_property, UCHAR **device_prop_value, ULONG *device_prop_value_length); + UINT (*ux_device_class_pima_device_prop_value_set)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG device_property, UCHAR *device_prop_value, ULONG device_prop_value_length); + + UINT (*ux_device_class_pima_storage_format)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG storage_id); + UINT (*ux_device_class_pima_storage_info_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG storage_id); + UINT (*ux_device_class_pima_object_number_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_format_code, ULONG object_association, ULONG *object_number); + UINT (*ux_device_class_pima_object_handles_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handles_format_code, + ULONG object_handles_association, + ULONG *object_handles_array, + ULONG object_handles_max_number); + UINT (*ux_device_class_pima_object_info_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UX_SLAVE_CLASS_PIMA_OBJECT **object); + UINT (*ux_device_class_pima_object_data_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UCHAR *object_buffer, ULONG object_offset, + ULONG object_length_requested, ULONG *object_actual_length); + UINT (*ux_device_class_pima_object_info_send)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, UX_SLAVE_CLASS_PIMA_OBJECT *object, ULONG storage_id, ULONG parent_object_handle, ULONG *object_handle); + UINT (*ux_device_class_pima_object_data_send)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, ULONG phase, UCHAR *object_buffer, ULONG object_offset, + ULONG object_length); + UINT (*ux_device_class_pima_object_delete)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle); + UINT (*ux_device_class_pima_object_prop_desc_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_property, ULONG object_format_code, UCHAR **object_prop_value_dataset, ULONG *object_prop_value_dataset_length); + UINT (*ux_device_class_pima_object_prop_value_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, ULONG object_property, UCHAR **object_prop_value, ULONG *object_prop_value_length); + UINT (*ux_device_class_pima_object_prop_value_set)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, ULONG object_property, UCHAR *object_prop_value, ULONG object_prop_value_length); + UINT (*ux_device_class_pima_object_references_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UCHAR **object_handle_array, ULONG *object_handle_array_length); + UINT (*ux_device_class_pima_object_references_set)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UCHAR *object_handle_array, ULONG object_handle_array_length); + VOID *ux_device_class_pima_application; + VOID (*ux_device_class_pima_instance_activate)(VOID *); + VOID (*ux_device_class_pima_instance_deactivate)(VOID *); + + +} UX_SLAVE_CLASS_PIMA; + +/* Define PIMA initialization command structure. */ + +typedef struct UX_SLAVE_CLASS_PIMA_PARAMETER_STRUCT +{ + + VOID (*ux_device_class_pima_instance_activate)(VOID *); + VOID (*ux_device_class_pima_instance_deactivate)(VOID *); + UCHAR *ux_device_class_pima_parameter_manufacturer; + UCHAR *ux_device_class_pima_parameter_model; + UCHAR *ux_device_class_pima_parameter_device_version; + UCHAR *ux_device_class_pima_parameter_serial_number; + ULONG ux_device_class_pima_parameter_storage_id; + ULONG ux_device_class_pima_parameter_storage_type; + ULONG ux_device_class_pima_parameter_storage_file_system_type; + ULONG ux_device_class_pima_parameter_storage_access_capability; + ULONG ux_device_class_pima_parameter_storage_max_capacity_low; + ULONG ux_device_class_pima_parameter_storage_max_capacity_high; + ULONG ux_device_class_pima_parameter_storage_free_space_low; + ULONG ux_device_class_pima_parameter_storage_free_space_high; + ULONG ux_device_class_pima_parameter_storage_free_space_image; + UCHAR *ux_device_class_pima_parameter_storage_description; + UCHAR *ux_device_class_pima_parameter_storage_volume_label; + USHORT *ux_device_class_pima_parameter_device_properties_list; + USHORT *ux_device_class_pima_parameter_supported_capture_formats_list; + USHORT *ux_device_class_pima_parameter_supported_image_formats_list; + USHORT *ux_device_class_pima_parameter_object_properties_list; + UINT (*ux_device_class_pima_parameter_device_reset)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima); + UINT (*ux_device_class_pima_parameter_device_prop_desc_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG device_property, UCHAR **device_prop_dataset, ULONG *device_prop_dataset_length); + UINT (*ux_device_class_pima_parameter_device_prop_value_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG device_property, UCHAR **device_prop_value, ULONG *device_prop_value_length); + UINT (*ux_device_class_pima_parameter_device_prop_value_set)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG device_property, UCHAR *device_prop_value, ULONG device_prop_value_length); + UINT (*ux_device_class_pima_parameter_storage_format)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG storage_id); + UINT (*ux_device_class_pima_parameter_storage_info_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG storage_id); + UINT (*ux_device_class_pima_parameter_object_number_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_format_code, ULONG object_association, ULONG *object_number); + UINT (*ux_device_class_pima_parameter_object_handles_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handles_format_code, + ULONG object_handles_association, + ULONG *object_handles_array, + ULONG object_handles_max_number); + UINT (*ux_device_class_pima_parameter_object_info_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UX_SLAVE_CLASS_PIMA_OBJECT **object); + UINT (*ux_device_class_pima_parameter_object_data_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UCHAR *object_buffer, ULONG object_offset, + ULONG object_length_requested, ULONG *object_actual_length); + + UINT (*ux_device_class_pima_parameter_object_info_send)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, UX_SLAVE_CLASS_PIMA_OBJECT *object, ULONG storage_id, ULONG parent_object_handle, ULONG *object_handle); + UINT (*ux_device_class_pima_parameter_object_data_send)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, ULONG phase ,UCHAR *object_buffer, ULONG object_offset, + ULONG object_length); + UINT (*ux_device_class_pima_parameter_object_delete)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle); + UINT (*ux_device_class_pima_parameter_object_prop_desc_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, ULONG object_property, UCHAR **object_prop_dataset, ULONG *object_prop_dataset_length); + UINT (*ux_device_class_pima_parameter_object_prop_value_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, ULONG object_property, UCHAR **object_prop_value, ULONG *object_prop_value_length); + UINT (*ux_device_class_pima_parameter_object_prop_value_set)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, ULONG object_property, UCHAR *object_prop_value, ULONG object_prop_value_length); + UINT (*ux_device_class_pima_parameter_object_references_get)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UCHAR **object_handle_array, ULONG *object_handle_array_length); + UINT (*ux_device_class_pima_parameter_object_references_set)(struct UX_SLAVE_CLASS_PIMA_STRUCT *pima, ULONG object_handle, UCHAR *object_handle_array, ULONG object_handle_array_length); + VOID *ux_device_class_pima_parameter_application; + + +} UX_SLAVE_CLASS_PIMA_PARAMETER; + + +/* Define PIMA Object decompaction structure. */ + +#define UX_DEVICE_CLASS_PIMA_OBJECT_MAX_LENGTH 512 +#define UX_DEVICE_CLASS_PIMA_OBJECT_VARIABLE_OFFSET 52 +#define UX_DEVICE_CLASS_PIMA_OBJECT_ENTRIES 15 + + +/* Define PIMA storage decompaction structure. */ + +#define UX_DEVICE_CLASS_PIMA_STORAGE_TYPE (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 0 ) +#define UX_DEVICE_CLASS_PIMA_STORAGE_FILE_SYSTEM_TYPE (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 2 ) +#define UX_DEVICE_CLASS_PIMA_STORAGE_ACCESS_CAPABILITY (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 4 ) +#define UX_DEVICE_CLASS_PIMA_STORAGE_MAX_CAPACITY_LOW (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 6 ) +#define UX_DEVICE_CLASS_PIMA_STORAGE_MAX_CAPACITY_HIGH (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 10) +#define UX_DEVICE_CLASS_PIMA_STORAGE_FREE_SPACE_LOW (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 14) +#define UX_DEVICE_CLASS_PIMA_STORAGE_FREE_SPACE_HIGH (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 18) +#define UX_DEVICE_CLASS_PIMA_STORAGE_FREE_SPACE_IMAGE (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 22) +#define UX_DEVICE_CLASS_PIMA_STORAGE_FREE_STORAGE_DESCRIPTION (UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + 26) + +/* Define Pima Class function prototypes. */ +UINT _ux_device_class_pima_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_pima_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_pima_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_pima_control_request(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_pima_device_info_send(UX_SLAVE_CLASS_PIMA *pima); +UINT _ux_device_class_pima_entry(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_pima_event_get(UX_SLAVE_CLASS_PIMA *pima, + UX_SLAVE_CLASS_PIMA_EVENT *pima_event); +UINT _ux_device_class_pima_event_set(UX_SLAVE_CLASS_PIMA *pima, + UX_SLAVE_CLASS_PIMA_EVENT *pima_event); +VOID _ux_device_class_pima_interrupt_thread(ULONG pima_class); +UINT _ux_device_class_pima_response_send(UX_SLAVE_CLASS_PIMA *pima, ULONG response_code, + ULONG number_parameters, + ULONG parameter_1, ULONG parameter_2, ULONG paramater_3); +VOID _ux_device_class_pima_thread(ULONG pima_class); +UINT _ux_device_class_pima_object_handles_send(UX_SLAVE_CLASS_PIMA *pima, + ULONG storage_id, + ULONG object_format_code, + ULONG object_association); +UINT _ux_device_class_pima_objects_number_send(UX_SLAVE_CLASS_PIMA *pima, + ULONG storage_id, + ULONG object_format_code, + ULONG object_association); + +UINT _ux_device_class_pima_device_prop_desc_get(UX_SLAVE_CLASS_PIMA *pima, ULONG device_property_code); +UINT _ux_device_class_pima_device_prop_value_get(UX_SLAVE_CLASS_PIMA *pima, ULONG device_property_code); +UINT _ux_device_class_pima_device_prop_value_set(UX_SLAVE_CLASS_PIMA *pima, ULONG device_property_code); +UINT _ux_device_class_pima_object_info_get(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle); +UINT _ux_device_class_pima_object_info_send(UX_SLAVE_CLASS_PIMA *pima, ULONG storage_id, ULONG parent_object_handle); +UINT _ux_device_class_pima_object_data_get(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle); +UINT _ux_device_class_pima_object_data_send(UX_SLAVE_CLASS_PIMA *pima); +UINT _ux_device_class_pima_object_delete(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle); +UINT _ux_device_class_pima_object_add(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle); +UINT _ux_device_class_pima_partial_object_data_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG offset_requested, + ULONG length_requested); + +UINT _ux_device_class_pima_storage_id_send(UX_SLAVE_CLASS_PIMA *pima); +UINT _ux_device_class_pima_storage_info_get(UX_SLAVE_CLASS_PIMA *pima, ULONG storage_id); +UINT _ux_device_class_pima_object_props_supported_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_format_code); +UINT _ux_device_class_pima_object_prop_value_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG object_property_code); +UINT _ux_device_class_pima_object_prop_value_set(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG object_property_code); +UINT _ux_device_class_pima_object_prop_desc_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_property, + ULONG object_format_code); +UINT _ux_device_class_pima_object_references_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle); +UINT _ux_device_class_pima_object_references_set(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle); +UINT _ux_device_class_pima_object_prop_value_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG object_property_code); +UINT _ux_device_class_pima_object_prop_value_set(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG object_property_code); +UINT _ux_device_class_pima_storage_format(UX_SLAVE_CLASS_PIMA *pima, ULONG storage_id); +UINT _ux_device_class_pima_device_reset(UX_SLAVE_CLASS_PIMA *pima); + +/* Define Device PIMA Class API prototypes. */ + +#define ux_device_class_pima_initialize _ux_device_class_pima_initialize +#define ux_device_class_pima_activate _ux_device_class_pima_activate +#define ux_device_class_pima_dectivate _ux_device_class_pima_dectivate +#define ux_device_class_pima_entry _ux_device_class_pima_entry +#define ux_device_class_pima_control_request _ux_device_class_pima_control_request +#define ux_device_class_pima_object_add _ux_device_class_pima_object_add + +/* Define Pima Class data prototypes. */ + +extern UCHAR _ux_device_class_pima_vendor_extension_descriptor[]; +extern USHORT _ux_device_class_pima_supported_operations[]; +extern USHORT _ux_device_class_pima_supported_events[]; +extern USHORT _ux_device_class_pima_supported_capture_formats[]; +extern USHORT _ux_device_class_pima_supported_image_formats[]; +extern USHORT _ux_device_class_pima_device_prop_supported[]; + + + +#endif diff --git a/common/usbx_device_classes/inc/ux_device_class_rndis.h b/common/usbx_device_classes/inc/ux_device_class_rndis.h new file mode 100644 index 0000000..c934b18 --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_rndis.h @@ -0,0 +1,570 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_rndis.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the equivalences for the USBX Device Class RNDIS */ +/* component. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_RNDIS_H +#define UX_DEVICE_CLASS_RNDIS_H + +#include "nx_api.h" +#include "ux_network_driver.h" + +/* Define generic RNDIS equivalences. */ +#define UX_DEVICE_CLASS_RNDIS_CLASS_COMMUNICATION_CONTROL 0x02 +#define UX_DEVICE_CLASS_RNDIS_CLASS_COMMUNICATION_DATA 0x0A +#define UX_DEVICE_CLASS_RNDIS_NEW_INTERRUPT_EVENT 1 +#define UX_DEVICE_CLASS_RNDIS_NEW_BULKOUT_EVENT 2 +#define UX_DEVICE_CLASS_RNDIS_NEW_BULKIN_EVENT 4 +#define UX_DEVICE_CLASS_RNDIS_NEW_DEVICE_STATE_CHANGE_EVENT 8 +#define UX_DEVICE_CLASS_RNDIS_INTERRUPT_RESPONSE_LENGTH 8 +#define UX_DEVICE_CLASS_RNDIS_INTERRUPT_RESPONSE_AVAILABLE_FLAG 1 +#define UX_DEVICE_CLASS_RNDIS_BASE_IP_ADDRESS 0xC0A80001 +#define UX_DEVICE_CLASS_RNDIS_BASE_IP_MASK 0xFFFFFF00 +#define UX_DEVICE_CLASS_RNDIS_MAX_MTU 1518 +#define UX_DEVICE_CLASS_RNDIS_ETHERNET_IP 0x0800 +#define UX_DEVICE_CLASS_RNDIS_ETHERNET_ARP 0x0806 +#define UX_DEVICE_CLASS_RNDIS_ETHERNET_RARP 0x8035 +#define UX_DEVICE_CLASS_RNDIS_ETHERNET_PACKET_SIZE 1536 +#define UX_DEVICE_CLASS_RNDIS_NX_ALIGN_PADDING 2 +#define UX_DEVICE_CLASS_RNDIS_NX_PKPOOL_ENTRIES 8 + +#define UX_DEVICE_CLASS_RNDIS_NX_PACKET_SIZE sizeof(NX_PACKET) + +#define UX_DEVICE_CLASS_RNDIS_NX_PAYLOAD_SIZE_ASSERT UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG(UX_DEVICE_CLASS_RNDIS_ETHERNET_PACKET_SIZE, UX_DEVICE_CLASS_RNDIS_NX_ALIGN_PADDING), UX_DEVICE_CLASS_RNDIS_NX_PAYLOAD_SIZE_calc_ovf) +#define UX_DEVICE_CLASS_RNDIS_NX_PAYLOAD_SIZE (UX_DEVICE_CLASS_RNDIS_ETHERNET_PACKET_SIZE + UX_DEVICE_CLASS_RNDIS_NX_ALIGN_PADDING) + +#define UX_DEVICE_CLASS_RNDIS_NX_BUFF_SIZE_ASSERT \ + UX_DEVICE_CLASS_RNDIS_NX_PAYLOAD_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_DEVICE_CLASS_RNDIS_NX_PAYLOAD_SIZE, \ + UX_DEVICE_CLASS_RNDIS_NX_PACKET_SIZE), \ + UX_DEVICE_CLASS_RNDIS_NX_BUFF_SIZE_calc_ovf) +#define UX_DEVICE_CLASS_RNDIS_NX_BUFF_SIZE (UX_DEVICE_CLASS_RNDIS_NX_PAYLOAD_SIZE + UX_DEVICE_CLASS_RNDIS_NX_PACKET_SIZE) + +#define UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT \ + UX_DEVICE_CLASS_RNDIS_NX_BUFF_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG( \ + UX_DEVICE_CLASS_RNDIS_NX_PKPOOL_ENTRIES, \ + UX_DEVICE_CLASS_RNDIS_NX_BUFF_SIZE), \ + UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE_calc1_ovf) \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_DEVICE_CLASS_RNDIS_NX_PKPOOL_ENTRIES * \ + UX_DEVICE_CLASS_RNDIS_NX_BUFF_SIZE, \ + 32), UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE_calc2_ovf) +#define UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE (UX_DEVICE_CLASS_RNDIS_NX_PKPOOL_ENTRIES * UX_DEVICE_CLASS_RNDIS_NX_BUFF_SIZE + 32) + +#define UX_DEVICE_CLASS_RNDIS_ETHERNET_SIZE 14 +#define UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH 6 +#define UX_DEVICE_CLASS_RNDIS_VENDOR_DESCRIPTION_MAX_LENGTH 64 +#define UX_DEVICE_CLASS_RNDIS_MAC_OPTIONS 8 +#define UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_MSG 1 + +#define UX_DEVICE_CLASS_RNDIS_OID_SUPPORTED_LIST_LENGTH 30 + +/* Device RNDIS Requests */ +#define UX_DEVICE_CLASS_RNDIS_SEND_ENCAPSULATED_COMMAND 0x00 +#define UX_DEVICE_CLASS_RNDIS_GET_ENCAPSULATED_RESPONSE 0x01 + +/* Define RNDIS Versions. Set to 1.0 here. */ +#define UX_DEVICE_CLASS_RNDIS_VERSION_MAJOR 0x00000001 +#define UX_DEVICE_CLASS_RNDIS_VERSION_MINOR 0x00000000 + +/* Define RNDIS Conection type supported. Set to conectionless. */ +#define UX_DEVICE_CLASS_RNDIS_DF_CONNECTIONLESS 0x00000001 +#define UX_DEVICE_CLASS_RNDIS_DF_CONNECTION_ORIENTED 0x00000002 +#define UX_DEVICE_CLASS_RNDIS_DF_CONNECTION_SUPPORTED UX_DEVICE_CLASS_RNDIS_DF_CONNECTIONLESS + +/* Define RNDIS Medium supported by the device. */ +#define UX_DEVICE_CLASS_RNDIS_MEDIUM_SUPPORTED 0x00000000 + +/* Define RNDIS Packet size and types supported. */ +#define UX_DEVICE_CLASS_RNDIS_MAX_PACKET_PER_TRANSFER 0x00000001 +#define UX_DEVICE_CLASS_RNDIS_MAX_PACKET_TRANSFER_SIZE 0x00000640 +#define UX_DEVICE_CLASS_RNDIS_PACKET_ALIGNEMENT_FACTOR 0x00000003 +#define UX_DEVICE_CLASS_RNDIS_MAX_FRAME_SIZE 0x000005DC +#define UX_DEVICE_CLASS_RNDIS_MAX_PACKET_LENGTH 0x000005EA + +/* Define LINK speeds. */ +#define UX_DEVICE_CLASS_RNDIS_LINK_SPEED_FS 0x0001D4C0 + +/* Define LINK statess. */ +#define UX_DEVICE_CLASS_RNDIS_LINK_STATE_DOWN 0 +#define UX_DEVICE_CLASS_RNDIS_LINK_STATE_UP 1 +#define UX_DEVICE_CLASS_RNDIS_LINK_STATE_PENDING_UP 2 +#define UX_DEVICE_CLASS_RNDIS_LINK_STATE_PENDING_DOWN 3 + +/* Define media connection values. */ +#define UX_DEVICE_CLASS_RNDIS_MEDIA_CONNECTED 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_DISCONNECTED 0x00000001 + +/* Define media supported values. */ +#define UX_DEVICE_CLASS_RNDIS_MEDIA_802_3 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_802_5 0x00000001 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_FDDI 0x00000002 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_WAN 0x00000003 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_LOCAL_TALK 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_DIX 0x00000005 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_ARCNET_RAW 0x00000006 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_ARCNET_878_2 0x00000007 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_ATM 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_WIRELESS_WAN 0x00000009 +#define UX_DEVICE_CLASS_RNDIS_MEDIA_IRDA 0x0000000A + +/* Define RNDIS status values. */ +#define UX_DEVICE_CLASS_RNDIS_STATUS_SUCCESS 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_STATUS_FAILURE 0xC0000001 +#define UX_DEVICE_CLASS_RNDIS_STATUS_INVALID_DATA 0xC0010015 +#define UX_DEVICE_CLASS_RNDIS_STATUS_NOT_SUPPORTED 0xC00000BB +#define UX_DEVICE_CLASS_RNDIS_STATUS_MEDIA_CONNECTED 0x4001000B +#define UX_DEVICE_CLASS_RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000C + +/* Define RNDIS Control Messages values. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE 0x00000002 +#define UX_DEVICE_CLASS_RNDIS_MSG_HALT 0x00000003 +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_SET 0x00000005 +#define UX_DEVICE_CLASS_RNDIS_MSG_RESET 0x00000006 +#define UX_DEVICE_CLASS_RNDIS_MSG_INDICATE_STATUS 0x00000007 +#define UX_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE 0x00000008 + +/* Define RNDIS Control Completion values. */ +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE 0x80000002 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY 0x80000004 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_SET 0x80000005 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_RESET 0x80000006 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE 0x80000008 + +/* Define RNDIS Control Messages : MSG_INITIALIZE offsets. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MAJOR_VERSION 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MINOR_VERSION 0x00000010 +#define UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MAX_TRANSFER_SIZE 0x00000014 + +/* Define RNDIS Control Messages : CMPLT_INITIALIZE offsets. */ +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_STATUS 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MAJOR_VERSION 0x00000010 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MINOR_VERSION 0x00000014 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_DEVICE_FLAGS 0x00000018 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MEDIUM 0x0000001C +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MAX_PACKETS_PER_TRANSFER 0x00000020 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MAX_TRANSFER_SIZE 0x00000024 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_PACKET_ALIGNMENT 0x00000028 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_AFL_LIST_OFFSET 0x0000002C +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_AFL_LIST_SIZE 0x00000030 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_RESPONSE_LENGTH 0x00000034 + +/* Define RNDIS Control Messages : MSG_HALT offsets. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_HALT_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MSG_HALT_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_HALT_REQUEST_ID 0x00000008 + +/* Define RNDIS Control Messages : MSG_QUERY offsets. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY_OID 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY_INFO_BUFFER_LENGTH 0x00000010 +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY_INFO_BUFFER_OFFSET 0x00000014 +#define UX_DEVICE_CLASS_RNDIS_MSG_QUERY_DEVICE_VC_HANDLE 0x00000018 + +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_STATUS 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER_LENGTH 0x00000010 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER_OFFSET 0x00000014 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER 0x00000018 + +/* Define RNDIS Control Messages : MSG_SET offsets. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_SET_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MSG_SET_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_SET_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_MSG_SET_OID 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_MSG_SET_INFO_BUFFER_LENGTH 0x00000010 +#define UX_DEVICE_CLASS_RNDIS_MSG_SET_INFO_BUFFER_OFFSET 0x00000014 +#define UX_DEVICE_CLASS_RNDIS_MSG_SET_DEVICE_VC_HANDLE 0x00000018 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_SET_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_SET_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_SET_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_SET_STATUS 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_CMPLT_SET_RESPONSE_LENGTH 0x00000010 + +/* Define RNDIS Control Messages : MSG_RESET offsets. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_RESET_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MSG_RESET_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_RESET_RESERVED 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_STATUS 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_ADDRESSING_RESET 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_RESPONSE_LENGTH 0x00000010 + +/* Define RNDIS Control Messages : MSG_INDICATE_STATUS offsets. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_INDICATE_STATUS_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MSG_INDICATE_STATUS_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_INDICATE_STATUS_STATUS 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_MSG_INDICATE_STATUS_STATUS_BUFFER_LENGTH 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_MSG_INDICATE_STATUS_STATUS_BUFFER_OFFSET 0x00000010 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INDICATE_STATUS_DIAG 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_INDICATE_STATUS_ERROR_OFFSET 0x00000004 + +/* Define RNDIS Control Messages : MSG_KEEP_ALIVE offsets. */ +#define UX_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_REQUEST_ID 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_STATUS 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_RESPONSE_LENGTH 0x00000010 + +/* Define RNDIS State machine. */ +#define UX_DEVICE_CLASS_RNDIS_STATE_UNINITIALIZED 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_STATE_INITIALIZED 0x00000001 +#define UX_DEVICE_CLASS_RNDIS_STATE_DATA_INITIALIZED 0x00000002 + +/* Define Required Object IDs (OIDs) */ +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_SUPPORTED_LIST 0x00010101 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_HARDWARE_STATUS 0x00010102 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_IN_USE 0x00010104 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_LINK_SPEED 0x00010107 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_ID 0x0001010C +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_DRIVER_VERSION 0x00010110 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MAC_OPTIONS 0x00010113 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_SUPPORTED_GUIDS 0x00010117 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MACHINE_NAME 0x0001021A +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_VLAN_ID 0x0001021C + +/* Define Optional OIDs */ +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_CAPABILITIES 0x00010201 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_PHYSICAL_MEDIUM 0x00010202 + +/* Define Required statistics OIDs */ +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_OK 0x00020101 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_OK 0x00020102 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_ERROR 0x00020103 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_ERROR 0x00020104 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* Define Optional statistics OIDs */ +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_CRC_ERROR 0x0002020D +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_GET_TIME_CAPS 0x0002020F +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_GET_NETCARD_TIME 0x00020210 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_NETCARD_LOAD 0x00020211 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_DEVICE_PROFILE 0x00020212 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_INIT_TIME_MS 0x00020213 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RESET_COUNTS 0x00020214 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_FRIENDLY_NAME 0x00020216 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_MINIPORT_INFO 0x00020217 +#define UX_DEVICE_CLASS_RNDIS_OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 + +/* Define IEEE 802.3 (Ethernet) OIDs */ + +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_MULTICAST_LIST 0x01010103 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_MAC_OPTIONS 0x01010105 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_DEFERRED 0x01020201 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_RCV_OVERRUN 0x01020203 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_UNDERRUN 0x01020204 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +/* Define Hardware status code. */ +#define UX_DEVICE_CLASS_RNDIS_OID_HW_STATUS_READY 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_OID_HW_STATUS_INITIALIZING 0x00000001 +#define UX_DEVICE_CLASS_RNDIS_OID_HW_STATUS_RESET 0x00000002 +#define UX_DEVICE_CLASS_RNDIS_OID_HW_STATUS_CLOSING 0x00000003 +#define UX_DEVICE_CLASS_RNDIS_OID_HW_STATUS_NOT_READY 0x00000004 + +/* Define RNDIS Packet Header format. */ +#define UX_DEVICE_CLASS_RNDIS_PACKET_MESSAGE_TYPE 0x00000000 +#define UX_DEVICE_CLASS_RNDIS_PACKET_MESSAGE_LENGTH 0x00000004 +#define UX_DEVICE_CLASS_RNDIS_PACKET_DATA_OFFSET 0x00000008 +#define UX_DEVICE_CLASS_RNDIS_PACKET_DATA_LENGTH 0x0000000C +#define UX_DEVICE_CLASS_RNDIS_PACKET_OOB_DATA_OFFSET 0x00000010 +#define UX_DEVICE_CLASS_RNDIS_PACKET_OOB_DATA_LENGTH 0x00000014 +#define UX_DEVICE_CLASS_RNDIS_PACKET_NUM_OOB_DATA_ELEMENTS 0x00000018 +#define UX_DEVICE_CLASS_RNDIS_PACKET_INFO_OFFSET 0x0000001C +#define UX_DEVICE_CLASS_RNDIS_PACKET_INFO_LENGTH 0x00000020 +#define UX_DEVICE_CLASS_RNDIS_PACKET_VC_HANDLE 0x00000024 +#define UX_DEVICE_CLASS_RNDIS_PACKET_RESERVED 0x00000028 +#define UX_DEVICE_CLASS_RNDIS_PACKET_BUFFER 0x0000002C +#define UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH 0x0000002C + +/* Define NetX errors inside the RNDIS class. */ +#define UX_DEVICE_CLASS_RNDIS_NX_SUCCESS 0x00 +#define UX_DEVICE_CLASS_RNDIS_NX_NO_PACKET 0x01 +#define UX_DEVICE_CLASS_RNDIS_NX_UNDERFLOW 0x02 +#define UX_DEVICE_CLASS_RNDIS_NX_OVERFLOW 0x03 +#define UX_DEVICE_CLASS_RNDIS_NX_NO_MAPPING 0x04 +#define UX_DEVICE_CLASS_RNDIS_NX_DELETED 0x05 +#define UX_DEVICE_CLASS_RNDIS_NX_POOL_ERROR 0x06 +#define UX_DEVICE_CLASS_RNDIS_NX_PTR_ERROR 0x07 +#define UX_DEVICE_CLASS_RNDIS_NX_WAIT_ERROR 0x08 +#define UX_DEVICE_CLASS_RNDIS_NX_SIZE_ERROR 0x09 +#define UX_DEVICE_CLASS_RNDIS_NX_OPTION_ERROR 0x0a +#define UX_DEVICE_CLASS_RNDIS_NX_DELETE_ERROR 0x10 +#define UX_DEVICE_CLASS_RNDIS_NX_CALLER_ERROR 0x11 +#define UX_DEVICE_CLASS_RNDIS_NX_INVALID_PACKET 0x12 +#define UX_DEVICE_CLASS_RNDIS_NX_INVALID_SOCKET 0x13 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_ENABLED 0x14 +#define UX_DEVICE_CLASS_RNDIS_NX_ALREADY_ENABLED 0x15 +#define UX_DEVICE_CLASS_RNDIS_NX_ENTRY_NOT_FOUND 0x16 +#define UX_DEVICE_CLASS_RNDIS_NX_NO_MORE_ENTRIES 0x17 +#define UX_DEVICE_CLASS_RNDIS_NX_ARP_TIMER_ERROR 0x18 +#define UX_DEVICE_CLASS_RNDIS_NX_RESERVED_CODE0 0x19 +#define UX_DEVICE_CLASS_RNDIS_NX_WAIT_ABORTED 0x1A +#define UX_DEVICE_CLASS_RNDIS_NX_IP_INTERNAL_ERROR 0x20 +#define UX_DEVICE_CLASS_RNDIS_NX_IP_ADDRESS_ERROR 0x21 +#define UX_DEVICE_CLASS_RNDIS_NX_ALREADY_BOUND 0x22 +#define UX_DEVICE_CLASS_RNDIS_NX_PORT_UNAVAILABLE 0x23 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_BOUND 0x24 +#define UX_DEVICE_CLASS_RNDIS_NX_RESERVED_CODE1 0x25 +#define UX_DEVICE_CLASS_RNDIS_NX_SOCKET_UNBOUND 0x26 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_CREATED 0x27 +#define UX_DEVICE_CLASS_RNDIS_NX_SOCKETS_BOUND 0x28 +#define UX_DEVICE_CLASS_RNDIS_NX_NO_RESPONSE 0x29 +#define UX_DEVICE_CLASS_RNDIS_NX_POOL_DELETED 0x30 +#define UX_DEVICE_CLASS_RNDIS_NX_ALREADY_RELEASED 0x31 +#define UX_DEVICE_CLASS_RNDIS_NX_RESERVED_CODE2 0x32 +#define UX_DEVICE_CLASS_RNDIS_NX_MAX_LISTEN 0x33 +#define UX_DEVICE_CLASS_RNDIS_NX_DUPLICATE_LISTEN 0x34 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_CLOSED 0x35 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_LISTEN_STATE 0x36 +#define UX_DEVICE_CLASS_RNDIS_NX_IN_PROGRESS 0x37 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_CONNECTED 0x38 +#define UX_DEVICE_CLASS_RNDIS_NX_WINDOW_OVERFLOW 0x39 +#define UX_DEVICE_CLASS_RNDIS_NX_ALREADY_SUSPENDED 0x40 +#define UX_DEVICE_CLASS_RNDIS_NX_DISCONNECT_FAILED 0x41 +#define UX_DEVICE_CLASS_RNDIS_NX_STILL_BOUND 0x42 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_SUCCESSFUL 0x43 +#define UX_DEVICE_CLASS_RNDIS_NX_UNHANDLED_COMMAND 0x44 +#define UX_DEVICE_CLASS_RNDIS_NX_NO_FREE_PORTS 0x45 +#define UX_DEVICE_CLASS_RNDIS_NX_INVALID_PORT 0x46 +#define UX_DEVICE_CLASS_RNDIS_NX_INVALID_RELISTEN 0x47 +#define UX_DEVICE_CLASS_RNDIS_NX_CONNECTION_PENDING 0x48 +#define UX_DEVICE_CLASS_RNDIS_NX_TX_QUEUE_DEPTH 0x49 +#define UX_DEVICE_CLASS_RNDIS_NX_NOT_IMPLEMENTED 0x80 + +/* Define timeout packet allocation value. */ +#ifndef UX_DEVICE_CLASS_RNDIS_PACKET_POOL_WAIT +#define UX_DEVICE_CLASS_RNDIS_PACKET_POOL_WAIT 10 +#endif + +/* Calculate response buffer length. */ +#define UX_DEVICE_CLASS_RNDIS_OID_SUPPORTED_RESPONSE_LENGTH (UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER + UX_DEVICE_CLASS_RNDIS_OID_SUPPORTED_LIST_LENGTH * 4) +#define UX_DEVICE_CLASS_RNDIS_VENDOR_DESCRIPTION_MAX_RESPONSE_LENGTH (UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER + UX_DEVICE_CLASS_RNDIS_VENDOR_DESCRIPTION_MAX_LENGTH) +#define UX_DEVICE_CLASS_RNDIS_NODE_ID_RESPONSE_LENGTH (UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER + UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH) + +/* Decide maximun size of RNDIS response buffer (with 1 ~ 4 bytes as padding and aligned to 4 bytes). */ +#define UX_DEVICE_CLASS_RNDIS_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define UX_DEVICE_CLASS_RNDIS_MAX_CONTROL_RESPONSE_LENGTH ((( \ + UX_DEVICE_CLASS_RNDIS_MAX(UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_RESPONSE_LENGTH, \ + UX_DEVICE_CLASS_RNDIS_MAX(UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_RESPONSE_LENGTH, \ + UX_DEVICE_CLASS_RNDIS_MAX(UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_RESPONSE_LENGTH, \ + UX_DEVICE_CLASS_RNDIS_MAX(UX_DEVICE_CLASS_RNDIS_CMPLT_SET_RESPONSE_LENGTH, \ + UX_DEVICE_CLASS_RNDIS_MAX(UX_DEVICE_CLASS_RNDIS_OID_SUPPORTED_RESPONSE_LENGTH, \ + UX_DEVICE_CLASS_RNDIS_MAX(UX_DEVICE_CLASS_RNDIS_VENDOR_DESCRIPTION_MAX_RESPONSE_LENGTH, \ + UX_DEVICE_CLASS_RNDIS_NODE_ID_RESPONSE_LENGTH))))))) & (~0x3)) + 0x4) + +/* Ensure maximum-sized RNDIS response can fit in the control endpoint's transfer buffer. */ +#if UX_DEVICE_CLASS_RNDIS_MAX_CONTROL_RESPONSE_LENGTH > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH +#error "Error: the maximum-sized RNDIS response cannot fit inside the control endpoint's data buffer. Increase UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH." +#endif + +/* Define Slave RNDIS Class Calling Parameter structure */ + +typedef struct UX_SLAVE_CLASS_RNDIS_PARAMETER_STRUCT +{ + VOID (*ux_slave_class_rndis_instance_activate)(VOID *); + VOID (*ux_slave_class_rndis_instance_deactivate)(VOID *); + ULONG ux_slave_class_rndis_parameter_media; + ULONG ux_slave_class_rndis_parameter_vendor_id; + ULONG ux_slave_class_rndis_parameter_driver_version; + UCHAR ux_slave_class_rndis_parameter_vendor_description[UX_DEVICE_CLASS_RNDIS_VENDOR_DESCRIPTION_MAX_LENGTH]; + UCHAR ux_slave_class_rndis_parameter_local_node_id[UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH]; + UCHAR ux_slave_class_rndis_parameter_remote_node_id[UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH]; + NX_IP *ux_slave_class_rndis_parameter_nx_ip; + ULONG ux_slave_class_rndis_parameter_nx_ip_address; + ULONG ux_slave_class_rndis_parameter_nx_ip_network_mask; + +} UX_SLAVE_CLASS_RNDIS_PARAMETER; + +/* Define RNDIS Class structure. */ + +typedef struct UX_SLAVE_CLASS_RNDIS_STRUCT +{ + UX_SLAVE_INTERFACE *ux_slave_class_rndis_interface; + UX_SLAVE_CLASS_RNDIS_PARAMETER ux_slave_class_rndis_parameter; + TX_SEMAPHORE ux_slave_class_rndis_semaphore; + UX_SLAVE_ENDPOINT *ux_slave_class_rndis_interrupt_endpoint; + UX_SLAVE_ENDPOINT *ux_slave_class_rndis_bulkin_endpoint; + UX_SLAVE_ENDPOINT *ux_slave_class_rndis_bulkout_endpoint; + UCHAR ux_slave_class_rndis_response[UX_DEVICE_CLASS_RNDIS_MAX_CONTROL_RESPONSE_LENGTH]; + ULONG ux_slave_class_rndis_response_length; + ULONG ux_slave_class_rndis_state; + ULONG ux_slave_class_rndis_major_version; + ULONG ux_slave_class_rndis_minor_version; + ULONG ux_slave_class_rndis_max_transfer_size; + ULONG ux_slave_class_rndis_request_id; + ULONG ux_slave_class_rndis_statistics_xmit_ok; + ULONG ux_slave_class_rndis_statistics_rcv_ok; + ULONG ux_slave_class_rndis_statistics_xmit_error; + ULONG ux_slave_class_rndis_statistics_rcv_error; + ULONG ux_slave_class_rndis_statistics_rcv_no_buffer; + ULONG ux_slave_class_rndis_statistics_rcv_error_alignment; + ULONG ux_slave_class_rndis_statistics_xmit_one_collision; + ULONG ux_slave_class_rndis_statistics_xmit_more_collisions; + TX_EVENT_FLAGS_GROUP ux_slave_class_rndis_event_flags_group; + UCHAR ux_slave_class_rndis_local_node_id[UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH]; + UCHAR ux_slave_class_rndis_remote_node_id[UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH]; + NX_IP *ux_slave_class_rndis_nx_ip; + ULONG ux_slave_class_rndis_nx_ip_address; + ULONG ux_slave_class_rndis_nx_ip_network_mask; + NX_INTERFACE *ux_slave_class_rndis_nx_interface; + NX_PACKET *ux_slave_class_rndis_xmit_queue; + NX_PACKET *ux_slave_class_rndis_receive_queue; + UCHAR *ux_slave_class_rndis_pool_memory; + NX_PACKET_POOL ux_slave_class_rndis_packet_pool; + TX_THREAD ux_slave_class_rndis_interrupt_thread; + TX_THREAD ux_slave_class_rndis_bulkin_thread; + TX_THREAD ux_slave_class_rndis_bulkout_thread; + UCHAR *ux_slave_class_rndis_interrupt_thread_stack; + UCHAR *ux_slave_class_rndis_bulkin_thread_stack; + UCHAR *ux_slave_class_rndis_bulkout_thread_stack; + ULONG ux_slave_class_rndis_link_state; + TX_MUTEX ux_slave_class_rndis_mutex; + VOID *ux_slave_class_rndis_network_handle; + +} UX_SLAVE_CLASS_RNDIS; + + +/* Requests - Ethernet Networking Control Model */ + +#define UX_DEVICE_CLASS_RNDIS_SEND_ENCAPSULATED_COMMAND 0x00 + /* Issues a command in the format of the supported control + protocol. The intent of this mechanism is to support + networking devices (e.g., host-based cable modems) + that require an additional vendor-defined interface for + media specific hardware configuration and + management. */ +#define UX_DEVICE_CLASS_RNDIS_GET_ENCAPSULATED_RESPONSE 0x01 + /* Requests a response in the format of the supported + control protocol. */ + + +/* Define buffer length for IN/OUT pipes. */ + +#define UX_DEVICE_CLASS_RNDIS_BUFFER_SIZE 4096 + + +/* Define Device RNDIS Class prototypes. */ + +UINT _ux_device_class_rndis_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_rndis_control_request(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_rndis_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_rndis_entry(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_rndis_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_rndis_write(VOID *rndis_class, NX_PACKET *packet); +UINT _ux_device_class_rndis_msg_query(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request); +UINT _ux_device_class_rndis_msg_reset(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request); +UINT _ux_device_class_rndis_msg_set(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request); +UINT _ux_device_class_rndis_msg_initialize(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request); +UINT _ux_device_class_rndis_msg_keep_alive(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request); +VOID _ux_device_class_rndis_interrupt_thread(ULONG rndis_class); +VOID _ux_device_class_rndis_bulkin_thread(ULONG rndis_class); +VOID _ux_device_class_rndis_bulkout_thread(ULONG rndis_class); + + +/* Define Device RNDIS Class API prototypes. */ + +#define ux_device_class_rndis_entry _ux_device_class_rndis_entry + + +/* Define OID supported List. */ +extern ULONG ux_device_class_rndis_oid_supported_list[]; + +#endif /* UX_DEVICE_CLASS_RNDIS_H */ diff --git a/common/usbx_device_classes/inc/ux_device_class_storage.h b/common/usbx_device_classes/inc/ux_device_class_storage.h new file mode 100644 index 0000000..c1ca49b --- /dev/null +++ b/common/usbx_device_classes/inc/ux_device_class_storage.h @@ -0,0 +1,527 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_device_class_storage.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX device storage class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_DEVICE_CLASS_STORAGE_H +#define UX_DEVICE_CLASS_STORAGE_H + +/* Define User configurable Storage Class constants. */ + +#ifndef UX_MAX_SLAVE_LUN +#define UX_MAX_SLAVE_LUN 2 +#endif + +/* Define Storage Class USB Class constants. */ + +#define UX_SLAVE_CLASS_STORAGE_CLASS 8 +#define UX_SLAVE_CLASS_STORAGE_SUBCLASS_RBC 1 +#define UX_SLAVE_CLASS_STORAGE_SUBCLASS_SFF8020 2 +#define UX_SLAVE_CLASS_STORAGE_SUBCLASS_UFI 4 +#define UX_SLAVE_CLASS_STORAGE_SUBCLASS_SFF8070 5 +#define UX_SLAVE_CLASS_STORAGE_SUBCLASS_SCSI 6 +#define UX_SLAVE_CLASS_STORAGE_PROTOCOL_CBI 0 +#define UX_SLAVE_CLASS_STORAGE_PROTOCOL_CB 1 +#define UX_SLAVE_CLASS_STORAGE_PROTOCOL_BO 0x50 + +/* Define Storage Class USB MEDIA types. */ +#define UX_SLAVE_CLASS_STORAGE_MEDIA_FAT_DISK 0 +#define UX_SLAVE_CLASS_STORAGE_MEDIA_CDROM 5 +#define UX_SLAVE_CLASS_STORAGE_MEDIA_OPTICAL_DISK 7 +#define UX_SLAVE_CLASS_STORAGE_MEDIA_IOMEGA_CLICK 0x55 + + + +/* Define Storage Class SCSI command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_SCSI_TEST_READY 0x00 +#define UX_SLAVE_CLASS_STORAGE_SCSI_REQUEST_SENSE 0x03 +#define UX_SLAVE_CLASS_STORAGE_SCSI_FORMAT 0x04 +#define UX_SLAVE_CLASS_STORAGE_SCSI_INQUIRY 0x12 +#define UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT 0x1a +#define UX_SLAVE_CLASS_STORAGE_SCSI_START_STOP 0x1b +#define UX_SLAVE_CLASS_STORAGE_SCSI_PREVENT_ALLOW_MEDIA_REMOVAL 0x1e +#define UX_SLAVE_CLASS_STORAGE_SCSI_READ_FORMAT_CAPACITY 0x23 +#define UX_SLAVE_CLASS_STORAGE_SCSI_READ_CAPACITY 0x25 +#define UX_SLAVE_CLASS_STORAGE_SCSI_READ16 0x28 +#define UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16 0x2a +#define UX_SLAVE_CLASS_STORAGE_SCSI_VERIFY 0x2f +#define UX_SLAVE_CLASS_STORAGE_SCSI_SYNCHRONIZE_CACHE 0x35 +#define UX_SLAVE_CLASS_STORAGE_SCSI_READ_TOC 0x43 +#define UX_SLAVE_CLASS_STORAGE_SCSI_GET_CONFIGURATION 0x46 +#define UX_SLAVE_CLASS_STORAGE_SCSI_GET_STATUS_NOTIFICATION 0x4A +#define UX_SLAVE_CLASS_STORAGE_SCSI_READ_DISK_INFORMATION 0x51 +#define UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SELECT 0x55 +#define UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE 0x5a +#define UX_SLAVE_CLASS_STORAGE_SCSI_READ32 0xa8 +#define UX_SLAVE_CLASS_STORAGE_SCSI_REPORT_KEY 0xa4 +#define UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32 0xaa +#define UX_SLAVE_CLASS_STORAGE_SCSI_GET_PERFORMANCE 0xac +#define UX_SLAVE_CLASS_STORAGE_SCSI_READ_DVD_STRUCTURE 0xad + +/* Define Storage Class SCSI command block wrapper constants. */ + +#define UX_SLAVE_CLASS_STORAGE_CBW_SIGNATURE_MASK 0x43425355 +#define UX_SLAVE_CLASS_STORAGE_CBW_SIGNATURE 0 +#define UX_SLAVE_CLASS_STORAGE_CBW_TAG 4 +#define UX_SLAVE_CLASS_STORAGE_CBW_DATA_LENGTH 8 +#define UX_SLAVE_CLASS_STORAGE_CBW_FLAGS 12 +#define UX_SLAVE_CLASS_STORAGE_CBW_LUN 13 +#define UX_SLAVE_CLASS_STORAGE_CBW_CB_LENGTH 14 +#define UX_SLAVE_CLASS_STORAGE_CBW_CB 15 +#define UX_SLAVE_CLASS_STORAGE_CBW_LENGTH 31 + + +/* Define Storage Class SCSI response status wrapper constants. */ + +#define UX_SLAVE_CLASS_STORAGE_CSW_SIGNATURE_MASK 0x53425355 +#define UX_SLAVE_CLASS_STORAGE_CSW_SIGNATURE 0 +#define UX_SLAVE_CLASS_STORAGE_CSW_TAG 4 +#define UX_SLAVE_CLASS_STORAGE_CSW_DATA_RESIDUE 8 +#define UX_SLAVE_CLASS_STORAGE_CSW_STATUS 12 +#define UX_SLAVE_CLASS_STORAGE_CSW_LENGTH 13 + + +/* Define Storage Class SCSI inquiry command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_LUN 1 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_PAGE_CODE 2 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_ALLOCATION_LENGTH 4 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_COMMAND_LENGTH_SBC 06 + + +/* Define Storage Class SCSI inquiry response constants. */ + +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_PERIPHERAL_TYPE 0 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_REMOVABLE_MEDIA 1 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_DATA_FORMAT 3 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_ADDITIONAL_LENGTH 4 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_VENDOR_INFORMATION 8 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_PRODUCT_ID 16 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_PRODUCT_REVISION 32 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH 36 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH_CD_ROM 0x5b + + +/* Define Storage Class SCSI start/stop command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_START_STOP_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_START_STOP_LBUFLAGS 1 +#define UX_SLAVE_CLASS_STORAGE_START_STOP_START_BIT 4 +#define UX_SLAVE_CLASS_STORAGE_START_STOP_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_START_STOP_COMMAND_LENGTH_SBC 6 + + +/* Define Storage Class SCSI mode sense command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_LUN 1 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PC_PAGE_CODE 2 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_LIST_LENGTH_6 4 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_LIST_LENGTH_10 7 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_ALLOCATION_LENGTH_6 4 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_ALLOCATION_LENGTH_10 7 + +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_MEDIUM_TYPE_6 1 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_MEDIUM_TYPE_10 2 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_HEADER_LENGTH_6 4 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_HEADER_LENGTH_10 8 + +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_SBC 12 + + +/* Define Storage Class SCSI request sense command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_LUN 1 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_ALLOCATION_LENGTH 4 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_COMMAND_LENGTH_SBC 12 + + +/* Define Storage Class request sense response constants. */ + +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ERROR_CODE 0 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_SENSE_KEY 2 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_INFORMATION 3 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ADD_LENGTH 7 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE 12 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE_QUALIFIER 13 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH 18 +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_HEADER_LENGTH 8 + + +/* Define Storage Class read capacity command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_LUN 1 +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_LBA 2 +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_COMMAND_LENGTH_SBC 10 + + +/* Define Storage Class read capacity response constants. */ + +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LAST_LBA 0 +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_BLOCK_SIZE 4 +#define UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH 8 + +/* Define Storage Class read capacity response constants. */ + +#define UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_SIZE 0 +#define UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LAST_LBA 4 +#define UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_DESC_CODE 8 +#define UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_BLOCK_SIZE 8 +#define UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LENGTH 12 + +/* Define Storage Class test unit read command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_TEST_READY_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_TEST_READY_LUN 1 +#define UX_SLAVE_CLASS_STORAGE_TEST_READY_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_TEST_READY_COMMAND_LENGTH_SBC 6 + + +/* Define Storage Class SCSI read command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_READ_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_READ_LUN 1 +#define UX_SLAVE_CLASS_STORAGE_READ_LBA 2 +#define UX_SLAVE_CLASS_STORAGE_READ_TRANSFER_LENGTH_32 6 +#define UX_SLAVE_CLASS_STORAGE_READ_TRANSFER_LENGTH_16 7 +#define UX_SLAVE_CLASS_STORAGE_READ_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_READ_COMMAND_LENGTH_SBC 10 + + +/* Define Storage Class SCSI write command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_WRITE_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_WRITE_LUN 1 +#define UX_SLAVE_CLASS_STORAGE_WRITE_LBA 2 +#define UX_SLAVE_CLASS_STORAGE_WRITE_TRANSFER_LENGTH_32 6 +#define UX_SLAVE_CLASS_STORAGE_WRITE_TRANSFER_LENGTH_16 7 +#define UX_SLAVE_CLASS_STORAGE_WRITE_COMMAND_LENGTH_UFI 12 +#define UX_SLAVE_CLASS_STORAGE_WRITE_COMMAND_LENGTH_SBC 10 + + +/* Define Storage Class SCSI sense key definition constants. */ + +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NO_SENSE 0x0 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_RECOVERED_ERROR 0x1 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NOT_READY 0x2 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_MEDIUM_ERROR 0x3 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_HARDWARE_ERROR 0x4 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_ILLEGAL_REQUEST 0x5 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_UNIT_ATTENTION 0x6 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_DATA_PROTECT 0x7 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_BLANK_CHECK 0x8 +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_ABORTED_COMMAND 0x0b +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_VOLUME_OVERFLOW 0x0d +#define UX_SLAVE_CLASS_STORAGE_SENSE_KEY_MISCOMPARE 0x0e + + +/* Define Storage Class SCSI sense key definition constants. */ + +#define UX_SLAVE_CLASS_STORAGE_REQUEST_CODE_MEDIA_PROTECTED 0x27 + +/* Define Storage Class SCSI GET CONFIGURATION command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_RT 1 +#define UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_STARTING_FEATURE 2 +#define UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_ALLOCATION_LENGTH 7 +#define UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_COMMAND_LENGTH_SBC 9 + +/* Define Storage Class SCSI ASC return codes. */ +#define UX_SLAVE_CLASS_STORAGE_ASC_KEY_INVALID_COMMAND 0x20 + +/* Define Storage Class CSW status. */ + +#define UX_SLAVE_CLASS_STORAGE_CSW_PASSED 0 +#define UX_SLAVE_CLASS_STORAGE_CSW_FAILED 1 +#define UX_SLAVE_CLASS_STORAGE_CSW_PHASE_ERROR 2 + +/* Define generic SCSI values. */ + +#define UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ERROR_CODE_VALUE 0x70 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_PAGE_CODE_STANDARD 0x00 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_PAGE_CODE_SERIAL 0x80 +#define UX_SLAVE_CLASS_STORAGE_INQUIRY_PERIPHERAL_TYPE 0x00 +#define UX_SLAVE_CLASS_STORAGE_RESET 0xff +#define UX_SLAVE_CLASS_STORAGE_GET_MAX_LUN 0xfe +#define UX_SLAVE_CLASS_STORAGE_MAX_LUN 0 +#define UX_SLAVE_CLASS_STORAGE_RESPONSE_LENGTH 64 + +/* Define Storage Class SCSI read disk information command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_READ_DISK_INFORMATION_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_READ_DISK_INFORMATION_STATUS 2 +#define UX_SLAVE_CLASS_STORAGE_READ_DISK_INFORMATION_ALLOCATION_LENGTH 7 +#define UX_SLAVE_CLASS_STORAGE_READ_DISK_INFORMATION_LENGTH 10 + +/* Define Storage Class Feature Descriptor generic format. */ + +#define USBX_DEVICE_CLASS_STORAGE_FEATURE_DESCRIPTOR_FEATURE_CODE 0 +#define USBX_DEVICE_CLASS_STORAGE_FEATURE_DESCRIPTOR_FEATURE_PARAMETER 2 +#define USBX_DEVICE_CLASS_STORAGE_FEATURE_DESCRIPTOR_FEATURE_ADD_LENGTH 3 +#define USBX_DEVICE_CLASS_STORAGE_FEATURE_DESCRIPTOR_FEATURE_LENGTH 4 + + +/* Define Storage Class SCSI REPORT_KEY command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_OPERATION 0 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ALLOCATION_LENGTH 8 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT 10 + +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_LENGTH 8 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_PAYLOAD 6 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_RESET_FIELD 4 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_REGION_MASK 5 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_RPC_SCHEME 6 + + +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT_AGID 0 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT_CHALLENGE 1 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT_KEY2 2 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT_TITLE 4 +#define UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT_RPC 8 + +/* Define Storage Class SCSI get event status notification command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_EVENT_NOTIFICATION_OPCODE 0 +#define UX_SLAVE_CLASS_STORAGE_EVENT_NOTIFICATION_IMMEDIATE 1 +#define UX_SLAVE_CLASS_STORAGE_EVENT_NOTIFICATION_CLASS_REQUEST 4 +#define UX_SLAVE_CLASS_STORAGE_EVENT_NOTIFICATION_ALLOCATION_LENGTH 7 +#define UX_SLAVE_CLASS_STORAGE_EVENT_NOTIFICATION_LENGTH 10 + +/* Define Storage Class SCSI read toc command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_READ_TOC_OPCODE 0 +#define UX_SLAVE_CLASS_STORAGE_READ_TOC_FORMAT 2 +#define UX_SLAVE_CLASS_STORAGE_READ_TOC_TRACK_NUMBER 6 +#define UX_SLAVE_CLASS_STORAGE_READ_TOC_ALLOCATION_LENGTH 7 + +/* Define Storage Class SCSI read DVD structure command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_READ_DVD_STRUCTURE_OPCODE 0 +#define UX_SLAVE_CLASS_STORAGE_READ_DVD_STRUCTURE_ADDRESS 2 +#define UX_SLAVE_CLASS_STORAGE_READ_DVD_STRUCTURE_FORMAT 7 +#define UX_SLAVE_CLASS_STORAGE_READ_DVD_STRUCTURE_ALLOCATION_LENGTH 8 + +#define UX_SLAVE_CLASS_STORAGE_READ_DVD_STRUCTURE_RESP_LENGTH 0 +#define UX_SLAVE_CLASS_STORAGE_READ_DVD_STRUCTURE_BOOK_TYPE 4 +#define UX_SLAVE_CLASS_STORAGE_READ_DVD_STRUCTURE_DS_MR 5 + + +/* Define Storage Class SCSI get performance command constants. */ + +#define UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAGE 1 +#define UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAGE_14 0x14 +#define UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAGE_0 0x00 +#define UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAYLOAD_LENGTH 4 +#define UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_RESPONSE_LENGTH 8 +#define UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAYLOAD 0x20000000 + +/* Define buffer length for IN/OUT pipes. This should match the size of the endpoint maximum buffer size. */ + +#define UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE UX_SLAVE_REQUEST_DATA_MAX_LENGTH + + +/* Define MMC2 CD-ROM / DVD-ROM bit fields */ + +#define UX_SLAVE_CLASS_STORAGE_MMC2_DISK_STATUS_EMPTY 0 +#define UX_SLAVE_CLASS_STORAGE_MMC2_DISK_STATUS_INCOMPLETE 1 +#define UX_SLAVE_CLASS_STORAGE_MMC2_DISK_STATUS_COMPLETE 2 +#define UX_SLAVE_CLASS_STORAGE_MMC2_DISK_STATUS_OTHERS 3 + +#define UX_SLAVE_CLASS_STORAGE_MMC2_LAT_SESSION_EMPTY 0 +#define UX_SLAVE_CLASS_STORAGE_MMC2_LAT_SESSION_INCOMPLETE 1 +#define UX_SLAVE_CLASS_STORAGE_MMC2_LAT_SESSION_COMPLETE 3 + + +/* Define Storage Class SCSI synchronize cache constants. */ + +#define UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_FLAGS 1 +#define UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_LBA 2 +#define UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_NUMBER_OF_BLOCKS 7 + +#define UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_FLAGS_IMMED 0x02 + + +/* Define MODE SENSE Page Codes. */ +#define UX_SLAVE_CLASS_STORAGE_MMC2_PAGE_CODE_CDROM 0x2a + +#define UX_SLAVE_CLASS_STORAGE_PAGE_CODE_CACHE 0x08 +#define UX_SLAVE_CLASS_STORAGE_PAGE_CODE_IEC 0x1C +#define UX_SLAVE_CLASS_STORAGE_PAGE_CODE_ALL 0x3F + + +/* Define Slave Storage Class LUN structure. */ + +typedef struct UX_SLAVE_CLASS_STORAGE_LUN_STRUCT +{ + ULONG ux_slave_class_storage_media_last_lba; + ULONG ux_slave_class_storage_media_block_length; + ULONG ux_slave_class_storage_media_type; + ULONG ux_slave_class_storage_media_removable_flag; + ULONG ux_slave_class_storage_media_read_only_flag; + ULONG ux_slave_class_storage_media_id; + ULONG ux_slave_class_storage_scsi_tag; + UCHAR ux_slave_class_storage_request_sense_key; + UCHAR ux_slave_class_storage_request_code; + UCHAR ux_slave_class_storage_request_code_qualifier; + ULONG ux_slave_class_storage_disk_status; + ULONG ux_slave_class_storage_last_session_state; + UINT (*ux_slave_class_storage_media_read)(VOID *storage, ULONG lun, UCHAR *data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status); + UINT (*ux_slave_class_storage_media_write)(VOID *storage, ULONG lun, UCHAR *data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status); + UINT (*ux_slave_class_storage_media_flush)(VOID *storage, ULONG lun, ULONG number_blocks, ULONG lba, ULONG *media_status); + UINT (*ux_slave_class_storage_media_status)(VOID *storage, ULONG lun, ULONG media_id, ULONG *media_status); + UINT (*ux_slave_class_storage_media_notification)(VOID *storage, ULONG lun, ULONG media_id, ULONG notification_class, UCHAR **media_notification, ULONG *media_notification_length); +} UX_SLAVE_CLASS_STORAGE_LUN; + +/* Define Slave Storage Class structure. */ + +typedef struct UX_SLAVE_CLASS_STORAGE_STRUCT +{ + UX_SLAVE_INTERFACE *ux_slave_class_storage_interface; + ULONG ux_slave_class_storage_number_lun; + UX_SLAVE_CLASS_STORAGE_LUN ux_slave_class_storage_lun[UX_MAX_SLAVE_LUN]; + ULONG ux_slave_class_storage_phase_error; + VOID (*ux_slave_class_storage_instance_activate)(VOID *); + VOID (*ux_slave_class_storage_instance_deactivate)(VOID *); + UCHAR *ux_slave_class_storage_vendor_id; + UCHAR *ux_slave_class_storage_product_id; + UCHAR *ux_slave_class_storage_product_rev; + UCHAR *ux_slave_class_storage_product_serial; + +} UX_SLAVE_CLASS_STORAGE; + +/* Define Slave Storage Class Calling Parameter structure */ + +typedef struct UX_SLAVE_CLASS_STORAGE_PARAMETER_STRUCT +{ + VOID (*ux_slave_class_storage_instance_activate)(VOID *); + VOID (*ux_slave_class_storage_instance_deactivate)(VOID *); + ULONG ux_slave_class_storage_parameter_number_lun; + UX_SLAVE_CLASS_STORAGE_LUN ux_slave_class_storage_parameter_lun[UX_MAX_SLAVE_LUN]; + UCHAR *ux_slave_class_storage_parameter_vendor_id; + UCHAR *ux_slave_class_storage_parameter_product_id; + UCHAR *ux_slave_class_storage_parameter_product_rev; + UCHAR *ux_slave_class_storage_parameter_product_serial; + +} UX_SLAVE_CLASS_STORAGE_PARAMETER; + +/* Define Device Storage Class prototypes. */ + +UINT _ux_device_class_storage_initialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_storage_uninitialize(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_storage_activate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_storage_control_request(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_storage_csw_send(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, UCHAR csw_status); +UINT _ux_device_class_storage_deactivate(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_storage_entry(UX_SLAVE_CLASS_COMMAND *command); +UINT _ux_device_class_storage_format(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_inquiry(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_mode_select(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_mode_sense(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_prevent_allow_media_removal(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb); +UINT _ux_device_class_storage_read(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb, UCHAR scsi_command); +UINT _ux_device_class_storage_read_capacity(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_read_format_capacity(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_read_toc(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb); +UINT _ux_device_class_storage_request_sense(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_start_stop(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_test_ready(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +VOID _ux_device_class_storage_thread(ULONG storage_instance); +UINT _ux_device_class_storage_verify(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_write(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb, UCHAR scsi_command); +UINT _ux_device_class_storage_synchronize_cache(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb, UCHAR scsi_command); +UINT _ux_device_class_storage_read_disk_information(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); + +UINT _ux_device_class_storage_get_configuration(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_get_status_notification(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); +UINT _ux_device_class_storage_report_key(UX_SLAVE_CLASS_STORAGE *storage, + ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, + UCHAR *cbwcb); + +UINT _ux_device_class_storage_get_performance(UX_SLAVE_CLASS_STORAGE *storage, + ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, + UCHAR *cbwcb); +UINT _ux_device_class_storage_read_dvd_structure(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb); + +/* Define Device Storage Class API prototypes. */ + +#define ux_device_class_storage_entry _ux_device_class_storage_entry + +#endif diff --git a/common/usbx_device_classes/src/ux_device_class_audio10_control_process.c b/common/usbx_device_classes/src/ux_device_class_audio10_control_process.c new file mode 100644 index 0000000..d742db1 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio10_control_process.c @@ -0,0 +1,237 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_class_audio10.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio10_control_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* audio Address of audio class */ +/* instance */ +/* transfer Address of transfer request */ +/* instance */ +/* group Request process data */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_short_get Get 2-byte value from buffer */ +/* _ux_utility_short_put Put 2-byte value to buffer */ +/* _ux_device_stack_transfer_request Issue a transfer request */ +/* _ux_device_stack_endpoint_stall Endpoint stall */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio10_control_process(UX_DEVICE_CLASS_AUDIO *audio, + UX_SLAVE_TRANSFER *transfer, UX_DEVICE_CLASS_AUDIO10_CONTROL_GROUP *group) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_DEVICE_CLASS_AUDIO10_CONTROL *control; +UCHAR request; +UCHAR unit_id; +UCHAR control_selector; +UCHAR channel_number; +ULONG request_length; +ULONG i; + + + /* Get instances. */ + endpoint = &audio -> ux_device_class_audio_device -> ux_slave_device_control_endpoint; + transfer = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST); + unit_id = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_ENEITY_ID); + control_selector = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CONTROL_SELECTOR); + channel_number = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CHANNEL_NUMBER); + request_length = _ux_utility_short_get(transfer -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); + + /* Process controls one by one. */ + for (i = 0; i < group -> ux_device_class_audio10_control_group_controls_nb; i ++) + { + control = &group -> ux_device_class_audio10_control_group_controls[i]; + + /* Reset change map. */ + control -> ux_device_class_audio10_control_changed = 0; + + /* We handle feature unit requests. */ + if (unit_id == control -> ux_device_class_audio10_control_fu_id) + { + + /* Handle the request. */ + switch(request) + { + case UX_DEVICE_CLASS_AUDIO10_SET_CUR: + + /* We only support master channel, so channel number must be 0 or 0xFF. */ + if (channel_number != 0 && channel_number != 0xFF) + break; + + /* Set request. */ + switch(control_selector) + { + case UX_DEVICE_CLASS_AUDIO10_FU_MUTE_CONTROL: + + if (transfer -> ux_slave_transfer_request_actual_length < 1) + + /* No change applied. */ + break; + + if (control -> ux_device_class_audio10_control_mute[0] != transfer -> ux_slave_transfer_request_data_pointer[0]) + { + control -> ux_device_class_audio10_control_changed = UX_DEVICE_CLASS_AUDIO10_CONTROL_MUTE_CHANGED; + control -> ux_device_class_audio10_control_mute[0] = transfer -> ux_slave_transfer_request_data_pointer[0]; + + } + + /* Done success. */ + return(UX_SUCCESS); + + case UX_DEVICE_CLASS_AUDIO10_FU_VOLUME_CONTROL: + + if (transfer -> ux_slave_transfer_request_actual_length < 2) + + /* No change applied. */ + break; + + if (control -> ux_device_class_audio10_control_volume[0] != (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer)) + { + control -> ux_device_class_audio10_control_changed = UX_DEVICE_CLASS_AUDIO10_CONTROL_VOLUME_CHANGED; + control -> ux_device_class_audio10_control_volume[0] = (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer); + } + + /* Done success. */ + return(UX_SUCCESS); + + + default: + + /* No change applied. */ + break; + } + + /* Request or parameter problem. */ + break; + + case UX_DEVICE_CLASS_AUDIO10_GET_MIN: + case UX_DEVICE_CLASS_AUDIO10_GET_MAX: + case UX_DEVICE_CLASS_AUDIO10_GET_RES: + case UX_DEVICE_CLASS_AUDIO10_GET_CUR: + + /* We only support master channel, so channel number must be 0 or 0xFF. */ + if (channel_number != 0 && channel_number != 0xFF) + break; + + /* Get request. */ + switch(control_selector) + { + case UX_DEVICE_CLASS_AUDIO10_FU_MUTE_CONTROL: + + /* We only support _CUR. */ + if (request != UX_DEVICE_CLASS_AUDIO10_GET_CUR) + break; + + /* Not enough buffer for data. */ + if (request_length < 1) + break; + + /* Send mute status. */ + transfer -> ux_slave_transfer_request_data_pointer[0] = (UCHAR)control -> ux_device_class_audio10_control_mute[0]; + _ux_device_stack_transfer_request(transfer, 1, request_length); + + /* Done success. */ + return(UX_SUCCESS); + + case UX_DEVICE_CLASS_AUDIO10_FU_VOLUME_CONTROL: + + /* Not enough buffer for data. */ + if (request_length < 2) + break; + + /* Send volume value. */ + _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, (USHORT) + (request == UX_DEVICE_CLASS_AUDIO10_GET_MIN ? control -> ux_device_class_audio10_control_volume_min[0] : + (request == UX_DEVICE_CLASS_AUDIO10_GET_MAX ? control -> ux_device_class_audio10_control_volume_max[0] : + (request == UX_DEVICE_CLASS_AUDIO10_GET_RES ? 0x0001 : + control -> ux_device_class_audio10_control_volume[0])))); + _ux_device_stack_transfer_request(transfer, 2, request_length); + + /* Done success. */ + return(UX_SUCCESS); + + default: + break; + } + + default: + break; + } + + /* We are here when there is error, break the loop. */ + break; + } + + /* Now try next. */ + } + + + /* Request or parameter not supported. */ + _ux_device_stack_endpoint_stall(endpoint); + + /* Done error. */ + return(UX_ERROR); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio20_control_process.c b/common/usbx_device_classes/src/ux_device_class_audio20_control_process.c new file mode 100644 index 0000000..9474f87 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio20_control_process.c @@ -0,0 +1,295 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_class_audio20.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* audio Address of audio class */ +/* instance */ +/* transfer Address of transfer request */ +/* instance */ +/* group Request process data */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_short_get Get 2-byte value from buffer */ +/* _ux_utility_short_put Put 2-byte value to buffer */ +/* _ux_utility_long_put Put 4-byte value to buffer */ +/* _ux_device_stack_transfer_request Issue a transfer request */ +/* _ux_device_stack_endpoint_stall Endpoint stall */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO *audio, + UX_SLAVE_TRANSFER *transfer, + UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP *group) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_DEVICE_CLASS_AUDIO20_CONTROL *control; +UCHAR request; +UCHAR request_type; +UCHAR unit_id; +UCHAR control_selector; +UCHAR channel_number; +ULONG request_length; +ULONG i; + + + /* Get instances. */ + endpoint = &audio -> ux_device_class_audio_device -> ux_slave_device_control_endpoint; + transfer = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST); + request_type = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST_TYPE); + unit_id = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_ENEITY_ID); + control_selector = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CONTROL_SELECTOR); + channel_number = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CHANNEL_NUMBER); + request_length = _ux_utility_short_get(transfer -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); + + for (i = 0; i < group -> ux_device_class_audio20_control_group_controls_nb; i ++) + { + control = &group -> ux_device_class_audio20_control_group_controls[i]; + + /* Reset change map. */ + control -> ux_device_class_audio20_control_changed = 0; + + /* Is this request a clock unit request? */ + if (unit_id == control -> ux_device_class_audio20_control_cs_id) + { + + /* Clock Source request. + * We only support Sampling Frequency Control here. + * The Sampling Frequency Control must support the CUR and RANGE(MIN, MAX, RES) attributes. + */ + + /* We just support sampling frequency control, GET request. */ + if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN && + (control_selector == UX_DEVICE_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL)) + { + + switch(request) + { + case UX_DEVICE_CLASS_AUDIO20_CUR: + + /* Check request parameter. */ + if (request_length < 4) + break; + + /* Send sampling frequency. + * We only support one here (from extension data). + */ + _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer, control -> ux_device_class_audio20_control_sampling_frequency); + _ux_device_stack_transfer_request(transfer, 4, request_length); + return(UX_SUCCESS); + + case UX_DEVICE_CLASS_AUDIO20_RANGE: + + /* Check request parameter. */ + if (request_length < 2) + break; + + /* Send range parameters. + * We only support one here (from extension data). + * wNumSubRanges : 1 + * dMIN : sampling frequency + * dMAX : sampling frequency + * dRES : 1 + */ + _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, 1); + _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 2, control -> ux_device_class_audio20_control_sampling_frequency); + _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 6, control -> ux_device_class_audio20_control_sampling_frequency); + _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 10, 0); + _ux_device_stack_transfer_request(transfer, 14, request_length); + return(UX_SUCCESS); + + default: + break; + } + } + + /* We are here when there is error on handling CU, break. */ + break; + } /* if (unit_id == control -> ux_device_class_audio20_control_cs_id) */ + + /* Is this request a feature unit request? */ + if (unit_id == control -> ux_device_class_audio20_control_fu_id) + { + + /* Feature Unit request. + * We only support master channel, mute and volume here. + * Mute have only the CUR. + * Volume must support CUR and RANGE(MIN,MAX,RES). + */ + + /* Check control selector. */ + switch(control_selector) + { + case UX_DEVICE_CLASS_AUDIO20_FU_MUTE_CONTROL: + + /* Check channel number, we support master channel only. */ + if (channel_number != 0) + break; + + /* Handle request. */ + switch(request) + { + case UX_DEVICE_CLASS_AUDIO20_CUR: + + + /* Check buffer size. */ + if (request_length < 1) + break; + + /* GET_CUR. */ + if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + { + + /* Send mute. */ + transfer -> ux_slave_transfer_request_data_pointer[0] = (UCHAR)control -> ux_device_class_audio20_control_mute[0]; + _ux_device_stack_transfer_request(transfer, 1, request_length); + } + else if (control -> ux_device_class_audio20_control_mute[0] != transfer -> ux_slave_transfer_request_data_pointer[0]) + { + + /* Update mute. */ + control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_MUTE_CHANGED; + control -> ux_device_class_audio20_control_mute[0] = transfer -> ux_slave_transfer_request_data_pointer[0]; + } + /* Done success. */ + return(UX_SUCCESS); + + default: + break; + } + break; + + case UX_DEVICE_CLASS_AUDIO20_FU_VOLUME_CONTROL: + + /* Check channel number, we support master channel only. */ + if (channel_number != 0) + break; + + /* Handle request. */ + switch(request) + { + case UX_DEVICE_CLASS_AUDIO20_CUR: + + /* Check buffer size. */ + if (request_length < 2) + break; + + /* GET_CUR. */ + if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + { + + /* Send volume. */ + _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, (USHORT)control -> ux_device_class_audio20_control_volume[0]); + _ux_device_stack_transfer_request(transfer, 2, request_length); + } + else if (control -> ux_device_class_audio20_control_volume[0] != (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer)) + { + + /* Update volume. */ + control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_VOLUME_CHANGED; + control -> ux_device_class_audio20_control_volume[0] = (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer); + } + + /* Done success. */ + return(UX_SUCCESS); + + case UX_DEVICE_CLASS_AUDIO20_RANGE: + + /* Only support GET_CUR. */ + if ((request_type & UX_REQUEST_DIRECTION) != UX_REQUEST_IN) + break; + + /* Check buffer size. */ + if (request_length < 8) + break; + + /* Send wNumSubRanges,wMIN,wMAX,wRES. */ + _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 0, 1); + _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 2, (USHORT)control -> ux_device_class_audio20_control_volume_min[0]); + _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 4, (USHORT)control -> ux_device_class_audio20_control_volume_max[0]); + _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 6, 1); + _ux_device_stack_transfer_request(transfer, 8, request_length); + + /* Done success. */ + return(UX_SUCCESS); + + default: + break; + } + break; + + default: + break; + } + + /* We are here when there is error on handling FU, break. */ + break; + } /* if (unit_id == control -> ux_device_class_audio20_control_fu_id) */ + } + + /* Request or parameter not supported. */ + _ux_device_stack_endpoint_stall(endpoint); + return(UX_ERROR); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_activate.c b/common/usbx_device_classes/src/ux_device_class_audio_activate.c new file mode 100644 index 0000000..24e1ca1 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_activate.c @@ -0,0 +1,166 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB Audio device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to audio command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler System error trap */ +/* */ +/* CALLED BY */ +/* */ +/* Device Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_INTERFACE *control_interface; +UX_SLAVE_INTERFACE *stream_interface; +UX_DEVICE_CLASS_AUDIO *audio; +UX_DEVICE_CLASS_AUDIO_STREAM *stream; +UX_SLAVE_CLASS *class; +ULONG stream_index; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + audio = (UX_DEVICE_CLASS_AUDIO *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Get the device instance. */ + device = &_ux_system_slave -> ux_system_slave_device; + audio -> ux_device_class_audio_device = device; + + /* We only support audio interface here. */ + if (interface -> ux_slave_interface_descriptor.bInterfaceClass != UX_DEVICE_CLASS_AUDIO_CLASS) + return(UX_NO_CLASS_MATCH); + + /* It's control interface? */ + if (interface -> ux_slave_interface_descriptor.bInterfaceSubClass == UX_DEVICE_CLASS_AUDIO_SUBCLASS_CONTROL) + { + + /* Store the interface in the class instance. */ + audio -> ux_device_class_audio_interface = interface; + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)audio; + } + else + { + + /* It's streaming interface. */ + stream_interface = interface; + + /* Separate driver for each interface (IAD not used)? */ + if (audio -> ux_device_class_audio_interface == UX_NULL) + { + + /* Always use just 1 stream for 1 interface. */ + stream_index = 0; + } + + /* Re-use the same driver (IAD used)? */ + else + { + + /* Re-use saved control interface. */ + control_interface = audio -> ux_device_class_audio_interface; + + /* Calculate stream index. */ + stream_index = stream_interface -> ux_slave_interface_descriptor.bInterfaceNumber; + stream_index -= control_interface -> ux_slave_interface_descriptor.bInterfaceNumber; + stream_index --; + + } + + /* Sanity check. */ + if (stream_index >= audio -> ux_device_class_audio_streams_nb) + { + + /* Error trap! */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + return(UX_MEMORY_INSUFFICIENT); + } + + /* Locate stream based on interface number. */ + stream = &audio -> ux_device_class_audio_streams[stream_index]; + + /* Store the interface for the stream. */ + stream -> ux_device_class_audio_stream_interface = stream_interface; + + /* Store the class instance into the interface. */ + stream_interface -> ux_slave_interface_class_instance = (VOID *)audio; + } + + /* If there is a activate function call it. */ + if (audio -> ux_device_class_audio_callbacks.ux_slave_class_audio_instance_activate != UX_NULL) + { + + /* Invoke the application. */ + audio -> ux_device_class_audio_callbacks.ux_slave_class_audio_instance_activate(audio); + } + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_change.c b/common/usbx_device_classes/src/ux_device_class_audio_change.c new file mode 100644 index 0000000..41051c9 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_change.c @@ -0,0 +1,192 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_change PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function changes the interface of the Audio device */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to audio command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler System error trap */ +/* */ +/* CALLED BY */ +/* */ +/* Device Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_change(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_DEVICE_CLASS_AUDIO *audio; +UX_DEVICE_CLASS_AUDIO_STREAM *stream; +UX_SLAVE_CLASS *class; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_ENDPOINT *endpoint; +UCHAR *frame_buffer; +ULONG stream_index; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + audio = (UX_DEVICE_CLASS_AUDIO *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Get the interface number (base 0). */ + if (audio -> ux_device_class_audio_interface) + { + + /* If IAD used, calculate stream index based on interface number. */ + stream_index = interface -> ux_slave_interface_descriptor.bInterfaceNumber; + stream_index -= audio -> ux_device_class_audio_interface -> ux_slave_interface_descriptor.bInterfaceNumber; + stream_index --; + } + else + + /* One stream for one driver! */ + stream_index = 0; + + /* Get the stream instance. */ + stream = &audio -> ux_device_class_audio_streams[stream_index]; + + /* Update the interface. */ + stream -> ux_device_class_audio_stream_interface = interface; + + /* If the interface to mount has a non zero alternate setting, the class is really active with + the endpoints active. If the interface reverts to alternate setting 0, it needs to have + the pending transactions terminated. */ + if (interface -> ux_slave_interface_descriptor.bAlternateSetting != 0) + { + + /* Locate the endpoints. ISO IN/OUT for Streaming Interface. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Parse all endpoints. */ + stream -> ux_device_class_audio_stream_endpoint = UX_NULL; + while (endpoint != UX_NULL) + { + + /* Check the endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & + (UX_DEVICE_CLASS_AUDIO_EP_TRANSFER_TYPE_MASK | UX_DEVICE_CLASS_AUDIO_EP_USAGE_TYPE_MASK)) == UX_ISOCHRONOUS_ENDPOINT) + { + + /* We found the endpoint, check its size. */ + if (endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize > stream -> ux_device_class_audio_stream_frame_buffer_size - 8) + { + + /* Error trap! */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* Frame buffer too small for endpoints. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Save it. */ + stream -> ux_device_class_audio_stream_endpoint = endpoint; + break; + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + /* Now check if all endpoints have been found. */ + if (stream -> ux_device_class_audio_stream_endpoint == UX_NULL) + { + + /* Error trap! */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* Not all endpoints have been found. Major error, do not proceed. */ + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Now reset frame buffers. */ + frame_buffer = stream -> ux_device_class_audio_stream_buffer; + while(frame_buffer < stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + { + + /* Reset header information. */ + *((ULONG *) frame_buffer ) = 0; + *((ULONG *)(frame_buffer + 4)) = 0; + + /* Next. */ + frame_buffer += stream -> ux_device_class_audio_stream_frame_buffer_size; + } + stream -> ux_device_class_audio_stream_transfer_pos = stream -> ux_device_class_audio_stream_access_pos; + } + else + { + + /* There is no data endpoint. */ + stream -> ux_device_class_audio_stream_endpoint = UX_NULL; + + /* In this case, we are reverting to the Alternate Setting 0. We need to terminate the pending transactions. */ + /* Endpoints actually aborted and destroyed before change command. */ + /* + _ux_device_stack_transfer_all_request_abort(stream -> ux_device_class_audio_stream_endpoint, UX_TRANSFER_APPLICATION_RESET); + */ + } + + /* Invoke stream change callback. */ + if (stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_change) + stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_change(stream, interface -> ux_slave_interface_descriptor.bAlternateSetting); + + /* Return completion status. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_control_request.c b/common/usbx_device_classes/src/ux_device_class_audio_control_request.c new file mode 100644 index 0000000..da96385 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_control_request.c @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_endpoint_stall Endpoint stall */ +/* */ +/* CALLED BY */ +/* */ +/* Device Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_CLASS *class; +UX_DEVICE_CLASS_AUDIO *audio; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the audio instance from this class container. */ + audio = (UX_DEVICE_CLASS_AUDIO *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Invoke callback. */ + if (audio -> ux_device_class_audio_callbacks.ux_device_class_audio_control_process != UX_NULL) + { + + /* Handled by callback. */ + return audio -> ux_device_class_audio_callbacks.ux_device_class_audio_control_process(audio, transfer_request); + } + + /* Not handled. */ + return(UX_ERROR); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_deactivate.c b/common/usbx_device_classes/src/ux_device_class_audio_deactivate.c new file mode 100644 index 0000000..6e1eaf7 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_deactivate.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the audio class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort */ +/* Abort all transfers */ +/* */ +/* CALLED BY */ +/* */ +/* Device Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_DEVICE_CLASS_AUDIO *audio; +UX_DEVICE_CLASS_AUDIO_STREAM *stream; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_CLASS *class; +UINT i; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + audio = (UX_DEVICE_CLASS_AUDIO *) class -> ux_slave_class_instance; + + /* Stop pending streams. */ + stream = audio -> ux_device_class_audio_streams; + for (i = 0; i < audio -> ux_device_class_audio_streams_nb; i ++) + { + + /* Locate the endpoint. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + + /* Terminate the transactions pending on the endpoint. */ + if (endpoint) + _ux_device_stack_transfer_all_request_abort(endpoint, UX_TRANSFER_BUS_RESET); + + /* Free the stream. */ + stream -> ux_device_class_audio_stream_endpoint = UX_NULL; + stream -> ux_device_class_audio_stream_interface = UX_NULL; + + stream ++; + } + + /* Free the control. */ + audio -> ux_device_class_audio_interface = UX_NULL; + + /* If there is a deactivate function call it. */ + if (audio -> ux_device_class_audio_callbacks.ux_slave_class_audio_instance_deactivate != UX_NULL) + + /* Invoke the application. */ + audio -> ux_device_class_audio_callbacks.ux_slave_class_audio_instance_deactivate(audio); + + /* Return completion status. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_entry.c b/common/usbx_device_classes/src/ux_device_class_audio_entry.c new file mode 100644 index 0000000..ccbd65d --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_entry.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_audio_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the audio class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the audio interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_audio_initialize Initialize audio class */ +/* _ux_device_class_audio_uninitialize Uninitialize audio class */ +/* _ux_device_class_audio_activate Activate audio class */ +/* _ux_device_class_audio_change Change audio interface */ +/* _ux_device_class_audio_deactivate Deactivate audio class */ +/* _ux_device_class_audio_control_request Request control */ +/* */ +/* CALLED BY */ +/* */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the Audio class. */ + status = _ux_device_class_audio_initialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_UNINITIALIZE: + + /* Call the init function of the Audio class. */ + status = _ux_device_class_audio_uninitialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_AUDIO_CLASS) + { + if (command -> ux_slave_class_command_subclass == UX_DEVICE_CLASS_AUDIO_SUBCLASS_CONTROL) + return(UX_SUCCESS); + if (command -> ux_slave_class_command_subclass == UX_DEVICE_CLASS_AUDIO_SUBCLASS_AUDIOSTREAMING) + return(UX_SUCCESS); + } + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the audio thread needs to be activated. */ + status = _ux_device_class_audio_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_CHANGE: + + /* The change command is used when the host has sent a SET_INTERFACE command + to go from Alternate Setting 0 to 1 or revert to the default mode. */ + status = _ux_device_class_audio_change(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the audio thread canceled. */ + status = _ux_device_class_audio_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + _ux_device_class_audio_control_request(command); + + /* Return the completion status. */ + return(UX_SUCCESS); + + default: + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_frame_write.c b/common/usbx_device_classes/src/ux_device_class_audio_frame_write.c new file mode 100644 index 0000000..8042209 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_frame_write.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_frame_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes frame to the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* frame Pointer to buffer to save */ +/* frame data */ +/* length Frame length in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_copy Copy data */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_frame_write(UX_DEVICE_CLASS_AUDIO_STREAM *stream, UCHAR *frame, ULONG length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UCHAR *next_frame_buffer; +ULONG frame_buffer_size; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK (IN). */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Check frame length. */ + frame_buffer_size = stream -> ux_device_class_audio_stream_frame_buffer_size; + if (frame_buffer_size < length) + return(UX_ERROR); + + /* Check overflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos == stream -> ux_device_class_audio_stream_transfer_pos && + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length != 0) + return(UX_BUFFER_OVERFLOW); + + /* Calculate next frame buffer. */ + next_frame_buffer = (UCHAR *)stream -> ux_device_class_audio_stream_access_pos; + next_frame_buffer += frame_buffer_size; + if (next_frame_buffer >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_frame_buffer = stream -> ux_device_class_audio_stream_buffer; + + /* Copy frame. */ + _ux_utility_memory_copy(stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_data, frame, length); + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = length; + + /* Move frame position. */ + stream -> ux_device_class_audio_stream_access_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_frame_buffer; + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_initialize.c b/common/usbx_device_classes/src/ux_device_class_audio_initialize.c new file mode 100644 index 0000000..a085e28 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_initialize.c @@ -0,0 +1,238 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB Audio device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to audio 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 Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status = UX_SUCCESS; +UX_DEVICE_CLASS_AUDIO *audio; +UX_DEVICE_CLASS_AUDIO_PARAMETER *audio_parameter; +UX_DEVICE_CLASS_AUDIO_STREAM *stream; +UX_DEVICE_CLASS_AUDIO_STREAM_PARAMETER *stream_parameter; +UX_SLAVE_CLASS *class; +ULONG memory_size; +ULONG streams_size; +ULONG i; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the pointer to the application parameters for the audio class. */ + audio_parameter = (UX_DEVICE_CLASS_AUDIO_PARAMETER *)command -> ux_slave_class_command_parameter; + + /* Create an instance of the device audio class, with additional streams and controls instances. */ + memory_size = sizeof(UX_DEVICE_CLASS_AUDIO); + + /* Put 0 to default result. */ + streams_size = 0; + + /* Confirm there is no overflow on multiply. */ + UX_UTILITY_MULC_SAFE(audio_parameter -> ux_device_class_audio_parameter_streams_nb, (ULONG)sizeof(UX_DEVICE_CLASS_AUDIO_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 audio and controls. */ + audio = (UX_DEVICE_CLASS_AUDIO *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size); + + /* Check for successful allocation. */ + if (audio == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save streams. */ + if (streams_size) + { + audio -> ux_device_class_audio_streams = (UX_DEVICE_CLASS_AUDIO_STREAM *)((UCHAR *)audio + sizeof(UX_DEVICE_CLASS_AUDIO)); + audio -> ux_device_class_audio_streams_nb = audio_parameter -> ux_device_class_audio_parameter_streams_nb; + } + + /* Allocate resources for streams. */ + stream = audio -> ux_device_class_audio_streams; + stream_parameter = audio_parameter -> ux_device_class_audio_parameter_streams; + for (i = 0; i < audio -> ux_device_class_audio_streams_nb; i ++) + { + + /* Create memory block based on max frame buffer size and max number of frames buffered. + Each frame require some additional header memory (8 bytes). */ + stream -> ux_device_class_audio_stream_frame_buffer_size = stream_parameter -> ux_device_class_audio_stream_parameter_max_frame_buffer_size; + + if (UX_OVERFLOW_CHECK_ADD_USHORT(stream -> ux_device_class_audio_stream_frame_buffer_size, 8)) + { + status = UX_ERROR; + break; + } + stream -> ux_device_class_audio_stream_frame_buffer_size += 8; + + if (UX_OVERFLOW_CHECK_MULV_ULONG(stream -> ux_device_class_audio_stream_frame_buffer_size, + stream_parameter -> ux_device_class_audio_stream_parameter_max_frame_buffer_nb)) + { + status = UX_ERROR; + break; + } + memory_size = stream -> ux_device_class_audio_stream_frame_buffer_size * + stream_parameter -> ux_device_class_audio_stream_parameter_max_frame_buffer_nb; + + /* Create block of buffer buffer is cache safe for USB transfer. */ + stream -> ux_device_class_audio_stream_buffer = (UCHAR *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size); + + /* Check for successful allocation. */ + if (stream -> ux_device_class_audio_stream_buffer == UX_NULL) + { + status = UX_MEMORY_INSUFFICIENT; + break; + } + + stream -> ux_device_class_audio_stream_buffer_size = memory_size; + stream -> ux_device_class_audio_stream_transfer_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)stream -> ux_device_class_audio_stream_buffer; + stream -> ux_device_class_audio_stream_access_pos = stream -> ux_device_class_audio_stream_transfer_pos; + + /* Create memory block for streaming thread stack in addition. */ + if (stream_parameter -> ux_device_class_audio_stream_parameter_thread_stack_size == 0) + memory_size = UX_THREAD_STACK_SIZE; + else + memory_size = stream_parameter -> ux_device_class_audio_stream_parameter_thread_stack_size; + stream -> ux_device_class_audio_stream_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, memory_size); + + /* Check for successful allocation. */ + if (stream -> ux_device_class_audio_stream_thread_stack == UX_NULL) + { + status = UX_MEMORY_INSUFFICIENT; + break; + } + + /* Create streaming thread. */ + status = _ux_utility_thread_create(&stream -> ux_device_class_audio_stream_thread , "ux_device_class_audio_stream_thread", + stream_parameter -> ux_device_class_audio_stream_parameter_thread_entry, + (ULONG)(ALIGN_TYPE)stream, (VOID *) stream -> ux_device_class_audio_stream_thread_stack, + memory_size, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + + /* Check for successful allocation. */ + if (status != UX_SUCCESS) + break; + + UX_THREAD_EXTENSION_PTR_SET(&(stream -> ux_device_class_audio_stream_thread), stream) + + /* Save callbacks. */ + _ux_utility_memory_copy(&stream -> ux_device_class_audio_stream_callbacks, + &stream_parameter -> ux_device_class_audio_stream_parameter_callbacks, + sizeof(UX_DEVICE_CLASS_AUDIO_STREAM_CALLBACKS)); + + /* Save audio instance. */ + stream -> ux_device_class_audio_stream_audio = audio; + + stream ++; + stream_parameter ++; + } + + /* Check for successful creation. */ + if (status == UX_SUCCESS) + { + + /* Save the address of the Audio instance inside the Audio container. */ + class -> ux_slave_class_instance = (VOID *) audio; + + /* Link to class instance. */ + audio -> ux_device_class_audio_class = class; + + /* Save callbacks. */ + _ux_utility_memory_copy(&audio -> ux_device_class_audio_callbacks, + &audio_parameter -> ux_device_class_audio_parameter_callbacks, + sizeof(UX_DEVICE_CLASS_AUDIO_CALLBACKS)); + + /* 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 = audio -> ux_device_class_audio_streams; + for (i = 0; i < audio -> ux_device_class_audio_streams_nb; i ++) + { + if (stream -> ux_device_class_audio_stream_thread.tx_thread_id) + _ux_utility_thread_delete(&stream -> ux_device_class_audio_stream_thread); + if (stream -> ux_device_class_audio_stream_thread_stack) + _ux_utility_memory_free(stream -> ux_device_class_audio_stream_thread_stack); + if (stream -> ux_device_class_audio_stream_buffer) + _ux_utility_memory_free(stream -> ux_device_class_audio_stream_buffer); + stream ++; + } + _ux_utility_memory_free(audio); + + return(status); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_ioctl.c b/common/usbx_device_classes/src/ux_device_class_audio_ioctl.c new file mode 100644 index 0000000..99797de --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_ioctl.c @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs certain functions on the audio instance */ +/* */ +/* INPUT */ +/* */ +/* audio Address of audio class */ +/* instance */ +/* ioctl_function IOCTL function code */ +/* parameter Parameter for function */ +/* */ +/* OUTPUT */ +/* */ +/* Status */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_ioctl(UX_DEVICE_CLASS_AUDIO *audio, ULONG ioctl_function, + VOID *parameter) +{ + +UINT status; +VOID **pptr_parameter; + + + /* Let's be optimist ! */ + status = UX_SUCCESS; + + /* The command request will tell us what we need to do here. */ + switch (ioctl_function) + { + + case UX_DEVICE_CLASS_AUDIO_IOCTL_GET_ARG: + + /* Properly cast the parameter pointer. */ + pptr_parameter = (VOID **) parameter; + + /* Save argument. */ + *pptr_parameter = audio -> ux_device_class_audio_callbacks.ux_device_class_audio_arg; + + break; + + default: + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return status to caller. */ + return(status); + +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_read_frame_free.c b/common/usbx_device_classes/src/ux_device_class_audio_read_frame_free.c new file mode 100644 index 0000000..b03a0fd --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_read_frame_free.c @@ -0,0 +1,124 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_read_frame_free PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function free access buffer for transfer in the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_read_frame_free(UX_DEVICE_CLASS_AUDIO_STREAM *stream) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UCHAR *next_frame; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Underflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length == 0) + { + return(UX_BUFFER_OVERFLOW); + } + + /* Calculate next frame. */ + if (stream -> ux_device_class_audio_stream_access_pos != stream -> ux_device_class_audio_stream_transfer_pos) + { + next_frame = (UCHAR *)stream -> ux_device_class_audio_stream_access_pos; + next_frame += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_frame >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_frame = stream -> ux_device_class_audio_stream_buffer; + + /* Re-free current frame. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = 0; + + /* Move to next frame. */ + stream -> ux_device_class_audio_stream_access_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_frame; + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos = 0; + } + else + + /* Just re-free current frame. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = 0; + + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_read_frame_get.c b/common/usbx_device_classes/src/ux_device_class_audio_read_frame_get.c new file mode 100644 index 0000000..d5ed96f --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_read_frame_get.c @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_read_frame_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtain frame access pointer from the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* frame_data Pointer to buffer to save */ +/* pointer to frame data */ +/* frame_length Pointer to buffer to save */ +/* pointer to frame length */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_read_frame_get(UX_DEVICE_CLASS_AUDIO_STREAM *stream, + UCHAR **frame_data, ULONG *frame_length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Underflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length == 0) + { + return(UX_BUFFER_OVERFLOW); + } + + /* Obtain frame structure pointer. */ + *frame_data = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_data; + *frame_length = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length; + + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_read_thread_entry.c b/common/usbx_device_classes/src/ux_device_class_audio_read_thread_entry.c new file mode 100644 index 0000000..7f668d4 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_read_thread_entry.c @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_read_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is thread of ISO OUT from the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* audio_stream Address of audio stream */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler System error trap */ +/* _ux_utility_thread_suspend Suspend thread used */ +/* _ux_device_stack_transfer_request Issue transfer request */ +/* _ux_utility_memory_copy Copy data */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_audio_read_thread_entry(ULONG audio_stream) +{ + +UINT status; +UX_DEVICE_CLASS_AUDIO_STREAM *stream; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_TRANSFER *transfer; +UCHAR *next_pos; +UX_DEVICE_CLASS_AUDIO_FRAME *next_frame; +ULONG max_packet_size; +ULONG transactions; +ULONG actual_length; + + + /* Get Audio class instance. */ + UX_THREAD_EXTENSION_PTR_GET(stream, UX_DEVICE_CLASS_AUDIO_STREAM, audio_stream) + + /* Get stack device instance. */ + device = stream -> ux_device_class_audio_stream_audio -> ux_device_class_audio_device; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + max_packet_size = 0; + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Get endpoint instance. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + + /* Endpoint not available, maybe it's alternate setting 0. */ + if (endpoint == UX_NULL) + break; + + /* Calculate transfer size based on packet size and number transactions once endpoint is available. */ + if (max_packet_size == 0) + { + max_packet_size = endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize & 0x7FF; + transactions = (endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize >> 11) & 0x3; + if (transactions) + max_packet_size *= (transactions + 1); + } + + /* Get transfer instance. */ + transfer = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Start frame transfer anyway. */ + status = _ux_device_stack_transfer_request(transfer, max_packet_size, max_packet_size); + + /* Check error. */ + if (status != UX_SUCCESS) + { + + /* Error notification! */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_ERROR); + break; + } + + /* Get actual transfer length. */ + actual_length = transfer -> ux_slave_transfer_request_actual_length; + + /* Frame received, log it. */ + stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length = actual_length; + stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_pos = 0; + _ux_utility_memory_copy(stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_data, + transfer -> ux_slave_transfer_request_data_pointer, + actual_length); + + /* For simple, do not advance the transfer position if there is overflow. */ + next_pos = (UCHAR *)stream -> ux_device_class_audio_stream_transfer_pos; + next_pos += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_pos >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_pos = stream -> ux_device_class_audio_stream_buffer; + next_frame = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_pos; + + /* Check overflow! */ + if (next_frame -> ux_device_class_audio_frame_length > 0) + { + + /* Error notification! */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + } + else + + /* Update transfer position. */ + stream -> ux_device_class_audio_stream_transfer_pos = next_frame; + + /* Invoke notification callback. */ + if (stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done != UX_NULL) + stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done(stream, actual_length); + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module or when a change of alternate setting happens. */ + _ux_utility_thread_suspend(&stream -> ux_device_class_audio_stream_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_reception_start.c b/common/usbx_device_classes/src/ux_device_class_audio_reception_start.c new file mode 100644 index 0000000..6a471fe --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_reception_start.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_reception_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function start receiving frames in the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_resume Resume thread used */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_reception_start(UX_DEVICE_CLASS_AUDIO_STREAM *stream) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Check if overflow. */ + if (stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length > 0) + return(UX_BUFFER_OVERFLOW); + + /* Start read thread. */ + _ux_utility_thread_resume(&stream -> ux_device_class_audio_stream_thread); + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_sample_read16.c b/common/usbx_device_classes/src/ux_device_class_audio_sample_read16.c new file mode 100644 index 0000000..f218054 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_sample_read16.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_sample_read16 PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads 16-bit sample value from the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* buffer Pointer to buffer to save */ +/* sample data */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_sample_read16(UX_DEVICE_CLASS_AUDIO_STREAM *stream, + USHORT *buffer) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UCHAR *sample_ptr; +UCHAR *next_frame_buffer; +ULONG next_frame_sample; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Underflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length == 0) + { + return(UX_BUFFER_OVERFLOW); + } + + /* Try to read a sample. */ + sample_ptr = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_data + + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos; + if (buffer) + *buffer = *(USHORT *)(sample_ptr); + + /* Update sample read state. */ + next_frame_sample = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos + 2; + if (next_frame_sample >= stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length) + { + + /* Set frame length to 0 to indicate no data. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = 0; + + /* Move to next frame buffer. */ + next_frame_sample = 0; + + /* Move frame if it's not the last one. */ + if (stream -> ux_device_class_audio_stream_access_pos != stream -> ux_device_class_audio_stream_transfer_pos) + { + next_frame_buffer = (UCHAR *)stream -> ux_device_class_audio_stream_access_pos; + next_frame_buffer += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_frame_buffer >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_frame_buffer = stream -> ux_device_class_audio_stream_buffer; + stream -> ux_device_class_audio_stream_access_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_frame_buffer; + } + } + + /* Update next sample position. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos = next_frame_sample; + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_sample_read24.c b/common/usbx_device_classes/src/ux_device_class_audio_sample_read24.c new file mode 100644 index 0000000..1ce43fd --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_sample_read24.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_sample_read24 PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads 24-bit sample value from the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* buffer Pointer to buffer to save */ +/* sample data */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_sample_read24(UX_DEVICE_CLASS_AUDIO_STREAM *stream, + ULONG *buffer) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UCHAR *sample_ptr; +UCHAR *next_frame_buffer; +ULONG next_frame_sample; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Underflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length == 0) + { + return(UX_BUFFER_OVERFLOW); + } + + /* Try to read a sample. */ + sample_ptr = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_data + + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos; + if (buffer) + *buffer = sample_ptr[0] + ((ULONG)sample_ptr[1] << 8) + ((ULONG)sample_ptr[2] << 16); + + /* Update sample read state. */ + next_frame_sample = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos + 3; + if (next_frame_sample >= stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length) + { + + /* Set frame length to 0 to indicate no data. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = 0; + + /* Move to next frame buffer. */ + next_frame_sample = 0; + + /* Move frame if it's not the last one. */ + if (stream -> ux_device_class_audio_stream_access_pos != stream -> ux_device_class_audio_stream_transfer_pos) + { + next_frame_buffer = (UCHAR *)stream -> ux_device_class_audio_stream_access_pos; + next_frame_buffer += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_frame_buffer >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_frame_buffer = stream -> ux_device_class_audio_stream_buffer; + stream -> ux_device_class_audio_stream_access_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_frame_buffer; + } + } + + /* Update next sample position. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos = next_frame_sample; + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_sample_read32.c b/common/usbx_device_classes/src/ux_device_class_audio_sample_read32.c new file mode 100644 index 0000000..e0ecc6d --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_sample_read32.c @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_sample_read32 PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads 32-bit sample from the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* buffer Pointer to buffer to save */ +/* sample data */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_sample_read32(UX_DEVICE_CLASS_AUDIO_STREAM *stream, ULONG *buffer) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UCHAR *sample_ptr; +UCHAR *next_frame_buffer; +ULONG next_frame_sample; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Underflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length == 0) + { + return(UX_BUFFER_OVERFLOW); + } + + /* Try to read a sample. */ + sample_ptr = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_data + + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos; + if (buffer) + *buffer = *(ULONG *)(sample_ptr); + + /* Update sample read state. */ + next_frame_sample = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos + 4; + if (next_frame_sample >= stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length) + { + + /* Set frame length to 0 to indicate no data. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = 0; + + /* Move to next frame buffer. */ + next_frame_sample = 0; + + /* Move frame if it's not the last one. */ + if (stream -> ux_device_class_audio_stream_access_pos != stream -> ux_device_class_audio_stream_transfer_pos) + { + next_frame_buffer = (UCHAR *)stream -> ux_device_class_audio_stream_access_pos; + next_frame_buffer += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_frame_buffer >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_frame_buffer = stream -> ux_device_class_audio_stream_buffer; + stream -> ux_device_class_audio_stream_access_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_frame_buffer; + } + } + + /* Update next sample position. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos = next_frame_sample; + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_sample_read8.c b/common/usbx_device_classes/src/ux_device_class_audio_sample_read8.c new file mode 100644 index 0000000..580d28c --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_sample_read8.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_sample_read8 PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads 8-bit sample from the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* buffer Pointer to buffer to save */ +/* sample data */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_sample_read8(UX_DEVICE_CLASS_AUDIO_STREAM *stream, + UCHAR *buffer) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UCHAR *sample_ptr; +UCHAR *next_frame_buffer; +ULONG next_frame_sample; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Underflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length == 0) + { + return(UX_BUFFER_OVERFLOW); + } + + /* Try to read a sample. */ + sample_ptr = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_data + + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos; + if (buffer) + *buffer = *sample_ptr; + + /* Update sample read state. */ + next_frame_sample = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos + 1; + if (next_frame_sample >= stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length) + { + + /* Set frame length to 0 to indicate no data. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = 0; + + /* Move to next frame buffer. */ + next_frame_sample = 0; + + /* Move frame if it's not the last one. */ + if (stream -> ux_device_class_audio_stream_access_pos != stream -> ux_device_class_audio_stream_transfer_pos) + { + next_frame_buffer = (UCHAR *)stream -> ux_device_class_audio_stream_access_pos; + next_frame_buffer += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_frame_buffer >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_frame_buffer = stream -> ux_device_class_audio_stream_buffer; + stream -> ux_device_class_audio_stream_access_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_frame_buffer; + } + } + + /* Update next sample position. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_pos = next_frame_sample; + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_stream_get.c b/common/usbx_device_classes/src/ux_device_class_audio_stream_get.c new file mode 100644 index 0000000..4072478 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_stream_get.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_stream_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function get the stream instance of Audio class. */ +/* */ +/* INPUT */ +/* */ +/* audio Address of audio class */ +/* instance */ +/* stream_index Stream instance index 0 based */ +/* stream Pointer to buffer to fill */ +/* pointer to stream instance */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_delete Delete thread used */ +/* _ux_utility_memory_free Free used local memory */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_stream_get(UX_DEVICE_CLASS_AUDIO *audio, + ULONG stream_index, UX_DEVICE_CLASS_AUDIO_STREAM **stream) +{ + + + /* Sanity check. */ + if (audio == UX_NULL) + return(UX_ERROR); + + /* Index validation. */ + if (stream_index >= audio -> ux_device_class_audio_streams_nb) + return(UX_ERROR); + + /* Store the stream instance found. */ + if (stream) + *stream = audio -> ux_device_class_audio_streams + stream_index; + + /* Return completion status. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_transmission_start.c b/common/usbx_device_classes/src/ux_device_class_audio_transmission_start.c new file mode 100644 index 0000000..31d8480 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_transmission_start.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_transmission_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function start sending valid frames in the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_resume Resume thread used */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_transmission_start(UX_DEVICE_CLASS_AUDIO_STREAM *stream) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Check if there is frame to send (underflow). */ + if (stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length == 0) + return(UX_BUFFER_OVERFLOW); + + /* Start write thread. */ + _ux_utility_thread_resume(&stream -> ux_device_class_audio_stream_thread); + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_unitialize.c b/common/usbx_device_classes/src/ux_device_class_audio_unitialize.c new file mode 100644 index 0000000..28886dd --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_unitialize.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_uninitialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function uninitialize the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_delete Delete thread used */ +/* _ux_utility_memory_free Free used local memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_uninitialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_DEVICE_CLASS_AUDIO *audio; +UX_DEVICE_CLASS_AUDIO_STREAM *stream; +UX_SLAVE_CLASS *class; +ULONG i; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + audio = (UX_DEVICE_CLASS_AUDIO *) class -> ux_slave_class_instance; + + /* Sanity check. */ + if (audio != UX_NULL) + { + + /* Free the stream resources. */ + stream = (UX_DEVICE_CLASS_AUDIO_STREAM *)((UCHAR *)audio + sizeof(UX_DEVICE_CLASS_AUDIO)); + for (i = 0; i < audio -> ux_device_class_audio_streams_nb; i ++) + { + _ux_utility_thread_delete(&stream -> ux_device_class_audio_stream_thread); + _ux_utility_memory_free(stream -> ux_device_class_audio_stream_thread_stack); + _ux_utility_memory_free(stream -> ux_device_class_audio_stream_buffer); + } + + /* Free the audio instance with controls and streams. */ + _ux_utility_memory_free(audio); + } + + /* Return completion status. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_audio_write_frame_commit.c b/common/usbx_device_classes/src/ux_device_class_audio_write_frame_commit.c new file mode 100644 index 0000000..f8efba5 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_write_frame_commit.c @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_write_frame_commit PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function set frame buffer valid to send in the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* buffer Pointer to buffer to save */ +/* frame data */ +/* length Frame length in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_write_frame_commit(UX_DEVICE_CLASS_AUDIO_STREAM *stream, ULONG length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UCHAR *next_pos; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Check overflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos == stream -> ux_device_class_audio_stream_transfer_pos && + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length != 0) + return(UX_BUFFER_OVERFLOW); + + /* Check frame length. */ + if (stream -> ux_device_class_audio_stream_frame_buffer_size < length) + return(UX_ERROR); + + /* Calculate next frame buffer. */ + next_pos = (UCHAR *)stream -> ux_device_class_audio_stream_access_pos; + next_pos += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_pos >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_pos = stream -> ux_device_class_audio_stream_buffer; + + /* Commit frame length. */ + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length = length; + + /* Move frame position. */ + stream -> ux_device_class_audio_stream_access_pos = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_pos; + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_write_frame_get.c b/common/usbx_device_classes/src/ux_device_class_audio_write_frame_get.c new file mode 100644 index 0000000..5ad7945 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_write_frame_get.c @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_write_frame_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function get a writable frame buffer in the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* stream Address of audio stream */ +/* instance */ +/* frame Pointer to buffer to save */ +/* frame buffer pointer */ +/* length Pointer to save frame */ +/* buffer length in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_audio_write_frame_get(UX_DEVICE_CLASS_AUDIO_STREAM *stream, UCHAR **frame, ULONG *length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check if endpoint is available. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + if (endpoint == UX_NULL) + return(UX_ERROR); + + /* Check if endpoint direction is OK. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) + return(UX_ERROR); + + /* Check overflow!! */ + if (stream -> ux_device_class_audio_stream_access_pos == stream -> ux_device_class_audio_stream_transfer_pos && + stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_length != 0) + return(UX_BUFFER_OVERFLOW); + + /* Store frame buffer pointer. */ + *frame = stream -> ux_device_class_audio_stream_access_pos -> ux_device_class_audio_frame_data; + + /* Exclude header size in frame buffer size. */ + *length = stream -> ux_device_class_audio_stream_frame_buffer_size - 8; + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_audio_write_thread_entry.c b/common/usbx_device_classes/src/ux_device_class_audio_write_thread_entry.c new file mode 100644 index 0000000..a246cf1 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_audio_write_thread_entry.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_audio.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_audio_write_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is thread of ISO IN for the Audio class. */ +/* */ +/* INPUT */ +/* */ +/* audio_stream Address of audio stream */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler System error trap */ +/* _ux_utility_thread_suspend Suspend thread used */ +/* _ux_device_stack_transfer_request Issue transfer request */ +/* _ux_utility_memory_copy Copy data */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_audio_write_thread_entry(ULONG audio_stream) +{ + +UINT status; +UX_DEVICE_CLASS_AUDIO_STREAM *stream; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_TRANSFER *transfer; +UCHAR *next_pos; +UX_DEVICE_CLASS_AUDIO_FRAME *next_frame; +ULONG transfer_length; +ULONG actual_length; + + + /* Get Audio class stream instance. */ + UX_THREAD_EXTENSION_PTR_GET(stream, UX_DEVICE_CLASS_AUDIO_STREAM, audio_stream) + + /* Get stack device instance. */ + device = stream -> ux_device_class_audio_stream_audio -> ux_device_class_audio_device; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Get endpoint instance. */ + endpoint = stream -> ux_device_class_audio_stream_endpoint; + + /* Endpoint not available, maybe it's alternate setting 0. */ + if (endpoint == UX_NULL) + break; + + /* Get transfer instance. */ + transfer = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Start frame transfer anyway (even ZLP). */ + transfer_length = stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length; + if (transfer_length) + _ux_utility_memory_copy(transfer -> ux_slave_transfer_request_data_pointer, + stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_data, transfer_length); + + /* Issue transfer request, thread blocked until transfer done. */ + status = _ux_device_stack_transfer_request(transfer, transfer_length, transfer_length); + + /* Check error. */ + if (status != UX_SUCCESS) + { + + /* Error notification! */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_ERROR); + break; + } + + /* Frame sent, free it. */ + stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length = 0; + + /* Get actual transfer length. */ + actual_length = transfer -> ux_slave_transfer_request_actual_length; + + /* Calculate next position. */ + next_pos = (UCHAR *)stream -> ux_device_class_audio_stream_transfer_pos; + next_pos += stream -> ux_device_class_audio_stream_frame_buffer_size; + if (next_pos >= stream -> ux_device_class_audio_stream_buffer + stream -> ux_device_class_audio_stream_buffer_size) + next_pos = stream -> ux_device_class_audio_stream_buffer; + next_frame = (UX_DEVICE_CLASS_AUDIO_FRAME *)next_pos; + + /* Underflow check! */ + if (transfer_length) + { + + /* Advance position. */ + stream -> ux_device_class_audio_stream_transfer_pos = next_frame; + + /* Error trap! */ + if (next_frame -> ux_device_class_audio_frame_length == 0) + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + } + else + { + + /* Advance position if next frame available. */ + if (next_frame -> ux_device_class_audio_frame_length) + stream -> ux_device_class_audio_stream_transfer_pos = next_frame; + else + + /* Error trap! */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + } + + /* Invoke notification callback. */ + if (stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done != UX_NULL) + stream -> ux_device_class_audio_stream_callbacks.ux_device_class_audio_stream_frame_done(stream, actual_length); + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module or when a change of alternate setting happens. */ + _ux_utility_thread_suspend(&stream -> ux_device_class_audio_stream_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_activate.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_activate.c new file mode 100644 index 0000000..58c5f4b --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_activate.c @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB CDC device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to cdc_acm command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_CDC_ACM *cdc_acm; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_acm = (UX_SLAVE_CLASS_CDC_ACM *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)cdc_acm; + + /* Now the opposite, store the interface in the class instance. */ + cdc_acm -> ux_slave_class_cdc_acm_interface = interface; + + /* If there is a activate function call it. */ + if (cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_instance_activate != UX_NULL) + { + /* Invoke the application. */ + cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_instance_activate(cdc_acm); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ACM_ACTIVATE, cdc_acm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, cdc_acm, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkin_thread.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkin_thread.c new file mode 100644 index 0000000..9d0e4bc --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkin_thread.c @@ -0,0 +1,210 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_bulkin_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the cdc_acm bulkin endpoint. The bulk*/ +/* IN endpoint is used when the device wants to write data to be sent */ +/* to the host. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm_class Address of cdc_acm class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_cdc_acm_bulkin_thread(ULONG cdc_acm_class) +{ + +UX_SLAVE_CLASS_CDC_ACM *cdc_acm; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +ULONG actual_flags; +ULONG transfer_length; +ULONG total_length; +ULONG sent_length; + + + /* Get the cdc_acm instance from this class container. */ + UX_THREAD_EXTENSION_PTR_GET(cdc_acm, UX_SLAVE_CLASS_CDC_ACM, cdc_acm_class) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This is the first time we are activated. We need the interface to the class. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* So the next endpoint has to be the IN endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* Get the transfer request for the bulk IN pipe. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Wait until we have a event sent by the application. */ + status = _ux_utility_event_flags_get(&cdc_acm -> ux_slave_class_cdc_acm_event_flags_group, UX_DEVICE_CLASS_CDC_ACM_WRITE_EVENT, + TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); + + /* Check the completion code. */ + if (status == UX_SUCCESS) + { + + /* Get the length of the entire buffer to send. */ + total_length = cdc_acm -> ux_slave_class_cdc_acm_callback_total_length; + + /* Duplicate the data pointer to keep a current pointer. */ + cdc_acm -> ux_slave_class_cdc_acm_callback_current_data_pointer = cdc_acm -> ux_slave_class_cdc_acm_callback_data_pointer; + + /* Reset sent length. */ + sent_length = 0; + + /* Special ZLP case. */ + if (total_length == 0) + + /* Send the zlp to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, 0, 0); + + else + { + + /* We should send the total length. But we may have a case of ZLP. */ + while (total_length) + { + + /* Check the length remaining to send. */ + if (total_length > endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize) + + /* We can't fit all the length. */ + transfer_length = endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize; + + else + + /* We can send everything. */ + transfer_length = total_length; + + /* Copy the payload locally. */ + _ux_utility_memory_copy (transfer_request -> ux_slave_transfer_request_data_pointer, + cdc_acm -> ux_slave_class_cdc_acm_callback_current_data_pointer, + transfer_length); + + /* Send the acm payload to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, transfer_length, transfer_length); + + /* Check the status. */ + if (status != UX_SUCCESS) + { + + /* Reset total_length as we need to get out of loop. */ + total_length = 0; + } + else + { + + /* Update sent length. */ + sent_length += transfer_length; + + /* Decrement length to be sent. */ + total_length -= transfer_length; + + /* And update the current pointer. */ + cdc_acm -> ux_slave_class_cdc_acm_callback_current_data_pointer += transfer_length; + } + } + } + + /* Schedule of transmission was completed. */ + cdc_acm -> ux_slave_class_cdc_acm_scheduled_write = UX_FALSE; + + /* We get here when the entire user data payload has been sent or if there is an error. */ + /* If there is a callback defined by the application, send the transaction event to it. */ + if (cdc_acm -> ux_device_class_cdc_acm_write_callback != UX_NULL) + + /* Callback exists. */ + cdc_acm -> ux_device_class_cdc_acm_write_callback(cdc_acm, status, sent_length); + + /* Now we return to wait for an event from the application or the user to stop the transmission. */ + } + else + { + break; + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module or when a change of alternate setting happens. */ + _ux_utility_thread_suspend(&cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkout_thread.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkout_thread.c new file mode 100644 index 0000000..5ca343f --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_bulkout_thread.c @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_bulkout_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the cdc_acm bulk out endpoint. It */ +/* is waiting for the host to send data on the bulk out endpoint to */ +/* the device. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm_class Address of cdc_acm class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_cdc_acm_bulkout_thread(ULONG cdc_acm_class) +{ + +UX_SLAVE_CLASS_CDC_ACM *cdc_acm; +UX_SLAVE_DEVICE *device; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; + + /* Cast properly the cdc_acm instance. */ + UX_THREAD_EXTENSION_PTR_GET(cdc_acm, UX_SLAVE_CLASS_CDC_ACM, cdc_acm_class) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This is the first time we are activated. We need the interface to the class. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if OUT we have the correct endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + { + + /* So the next endpoint has to be the OUT endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + /* This thread runs forever but can be suspended or resumed by the user application. */ + while(1) + { + + /* Select the transfer request associated with BULK OUT endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize, + endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize); + + /* Check the completion code. */ + if (status == UX_SUCCESS) + { + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_slave_transfer_request_completion_code == UX_SUCCESS) + { + + /* If there is a callback defined by the application, send the transaction event to it. */ + if (cdc_acm -> ux_device_class_cdc_acm_read_callback != UX_NULL) + + /* Callback exists. */ + cdc_acm -> ux_device_class_cdc_acm_read_callback(cdc_acm, UX_SUCCESS, transfer_request -> ux_slave_transfer_request_data_pointer, + transfer_request -> ux_slave_transfer_request_actual_length); + + } + else + { + + /* We have an error. If there is a callback defined by the application, send the transaction event to it. */ + if (cdc_acm -> ux_device_class_cdc_acm_read_callback != UX_NULL) + + /* Callback exists. */ + cdc_acm -> ux_device_class_cdc_acm_read_callback(cdc_acm, status, UX_NULL, 0); + + } + } + } + + /* We need to suspend ourselves. We will be resumed by the application if needed. */ + _ux_utility_thread_suspend(&cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_control_request.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_control_request.c new file mode 100644 index 0000000..8ae6921 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_control_request.c @@ -0,0 +1,179 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* CDC Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ +UX_SLAVE_CLASS_CDC_ACM *cdc_acm; +UX_SLAVE_CLASS *class; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +ULONG request; +ULONG value; +ULONG request_length; +ULONG transmit_length; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_acm = (UX_SLAVE_CLASS_CDC_ACM *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + + /* Extract all necessary fields of the value. */ + value = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE); + + /* Pickup the request length. */ + request_length = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); + + transmit_length = request_length ; + + /* Here we proceed only the standard request we know of at the device level. */ + switch (request) + { + + case UX_SLAVE_CLASS_CDC_ACM_SET_CONTROL_LINE_STATE: + + /* Reset current line state values. */ + cdc_acm -> ux_slave_class_cdc_acm_data_dtr_state = 0; + cdc_acm -> ux_slave_class_cdc_acm_data_rts_state = 0; + + /* Get the line state parameters from the host. DTR signal. */ + if (value & UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_DTR) + cdc_acm -> ux_slave_class_cdc_acm_data_dtr_state = UX_TRUE; + + /* Get the line state parameters from the host. RTS signal. */ + if (value & UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_RTS) + cdc_acm -> ux_slave_class_cdc_acm_data_rts_state = UX_TRUE; + + /* If there is a parameter change function call it. */ + if (cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_parameter_change != UX_NULL) + { + + /* Invoke the application. */ + cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_parameter_change(cdc_acm); + } + + break ; + + case UX_SLAVE_CLASS_CDC_ACM_GET_LINE_CODING: + + /* Setup the length appropriately. */ + if (request_length > UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_RESPONSE_SIZE) + transmit_length = UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_RESPONSE_SIZE; + + /* Send the line coding default parameters back to the host. */ + _ux_utility_long_put(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_BAUDRATE_STRUCT, + cdc_acm -> ux_slave_class_cdc_acm_baudrate); + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_STRUCT) = cdc_acm -> ux_slave_class_cdc_acm_stop_bit; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARITY_STRUCT) = cdc_acm -> ux_slave_class_cdc_acm_parity; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_DATA_BIT_STRUCT) = cdc_acm -> ux_slave_class_cdc_acm_data_bit; + + /* Set the phase of the transfer to data out. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Perform the data transfer. */ + _ux_device_stack_transfer_request(transfer_request, transmit_length, request_length); + break; + + case UX_SLAVE_CLASS_CDC_ACM_SET_LINE_CODING: + + /* Get the line coding parameters from the host. */ + cdc_acm -> ux_slave_class_cdc_acm_baudrate = _ux_utility_long_get(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_BAUDRATE_STRUCT); + cdc_acm -> ux_slave_class_cdc_acm_stop_bit = *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_STRUCT); + cdc_acm -> ux_slave_class_cdc_acm_parity = *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARITY_STRUCT); + cdc_acm -> ux_slave_class_cdc_acm_data_bit = *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_DATA_BIT_STRUCT); + + /* If there is a parameter change function call it. */ + if (cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_parameter_change != UX_NULL) + { + + /* Invoke the application. */ + cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_parameter_change(cdc_acm); + } + + break ; + + default: + + /* Unknown function. It's not handled. */ + return(UX_ERROR); + } + + /* It's handled. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_deactivate.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_deactivate.c new file mode 100644 index 0000000..dc132e1 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_deactivate.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the cdc_acm class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort Abort all transfers */ +/* _ux_device_class_cdc_acm_ioctl IO control */ +/* */ +/* CALLED BY */ +/* */ +/* CDC Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_CDC_ACM *cdc_acm; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_acm = (UX_SLAVE_CLASS_CDC_ACM *) class -> ux_slave_class_instance; + + /* We need the interface to the class. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + } + + /* Terminate the transactions pending on the endpoints. */ + _ux_device_stack_transfer_all_request_abort(endpoint_in, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(endpoint_out, UX_TRANSFER_BUS_RESET); + + /* Terminate transmission and free resources. */ + _ux_device_class_cdc_acm_ioctl(cdc_acm, UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_STOP, UX_NULL); + + /* If there is a deactivate function call it. */ + if (cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_instance_deactivate != UX_NULL) + { + + /* Invoke the application. */ + cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_instance_deactivate(cdc_acm); + } + + /* We need to reset the DTR and RTS values so they do not carry over to the + next connection. */ + cdc_acm -> ux_slave_class_cdc_acm_data_dtr_state = 0; + cdc_acm -> ux_slave_class_cdc_acm_data_rts_state = 0; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ACM_DEACTIVATE, cdc_acm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(cdc_acm); + + /* Return completion status. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_entry.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_entry.c new file mode 100644 index 0000000..1cf3e63 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_entry.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_cdc_acm_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the cdc_acm class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the cdc_acm interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_cdc_acm_initialize Initialize cdc_acm class */ +/* _ux_device_class_cdc_acm_uninitialize Uninitialize cdc_acm class*/ +/* _ux_device_class_cdc_acm_activate Activate cdc_acm class */ +/* _ux_device_class_cdc_acm_deactivate Deactivate cdc_acm class */ +/* _ux_device_class_cdc_acm_control_request Request control */ +/* */ +/* CALLED BY */ +/* */ +/* CDC Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the CDC ACM class. */ + status = _ux_device_class_cdc_acm_initialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_UNINITIALIZE: + + /* Call the init function of the CDC ACM class. */ + status = _ux_device_class_cdc_acm_uninitialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_SLAVE_CLASS_CDC_ACM_CLASS) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the cdc_acm thread needs to be activated. */ + status = _ux_device_class_cdc_acm_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the cdc_acm thread canceled. */ + status = _ux_device_class_cdc_acm_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + status = _ux_device_class_cdc_acm_control_request(command); + + /* Return the completion status. */ + return(status); + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_initialize.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_initialize.c new file mode 100644 index 0000000..e858c2c --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_initialize.c @@ -0,0 +1,140 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB CDC device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to cdc_acm command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_mutex_create Create mutex */ +/* _ux_utility_mutex_delete Delete mutex */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_CDC_ACM *cdc_acm; +UX_SLAVE_CLASS_CDC_ACM_PARAMETER *cdc_acm_parameter; +UX_SLAVE_CLASS *class; +UINT status; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device cdc_acm class. */ + cdc_acm = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_CDC_ACM)); + + /* Check for successful allocation. */ + if (cdc_acm == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save the address of the CDC instance inside the CDC container. */ + class -> ux_slave_class_instance = (VOID *) cdc_acm; + + /* Get the pointer to the application parameters for the cdc_acm class. */ + cdc_acm_parameter = command -> ux_slave_class_command_parameter; + + /* Store the start and stop signals if needed by the application. */ + cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_instance_activate = cdc_acm_parameter -> ux_slave_class_cdc_acm_instance_activate; + cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_instance_deactivate = cdc_acm_parameter -> ux_slave_class_cdc_acm_instance_deactivate; + cdc_acm -> ux_slave_class_cdc_acm_parameter.ux_slave_class_cdc_acm_parameter_change = cdc_acm_parameter -> ux_slave_class_cdc_acm_parameter_change; + + /* Create the Mutex for each endpoint as multiple threads cannot access each pipe at the same time. */ + status = _ux_utility_mutex_create(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex, "ux_slave_class_cdc_acm_in_mutex"); + + /* Check Mutex creation error. */ + if(status != UX_SUCCESS) + { + + /* Free the resources. */ + _ux_utility_memory_free(cdc_acm); + + /* Return fatal error. */ + return(UX_MUTEX_ERROR); + } + + /* Out Mutex. */ + status = _ux_utility_mutex_create(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_out_mutex, "ux_slave_class_cdc_acm_out_mutex"); + + /* Check Mutex creation error. */ + if(status != UX_SUCCESS) + { + + /* Delete the endpoint IN mutex. */ + _ux_utility_mutex_delete(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex); + + /* Free the resources. */ + _ux_utility_memory_free(cdc_acm); + + /* Return fatal error. */ + return(UX_MUTEX_ERROR); + } + + /* Update the line coding fields with default values. */ + cdc_acm -> ux_slave_class_cdc_acm_baudrate = UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_BAUDRATE; + cdc_acm -> ux_slave_class_cdc_acm_stop_bit = UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_STOP_BIT; + cdc_acm -> ux_slave_class_cdc_acm_parity = UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARITY; + cdc_acm -> ux_slave_class_cdc_acm_data_bit = UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_DATA_BIT; + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_ioctl.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_ioctl.c new file mode 100644 index 0000000..3aacab0 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_ioctl.c @@ -0,0 +1,428 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs certain functions on the cdc acm instance */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Address of cdc_acm class */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_abort Abort transfer */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_event_flags_create Create event flags */ +/* _ux_utility_event_flags_delete Delete event flags */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_delete Delete thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_ioctl(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, ULONG ioctl_function, + VOID *parameter) +{ + +UINT status; +UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER *line_coding; +UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER *line_state; +UX_SLAVE_CLASS_CDC_ACM_CALLBACK_PARAMETER *callback; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_TRANSFER *transfer_request; + + /* Let's be optimist ! */ + status = UX_SUCCESS; + + /* The command request will tell us what we need to do here. */ + switch (ioctl_function) + { + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING: + + /* Properly cast the parameter pointer. */ + line_coding = (UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER *) parameter; + + /* Save the parameters in the cdc_acm function. */ + cdc_acm -> ux_slave_class_cdc_acm_baudrate = line_coding -> ux_slave_class_cdc_acm_parameter_baudrate; + cdc_acm -> ux_slave_class_cdc_acm_stop_bit = line_coding -> ux_slave_class_cdc_acm_parameter_stop_bit; + cdc_acm -> ux_slave_class_cdc_acm_parity = line_coding -> ux_slave_class_cdc_acm_parameter_parity; + cdc_acm -> ux_slave_class_cdc_acm_data_bit = line_coding -> ux_slave_class_cdc_acm_parameter_data_bit; + + break; + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING: + + /* Properly cast the parameter pointer. */ + line_coding = (UX_SLAVE_CLASS_CDC_ACM_LINE_CODING_PARAMETER *) parameter; + + /* Save the parameters in the cdc_acm function. */ + line_coding -> ux_slave_class_cdc_acm_parameter_baudrate = cdc_acm -> ux_slave_class_cdc_acm_baudrate; + line_coding -> ux_slave_class_cdc_acm_parameter_stop_bit = cdc_acm -> ux_slave_class_cdc_acm_stop_bit; + line_coding -> ux_slave_class_cdc_acm_parameter_parity = cdc_acm -> ux_slave_class_cdc_acm_parity; + line_coding -> ux_slave_class_cdc_acm_parameter_data_bit = cdc_acm -> ux_slave_class_cdc_acm_data_bit; + + break; + + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_GET_LINE_STATE: + + /* Properly cast the parameter pointer. */ + line_state = (UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER *) parameter; + + /* Return the DTR/RTS signals. */ + line_state -> ux_slave_class_cdc_acm_parameter_rts = cdc_acm -> ux_slave_class_cdc_acm_data_rts_state; + line_state -> ux_slave_class_cdc_acm_parameter_dtr = cdc_acm -> ux_slave_class_cdc_acm_data_dtr_state; + + break; + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE: + + /* Properly cast the parameter pointer. */ + line_state = (UX_SLAVE_CLASS_CDC_ACM_LINE_STATE_PARAMETER *) parameter; + + /* Set the DTR/RTS signals. */ + cdc_acm -> ux_slave_class_cdc_acm_data_rts_state = line_state -> ux_slave_class_cdc_acm_parameter_rts; + cdc_acm -> ux_slave_class_cdc_acm_data_dtr_state = line_state -> ux_slave_class_cdc_acm_parameter_dtr; + + break; + + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_ABORT_PIPE: + + /* Get the interface from the instance. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* What direction ? */ + switch( (ULONG) (ALIGN_TYPE) parameter) + { + case UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_XMIT : + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* So the next endpoint has to be the XMIT endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + break; + + case UX_SLAVE_CLASS_CDC_ACM_ENDPOINT_RCV : + + /* Check the endpoint direction, if OUT we have the correct endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + { + + /* So the next endpoint has to be the RCV endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + break; + + + + default : + + /* Parameter not supported. Return an error. */ + status = UX_ENDPOINT_HANDLE_UNKNOWN; + } + + /* Get the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Check the status of the transfer. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_PENDING) + { + + /* Abort the transfer. */ + _ux_device_stack_transfer_abort(transfer_request, UX_TRANSFER_STATUS_ABORT); + + } + + break; + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_READ_TIMEOUT: + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_WRITE_TIMEOUT: + + /* Get the interface from the instance. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* If it's reading timeout but endpoint is OUT, it should be the next one. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != + (ULONG)((ioctl_function == UX_SLAVE_CLASS_CDC_ACM_IOCTL_SET_READ_TIMEOUT) ? UX_ENDPOINT_OUT : UX_ENDPOINT_IN)) + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + + /* Get the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Check the status of the transfer. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_PENDING) + status = UX_ERROR; + else + transfer_request -> ux_slave_transfer_request_timeout = (ULONG) (ALIGN_TYPE) parameter; + + break; + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_START: + + + /* Check if we are in callback transmission already. */ + if (cdc_acm -> ux_slave_class_cdc_acm_transmission_status == UX_TRUE) + { + /* We should not to that ! */ + return(UX_ERROR); + + } + + /* Properly cast the parameter pointer. */ + callback = (UX_SLAVE_CLASS_CDC_ACM_CALLBACK_PARAMETER *) parameter; + + /* We need to start the 2 threads for sending and receiving. */ + /* Allocate some memory for the bulk out thread stack. */ + cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack == UX_NULL) + { + + /* Return the status to the caller. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Allocate some memory for the bulk in thread stack. */ + cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread_stack == UX_NULL) + { + + /* Free resources. */ + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack ); + + /* Return the status to the caller. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Create a event flag group for the cdc_acm class to synchronize with the application writing event . */ + status = _ux_utility_event_flags_create(&cdc_acm -> ux_slave_class_cdc_acm_event_flags_group, "ux_device_class_cdc_acm_event_flag"); + + /* Check status. */ + if (status != UX_SUCCESS) + { + + /* Free resources. */ + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack ); + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread_stack ); + + /* Do not proceed. */ + return(UX_EVENT_ERROR); + } + + /* Bulk endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the cdc_acm instance to the new thread. This thread + does not start until we have a instance of the class. */ + status = _ux_utility_thread_create(&cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread , "ux_slave_class_cdc_acm_bulkin_thread", + _ux_device_class_cdc_acm_bulkin_thread, + (ULONG) (ALIGN_TYPE) cdc_acm, (VOID *) cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + { + + /* Delete the event flag group for the acm class. */ + _ux_utility_event_flags_delete(&cdc_acm -> ux_slave_class_cdc_acm_event_flags_group); + + /* Free some of the resource used. */ + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack ); + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread_stack ); + + return(UX_THREAD_ERROR); + } + + UX_THREAD_EXTENSION_PTR_SET(&(cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread), cdc_acm) + + /* Bulk endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the cdc_acm instance to the new thread. This thread + does not start until we have a instance of the class. */ + status = _ux_utility_thread_create(&cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread , "ux_slave_class_cdc_acm_bulkout_thread", + _ux_device_class_cdc_acm_bulkout_thread, + (ULONG) (ALIGN_TYPE) cdc_acm, (VOID *) cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + { + + /* Delete the event flag group for the acm class. */ + _ux_utility_event_flags_delete(&cdc_acm -> ux_slave_class_cdc_acm_event_flags_group); + + /* Delete bulk in thread. */ + _ux_utility_thread_delete(&cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread); + + /* Free some of the resource used. */ + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack ); + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread_stack ); + + return(UX_THREAD_ERROR); + } + + UX_THREAD_EXTENSION_PTR_SET(&(cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread), cdc_acm) + + /* Save the callback function for write. */ + cdc_acm -> ux_device_class_cdc_acm_write_callback = callback -> ux_device_class_cdc_acm_parameter_write_callback; + + /* Save the callback function for read. */ + cdc_acm -> ux_device_class_cdc_acm_read_callback = callback -> ux_device_class_cdc_acm_parameter_read_callback; + + /* Declare the transmission with callback on. */ + cdc_acm -> ux_slave_class_cdc_acm_transmission_status = UX_TRUE; + + /* We are done here. */ + return(UX_SUCCESS); + + break; + + case UX_SLAVE_CLASS_CDC_ACM_IOCTL_TRANSMISSION_STOP: + + /* Check if we are in callback transmission already. */ + if (cdc_acm -> ux_slave_class_cdc_acm_transmission_status == UX_TRUE) + { + + /* Get the interface from the instance. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Get the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Check the status of the transfer. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_PENDING) + { + + /* Abort the transfer. */ + _ux_device_stack_transfer_abort(transfer_request, UX_TRANSFER_STATUS_ABORT); + + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + + /* Get the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Check the status of the transfer. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_PENDING) + { + + /* Abort the transfer. */ + _ux_device_stack_transfer_abort(transfer_request, UX_TRANSFER_STATUS_ABORT); + + } + + /* Terminate threads. */ + _ux_utility_thread_delete(&cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread); + _ux_utility_thread_delete(&cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread); + + /* Free resources. */ + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkout_thread_stack ); + _ux_utility_memory_free(cdc_acm -> ux_slave_class_cdc_acm_bulkin_thread_stack ); + + /* Delete the event flag group for the acm class. */ + _ux_utility_event_flags_delete(&cdc_acm -> ux_slave_class_cdc_acm_event_flags_group); + + /* Clear scheduled write flag. */ + cdc_acm -> ux_slave_class_cdc_acm_scheduled_write = UX_FALSE; + + /* Declare the transmission with callback off. */ + cdc_acm -> ux_slave_class_cdc_acm_transmission_status = UX_FALSE; + } + else + + /* We should not try to stop an non existing transmission. */ + return(UX_ERROR); + + break; + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return status to caller. */ + return(status); + +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_read.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_read.c new file mode 100644 index 0000000..2994e36 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_read.c @@ -0,0 +1,220 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the CDC class. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Address of cdc_acm class */ +/* instance */ +/* buffer Pointer to buffer to save */ +/* received data */ +/* requested_length Length of bytes to read */ +/* actual_length Pointer to save number of */ +/* bytes read */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_mutex_off Release mutex */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_read(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_TRANSFER *transfer_request; +UINT status= UX_SUCCESS; +ULONG local_requested_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ACM_READ, cdc_acm, buffer, requested_length, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Check if current cdc-acm is using callback or not. We cannot use direct reads with callback on. */ + if (cdc_acm -> ux_slave_class_cdc_acm_transmission_status == UX_TRUE) + + /* Not allowed. */ + return(UX_ERROR); + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* This is the first time we are activated. We need the interface to the class. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if OUT we have the correct endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_OUT) + { + + /* So the next endpoint has to be the OUT endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + /* Protect this thread. */ + _ux_utility_mutex_on(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_out_mutex); + + /* All CDC reading are on the endpoint OUT, from the host. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Reset the actual length. */ + *actual_length = 0; + + /* Check if we need more transactions. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED && requested_length != 0) + { + + /* Check if we have enough in the local buffer. */ + if (requested_length > endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize) + + /* We have too much to transfer. */ + local_requested_length = endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize; + + else + + /* We can proceed with the demanded length. */ + local_requested_length = requested_length; + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, local_requested_length, local_requested_length); + + /* Check the status */ + if (status == UX_SUCCESS) + { + + /* We need to copy the buffer locally. */ + _ux_utility_memory_copy(buffer, transfer_request -> ux_slave_transfer_request_data_pointer, + transfer_request -> ux_slave_transfer_request_actual_length); + + /* Next buffer address. */ + buffer += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Set the length actually received. */ + *actual_length += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Decrement what left has to be done. */ + requested_length -= transfer_request -> ux_slave_transfer_request_actual_length; + + + /* Is this a short packet or a ZLP indicating we are done with this transfer ? */ + if (transfer_request -> ux_slave_transfer_request_actual_length < endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize) + { + + /* We are done. */ + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_out_mutex); + + /* Return with success. */ + return(UX_SUCCESS); + + } + } + else + { + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_out_mutex); + + /* We got an error. */ + return(status); + } + } + + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_out_mutex); + + /* Check why we got here, either completion or device was extracted. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_NO_ANSWER); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Device must have been extracted. */ + return (UX_TRANSFER_NO_ANSWER); + } + else + + /* Simply return the last transaction result. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_unitialize.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_unitialize.c new file mode 100644 index 0000000..e73e162 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_unitialize.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_uninitialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function uninitialize the cdc-acm class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_mutex_delete Delete Mutex */ +/* _ux_utility_memory_free Free used local memory */ +/* */ +/* CALLED BY */ +/* */ +/* CDC Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_uninitialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_CDC_ACM *cdc_acm; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_acm = (UX_SLAVE_CLASS_CDC_ACM *) class -> ux_slave_class_instance; + + /* Sanity check. */ + if (cdc_acm != UX_NULL) + { + + /* Delete the IN endpoint mutex. */ + _ux_utility_mutex_delete(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex); + + /* Out Mutex. */ + _ux_utility_mutex_delete(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_out_mutex); + + /* Free the resources. */ + _ux_utility_memory_free(cdc_acm); + + } + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_write.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_write.c new file mode 100644 index 0000000..0014a11 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_write.c @@ -0,0 +1,227 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the CDC class. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Address of cdc_acm class */ +/* instance */ +/* buffer Pointer to data to write */ +/* requested_length Length of bytes to write */ +/* actual_length Pointer to save number of */ +/* bytes written */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_mutex_on Take Mutex */ +/* _ux_utility_mutex_off Release Mutex */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_write(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_TRANSFER *transfer_request; +ULONG local_requested_length; +UINT status = 0; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ACM_WRITE, cdc_acm, buffer, requested_length, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Check if current cdc-acm is using callback or not. We cannot use direct reads with callback on. */ + if (cdc_acm -> ux_slave_class_cdc_acm_transmission_status == UX_TRUE) + + /* Not allowed. */ + return(UX_ERROR); + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* We need the interface to the class. */ + interface = cdc_acm -> ux_slave_class_cdc_acm_interface; + + /* Locate the endpoints. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* So the next endpoint has to be the IN endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + /* Protect this thread. */ + _ux_utility_mutex_on(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex); + + /* We are writing to the IN endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Reset the actual length. */ + *actual_length = 0; + + /* Check if the application forces a 0 length packet. */ + if (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED && requested_length == 0) + { + + /* Send the request for 0 byte packet to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, 0, 0); + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex); + + /* Return the status. */ + return(status); + + + } + else + { + /* Check if we need more transactions. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED && requested_length != 0) + { + + /* Check if we have enough in the local buffer. */ + if (requested_length > UX_SLAVE_REQUEST_DATA_MAX_LENGTH) + + /* We have too much to transfer. */ + local_requested_length = UX_SLAVE_REQUEST_DATA_MAX_LENGTH; + + else + + /* We can proceed with the demanded length. */ + local_requested_length = requested_length; + + /* On a out, we copy the buffer to the caller. Not very efficient but it makes the API + easier. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + buffer, local_requested_length); + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, local_requested_length, local_requested_length); + + /* Check the status */ + if (status == UX_SUCCESS) + { + + /* Next buffer address. */ + buffer += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Set the length actually received. */ + *actual_length += transfer_request -> ux_slave_transfer_request_actual_length; + + /* Decrement what left has to be done. */ + requested_length -= transfer_request -> ux_slave_transfer_request_actual_length; + + } + + else + { + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex); + + /* We had an error, abort. */ + return(status); + } + } + } + + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_acm -> ux_slave_class_cdc_acm_endpoint_in_mutex); + + /* Check why we got here, either completion or device was extracted. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_NO_ANSWER); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Device must have been extracted. */ + return (UX_TRANSFER_NO_ANSWER); + } + else + + /* Simply return the last transaction result. */ + return(status); + +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_acm_write_with_callback.c b/common/usbx_device_classes/src/ux_device_class_cdc_acm_write_with_callback.c new file mode 100644 index 0000000..15d4824 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_acm_write_with_callback.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_acm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_acm_write_with_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the CDC class with callback */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Address of cdc_acm class */ +/* instance */ +/* buffer Pointer to data to write */ +/* requested_length Length of bytes to write */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_acm_write_with_callback(UX_SLAVE_CLASS_CDC_ACM *cdc_acm, UCHAR *buffer, + ULONG requested_length) +{ + +UX_SLAVE_DEVICE *device; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ACM_WRITE, cdc_acm, buffer, requested_length, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* As long as the device is in the CONFIGURED state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Cannot proceed with command, the interface is down. */ + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Are we already in transmission mode ? */ + if (cdc_acm -> ux_slave_class_cdc_acm_transmission_status != UX_TRUE) + { + + /* We should not to that ! */ + return(UX_ERROR); + + } + + /* Have we already scheduled a buffer ? */ + if (cdc_acm -> ux_slave_class_cdc_acm_scheduled_write == UX_TRUE) + { + + /* We should not to that ! */ + return(UX_ERROR); + } + + /* Save the length to be sent. */ + cdc_acm -> ux_slave_class_cdc_acm_callback_total_length = requested_length; + + /* And the buffer pointer. */ + cdc_acm -> ux_slave_class_cdc_acm_callback_data_pointer = buffer; + + /* Schedule a transmission. */ + cdc_acm -> ux_slave_class_cdc_acm_scheduled_write = UX_TRUE; + + /* Invoke the bulkin thread by sending a flag . */ + status = _ux_utility_event_flags_set(&cdc_acm -> ux_slave_class_cdc_acm_event_flags_group, UX_DEVICE_CLASS_CDC_ACM_WRITE_EVENT, TX_OR); + + /* Simply return the last function result. When we leave this function, the deferred writing has been scheduled. */ + return(status); + +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_activate.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_activate.c new file mode 100644 index 0000000..eca34ed --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_activate.c @@ -0,0 +1,244 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates the USB CDC_ECM device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to cdc_ecm command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_CLASS *class; +UX_SLAVE_ENDPOINT *endpoint; +ULONG physical_address_msw; +ULONG physical_address_lsw; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Check if this is the Control or Data interface. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_CDC_ECM_CLASS_COMMUNICATION_CONTROL) + { + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)cdc_ecm; + + /* Now the opposite, store the interface in the class instance. */ + cdc_ecm -> ux_slave_class_cdc_ecm_interface = interface; + + /* Locate the interrupt endpoint. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Parse all endpoints. */ + while (endpoint != UX_NULL) + { + + /* Check the endpoint direction, and type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) + { + + /* Look at type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT) + { + + /* We have found the interrupt endpoint, save it. */ + cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_endpoint = endpoint; + + /* Reset the endpoint buffers. */ + _ux_utility_memory_set(cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + + /* Resume the interrupt endpoint threads. */ + _ux_utility_thread_resume(&cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread); + + } + + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + } + else + + /* This is the DATA Class, only store the cdc_ecm instance in the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)cdc_ecm; + + /* Reset the CDC ECM alternate setting to 0. */ + cdc_ecm -> ux_slave_class_cdc_ecm_current_alternate_setting = 0; + + /* Check if this is the Control or Data interface. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_CDC_ECM_CLASS_COMMUNICATION_DATA) + { + + /* Reset endpoint instance pointers. */ + cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint = UX_NULL; + cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint = UX_NULL; + + /* Does the data class have bulk endpoint declared ? If yes we need to start link. + If not, the host will change the alternate setting at a later stage. */ + if (interface -> ux_slave_interface_descriptor.bNumEndpoints != 0) + { + + /* Locate the endpoints. Control and Bulk in/out for Data Interface. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Parse all endpoints. */ + while (endpoint != UX_NULL) + { + + /* Check the endpoint direction, and type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) + { + + /* Look at type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk in endpoint, save it. */ + cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint = endpoint; + + } + else + { + /* Look at type for out endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk out endpoint, save it. */ + cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint = endpoint; + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + + /* Now check if all endpoints have been found. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint == UX_NULL || cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint == UX_NULL) + + /* Not all endpoints have been found. Major error, do not proceed. */ + return(UX_ERROR); + + /* Declare the link to be up. That may need to change later to make it dependant on the + WAN/Wireless modem. */ + cdc_ecm -> ux_slave_class_cdc_ecm_link_state = UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP; + + /* Wake up the Interrupt thread and send a network notification to the host. */ + _ux_utility_event_flags_set(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, UX_DEVICE_CLASS_CDC_ECM_NETWORK_NOTIFICATION_EVENT, TX_OR); + + /* Reset the endpoint buffers. */ + _ux_utility_memory_set(cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + _ux_utility_memory_set(cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + + /* Resume the endpoint threads. */ + _ux_utility_thread_resume(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread); + _ux_utility_thread_resume(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread); + + } + + /* Setup the physical address of this IP instance. */ + physical_address_msw = (ULONG)((cdc_ecm -> ux_slave_class_cdc_ecm_local_node_id[0] << 8) | (cdc_ecm -> ux_slave_class_cdc_ecm_local_node_id[1])); + physical_address_lsw = (ULONG)((cdc_ecm -> ux_slave_class_cdc_ecm_local_node_id[2] << 24) | (cdc_ecm -> ux_slave_class_cdc_ecm_local_node_id[3] << 16) | + (cdc_ecm -> ux_slave_class_cdc_ecm_local_node_id[4] << 8) | (cdc_ecm -> ux_slave_class_cdc_ecm_local_node_id[5])); + + /* Register this interface to the NetX USB interface broker. */ + _ux_network_driver_activate((VOID *) cdc_ecm, _ux_device_class_cdc_ecm_write, + &cdc_ecm -> ux_slave_class_cdc_ecm_network_handle, + physical_address_msw, + physical_address_lsw); + + /* Check Link. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_link_state == UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP) + { + + /* Communicate the state with the network driver. */ + _ux_network_driver_link_up(cdc_ecm -> ux_slave_class_cdc_ecm_network_handle); + + /* If there is an activate function call it. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_activate != UX_NULL) + + /* Invoke the application. */ + cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_activate(cdc_ecm); + } + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ECM_ACTIVATE, cdc_ecm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, cdc_ecm, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkin_thread.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkin_thread.c new file mode 100644 index 0000000..d1ed308 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkin_thread.c @@ -0,0 +1,229 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_bulkin_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the cdc_ecm bulkin endpoint. The bulk*/ +/* IN endpoint is used when the device wants to write data to be sent */ +/* to the host. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm_class Address of cdc_ecm class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_event_flags_get Get event flags */ +/* _ux_utility_mutex_on Take mutex */ +/* _ux_utility_mutex_off Free mutex */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_cdc_ecm_bulkin_thread(ULONG cdc_ecm_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +ULONG actual_flags; +NX_PACKET *current_packet; +UCHAR *packet_header; +ULONG transfer_length; + + /* Cast properly the cdc_ecm instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, cdc_ecm_class) + + /* Get the cdc_ecm instance from this class container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This thread runs forever but can be suspended or resumed. */ + while (1) + { + + /* For as long we are configured. */ + while (1) + { + + /* Wait until either a new packet has been added to the xmit queue, + or until there has been a change in the device state (i.e. disconnection). */ + _ux_utility_event_flags_get(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, (UX_DEVICE_CLASS_CDC_ECM_NEW_BULKIN_EVENT | + UX_DEVICE_CLASS_CDC_ECM_NEW_DEVICE_STATE_CHANGE_EVENT), + TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); + + /* Check the completion code and the actual flags returned. */ + if ((actual_flags & UX_DEVICE_CLASS_CDC_ECM_NEW_DEVICE_STATE_CHANGE_EVENT) == 0) + { + + /* Get the transfer request for the bulk IN pipe. */ + transfer_request = &cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint -> ux_slave_endpoint_transfer_request; + + /* Parse all packets. */ + while (cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue != UX_NULL) + { + + /* Ensure no other threads are modifying the xmit queue. */ + _ux_utility_mutex_on(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* Get the current packet in the list. */ + current_packet = cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue; + + /* Set the next packet (or a NULL value) as the head of the xmit queue. */ + cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue = current_packet -> nx_packet_queue_next; + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* If the link is down no need to rearm a packet. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_link_state == UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP) + { + + /* Load the address of the current packet header at the physical header. */ + packet_header = current_packet -> nx_packet_prepend_ptr; + + /* Can the packet fit in the transfer requests data buffer? */ + if (current_packet -> nx_packet_length <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH) + { + + /* Copy the packet in the transfer descriptor buffer. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, packet_header, current_packet -> nx_packet_length); + + /* Calculate the transfer length. */ + transfer_length = current_packet -> nx_packet_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ECM_PACKET_TRANSMIT, cdc_ecm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, transfer_length, UX_DEVICE_CLASS_CDC_ECM_ETHERNET_PACKET_SIZE); + + /* Check error code. */ + if (status != UX_SUCCESS) + { + + /* Is this not a transfer abort? (this is expected to happen) */ + if (status != UX_TRANSFER_BUS_RESET) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + } + } + else + { + + /* Packet is too large. */ + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_BUFFER_OVERFLOW); + } + } + + /* Free the packet that was just sent. First do some housekeeping. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + } + } + else + { + + /* We need to ensure nobody is adding to the queue, so get the mutex protection. */ + _ux_utility_mutex_on(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* Since we got the mutex, we know no one is trying to modify the queue; we also know + no one can start modifying the queue since the link state is down, so we can just + release the mutex. */ + _ux_utility_mutex_off(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* We get here when the link is down. All packets pending must be freed. */ + while (cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue != UX_NULL) + { + + /* Get the current packet in the list. */ + current_packet = cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue; + + /* Set the next packet (or a NULL value) as the head of the xmit queue. */ + cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue = current_packet -> nx_packet_queue_next; + + /* Free the packet. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + } + + /* Was the change in the device state caused by a disconnection? */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* Yes. Break out of the loop and suspend ourselves, waiting for the next configuration. */ + break; + } + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module or when a change of alternate setting happens. */ + _ux_utility_thread_suspend(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread); + } +} diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkout_thread.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkout_thread.c new file mode 100644 index 0000000..958e7aa --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_bulkout_thread.c @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_bulkout_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the cdc_ecm bulk out endpoint. It */ +/* is waiting for the host to send data on the bulk out endpoint to */ +/* the device. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm_class Address of cdc_ecm class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_memory_copy Copy memory */ +/* nx_packet_allocate Allocate NetX packet */ +/* nx_packet_release Free NetX packet */ +/* _ux_utility_thread_suspend Suspend thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_cdc_ecm_bulkout_thread(ULONG cdc_ecm_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +NX_PACKET *packet; +ULONG ip_given_length; + + /* Cast properly the cdc_ecm instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, cdc_ecm_class) + + /* Get the cdc_ecm instance from this class container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This thread runs forever but can be suspended or resumed. */ + while (1) + { + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* We can accept new reception. Get a NX Packet */ + status = nx_packet_allocate(&cdc_ecm -> ux_slave_class_cdc_ecm_packet_pool, &packet, + NX_RECEIVE_PACKET, MS_TO_TICK(UX_DEVICE_CLASS_CDC_ECM_PACKET_POOL_WAIT)); + + if (status == NX_SUCCESS) + { + + /* Select the transfer request associated with BULK OUT endpoint. */ + transfer_request = &cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint -> ux_slave_endpoint_transfer_request; + + /* And length. */ + transfer_request -> ux_slave_transfer_request_requested_length = UX_DEVICE_CLASS_CDC_ECM_MAX_PACKET_LENGTH; + transfer_request -> ux_slave_transfer_request_actual_length = 0; + + /* Memorize this packet at the beginning of the queue. */ + cdc_ecm -> ux_slave_class_cdc_ecm_receive_queue = packet; + + /* Reset the queue pointer of this packet. */ + packet -> nx_packet_queue_next = UX_NULL; + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_CDC_ECM_MAX_PACKET_LENGTH, + UX_DEVICE_CLASS_CDC_ECM_MAX_PACKET_LENGTH); + + /* Check the completion code. */ + if (status == UX_SUCCESS) + { + + /* We only proceed with packets that are received OK, if error, ignore the packet. */ + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ECM_PACKET_RECEIVE, cdc_ecm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Adjust the prepend pointer to take into account the non 3 bit alignment of the ethernet header. */ + packet -> nx_packet_prepend_ptr += sizeof(USHORT); + + /* Set the prepend, length, and append fields. */ + packet -> nx_packet_length = transfer_request -> ux_slave_transfer_request_actual_length; + packet -> nx_packet_append_ptr = packet -> nx_packet_prepend_ptr + transfer_request -> ux_slave_transfer_request_actual_length; + + /* Copy the received packet in the IP packet data area. */ + _ux_utility_memory_copy(packet -> nx_packet_prepend_ptr, transfer_request -> ux_slave_transfer_request_data_pointer, packet -> nx_packet_length); + + /* Calculate the accurate packet length from ip header. */ + if((*(packet -> nx_packet_prepend_ptr + 12) == 0x08) && + (*(packet -> nx_packet_prepend_ptr + 13) == 0)) + { + + ip_given_length = _ux_utility_short_get_big_endian(packet -> nx_packet_prepend_ptr + 16) + UX_DEVICE_CLASS_CDC_ECM_ETHERNET_SIZE; + packet -> nx_packet_length = ip_given_length ; + packet -> nx_packet_append_ptr = packet -> nx_packet_prepend_ptr + ip_given_length; + } + + /* Send that packet to the NetX USB broker. */ + _ux_network_driver_packet_received(cdc_ecm -> ux_slave_class_cdc_ecm_network_handle, packet); + } + else + + /* Free the packet that was not successfully received. */ + nx_packet_release(packet); + } + else + { + + /* Packet allocation timed out. Note that the timeout value is + configurable. */ + + /* Error trap. No need for trace, since NetX does it. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_change.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_change.c new file mode 100644 index 0000000..4306003 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_change.c @@ -0,0 +1,200 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_change PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function changes the interface of the CDC_ECM device */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to cdc_ecm command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_network_driver_link_up Link status up */ +/* _ux_network_driver_link_down Link status down */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_thread_resume Resume thread */ +/* _ux_utility_event_flags_set Set event flags */ +/* _ux_device_stack_transfer_all_request_abort */ +/* Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_change(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_CLASS *class; +UX_SLAVE_ENDPOINT *endpoint; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Locate the endpoints. Control and Bulk in/out for Data Interface. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* If the interface to mount has a non zero alternate setting, the class is really active with + the endpoints active. If the interface reverts to alternate setting 0, it needs to have + the pending transactions terminated. */ + if (interface -> ux_slave_interface_descriptor.bAlternateSetting != 0) + { + + /* Parse all endpoints. */ + while (endpoint != UX_NULL) + { + + /* Check the endpoint direction, and type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) + { + + /* Look at type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk in endpoint, save it. */ + cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint = endpoint; + + } + else + { + /* Look at type for out endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk out endpoint, save it. */ + cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint = endpoint; + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + /* Now check if all endpoints have been found. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint == UX_NULL || cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint == UX_NULL) + + /* Not all endpoints have been found. Major error, do not proceed. */ + return(UX_ERROR); + + /* Declare the link to be up. */ + cdc_ecm -> ux_slave_class_cdc_ecm_link_state = UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP; + + /* Communicate the state with the network driver. */ + _ux_network_driver_link_up(cdc_ecm -> ux_slave_class_cdc_ecm_network_handle); + + /* Reset the endpoint buffers. */ + _ux_utility_memory_set(cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + _ux_utility_memory_set(cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + + /* Resume the endpoint threads. */ + _ux_utility_thread_resume(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread); + _ux_utility_thread_resume(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread); + + /* Wake up the Interrupt thread and send a network notification to the host. */ + _ux_utility_event_flags_set(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, UX_DEVICE_CLASS_CDC_ECM_NETWORK_NOTIFICATION_EVENT, TX_OR); + + /* If there is an activate function call it. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_activate != UX_NULL) + + /* Invoke the application. */ + cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_activate(cdc_ecm); + } + else + { + + /* In this case, we are reverting to the Alternate Setting 0. */ + + /* Declare the link to be down. */ + cdc_ecm -> ux_slave_class_cdc_ecm_link_state = UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_DOWN; + + /* Communicate the state with the network driver. */ + _ux_network_driver_link_down(cdc_ecm -> ux_slave_class_cdc_ecm_network_handle); + + /* Terminate the transactions pending on the bulk in endpoint. If there is a transfer on the + bulk out endpoint, we simply let it finish and let NetX throw it away. */ + _ux_device_stack_transfer_all_request_abort(cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint, UX_TRANSFER_APPLICATION_RESET); + + /* Notify the thread waiting for network notification events. In this case, + the event is that the link state has been switched to down. */ + _ux_utility_event_flags_set(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, UX_DEVICE_CLASS_CDC_ECM_NETWORK_NOTIFICATION_EVENT, TX_OR); + + /* Wake up the bulk in thread so that it can clean up the xmit queue. */ + _ux_utility_event_flags_set(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, UX_DEVICE_CLASS_CDC_ECM_NEW_DEVICE_STATE_CHANGE_EVENT, TX_OR); + + /* If there is a deactivate function call it. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_deactivate != UX_NULL) + + /* Invoke the application. */ + cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_deactivate(cdc_ecm); + } + + /* Set the CDC ECM alternate setting to the new one. */ + cdc_ecm -> ux_slave_class_cdc_ecm_current_alternate_setting = interface -> ux_slave_interface_descriptor.bAlternateSetting; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ECM_CHANGE, cdc_ecm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, cdc_ecm, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_control_request.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_control_request.c new file mode 100644 index 0000000..f4b4ce2 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_control_request.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm Pointer to cdc_ecm class */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_short_get Get 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* CDC_ECM Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +ULONG request; +ULONG request_value; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + request_value = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE); + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the cdc_ecm instance from this class container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Here we proceed only the standard request we know of at the device level. */ + switch (request) + { + + case UX_DEVICE_CLASS_CDC_ECM_SET_ETHERNET_MULTICAST_FILTER : + + /* Save the multicast filter. */ + cdc_ecm -> ux_slave_class_cdc_ecm_ethernet_multicast_filter = request_value; + break ; + + case UX_DEVICE_CLASS_CDC_ECM_SET_ETHERNET_POWER_MANAGEMENT_FILTER : + + /* Save the power management filter. */ + cdc_ecm -> ux_slave_class_cdc_ecm_ethernet_power_management_filter = request_value; + break ; + + case UX_DEVICE_CLASS_CDC_ECM_SET_ETHERNET_PACKET_FILTER : + + /* Save the packet filter. */ + cdc_ecm -> ux_slave_class_cdc_ecm_ethernet_packet_filter = request_value; + break ; + + case UX_DEVICE_CLASS_CDC_ECM_GET_ETHERNET_POWER_MANAGEMENT_FILTER : + default: + + /* Unknown function. It's not handled. */ + return(UX_ERROR); + } + + /* It's handled. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_deactivate.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_deactivate.c new file mode 100644 index 0000000..30d5319 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_deactivate.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the cdc_ecm class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort */ +/* Abort all transfers */ +/* _ux_utility_event_flags_set Set event flags */ +/* _ux_network_driver_deactivate Deactivate NetX USB interface */ +/* */ +/* CALLED BY */ +/* */ +/* CDC_ECM Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. Normally the interface can be derived + from the class instance but since CDC_ECM has 2 interfaces and we only store the Control + interface in the class container, we used the class_command pointer to retrieve the + correct interface which issued the deactivation. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Check if this is the Control or Data interface. We only need to dismount the link and abort the + transfer once for the 2 classes. */ + if (interface -> ux_slave_interface_descriptor.bInterfaceClass == UX_DEVICE_CLASS_CDC_ECM_CLASS_COMMUNICATION_CONTROL) + { + + /* Is the link state up? */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_link_state == UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP) + { + + /* Then we've found the bulk endpoints and started the threads. */ + + /* Abort transfers. Note that since the bulk out thread is most likely waiting for + a transfer from the host, this will allow it to resume and suspend itself. */ + _ux_device_stack_transfer_all_request_abort(cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint, UX_TRANSFER_BUS_RESET); + + /* Declare the link to be down. That may need to change later to make it dependent on the + WAN/Wireless modem. */ + cdc_ecm -> ux_slave_class_cdc_ecm_link_state = UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_DOWN; + + /* Is there an interrupt endpoint? */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_endpoint != UX_NULL) + + /* Abort the transfers on the interrupt endpoint as well. */ + _ux_device_stack_transfer_all_request_abort(cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_endpoint, UX_TRANSFER_BUS_RESET); + + /* Wake up the bulk in thread so it will release the NetX resources used and suspend. */ + _ux_utility_event_flags_set(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, UX_DEVICE_CLASS_CDC_ECM_NEW_DEVICE_STATE_CHANGE_EVENT, TX_OR); + + /* If there is a deactivate function call it. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_deactivate != UX_NULL) + + /* Invoke the application. */ + cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_deactivate(cdc_ecm); + + /* Deregister this interface to the NetX USB interface broker. */ + _ux_network_driver_deactivate((VOID *) cdc_ecm, cdc_ecm -> ux_slave_class_cdc_ecm_network_handle); + } + else + { + + /* The link state is down. */ + + /* Did activation succeed? */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_endpoint != UX_NULL && cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_endpoint != UX_NULL) + { + + /* The only thing we need to do is deregister this interface to the NetX USB interface broker. */ + _ux_network_driver_deactivate((VOID *) cdc_ecm, cdc_ecm -> ux_slave_class_cdc_ecm_network_handle); + } + else + { + + /* Activation did not succeed. Nothing to do. */ + } + } + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_CDC_ECM_DEACTIVATE, cdc_ecm, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(cdc_ecm); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_entry.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_entry.c new file mode 100644 index 0000000..802aaaa --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_entry.c @@ -0,0 +1,161 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_cdc_ecm_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the cdc_ecm class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the cdc_ecm interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_cdc_ecm_initialize Initialize cdc_ecm class */ +/* _ux_device_class_cdc_ecm_uninitialize Uninitialize cdc_ecm class*/ +/* _ux_device_class_cdc_ecm_activate Activate cdc_ecm class */ +/* _ux_device_class_cdc_ecm_deactivate Deactivate cdc_ecm class */ +/* _ux_device_class_cdc_ecm_change Alternate setting change */ +/* _ux_device_class_cdc_ecm_control_request Request control */ +/* */ +/* CALLED BY */ +/* */ +/* CDC_ECM Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the CDC_ECM class. */ + status = _ux_device_class_cdc_ecm_initialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_UNINITIALIZE: + + /* Call the init function of the CDC_ECM class. */ + status = _ux_device_class_cdc_ecm_uninitialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_CDC_ECM_CLASS_COMMUNICATION_CONTROL || + command -> ux_slave_class_command_class == UX_DEVICE_CLASS_CDC_ECM_CLASS_COMMUNICATION_DATA) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. In CDC ECM, the alternate setting 0 has no endpoints. + Only the Alternate Setting 1 has the Bulk IN and OUT endpoints active. */ + status = _ux_device_class_cdc_ecm_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_CHANGE: + + /* The change command is used when the host has sent a SET_INTERFACE command + to go from Alternate Setting 0 to 1 or revert to the default mode. */ + status = _ux_device_class_cdc_ecm_change(command); + + /* Return the completion status. */ + return(status); + + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the cdc_ecm thread canceled. */ + status = _ux_device_class_cdc_ecm_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + status = _ux_device_class_cdc_ecm_control_request(command); + + /* Return the completion status. */ + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_initialize.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_initialize.c new file mode 100644 index 0000000..b446066 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_initialize.c @@ -0,0 +1,266 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + +UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB CDC_ECM device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to cdc_ecm command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_mutex_create Create Mutex */ +/* _ux_utility_mutex_delete Delete Mutex */ +/* _ux_utility_event_flags_create Create Flag group */ +/* _ux_utility_event_flags_delete Delete Flag group */ +/* _ux_utility_thread_create Create Thread */ +/* _ux_utility_thread_delete Delete Thread */ +/* nx_packet_pool_create Create NetX packet pool */ +/* nx_packet_pool_delete Delete NetX packet pool */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_CLASS_CDC_ECM_PARAMETER *cdc_ecm_parameter; +UX_SLAVE_CLASS *class; +UINT status; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device cdc_ecm class. */ + cdc_ecm = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_CDC_ECM)); + + /* Check for successful allocation. */ + if (cdc_ecm == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a mutex to protect the CDC_ECM thread and the application messing up the transmit queue. */ + status = _ux_utility_mutex_create(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex, "ux_slave_class_cdc_ecm_mutex"); + if (status != UX_SUCCESS) + { + _ux_utility_memory_free(cdc_ecm); + return(UX_MUTEX_ERROR); + } + + /* Assume good result. */ + status = UX_SUCCESS; + + /* Allocate some memory for the bulk out thread stack. */ + cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + if (cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread_stack == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + + /* Allocate some memory for the interrupt thread stack. */ + if (status == UX_SUCCESS) + { + cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread_stack == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } + + /* Allocate some memory for the bulk in thread stack. */ + if (status == UX_SUCCESS) + { + cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread_stack == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } + + /* Allocate some packet pool for reception. */ + if (status == UX_SUCCESS) + { + + /* UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE overflow has been checked by + * UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT outside of function. + */ + cdc_ecm -> ux_slave_class_cdc_ecm_pool_memory = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE); + + /* Check for successful allocation. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_pool_memory == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + else + { + /* Create a packet pool. */ + status = nx_packet_pool_create(&cdc_ecm -> ux_slave_class_cdc_ecm_packet_pool, "CDC ECM Device Packet Pool", + UX_DEVICE_CLASS_CDC_ECM_NX_PAYLOAD_SIZE, cdc_ecm -> ux_slave_class_cdc_ecm_pool_memory, + UX_DEVICE_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE); + } + } + + /* Interrupt endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the cdc_ecm instance to the new thread. This thread + does not start until we have a instance of the class. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread , "ux_slave_class_cdc_ecm_interrupt_thread", + _ux_device_class_cdc_ecm_interrupt_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + if (status != UX_SUCCESS) + status = (UX_THREAD_ERROR); + } + + UX_THREAD_EXTENSION_PTR_SET(&(cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread), class) + + /* Check the creation of this thread. */ + if (status == UX_SUCCESS) + { + + /* Bulk endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the cdc_ecm instance to the new thread. This thread + does not start until we have a instance of the class. */ + status = _ux_utility_thread_create(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread , "ux_slave_class_cdc_ecm_bulkout_thread", + _ux_device_class_cdc_ecm_bulkout_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + if (status != UX_SUCCESS) + status = (UX_THREAD_ERROR); + else + { + + UX_THREAD_EXTENSION_PTR_SET(&(cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread), class) + + /* Bulk endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the cdc_ecm instance to the new thread. This thread + does not start until we have a instance of the class. */ + status = _ux_utility_thread_create(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread , "ux_slave_class_cdc_ecm_bulkin_thread", + _ux_device_class_cdc_ecm_bulkin_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + if (status != UX_SUCCESS) + status = (UX_THREAD_ERROR); + else + { + + UX_THREAD_EXTENSION_PTR_SET(&(cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread), class) + + /* Create a event flag group for the cdc_ecm class to synchronize with the event interrupt thread. */ + status = _ux_utility_event_flags_create(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, "ux_device_class_cdc_ecm_event_flag"); + if (status != UX_SUCCESS) + status = (UX_EVENT_ERROR); + else + { + + /* Save the address of the CDC_ECM instance inside the CDC_ECM container. */ + class -> ux_slave_class_instance = (VOID *) cdc_ecm; + + /* Get the pointer to the application parameters for the cdc_ecm class. */ + cdc_ecm_parameter = command -> ux_slave_class_command_parameter; + + /* Store the start and stop signals if needed by the application. */ + cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_activate = cdc_ecm_parameter -> ux_slave_class_cdc_ecm_instance_activate; + cdc_ecm -> ux_slave_class_cdc_ecm_parameter.ux_slave_class_cdc_ecm_instance_deactivate = cdc_ecm_parameter -> ux_slave_class_cdc_ecm_instance_deactivate; + + /* Copy the local node ID. */ + _ux_utility_memory_copy(cdc_ecm -> ux_slave_class_cdc_ecm_local_node_id, cdc_ecm_parameter -> ux_slave_class_cdc_ecm_parameter_local_node_id, + UX_DEVICE_CLASS_CDC_ECM_NODE_ID_LENGTH); + + /* Copy the remote node ID. */ + _ux_utility_memory_copy(cdc_ecm -> ux_slave_class_cdc_ecm_remote_node_id, cdc_ecm_parameter -> ux_slave_class_cdc_ecm_parameter_remote_node_id, + UX_DEVICE_CLASS_CDC_ECM_NODE_ID_LENGTH); + + /* Store the rest of the parameters as they are in the local instance. */ + _ux_utility_memory_copy(&cdc_ecm -> ux_slave_class_cdc_ecm_parameter, cdc_ecm_parameter, sizeof (UX_SLAVE_CLASS_CDC_ECM_PARAMETER)); + + return(UX_SUCCESS); + } + + _ux_utility_thread_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread); + } + + _ux_utility_thread_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread); + } + + _ux_utility_thread_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread); + } + + /* Free allocated resources. */ + + if (cdc_ecm -> ux_slave_class_cdc_ecm_packet_pool.nx_packet_pool_id) + nx_packet_pool_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_packet_pool); + if (cdc_ecm -> ux_slave_class_cdc_ecm_pool_memory) + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_pool_memory); + if (cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread_stack) + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread_stack); + if (cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread_stack) + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread_stack); + if (cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread_stack) + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread_stack); + _ux_utility_mutex_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + _ux_utility_memory_free(cdc_ecm); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_interrupt_thread.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_interrupt_thread.c new file mode 100644 index 0000000..8ca7bb8 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_interrupt_thread.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_interrupt_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the cdc_ecm interrupt endpoint */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm_class Address of cdc_ecm class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_event_flags_get Get event flags */ +/* _ux_utility_short_put Put 16-bit value to buffer */ +/* _ux_utility_thread_suspend Suspend thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_cdc_ecm_interrupt_thread(ULONG cdc_ecm_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +ULONG actual_flags; +UCHAR *notification_buffer; + + /* Cast properly the cdc_ecm instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, cdc_ecm_class) + + /* Get the cdc_ecm instance from this class container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* All CDC_ECM events are on the interrupt endpoint IN, from the host. */ + transfer_request = &cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + + /* Wait until we have a event sent by the application. We do not treat yet the case where a timeout based + on the interrupt pipe frequency or a change in the idle state forces us to send an empty report. */ + _ux_utility_event_flags_get(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, + UX_DEVICE_CLASS_CDC_ECM_NETWORK_NOTIFICATION_EVENT, + TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); + + /* Build the Network Notification response. */ + notification_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Set the request type. */ + *(notification_buffer + UX_SETUP_REQUEST_TYPE) = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + + /* Set the request itself. */ + *(notification_buffer + UX_SETUP_REQUEST) = 0; + + /* Set the value. It is the network link. */ + _ux_utility_short_put(notification_buffer + UX_SETUP_VALUE, (USHORT)(cdc_ecm -> ux_slave_class_cdc_ecm_link_state)); + + /* Set the Index. It is interface. The interface used is the DATA interface. Here we simply take the interface number of the CONTROL and add 1 to it + as it is assumed the classes are contiguous in number. */ + _ux_utility_short_put(notification_buffer + UX_SETUP_INDEX, (USHORT)(cdc_ecm -> ux_slave_class_cdc_ecm_interface -> ux_slave_interface_descriptor.bInterfaceNumber + 1)); + + /* And the length is zero. */ + *(notification_buffer + UX_SETUP_LENGTH) = 0; + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_CDC_ECM_INTERRUPT_RESPONSE_LENGTH, + UX_DEVICE_CLASS_CDC_ECM_INTERRUPT_RESPONSE_LENGTH); + + /* Check error code. */ + if (status != UX_SUCCESS) + { + + /* Since bus resets are expected, we do not treat it as an error. */ + if (status != UX_TRANSFER_BUS_RESET) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_uninitialize.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_uninitialize.c new file mode 100644 index 0000000..217b92b --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_uninitialize.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC-ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_uninitialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deinitializes the resources for the specified CDC-ECM */ +/* instance. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to storage command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_mutex_delete Delete mutex */ +/* _ux_utility_thread_delete Delete thread */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_event_flags_delete Delete event flags */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Device CDC-ECM Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_uninitialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; +UX_SLAVE_CLASS *class; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) class -> ux_slave_class_instance; + + /* Sanity check. */ + if (cdc_ecm != UX_NULL) + { + + /* Deinitialize resources. We do not check if they have been allocated + because if they weren't, the class register (called by the application) + would have failed. */ + + /* Delete the xmit queue mutex. */ + _ux_utility_mutex_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* Delete bulk out thread . */ + _ux_utility_thread_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread); + + /* Free bulk out thread stack. */ + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_bulkout_thread_stack); + + /* Delete interrupt thread. */ + _ux_utility_thread_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread); + + /* Free interrupt thread stack. */ + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_interrupt_thread_stack); + + /* Delete bulk in thread. */ + _ux_utility_thread_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread); + + /* Free bulk in thread stack. */ + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_bulkin_thread_stack); + + /* Delete the interrupt thread sync event flags group. */ + _ux_utility_event_flags_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group); + + /* Delete the reception packet pool. */ + nx_packet_pool_delete(&cdc_ecm -> ux_slave_class_cdc_ecm_packet_pool); + + /* Free the packet pool memory. */ + _ux_utility_memory_free(cdc_ecm -> ux_slave_class_cdc_ecm_pool_memory); + + /* Free the resources. */ + _ux_utility_memory_free(cdc_ecm); + } + + /* Return completion status. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_cdc_ecm_write.c b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_write.c new file mode 100644 index 0000000..4e2293a --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_cdc_ecm_write.c @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_cdc_ecm.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_cdc_ecm_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a packet into a queue for later thread */ +/* processing. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm Address of cdc_ecm class */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_mutex_off Release mutex */ +/* _ux_utility_event_flags_set Set event flags */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_cdc_ecm_write(VOID *cdc_ecm_class, NX_PACKET *packet) +{ + +UINT status; +UX_SLAVE_CLASS_CDC_ECM *cdc_ecm; + + /* Proper class casting. */ + cdc_ecm = (UX_SLAVE_CLASS_CDC_ECM *) cdc_ecm_class; + + /* Protect this thread. */ + _ux_utility_mutex_on(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* We only want to send the packet if the link is up. */ + if (cdc_ecm->ux_slave_class_cdc_ecm_link_state == UX_DEVICE_CLASS_CDC_ECM_LINK_STATE_UP) + { + + /* Check the queue. See if there is something that is being sent. */ + if (cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue == UX_NULL) + + /* Memorize this packet at the beginning of the queue. */ + cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue = packet; + + else + + /* Add the packet to the end of the queue. */ + cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue_tail -> nx_packet_queue_next = packet; + + /* Set the tail. */ + cdc_ecm -> ux_slave_class_cdc_ecm_xmit_queue_tail = packet; + + /* The packet to be sent is the last in the chain. */ + packet -> nx_packet_queue_next = NX_NULL; + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* Set an event to wake up the bulkin thread. */ + _ux_utility_event_flags_set(&cdc_ecm -> ux_slave_class_cdc_ecm_event_flags_group, UX_DEVICE_CLASS_CDC_ECM_NEW_BULKIN_EVENT, TX_OR); + + /* Packet successfully added. Return success. */ + status = UX_SUCCESS; + } + else + { + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&cdc_ecm -> ux_slave_class_cdc_ecm_mutex); + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_CDC_ECM_LINK_STATE_DOWN_ERROR); + + /* Return error. */ + status = UX_ERROR; + } + + /* We are done here. */ + return(status); +} diff --git a/common/usbx_device_classes/src/ux_device_class_dfu_activate.c b/common/usbx_device_classes/src/ux_device_class_dfu_activate.c new file mode 100644 index 0000000..1ac1c50 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_dfu_activate.c @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DFU Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dfu.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dfu_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB DFU device. */ +/* This class can be activated either as part of the device primary */ +/* framework or after a PORT_RESET detected. */ +/* This is detected through the protocol field. If 1, we are in the */ +/* device regular mode. If 2, we are activated through the DFU */ +/* mode. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to dfu command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_thread_resume Resume thread */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dfu_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_DFU *dfu; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + dfu = (UX_SLAVE_CLASS_DFU *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)dfu; + + /* Now the opposite, store the interface in the class instance. */ + dfu -> ux_slave_class_dfu_interface = interface; + + /* Check the protocol activation field to determine in which state of the DFU class + we are. */ + switch (command -> ux_slave_class_command_protocol) + { + + case UX_SLAVE_CLASS_DFU_PROTOCOL_RUNTIME : + + /* In the system, state the DFU state machine to application idle. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_APP_IDLE; + + /* Set the mode to Runtime. */ + _ux_system_slave -> ux_system_slave_device_dfu_mode = UX_DEVICE_CLASS_DFU_MODE_RUNTIME ; + + break; + + + case UX_SLAVE_CLASS_DFU_PROTOCOL_DFU_MODE : + + /* In the system, state the DFU state machine to DFU idle. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_IDLE; + + /* Reset the download/upload parameters. */ + dfu -> ux_slave_class_dfu_download_block_count = 0; + dfu -> ux_slave_class_dfu_upload_block_count = 0; + + /* Set the mode to DFU mode. */ + _ux_system_slave -> ux_system_slave_device_dfu_mode = UX_DEVICE_CLASS_DFU_MODE_DFU ; + + break; + + default : + + /* We should never get here. */ + return(UX_ERROR); + + } + + + /* If there is a activate function call it. */ + if (dfu -> ux_slave_class_dfu_instance_activate != UX_NULL) + { + /* Invoke the application. */ + dfu -> ux_slave_class_dfu_instance_activate(dfu); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_DFU_ACTIVATE, dfu, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, dfu, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_dfu_control_request.c b/common/usbx_device_classes/src/ux_device_class_dfu_control_request.c new file mode 100644 index 0000000..ff5b8db --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_dfu_control_request.c @@ -0,0 +1,635 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DFU Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dfu.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dfu_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* dfu Pointer to dfu class */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_endpoint_stall Endpoint stall */ +/* _ux_device_stack_transfer_request Transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* DFU Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dfu_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_DFU *dfu; + +ULONG request; +ULONG request_length; +ULONG actual_length; +ULONG media_status; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the storage instance from this class container. */ + dfu = (UX_SLAVE_CLASS_DFU *) class -> ux_slave_class_instance; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + + /* Pickup the request length. */ + request_length = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); + + /* What state are we in ? */ + switch (_ux_system_slave -> ux_system_slave_device_dfu_state_machine) + { + + case UX_SYSTEM_DFU_STATE_APP_IDLE : + + + /* Here we process only the request we can accept in the APP IDLE state. */ + switch (request) + { + + case UX_SLAVE_CLASS_DFU_COMMAND_DETACH : + + /* The host is asking for a Detach and switch to the DFU mode. Either we force the reset here or + we wait for a specified timer. If there is no reset while this timer is running we abandon + the DFU Detach.*/ + if (_ux_system_slave -> ux_system_slave_device_dfu_capabilities & UX_SLAVE_CLASS_DFU_CAPABILITY_WILL_DETACH) + { + + /* Wake up the DFU thread and send a detach request.. */ + _ux_utility_event_flags_set(&dfu -> ux_slave_class_dfu_event_flags_group, UX_DEVICE_CLASS_DFU_THREAD_EVENT_DISCONNECT, TX_OR); + + } + else + { + + /* We expect the host to issue a reset. Arm a timer in the DFU thread. */ + _ux_utility_event_flags_set(&dfu -> ux_slave_class_dfu_event_flags_group, UX_DEVICE_CLASS_DFU_THREAD_EVENT_WAIT_RESET, TX_OR); + + } + + /* We can switch dfu state machine. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_APP_DETACH; + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATUS : + + /* Fill the status data payload. First with status. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) dfu -> ux_slave_class_dfu_status; + + /* Poll time out value is set to 1ms. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT) = 1; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 1) = 0; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 2) = 0; + + /* Next state. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STATE) = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* String index set to 0. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STRING) = 0; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH); + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATE : + + /* Fill the status data payload. First with state. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH); + + break; + + default: + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + break; + } + + break; + + case UX_SYSTEM_DFU_STATE_APP_DETACH : + + /* Here we process only the request we can accept in the APP DETACH state. */ + switch (request) + { + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATUS : + + /* Fill the status data payload. First with status. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) dfu -> ux_slave_class_dfu_status; + + /* Poll time out value is set to 1ms. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT) = 1; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 1) = 0; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 2) = 0; + + /* Next state. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STATE) = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* String index set to 0. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STRING) = 0; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH); + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATE : + + /* Fill the status data payload. First with state. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH); + + break; + + default: + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + break; + } + + break; + + case UX_SYSTEM_DFU_STATE_DFU_IDLE : + + /* Here we process only the request we can accept in the DFU mode IDLE state. */ + switch (request) + { + + case UX_SLAVE_CLASS_DFU_COMMAND_DOWNLOAD : + + /* We received a DOWNLOAD command. Check the length field of the request. It cannot be 0. */ + if (request_length == 0) + { + + /* Zero length download is not accepted. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + + /* In the system, state the DFU state machine to dfu ERROR. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_ERROR; + + } + else + { + + /* Have we declared a DOWNLOAD possible in our device framework ? */ + if (_ux_system_slave -> ux_system_slave_device_dfu_capabilities & UX_SLAVE_CLASS_DFU_CAPABILITY_CAN_DOWNLOAD) + { + + /* Send a notification to the application. Begin of transfer. */ + dfu -> ux_slave_class_dfu_notify(dfu, UX_SLAVE_CLASS_DFU_NOTIFICATION_BEGIN_DOWNLOAD); + + /* Write the first block to the firmware. */ + dfu -> ux_slave_class_dfu_write(dfu, dfu -> ux_slave_class_dfu_download_block_count, + transfer_request -> ux_slave_transfer_request_data_pointer, + request_length, + &actual_length); + + /* In the system, state the DFU state machine to dfu DOWNLOAD SYNC. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_DNLOAD_SYNC; + + /* Increase the block count. */ + dfu -> ux_slave_class_dfu_download_block_count++; + + } + else + { + + /* Download is not accepted. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + + /* In the system, state the DFU state machine to dfu ERROR. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_ERROR; + + } + + } + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_ABORT : + + /* Send a notification to the application. */ + dfu -> ux_slave_class_dfu_notify(dfu, UX_SLAVE_CLASS_DFU_NOTIFICATION_ABORT_DOWNLOAD); + + /* In the system, state the DFU state machine to dfu IDLE. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_IDLE; + + /* Reset the download/upload parameters. */ + dfu -> ux_slave_class_dfu_download_block_count = 0; + dfu -> ux_slave_class_dfu_upload_block_count = 0; + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATUS : + + /* Fill the status data payload. First with status. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) dfu -> ux_slave_class_dfu_status; + + /* Poll time out value is set to 1ms. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT) = 1; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 1) = 0; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 2) = 0; + + /* Next state. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STATE) = (UCHAR) UX_SYSTEM_DFU_STATE_DFU_IDLE; + + /* String index set to 0. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STRING) = 0; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH); + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATE : + + /* Fill the status data payload. First with state. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH); + + break; + + default: + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + + /* In the system, state the DFU state machine to dfu ERROR. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_ERROR; + + break; + } + + break; + + + case UX_SYSTEM_DFU_STATE_DFU_DNLOAD_SYNC : + + /* Here we process only the request we can accept in the DFU mode DOWNLOAD state. */ + switch (request) + { + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATUS : + + /* Check if the device is still buys performing the write. Write could be delayed. */ + dfu -> ux_slave_class_dfu_get_status(dfu, &media_status); + + /* Check status of device. */ + switch (media_status) + { + + case UX_SLAVE_CLASS_DFU_MEDIA_STATUS_OK : + + /* Set the next state for idle and no error status. */ + dfu -> ux_slave_class_dfu_status = UX_SLAVE_CLASS_DFU_STATUS_OK ; + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_DNLOAD_IDLE; + break; + + case UX_SLAVE_CLASS_DFU_MEDIA_STATUS_BUSY : + + /* Set the next state for busy but no error status. */ + dfu -> ux_slave_class_dfu_status = UX_SLAVE_CLASS_DFU_STATUS_OK ; + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_DNBUSY; + break; + + case UX_SLAVE_CLASS_DFU_MEDIA_STATUS_ERROR : + + /* Set the next state for busy and error status. */ + dfu -> ux_slave_class_dfu_status = UX_SLAVE_CLASS_DFU_STATUS_ERROR_WRITE ; + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_ERROR; + break; + + } + + /* Fill the status data payload. First with status. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) dfu -> ux_slave_class_dfu_status; + + /* Poll time out value is set to 1ms. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT) = 1; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 1) = 0; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 2) = 0; + + /* Next state. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STATE) = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* String index set to 0. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STRING) = 0; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH); + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATE : + + /* Fill the status data payload. First with state. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH); + + break; + + default: + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + + /* In the system, state the DFU state machine to dfu ERROR. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_ERROR; + + break; + } + + break; + + case UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_DNLOAD_IDLE : + + /* Here we process only the request we can accept in the DFU mode DNLOAD state. */ + switch (request) + { + + case UX_SLAVE_CLASS_DFU_COMMAND_DOWNLOAD : + + /* We received a DOWNLOAD command. Check the length field of the request. If it is 0, + we are done with the transfer. */ + if (request_length == 0) + { + + /* Send the notification of end of download to application. */ + dfu -> ux_slave_class_dfu_notify(dfu, UX_SLAVE_CLASS_DFU_NOTIFICATION_END_DOWNLOAD); + + /* In the system, state the DFU state machine to DFU MANIFEST SYNCH. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_MANIFEST_SYNC; + + } + + else + { + + /* Write the next block to the firmware. */ + dfu -> ux_slave_class_dfu_write(dfu, dfu -> ux_slave_class_dfu_download_block_count, + transfer_request -> ux_slave_transfer_request_data_pointer, + request_length, + &actual_length); + + /* In the system, state the DFU state machine to dfu DOWNLOAD SYNC. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_DNLOAD_SYNC; + + /* Increase the block count. */ + dfu -> ux_slave_class_dfu_download_block_count++; + + } + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_ABORT : + + /* Send a notification to the application. */ + dfu -> ux_slave_class_dfu_notify(dfu, UX_SLAVE_CLASS_DFU_NOTIFICATION_ABORT_DOWNLOAD); + + /* In the system, state the DFU state machine to dfu IDLE. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_IDLE; + + /* Reset the download/upload parameters. */ + dfu -> ux_slave_class_dfu_download_block_count = 0; + dfu -> ux_slave_class_dfu_upload_block_count = 0; + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATUS : + + /* Fill the status data payload. First with status. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) dfu -> ux_slave_class_dfu_status; + + /* Poll time out value is set to 1ms. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT) = 1; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 1) = 0; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 2) = 0; + + /* Next state. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STATE) = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* String index set to 0. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STRING) = 0; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH); + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATE : + + /* Fill the status data payload. First with state. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH); + + break; + + default: + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + + /* In the system, state the DFU state machine to dfu ERROR. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_ERROR; + + break; + } + + break; + + case UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_MANIFEST_SYNC : + + /* Here we process only the request we can accept in the MANIFEST SYNCH state. */ + switch (request) + { + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATUS : + + /* Check if the device is still buys performing the write. Write could be delayed. */ + dfu -> ux_slave_class_dfu_get_status(dfu, &media_status); + + /* Check status of device. */ + switch (media_status) + { + + case UX_SLAVE_CLASS_DFU_MEDIA_STATUS_OK : + + /* Set the next state for wait reset and no error status. */ + dfu -> ux_slave_class_dfu_status = UX_SLAVE_CLASS_DFU_STATUS_OK ; + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_MANIFEST_WAIT_RESET; + + /* Check who is responsible for the RESET. */ + if (_ux_system_slave -> ux_system_slave_device_dfu_capabilities & UX_SLAVE_CLASS_DFU_CAPABILITY_WILL_DETACH) + { + + /* Wake up the DFU thread and send a detach request.. */ + _ux_utility_event_flags_set(&dfu -> ux_slave_class_dfu_event_flags_group, UX_DEVICE_CLASS_DFU_THREAD_EVENT_DISCONNECT, TX_OR); + + } + + break; + + case UX_SLAVE_CLASS_DFU_MEDIA_STATUS_BUSY : + + /* Set the next state for busy but no error status. */ + dfu -> ux_slave_class_dfu_status = UX_SLAVE_CLASS_DFU_STATUS_OK ; + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_MANIFEST; + break; + + case UX_SLAVE_CLASS_DFU_MEDIA_STATUS_ERROR : + + /* Set the next state for busy and error status. */ + dfu -> ux_slave_class_dfu_status = UX_SLAVE_CLASS_DFU_STATUS_ERROR_WRITE ; + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_ERROR; + break; + } + + /* Fill the status data payload. First with status. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) dfu -> ux_slave_class_dfu_status; + + /* Poll time out value is set to 1ms. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT) = 1; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 1) = 0; + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_POLL_TIMEOUT + 2) = 0; + + /* Next state. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STATE) = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* String index set to 0. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_DFU_GET_STATUS_STRING) = 0; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATUS_LENGTH); + + break; + + case UX_SLAVE_CLASS_DFU_COMMAND_GET_STATE : + + /* Fill the status data payload. First with state. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR) _ux_system_slave -> ux_system_slave_device_dfu_state_machine; + + /* We have a request to obtain the status of the DFU instance. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH, UX_SLAVE_CLASS_DFU_GET_STATE_LENGTH); + + break; + + default: + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + break; + } + + break; + + case UX_SLAVE_CLASS_DFU_STATUS_STATE_DFU_ERROR : + + /* Here we process only the request we can accept in the ERROR state. */ + switch (request) + { + + case UX_SLAVE_CLASS_DFU_COMMAND_CLEAR_STATUS : + + /* In the system, state the DFU state machine to dfu IDLE. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_DFU_IDLE; + + break; + + default: + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + break; + + } + + break; + + + default: + + /* Unknown state. Should not happen. */ + return(UX_ERROR); + } + + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_dfu_deactivate.c b/common/usbx_device_classes/src/ux_device_class_dfu_deactivate.c new file mode 100644 index 0000000..f43f0a1 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_dfu_deactivate.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DFU Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dfu.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dfu_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the dfu class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort Abort all transfers */ +/* */ +/* CALLED BY */ +/* */ +/* DFU Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dfu_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_DFU *dfu; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + dfu = (UX_SLAVE_CLASS_DFU *) class -> ux_slave_class_instance; + + /* We need the interface to the class. */ + interface = dfu -> ux_slave_class_dfu_interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + } + + /* Terminate the transactions pending on the endpoints. */ + _ux_device_stack_transfer_all_request_abort(endpoint_in, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(endpoint_out, UX_TRANSFER_BUS_RESET); + + /* If there is a deactivate function call it. */ + if (dfu -> ux_slave_class_dfu_instance_deactivate != UX_NULL) + { + /* Invoke the application. */ + dfu -> ux_slave_class_dfu_instance_deactivate(dfu); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_DFU_DEACTIVATE, dfu, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(dfu); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_dfu_entry.c b/common/usbx_device_classes/src/ux_device_class_dfu_entry.c new file mode 100644 index 0000000..9e76da3 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_dfu_entry.c @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DFU Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dfu.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_dfu_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the dfu class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the dfu interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_dfu_initialize Initialize dfu class */ +/* _ux_device_class_dfu_activate Activate dfu class */ +/* _ux_device_class_dfu_deactivate Deactivate dfu class */ +/* _ux_device_class_dfu_control_request Request control */ +/* */ +/* CALLED BY */ +/* */ +/* DFU Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dfu_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the DFU ACM class. */ + status = _ux_device_class_dfu_initialize(command); + + /* Return the completion status. */ + return(status); + + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_SLAVE_CLASS_DFU_CLASS) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the dfu thread needs to be activated. */ + status = _ux_device_class_dfu_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the dfu thread canceled. */ + status = _ux_device_class_dfu_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + status = _ux_device_class_dfu_control_request(command); + + /* Return the completion status. */ + return(status); + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_dfu_initialize.c b/common/usbx_device_classes/src/ux_device_class_dfu_initialize.c new file mode 100644 index 0000000..f65148f --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_dfu_initialize.c @@ -0,0 +1,213 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device DFU Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dfu.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dfu_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB DFU device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to dfu command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_descriptor_parse Parse a descriptor */ +/* _ux_utility_event_flags_create Create event flags */ +/* _ux_utility_event_flags_delete Delete event flags */ +/* _ux_utility_thread_create Create thread */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_dfu_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_DFU *dfu; +UX_SLAVE_CLASS_DFU_PARAMETER *dfu_parameter; +UX_SLAVE_CLASS *class; +UINT status; +UX_DFU_FUNCTIONAL_DESCRIPTOR dfu_functional_descriptor; +UCHAR *dfu_framework; +ULONG dfu_framework_length; +UCHAR descriptor_type; +ULONG descriptor_length; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device dfu class. */ + dfu = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_DFU)); + + /* Check for successful allocation. */ + if (dfu == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save the address of the DFU instance inside the DFU container. */ + class -> ux_slave_class_instance = (VOID *) dfu; + + /* Get the pointer to the application parameters for the dfu class. */ + dfu_parameter = command -> ux_slave_class_command_parameter; + + /* Save the calling parameters in the class instance. */ + dfu -> ux_slave_class_dfu_instance_activate = dfu_parameter -> ux_slave_class_dfu_parameter_instance_activate; + dfu -> ux_slave_class_dfu_instance_deactivate = dfu_parameter -> ux_slave_class_dfu_parameter_instance_deactivate; + dfu -> ux_slave_class_dfu_read = dfu_parameter -> ux_slave_class_dfu_parameter_read; + dfu -> ux_slave_class_dfu_write = dfu_parameter -> ux_slave_class_dfu_parameter_write; + dfu -> ux_slave_class_dfu_get_status = dfu_parameter -> ux_slave_class_dfu_parameter_get_status; + dfu -> ux_slave_class_dfu_notify = dfu_parameter -> ux_slave_class_dfu_parameter_notify; + + /* Store the device dfu in the project structure. */ + _ux_system_slave -> ux_system_slave_dfu_framework = dfu_parameter -> ux_slave_class_dfu_parameter_framework; + _ux_system_slave -> ux_system_slave_dfu_framework_length = dfu_parameter -> ux_slave_class_dfu_parameter_framework_length; + + /* There is a DFU descriptor. It has a device descriptor, a configuration descriptor, + an interface descriptor and finally a functional descriptor. */ + dfu_framework = _ux_system_slave -> ux_system_slave_dfu_framework; + dfu_framework_length = _ux_system_slave -> ux_system_slave_dfu_framework_length; + + /* Parse the device framework and locate interfaces and endpoint descriptor(s). */ + while (dfu_framework_length != 0) + { + + /* Get the length of this descriptor. */ + descriptor_length = (ULONG) *dfu_framework; + + /* And its type. */ + descriptor_type = *(dfu_framework + 1); + + /* Is this the Functional descriptor ? */ + if (descriptor_type == UX_DFU_FUNCTIONAL_DESCRIPTOR_ITEM) + { + + /* Parse the DFU descriptor in something more readable. */ + _ux_utility_descriptor_parse(dfu_framework, + _ux_system_dfu_functional_descriptor_structure, + UX_DFU_FUNCTIONAL_DESCRIPTOR_ENTRIES, + (UCHAR *) &dfu_functional_descriptor); + + /* Retrieve the DFU capabilities and store them in the system. */ + _ux_system_slave -> ux_system_slave_device_dfu_capabilities = dfu_functional_descriptor.bmAttributes; + + /* Retrieve the DFU timeout value. */ + _ux_system_slave -> ux_system_slave_device_dfu_detach_timeout = dfu_functional_descriptor.wDetachTimeOut; + + /* Retrieve the DFU transfer size value. */ + _ux_system_slave -> ux_system_slave_device_dfu_transfer_size = dfu_functional_descriptor.wDetachTimeOut; + + /* In the system, state the DFU state machine. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_APP_IDLE; + + } + + /* Adjust what is left of the device framework. */ + dfu_framework_length -= descriptor_length; + + /* Point to the next descriptor. */ + dfu_framework += descriptor_length; + + } + + /* Create a event flag group for the dfu class to synchronize with the event interrupt thread. */ + status = _ux_utility_event_flags_create(&dfu -> ux_slave_class_dfu_event_flags_group, "ux_device_class_dfu_event_flag"); + + /* Check status. */ + if (status != UX_SUCCESS) + status = UX_EVENT_ERROR; + + /* Allocate some memory for the dfu thread stack. */ + if (status == UX_SUCCESS) + { + dfu -> ux_slave_class_dfu_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (dfu -> ux_slave_class_dfu_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* dfu needs a thread to watch for disconnect and timer event for the DFU_DETACH sequence. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&dfu -> ux_slave_class_dfu_thread , "ux_slave_class_dfu_thread", + _ux_device_class_dfu_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) dfu -> ux_slave_class_dfu_thread_stack, + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + + UX_THREAD_EXTENSION_PTR_SET(&(dfu -> ux_slave_class_dfu_thread), class) + + /* Return completion status. */ + if (status == UX_SUCCESS) + return(UX_SUCCESS); + + /* There is error, free resources. */ + /* The last resource, thread is not created or created error, no need to free. */ + + if (dfu -> ux_slave_class_dfu_thread_stack) + _ux_utility_memory_free(dfu -> ux_slave_class_dfu_thread_stack); + if (dfu -> ux_slave_class_dfu_event_flags_group.tx_event_flags_group_id != 0) + _ux_utility_event_flags_delete(&dfu -> ux_slave_class_dfu_event_flags_group); + + /* Detach from container and free instance memory. */ + class -> ux_slave_class_instance = UX_NULL; + _ux_utility_memory_free(dfu); + + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_dfu_thread.c b/common/usbx_device_classes/src/ux_device_class_dfu_thread.c new file mode 100644 index 0000000..c6fbbfc --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_dfu_thread.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device dfu Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_dfu.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_dfu_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the dfu class. It waits for the */ +/* dfu command to signal a DFU_DETACH stage and either force a */ +/* disconnect from the device or wait for the host to detach. */ +/* */ +/* INPUT */ +/* */ +/* dfu_class Address of dfu class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_event_flags_get Get event flags */ +/* _ux_utility_delay_ms Delay in milliseconds */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_dfu_thread(ULONG dfu_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_DFU *dfu; +UX_SLAVE_DCD *dcd; +UINT status; +ULONG actual_flags; + + /* Cast properly the dfu instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, dfu_class) + + /* Get the dfu instance from this class container. */ + dfu = (UX_SLAVE_CLASS_DFU *) class -> ux_slave_class_instance; + + /* This thread runs forever. */ + while(1) + { + + + /* Wait until we have a event sent by the application. */ + status = _ux_utility_event_flags_get(&dfu -> ux_slave_class_dfu_event_flags_group, (UX_DEVICE_CLASS_DFU_THREAD_EVENT_DISCONNECT | + UX_DEVICE_CLASS_DFU_THREAD_EVENT_WAIT_RESET), + TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); + + /* Check the completion code and the actual flags returned. */ + if (status == UX_SUCCESS) + { + + /* Check the source of event. */ + if (actual_flags & UX_DEVICE_CLASS_DFU_THREAD_EVENT_DISCONNECT) + { + + /* We need to disconnect. The control command for DETACH is still being processed, wait 2-3 ms. */ + _ux_utility_delay_ms(2); + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Issue a Soft Disconnect. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_CHANGE_STATE, (VOID *) UX_DEVICE_FORCE_DISCONNECT); + + } + + /* Check the source of event. */ + if (actual_flags & UX_DEVICE_CLASS_DFU_THREAD_EVENT_WAIT_RESET) + { + + /* We need to wait for reset. Arm a timer. The timeout value is indicated in ms from + the device framework. */ + _ux_utility_delay_ms(_ux_system_slave -> ux_system_slave_device_dfu_detach_timeout); + + /* Check the mode. */ + if (_ux_system_slave -> ux_system_slave_device_dfu_mode == UX_DEVICE_CLASS_DFU_MODE_RUNTIME) + + /* We are still in RunTime mode. The host never reset. Revert to AppIdle state. */ + _ux_system_slave -> ux_system_slave_device_dfu_state_machine = UX_SYSTEM_DFU_STATE_APP_IDLE; + + } + } + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_activate.c b/common/usbx_device_classes/src/ux_device_class_hid_activate.c new file mode 100644 index 0000000..fcb560a --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_activate.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates an instance of a HID device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to hid command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_resume Resume thread */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_HID *hid; +UX_SLAVE_CLASS *class; +UX_SLAVE_ENDPOINT *endpoint_interrupt; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + hid = (UX_SLAVE_CLASS_HID *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)hid; + + /* Now the opposite, store the interface in the class instance. */ + hid -> ux_slave_class_hid_interface = interface; + + /* Locate the endpoints. */ + endpoint_interrupt = interface -> ux_slave_interface_first_endpoint; + + /* Check if interrupt IN endpoint exists. */ + while (endpoint_interrupt != UX_NULL) + { + + if ((endpoint_interrupt -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN && + (endpoint_interrupt -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT) + { + + /* It's right endpoint we need. */ + break; + } + + /* Try next endpoint. */ + endpoint_interrupt = endpoint_interrupt -> ux_slave_endpoint_next_endpoint; + } + + /* Check if we found right endpoint. */ + if (endpoint_interrupt == UX_NULL) + return (UX_ERROR); + + /* Save the endpoint in the hid instance. */ + hid -> ux_device_class_hid_interrupt_endpoint = endpoint_interrupt; + + /* Resume thread. */ + _ux_utility_thread_resume(&class -> ux_slave_class_thread); + + /* If there is a activate function call it. */ + if (hid -> ux_slave_class_hid_instance_activate != UX_NULL) + { + + /* Invoke the application. */ + hid -> ux_slave_class_hid_instance_activate(hid); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_HID_ACTIVATE, hid, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, hid, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_control_request.c b/common/usbx_device_classes/src/ux_device_class_hid_control_request.c new file mode 100644 index 0000000..64d1b1f --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_control_request.c @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to hid class */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_class_hid_report_get Process Get_Report request */ +/* _ux_device_class_hid_report_set Process Set_Report request */ +/* _ux_device_class_hid_descriptor_send Send requested descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_CLASS *class; +ULONG request; +ULONG request_value; +ULONG request_index; +ULONG request_length; +ULONG descriptor_type; +UCHAR duration; +UX_SLAVE_CLASS_HID *hid; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + request_value = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE); + request_index = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_INDEX); + request_length = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); + + /* Duration - upper byte of wValue. */ + duration = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE + 1); + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the storage instance from this class container. */ + hid = (UX_SLAVE_CLASS_HID *) class -> ux_slave_class_instance; + + /* Here we proceed only the standard request we know of at the device level. */ + switch (request) + { + + case UX_DEVICE_CLASS_HID_COMMAND_GET_REPORT: + + /* Send the requested report to the host. */ + _ux_device_class_hid_report_get(hid, request_value, request_index, request_length); + break; + + case UX_DEVICE_CLASS_HID_COMMAND_SET_REPORT: + + /* Extract the descriptor type. */ + descriptor_type = (request_value & 0xff00) >> 8; + + /* Get the requested report from the host. */ + _ux_device_class_hid_report_set(hid, descriptor_type, request_index, request_length); + break; + + case UX_GET_DESCRIPTOR: + + /* Send the requested descriptor to the host. */ + _ux_device_class_hid_descriptor_send(hid, request_value, request_index, request_length); + break; + + case UX_DEVICE_CLASS_HID_COMMAND_GET_IDLE: + case UX_DEVICE_CLASS_HID_COMMAND_SET_IDLE: + + /* Ignore Report ID for now. */ + + if (request == UX_DEVICE_CLASS_HID_COMMAND_GET_IDLE) + { + + /* Send the idle rate. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR)hid -> ux_device_class_hid_event_idle_rate; + _ux_device_stack_transfer_request(transfer_request, 1, request_length); + } + else + { + + /* Accept the idle rate if it changes. */ + if ((UCHAR)hid -> ux_device_class_hid_event_idle_rate != duration) + { + + hid -> ux_device_class_hid_event_idle_rate = duration; + if (duration == 0) + { + + /* No need to repeat last report, no timeout. */ + hid -> ux_device_class_hid_event_wait_timeout = TX_WAIT_FOREVER; + } + else + { + + /* Calculate the timeout value. Weighted as 4ms. */ + hid -> ux_device_class_hid_event_wait_timeout = MS_TO_TICK((ULONG)duration << 2u); + + /* Be sure to have a timeout that is not zero. */ + if (hid -> ux_device_class_hid_event_wait_timeout == 0) + hid -> ux_device_class_hid_event_wait_timeout ++; + + /* Set an event to wake up the interrupt thread. */ + _ux_utility_event_flags_set(&hid -> ux_device_class_hid_event_flags_group, UX_DEVICE_CLASS_HID_NEW_IDLE_RATE, TX_OR); + } + } + } + break; + + case UX_DEVICE_CLASS_HID_COMMAND_GET_PROTOCOL: + case UX_DEVICE_CLASS_HID_COMMAND_SET_PROTOCOL: + + /* Not supported now. */ + + default: + + /* Unknown function. It's not handled. */ + return(UX_ERROR); + } + + /* It's handled. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_deactivate.c b/common/usbx_device_classes/src/ux_device_class_hid_deactivate.c new file mode 100644 index 0000000..4f4892d --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_deactivate.c @@ -0,0 +1,99 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the hid class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort Abort all transfers */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_HID *hid; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + hid = (UX_SLAVE_CLASS_HID *) class -> ux_slave_class_instance; + + /* Terminate the transactions pending on the endpoints. */ + _ux_device_stack_transfer_all_request_abort(hid -> ux_device_class_hid_interrupt_endpoint, UX_TRANSFER_BUS_RESET); + + /* If there is a deactivate function call it. */ + if (hid -> ux_slave_class_hid_instance_deactivate != UX_NULL) + { + /* Invoke the application. */ + hid -> ux_slave_class_hid_instance_deactivate(hid); + } + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_HID_DEACTIVATE, hid, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(hid); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_descriptor_send.c b/common/usbx_device_classes/src/ux_device_class_hid_descriptor_send.c new file mode 100644 index 0000000..61b41f9 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_descriptor_send.c @@ -0,0 +1,212 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_descriptor_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends back the class descriptor required by the host. */ +/* */ +/* INPUT */ +/* */ +/* descriptor_type Descriptor type */ +/* descriptor_index Index of descriptor */ +/* host_length Length requested by host */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_dcd_function) DCD dispatch function */ +/* _ux_device_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_copy Memory copy */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_descriptor_send(UX_SLAVE_CLASS_HID *hid, ULONG descriptor_type, + ULONG request_index, ULONG host_length) +{ + +UX_SLAVE_DCD *dcd; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_ENDPOINT *endpoint; +UCHAR * device_framework; +UCHAR * device_framework_end; +ULONG descriptor_length; +UINT status = UX_ERROR; +ULONG length; + + UX_PARAMETER_NOT_USED(request_index); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_HID_DESCRIPTOR_SEND, hid, descriptor_type, request_index, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the DCD. */ + dcd = &_ux_system_slave -> ux_system_slave_dcd; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint associated with the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Set the direction to OUT. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Shift the descriptor type in the low byte field. */ + descriptor_type = (UCHAR) ((descriptor_type >> 8) & 0xff); + + /* What type of descriptor do we need to return? */ + switch (descriptor_type) + { + + case UX_DEVICE_CLASS_HID_DESCRIPTOR_HID: + + /* We should have a HID descriptor as part of the config descriptor. */ + device_framework = _ux_system_slave -> ux_system_slave_device_framework; + device_framework_end = device_framework + _ux_system_slave -> ux_system_slave_device_framework_length; + + /* Parse the device framework and locate the HID descriptor. + There is only one HID descriptor. */ + while (device_framework < device_framework_end) + { + + /* Get the type of the current descriptor. */ + descriptor_type = *(device_framework + 1); + + /* And its length. */ + descriptor_length = (ULONG) *device_framework; + + /* Check if this is a HID report descriptor. */ + if (descriptor_type == UX_DEVICE_CLASS_HID_DESCRIPTOR_HID) + { + /* Ensure the host does not demand a length beyond our descriptor (Windows does that) + and do not return more than what is allowed. */ + if (descriptor_length < host_length) + length = descriptor_length; + else + length = host_length; + + /* Copy the device descriptor into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + device_framework, length); + + /* We can return the configuration descriptor. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + + } + + /* Point to the next descriptor. */ + device_framework += descriptor_length; + } + + /* Stall the endpoint if not found or corrupt. */ + if (device_framework >= device_framework_end) + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + + break; + + case UX_DEVICE_CLASS_HID_DESCRIPTOR_REPORT: + + /* Get the length of entire configuration descriptor. */ + descriptor_length = hid -> ux_device_class_hid_report_length; + + /* Ensure the host does not demand a length beyond our descriptor (Windows does that) + and do not return more than what is allowed. */ + if (descriptor_length < host_length) + length = descriptor_length; + else + length = host_length; + + /* Check buffer length, since total descriptors length may exceed buffer... */ + if (length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Stall the endpoint. */ + status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + break; + } + + /* Copy the device descriptor into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + hid -> ux_device_class_hid_report_address, length); + + /* We can return the report descriptor. */ + status = _ux_device_stack_transfer_request(transfer_request, length, host_length); + break; + + case UX_DEVICE_CLASS_HID_DESCRIPTOR_PHYSICAL: + + /* Not treated for now. Fall through and Stall endpoint. */ + + default: + + /* Stall the endpoint. */ + dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); + return(UX_ERROR); + } + + /* Return the status to the caller. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_entry.c b/common/usbx_device_classes/src/ux_device_class_hid_entry.c new file mode 100644 index 0000000..9a99766 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_entry.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_hid_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the hid class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the hid interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_hid_initialize Initialize hid class */ +/* _ux_device_class_hid_uninitialize Uninitialize hid class */ +/* _ux_device_class_hid_activate Activate hid class */ +/* _ux_device_class_hid_deactivate Deactivate hid class */ +/* _ux_device_class_hid_control_request Request control */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the HID class. */ + status = _ux_device_class_hid_initialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_UNINITIALIZE: + + /* Call the init function of the HID class. */ + status = _ux_device_class_hid_uninitialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_HID_CLASS) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the hid thread needs to be activated. */ + status = _ux_device_class_hid_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the hid thread canceled. */ + status = _ux_device_class_hid_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + status = _ux_device_class_hid_control_request(command); + + /* Return the completion status. */ + return(status); + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_event_get.c b/common/usbx_device_classes/src/ux_device_class_hid_event_get.c new file mode 100644 index 0000000..ab1ce83 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_event_get.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_event_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if there is an event from the application */ +/* */ +/* INPUT */ +/* */ +/* hid Address of hid class */ +/* event Pointer of the event */ +/* */ +/* OUTPUT */ +/* */ +/* status UX_SUCCESS if there is an */ +/* event */ +/* CALLS */ +/* */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_event_get(UX_SLAVE_CLASS_HID *hid, + UX_SLAVE_CLASS_HID_EVENT *hid_event) +{ + +UX_SLAVE_CLASS_HID_EVENT *current_hid_event; +UX_SLAVE_DEVICE *device; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_HID_EVENT_GET, hid, hid_event, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Check the device state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + return(UX_DEVICE_HANDLE_UNKNOWN); + + /* Check if the head and the tail of the event array is the same. */ + if (hid -> ux_device_class_hid_event_array_head == + hid -> ux_device_class_hid_event_array_tail) + + /* No event to report. */ + return(UX_ERROR); + + /* There is an event to report, get the current pointer to the event. */ + current_hid_event = hid -> ux_device_class_hid_event_array_tail; + + /* fill in the event structure from the user. */ + hid_event -> ux_device_class_hid_event_length = current_hid_event -> ux_device_class_hid_event_length; + _ux_utility_memory_copy(hid_event -> ux_device_class_hid_event_buffer, current_hid_event -> ux_device_class_hid_event_buffer, + current_hid_event -> ux_device_class_hid_event_length); + + /* Adjust the tail pointer. Check if we are at the end. */ + if ((current_hid_event + 1) == hid -> ux_device_class_hid_event_array_end) + + /* We are at the end, go back to the beginning. */ + hid -> ux_device_class_hid_event_array_tail = hid -> ux_device_class_hid_event_array; + + else + /* We are not at the end, increment the tail position. */ + hid -> ux_device_class_hid_event_array_tail++; + + /* Return event status to the user. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_event_set.c b/common/usbx_device_classes/src/ux_device_class_hid_event_set.c new file mode 100644 index 0000000..ce83453 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_event_set.c @@ -0,0 +1,157 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_event_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends an event to the hid class. It is processed */ +/* asynchronously by the interrupt thread. */ +/* */ +/* INPUT */ +/* */ +/* hid Address of hid class */ +/* event Pointer of the event */ +/* */ +/* OUTPUT */ +/* */ +/* status UX_SUCCESS if there is an */ +/* event */ +/* CALLS */ +/* */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_event_flags_set Set event flags */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_event_set(UX_SLAVE_CLASS_HID *hid, + UX_SLAVE_CLASS_HID_EVENT *hid_event) +{ + +UX_SLAVE_CLASS_HID_EVENT *current_hid_event; +UX_SLAVE_CLASS_HID_EVENT *next_hid_event; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_HID_EVENT_SET, hid, hid_event, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Current position of the head. */ + current_hid_event = hid -> ux_device_class_hid_event_array_head; + + /* If the pointer is NULL, the round robin buffer has not been activated. */ + if (current_hid_event == UX_NULL) + return (UX_ERROR); + + /* Calculate the next position. */ + if ((current_hid_event + 1) == hid -> ux_device_class_hid_event_array_end) + + /* We are at the end, go back to the beginning. */ + next_hid_event = hid -> ux_device_class_hid_event_array; + + else + + /* We are not at the end, increment the head position. */ + next_hid_event = current_hid_event + 1; + + + /* Any place left for this event ? */ + if (next_hid_event == hid -> ux_device_class_hid_event_array_tail) + return (UX_ERROR); + + /* There is an event to report, get the current pointer to the event. */ + current_hid_event = hid -> ux_device_class_hid_event_array_head; + + /* Update the head. */ + hid -> ux_device_class_hid_event_array_head = next_hid_event; + + /* Check if this event has a report ID. */ + if (hid -> ux_device_class_hid_report_id == UX_TRUE) + { + + /* Yes, there's a report ID. Check to see if our event buffer can also + fit the extra byte. */ + if (hid_event -> ux_device_class_hid_event_length + 1 > UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return overflow error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Store the report ID. */ + *current_hid_event -> ux_device_class_hid_event_buffer = (UCHAR)(hid_event -> ux_device_class_hid_event_report_id); + + /* Store the data itself. */ + _ux_utility_memory_copy(current_hid_event -> ux_device_class_hid_event_buffer + 1, hid_event -> ux_device_class_hid_event_buffer, + hid_event -> ux_device_class_hid_event_length); + + /* fill in the event structure from the user. */ + current_hid_event -> ux_device_class_hid_event_length = hid_event -> ux_device_class_hid_event_length + 1; + } + else + { + + /* No report ID to consider. */ + _ux_utility_memory_copy(current_hid_event -> ux_device_class_hid_event_buffer, hid_event -> ux_device_class_hid_event_buffer, + hid_event -> ux_device_class_hid_event_length); + + /* fill in the event structure from the user. */ + current_hid_event -> ux_device_class_hid_event_length = hid_event -> ux_device_class_hid_event_length; + } + + /* Set an event to wake up the interrupt thread. */ + _ux_utility_event_flags_set(&hid -> ux_device_class_hid_event_flags_group, UX_DEVICE_CLASS_HID_NEW_EVENT, TX_OR); + + /* Return event status to the user. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_initialize.c b/common/usbx_device_classes/src/ux_device_class_hid_initialize.c new file mode 100644 index 0000000..9141a98 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_initialize.c @@ -0,0 +1,184 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB HID device. */ +/* This function is called by the class register function. It is only */ +/* done once. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to hid command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_delete Delete thread */ +/* _ux_utility_event_flags_create Create event flags group */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_HID *hid; +UX_SLAVE_CLASS_HID_PARAMETER *hid_parameter; +UX_SLAVE_CLASS *class; +UINT status = UX_SUCCESS; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device hid class. */ + hid = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_HID)); + + /* Check for successful allocation. */ + if (hid == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save the address of the HID instance inside the HID container. */ + class -> ux_slave_class_instance = (VOID *) hid; + + /* Allocate some memory for the thread stack. */ + class -> ux_slave_class_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (class -> ux_slave_class_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + + /* This instance needs to be running in a different thread. So start + a new thread. We pass a pointer to the class to the new thread. This thread + does not start until we have a instance of the class. */ + if (status == UX_SUCCESS) + status = _ux_utility_thread_create(&class -> ux_slave_class_thread, "ux_slave_hid_thread", + _ux_device_class_hid_interrupt_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) class -> ux_slave_class_thread_stack, + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + + /* Check the creation of this thread. */ + if (status == UX_SUCCESS) + { + + UX_THREAD_EXTENSION_PTR_SET(&(class -> ux_slave_class_thread), class) + + /* Get the pointer to the application parameters for the hid class. */ + hid_parameter = command -> ux_slave_class_command_parameter; + + /* Store all the application parameter information about the report. */ + hid -> ux_device_class_hid_report_address = hid_parameter -> ux_device_class_hid_parameter_report_address; + hid -> ux_device_class_hid_report_length = hid_parameter -> ux_device_class_hid_parameter_report_length; + hid -> ux_device_class_hid_report_id = hid_parameter -> ux_device_class_hid_parameter_report_id; + + /* Store the callback function. */ + hid -> ux_device_class_hid_callback = hid_parameter -> ux_device_class_hid_parameter_callback; + hid -> ux_device_class_hid_get_callback = hid_parameter -> ux_device_class_hid_parameter_get_callback; + + /* Create the event array. */ + hid -> ux_device_class_hid_event_array = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_HID_EVENT), UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE); + + /* Check for successful allocation. */ + if (hid -> ux_device_class_hid_event_array != UX_NULL) + { + + /* Initialize the head and tail of the notification round robin buffers. + At first, the head and tail are pointing to the beginning of the array. */ + hid -> ux_device_class_hid_event_array_head = hid -> ux_device_class_hid_event_array; + hid -> ux_device_class_hid_event_array_tail = hid -> ux_device_class_hid_event_array; + hid -> ux_device_class_hid_event_array_end = hid -> ux_device_class_hid_event_array + UX_DEVICE_CLASS_HID_MAX_EVENTS_QUEUE; + + /* Store the start and stop signals if needed by the application. */ + hid -> ux_slave_class_hid_instance_activate = hid_parameter -> ux_slave_class_hid_instance_activate; + hid -> ux_slave_class_hid_instance_deactivate = hid_parameter -> ux_slave_class_hid_instance_deactivate; + + /* By default no event wait timeout. */ + hid -> ux_device_class_hid_event_wait_timeout = TX_WAIT_FOREVER; + + /* Create a event flag group for the hid class to synchronize with the event interrupt thread. */ + status = _ux_utility_event_flags_create(&hid -> ux_device_class_hid_event_flags_group, "ux_device_class_hid_event_flag"); + + /* Check status. */ + if (status == UX_SUCCESS) + return(status); + + /* It's event error. */ + status = UX_EVENT_ERROR; + } + else + status = UX_MEMORY_INSUFFICIENT; + + /* Delete thread. */ + _ux_utility_thread_delete(&class -> ux_slave_class_thread); + } + else + status = (UX_THREAD_ERROR); + + /* Free stack. */ + if (class -> ux_slave_class_thread_stack) + _ux_utility_memory_free(class -> ux_slave_class_thread_stack); + + /* Unmount instance. */ + class -> ux_slave_class_instance = UX_NULL; + + /* Free HID instance. */ + _ux_utility_memory_free(hid); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_interrupt_thread.c b/common/usbx_device_classes/src/ux_device_class_hid_interrupt_thread.c new file mode 100644 index 0000000..4274a06 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_interrupt_thread.c @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_interrupt_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the hid interrupt endpoint */ +/* */ +/* INPUT */ +/* */ +/* hid_class Address of hid class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_event_flags_get Get event flags */ +/* _ux_device_class_hid_event_get Get HID event */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_thread_suspend Suspend thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_hid_interrupt_thread(ULONG hid_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_HID *hid; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request_in; +UX_SLAVE_CLASS_HID_EVENT hid_event; +UINT status; +UCHAR *buffer; +ULONG actual_flags; + + + /* Cast properly the hid instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, hid_class) + + /* Get the hid instance from this class container. */ + hid = (UX_SLAVE_CLASS_HID *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* All HID events are on the interrupt endpoint IN, from the host. */ + transfer_request_in = &hid -> ux_device_class_hid_interrupt_endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Wait until we have a event sent by the application + or a change in the idle state to send last or empty report. */ + status = _ux_utility_event_flags_get(&hid -> ux_device_class_hid_event_flags_group, + UX_DEVICE_CLASS_HID_EVENTS_MASK, TX_OR_CLEAR, &actual_flags, + hid -> ux_device_class_hid_event_wait_timeout); + + /* If there is no event, check if we have timeout defined. */ + if (status == TX_NO_EVENTS) + { + + /* There is no event exists on timeout, insert last. */ + + /* If last request is sent, repeat it, else fill zeros. */ + if (transfer_request_in -> ux_slave_transfer_request_requested_length) + { + hid_event.ux_device_class_hid_event_length = transfer_request_in -> ux_slave_transfer_request_requested_length; + _ux_utility_memory_copy(hid_event.ux_device_class_hid_event_buffer, + transfer_request_in -> ux_slave_transfer_request_data_pointer, + hid_event.ux_device_class_hid_event_length); + } + else + { + hid_event.ux_device_class_hid_event_report_id = 0; + hid_event.ux_device_class_hid_event_length = transfer_request_in -> ux_slave_transfer_request_endpoint -> ux_slave_endpoint_descriptor.wMaxPacketSize & 0x7FF; + _ux_utility_memory_set(hid_event.ux_device_class_hid_event_buffer, 0, + hid_event.ux_device_class_hid_event_length); + } + _ux_device_class_hid_event_set(hid, &hid_event); + + /* Continue to process queue. */ + status = UX_SUCCESS; + } + + /* Check the completion code. */ + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + /* Do not proceed. */ + return; + } + + + /* Check if we have an event to report. */ + while (_ux_device_class_hid_event_get(hid, &hid_event) == UX_SUCCESS) + { + + /* Prepare the event data payload from the hid event structure. Get a pointer to the buffer area. */ + buffer = transfer_request_in -> ux_slave_transfer_request_data_pointer; + + /* Copy the event buffer into the target buffer. */ + _ux_utility_memory_copy(buffer, hid_event.ux_device_class_hid_event_buffer, hid_event.ux_device_class_hid_event_length); + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request_in, hid_event.ux_device_class_hid_event_length, + hid_event.ux_device_class_hid_event_length); + + /* Check error code. We don't want to invoke the error callback + if the device was disconnected, since that's expected. */ + if (status != UX_SUCCESS && status != UX_TRANSFER_BUS_RESET) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&class -> ux_slave_class_thread); + } +} diff --git a/common/usbx_device_classes/src/ux_device_class_hid_report_get.c b/common/usbx_device_classes/src/ux_device_class_hid_report_get.c new file mode 100644 index 0000000..4782148 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_report_get.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_report_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a report to the host. */ +/* */ +/* INPUT */ +/* */ +/* descriptor_type Descriptor type */ +/* descriptor_index Index of descriptor */ +/* host_length Length requested by host */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_hid_event_get Get HID event */ +/* _ux_device_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_report_get(UX_SLAVE_CLASS_HID *hid, ULONG descriptor_type, + ULONG request_index, ULONG host_length) +{ + +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_ENDPOINT *endpoint; +UCHAR report_id; +UCHAR report_type; +UX_SLAVE_CLASS_HID_EVENT hid_event; +ULONG hid_event_length; +UCHAR *buffer; +UINT status = UX_ERROR; + + UX_PARAMETER_NOT_USED(descriptor_type); + UX_PARAMETER_NOT_USED(request_index); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_HID_REPORT_GET, hid, descriptor_type, request_index, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint associated with the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Get report ID (wValue.lower) and report type (wValue.higher). */ + report_id = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE + 0); + report_type = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE + 1); + + /* Set the direction to OUT. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* Prepare the event data payload from the hid event structure. Get a pointer to the buffer area. */ + buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Initialize event fields. */ + hid_event.ux_device_class_hid_event_report_id = report_id; + hid_event.ux_device_class_hid_event_report_type = report_type; + hid_event.ux_device_class_hid_event_length = UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH; + + /* If it's input report without ID try to get it from event queue head. */ + if (report_type == UX_DEVICE_CLASS_HID_REPORT_TYPE_INPUT && + hid -> ux_device_class_hid_report_id != UX_TRUE) + + /* Check if we have an event to report. */ + status = _ux_device_class_hid_event_get(hid, &hid_event); + + /* Try to get event from application callback. */ + else + { + + /* Let application fill event. */ + if (hid -> ux_device_class_hid_get_callback != UX_NULL) + status = hid -> ux_device_class_hid_get_callback(hid, &hid_event); + } + + if (status == UX_SUCCESS) + { + + /* Get the length to send back to the host. */ + if (host_length < hid_event.ux_device_class_hid_event_length) + hid_event_length = host_length; + else + hid_event_length = hid_event.ux_device_class_hid_event_length; + + /* First reset it. */ + _ux_utility_memory_set(buffer, 0, hid_event_length); + + /* Copy the event buffer into the target buffer. */ + _ux_utility_memory_copy(buffer, hid_event.ux_device_class_hid_event_buffer, hid_event_length); + } + else + { + + /* There's no event, so send back zero'd memory. */ + + /* Get the length to send back to the host. */ + if (host_length < UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) + hid_event_length = host_length; + else + hid_event_length = UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH; + + /* Reset it. */ + _ux_utility_memory_set(buffer, 0, hid_event_length); + } + + /* We can send the report. */ + status = _ux_device_stack_transfer_request(transfer_request, hid_event_length, host_length); + + /* Return the status to the caller. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_report_set.c b/common/usbx_device_classes/src/ux_device_class_hid_report_set.c new file mode 100644 index 0000000..69ec450 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_report_set.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_report_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets from the control pipe a report to be set from */ +/* the host. */ +/* */ +/* INPUT */ +/* */ +/* descriptor_type Descriptor type */ +/* descriptor_index Index of descriptor */ +/* host_length Length requested by host */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_copy Memory copy */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Device Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_report_set(UX_SLAVE_CLASS_HID *hid, ULONG descriptor_type, + ULONG request_index, ULONG host_length) +{ + +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_ENDPOINT *endpoint; +UX_SLAVE_CLASS_HID_EVENT hid_event; +UCHAR *hid_buffer; + + UX_PARAMETER_NOT_USED(request_index); + UX_PARAMETER_NOT_USED(host_length); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_HID_REPORT_SET, hid, descriptor_type, request_index, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the control endpoint associated with the device. */ + endpoint = &device -> ux_slave_device_control_endpoint; + + /* Get the pointer to the transfer request associated with the endpoint. */ + transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; + + /* Set the event type to OUTPUT. */ + hid_event.ux_device_class_hid_event_report_type = descriptor_type; + + /* Get HID data address. */ + hid_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Check for report ID in this HID descriptor. */ + if (hid -> ux_device_class_hid_report_id == UX_TRUE) + { + /* Set the report ID, First byte of data payload. */ + hid_event.ux_device_class_hid_event_report_id = (ULONG) *hid_buffer; + + /* Set the length = total length - report ID. */ + hid_event.ux_device_class_hid_event_length = transfer_request -> ux_slave_transfer_request_actual_length -1; + + /* Set HID data after report ID. */ + hid_buffer++; + } + + else + { + /* Set the report ID, not used here. */ + hid_event.ux_device_class_hid_event_report_id = 0; + + /* Set the length. */ + hid_event.ux_device_class_hid_event_length = transfer_request -> ux_slave_transfer_request_actual_length; + } + + /* Copy the buffer received from the host. Check for overflow. */ + if (hid_event.ux_device_class_hid_event_length > UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH) + + /* Overflow detected. */ + hid_event.ux_device_class_hid_event_length = UX_DEVICE_CLASS_HID_EVENT_BUFFER_LENGTH; + + /* Now we can safely copy the payload. */ + _ux_utility_memory_copy(hid_event.ux_device_class_hid_event_buffer, hid_buffer, + hid_event.ux_device_class_hid_event_length); + + /* If there is a callback defined by the application, send the hid event to it. */ + if (hid -> ux_device_class_hid_callback != UX_NULL) + + /* Callback exists. */ + hid -> ux_device_class_hid_callback(hid, &hid_event); + + /* Return the status to the caller. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_hid_uninitialize.c b/common/usbx_device_classes/src/ux_device_class_hid_uninitialize.c new file mode 100644 index 0000000..3477161 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_hid_uninitialize.c @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_hid.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_hid_uninitialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function uninitializes the USB HID device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to hid command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_delete Remove storage thread. */ +/* _ux_utility_memory_free Free memory used by storage */ +/* _ux_utility_event_flags_delete Remove flag event structure */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_hid_uninitialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_HID *hid; +UX_SLAVE_CLASS *class; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + hid = (UX_SLAVE_CLASS_HID *) class -> ux_slave_class_instance; + + /* Remove HID thread. */ + _ux_utility_thread_delete(&class -> ux_slave_class_thread); + + /* Remove the thread used by HID. */ + _ux_utility_memory_free(class -> ux_slave_class_thread_stack); + + /* Delete the event flag group for the hid class. */ + _ux_utility_event_flags_delete(&hid -> ux_device_class_hid_event_flags_group); + + /* Free memory for the array. */ + _ux_utility_memory_free(hid -> ux_device_class_hid_event_array); + + /* Free the resources. */ + _ux_utility_memory_free(hid); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_activate.c b/common/usbx_device_classes/src/ux_device_class_pima_activate.c new file mode 100644 index 0000000..06a1a02 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_activate.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates the USB Pima device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to pima command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_resume Resume thread */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + + UINT status; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_PIMA *pima; +UX_SLAVE_CLASS *class; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; +UX_SLAVE_ENDPOINT *endpoint_interrupt; + + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Store the class instance in the container. */ + pima = (UX_SLAVE_CLASS_PIMA *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)pima; + + /* Now the opposite, store the interface in the class instance. */ + pima -> ux_slave_class_pima_interface = interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + + /* And the endpoint after that interrupt. */ + endpoint_interrupt = endpoint_in -> ux_slave_endpoint_next_endpoint; + + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + + /* And the endpoint after that interrupt. */ + endpoint_interrupt = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + + /* Save the endpoints in the pima instance. */ + pima -> ux_device_class_pima_bulk_in_endpoint = endpoint_in; + pima -> ux_device_class_pima_bulk_out_endpoint = endpoint_out; + pima -> ux_device_class_pima_interrupt_endpoint = endpoint_interrupt; + + /* Resume thread. */ + status = _ux_utility_thread_resume(&class -> ux_slave_class_thread); + + /* If there is a activate function call it. */ + if (pima -> ux_device_class_pima_instance_activate != UX_NULL) + { + /* Invoke the application. */ + pima -> ux_device_class_pima_instance_activate(pima); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_ACTIVATE, pima, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, pima, 0, 0, 0) + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_control_request.c b/common/usbx_device_classes/src/ux_device_class_pima_control_request.c new file mode 100644 index 0000000..65a3172 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_control_request.c @@ -0,0 +1,159 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_transfer_abort Transfer abort */ +/* _ux_device_class_pima_event_set Put PIMA event into queue */ +/* _ux_utility_short_put Put 16-bit value into buffer */ +/* */ +/* CALLED BY */ +/* */ +/* PIMA Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_PIMA_EVENT pima_event; +ULONG request; +UX_SLAVE_CLASS_PIMA *pima; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the storage instance from this class container. */ + pima = (UX_SLAVE_CLASS_PIMA *) class -> ux_slave_class_instance; + + /* Here we proceed only the standard request we know of at the device level. */ + switch (request) + { + + case UX_DEVICE_CLASS_PIMA_REQUEST_CANCEL_COMMAND: + + /* We need to cancel the request that is currently pending. It could be either + on the bulk in or bulk out endpoint. Load the bulk in transfer request first. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* And check if we have something waiting on this one. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_PENDING) + + /* Yes, abort it. */ + _ux_device_stack_transfer_abort(transfer_request, UX_TRANSFER_STATUS_ABORT); + + /* Maybe the bulk out is busy too. */ + transfer_request = &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request; + + /* And check if we have something waiting on this one. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_PENDING) + + /* Yes, abort it. */ + _ux_device_stack_transfer_abort(transfer_request, UX_TRANSFER_STATUS_ABORT); + + /* The host is now waiting for an event of transfer cancelled to proceed. + Prepare an event command and set the event code to transaction cancelled*/ + pima_event.ux_device_class_pima_event_code = UX_DEVICE_CLASS_PIMA_EC_CANCEL_TRANSACTION; + + /* Set all parameters to a 0 value. */ + pima_event.ux_device_class_pima_event_parameter_1 = 0; + pima_event.ux_device_class_pima_event_parameter_2 = 0; + pima_event.ux_device_class_pima_event_parameter_3 = 0; + + /* Send the pima event into the queue. This will be processed by an asynchronous thread. */ + _ux_device_class_pima_event_set(pima, &pima_event); + + break ; + + case UX_DEVICE_CLASS_PIMA_REQUEST_STATUS_COMMAND: + + /* Fill the status data payload. First with length. */ + _ux_utility_short_put(transfer_request -> ux_slave_transfer_request_data_pointer, 4); + + /* Then the status. */ + _ux_utility_short_put(transfer_request -> ux_slave_transfer_request_data_pointer + 2, UX_DEVICE_CLASS_PIMA_RC_OK); + + /* We have a request to obtain the status of the MTP. */ + _ux_device_stack_transfer_request(transfer_request, 4, 4); + + break ; + + default: + + /* Unknown function. It's not handled. */ + return(UX_ERROR); + } + + /* It's handled. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_data.c b/common/usbx_device_classes/src/ux_device_class_pima_data.c new file mode 100644 index 0000000..79eeb77 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_data.c @@ -0,0 +1,274 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_pima_data PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This module contains all the data definition used by the PIMA */ +/* device class. */ +/* */ +/* INPUT */ +/* */ +/* OUTPUT */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +/* Define PIMA vendor extension descriptor. This is a regular string that needs to be put into unicode. */ +UCHAR _ux_device_class_pima_vendor_extension_descriptor[] = { +#ifdef UX_PIMA_WITH_MTP_SUPPORT + "microsoft.com: 1.0; microsoft.com/WMPPD: 11.0; microsoft.com/WMPPD: 10.0; microsoft.com/WMDRMPD: 10.1;" +#else + 0 +#endif + }; + +/* Define PIMA supported operations. The last entry MUST be a zero. The DeviceInfoSet command + will parse this array and compute the number of functions supported and return it to the + host. */ + +USHORT _ux_device_class_pima_supported_operations[] = { + + UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_INFO, + UX_DEVICE_CLASS_PIMA_OC_OPEN_SESSION, + UX_DEVICE_CLASS_PIMA_OC_CLOSE_SESSION, + UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_IDS, + UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_INFO, + UX_DEVICE_CLASS_PIMA_OC_GET_NUM_OBJECTS, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_HANDLES, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_INFO, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT, + UX_DEVICE_CLASS_PIMA_OC_GET_THUMB, + UX_DEVICE_CLASS_PIMA_OC_GET_PARTIAL_OBJECT, + UX_DEVICE_CLASS_PIMA_OC_DELETE_OBJECT, + UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT_INFO, + UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT, + UX_DEVICE_CLASS_PIMA_OC_INITIATE_CAPTURE, + UX_DEVICE_CLASS_PIMA_OC_FORMAT_STORE, + UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE, +#ifdef UX_PIMA_WITH_MTP_SUPPORT + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROPS_SUPPORTED, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_DESC, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_VALUE, + UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_PROP_VALUE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_REFERENCES, + UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_REFERENCES, +#endif + 0 + }; + + +/* Define PIMA supported events. The last entry MUST be a zero. The DeviceInfoSet command + will parse this array and compute the number of functions supported and return it to the + host. */ + +USHORT _ux_device_class_pima_supported_events[] = { + + UX_DEVICE_CLASS_PIMA_EC_CANCEL_TRANSACTION, + UX_DEVICE_CLASS_PIMA_EC_OBJECT_ADDED, + UX_DEVICE_CLASS_PIMA_EC_OBJECT_REMOVED, + UX_DEVICE_CLASS_PIMA_EC_STORE_ADDED, + UX_DEVICE_CLASS_PIMA_EC_STORE_REMOVED, + UX_DEVICE_CLASS_PIMA_EC_DEVICE_PROP_CHANGED, + UX_DEVICE_CLASS_PIMA_EC_OBJECT_INFO_CHANGED, + UX_DEVICE_CLASS_PIMA_EC_DEVICE_INFO_CHANGED, + UX_DEVICE_CLASS_PIMA_EC_REQUEST_OBJECT_TRANSFER, + UX_DEVICE_CLASS_PIMA_EC_STORE_FULL, + UX_DEVICE_CLASS_PIMA_EC_DEVICE_RESET, + UX_DEVICE_CLASS_PIMA_EC_STORAGE_INFO_CHANGED, + UX_DEVICE_CLASS_PIMA_EC_CAPTURE_COMPLETE, + UX_DEVICE_CLASS_PIMA_EC_UNREPORTED_STATUS, + 0 + }; + +/* Define PIMA supported device properties. The last entry MUST be a zero. The DeviceInfoSet command + will parse this array and compute the number of functions supported and return it to the + host. For each declared device property, a dataset must be created in the application. + This table is used is the application has not defined any device properties. */ +USHORT _ux_device_class_pima_device_prop_supported[] = { + + UX_DEVICE_CLASS_PIMA_DEV_PROP_UNDEFINED, + UX_DEVICE_CLASS_PIMA_DEV_PROP_BATTERY_LEVEL, + UX_DEVICE_CLASS_PIMA_DEV_PROP_FUNCTIONAL_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_IMAGE_SIZE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_COMPRESSION_SETTING, + UX_DEVICE_CLASS_PIMA_DEV_PROP_WHITE_BALANCE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_RGB_GAIN, + UX_DEVICE_CLASS_PIMA_DEV_PROP_F_NUMBER, + UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCAL_LENGTH, + UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCUS_DISTANCE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCUS_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_METERING_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_FLASH_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_TIME, + UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_PROGRAM_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_INDEX, + UX_DEVICE_CLASS_PIMA_DEV_PROP_EXPOSURE_BIAS_COMPENSATION, + UX_DEVICE_CLASS_PIMA_DEV_PROP_DATE_TIME, + UX_DEVICE_CLASS_PIMA_DEV_PROP_CAPTURE_DELAY, + UX_DEVICE_CLASS_PIMA_DEV_PROP_STILL_CAPTURE_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_CONTRAST, + UX_DEVICE_CLASS_PIMA_DEV_PROP_SHARPNESS, + UX_DEVICE_CLASS_PIMA_DEV_PROP_DIGITAL_ZOOM, + UX_DEVICE_CLASS_PIMA_DEV_PROP_EFFECT_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_BURST_NUMBER, + UX_DEVICE_CLASS_PIMA_DEV_PROP_BURST_INTERVAL, + UX_DEVICE_CLASS_PIMA_DEV_PROP_TIME_LAPSE_NUMBER, + UX_DEVICE_CLASS_PIMA_DEV_PROP_TIME_LAPSE_INTERVAL, + UX_DEVICE_CLASS_PIMA_DEV_PROP_FOCUS_METERING_MODE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_UPLOAD_URL, + UX_DEVICE_CLASS_PIMA_DEV_PROP_ARTIST, + UX_DEVICE_CLASS_PIMA_DEV_PROP_COPYRIGHT_INFO, +#ifdef UX_PIMA_WITH_MTP_SUPPORT + UX_DEVICE_CLASS_PIMA_DEV_PROP_SYNCHRONIZATION_PARTNER, + UX_DEVICE_CLASS_PIMA_DEV_PROP_DEVICE_FRIENDLY_NAME, + UX_DEVICE_CLASS_PIMA_DEV_PROP_VOLUME, + UX_DEVICE_CLASS_PIMA_DEV_PROP_SUPPORTED_FORMATS_ORDERED, + UX_DEVICE_CLASS_PIMA_DEV_PROP_DEVICE_ICON, + UX_DEVICE_CLASS_PIMA_DEV_PROP_PLAYBACK_RATE, + UX_DEVICE_CLASS_PIMA_DEV_PROP_PLAYBACK_OBJECT, + UX_DEVICE_CLASS_PIMA_DEV_PROP_PLAYBACK_CONTAINER, + UX_DEVICE_CLASS_PIMA_DEV_PROP_SESSION_INITIATOR_VERSION_INFO, + UX_DEVICE_CLASS_PIMA_DEV_PROP_PERCEIVED_DEVICE_TYPE, +#endif + 0 + }; + +/* Define PIMA supported capture formats. The last entry MUST be a zero. The DeviceInfoSet command + will parse this array and compute the number of functions supported and return it to the + host. + This table is used is the application has not defined any capture formats. */ +USHORT _ux_device_class_pima_supported_capture_formats[] = { + 0 + }; + +/* Define PIMA supported image formats. The last entry MUST be a zero. The DeviceInfoSet command + will parse this array and compute the number of formats supported and return it to the + host. + This table is used is the application has not defined any capture formats. */ +USHORT _ux_device_class_pima_supported_image_formats[] = { + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED, + UX_DEVICE_CLASS_PIMA_OFC_ASSOCIATION, + UX_DEVICE_CLASS_PIMA_OFC_SCRIPT, + UX_DEVICE_CLASS_PIMA_OFC_EXECUTABLE, + UX_DEVICE_CLASS_PIMA_OFC_TEXT, + UX_DEVICE_CLASS_PIMA_OFC_HTML, + UX_DEVICE_CLASS_PIMA_OFC_DPOF, + UX_DEVICE_CLASS_PIMA_OFC_AIFF, + UX_DEVICE_CLASS_PIMA_OFC_WAV, + UX_DEVICE_CLASS_PIMA_OFC_MP3, + UX_DEVICE_CLASS_PIMA_OFC_AVI, + UX_DEVICE_CLASS_PIMA_OFC_MPEG, + UX_DEVICE_CLASS_PIMA_OFC_ASF, + UX_DEVICE_CLASS_PIMA_OFC_DEFINED, + UX_DEVICE_CLASS_PIMA_OFC_EXIF_JPEG, + UX_DEVICE_CLASS_PIMA_OFC_TIFF_EP, + UX_DEVICE_CLASS_PIMA_OFC_FLASHPIX, + UX_DEVICE_CLASS_PIMA_OFC_BMP, + UX_DEVICE_CLASS_PIMA_OFC_CIFF, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED, + UX_DEVICE_CLASS_PIMA_OFC_GIF, + UX_DEVICE_CLASS_PIMA_OFC_JFIF, + UX_DEVICE_CLASS_PIMA_OFC_CD, + UX_DEVICE_CLASS_PIMA_OFC_PICT, + UX_DEVICE_CLASS_PIMA_OFC_PNG, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED, + UX_DEVICE_CLASS_PIMA_OFC_TIFF, + UX_DEVICE_CLASS_PIMA_OFC_TIFF_IT, + UX_DEVICE_CLASS_PIMA_OFC_JP2, + UX_DEVICE_CLASS_PIMA_OFC_JPX, +#ifdef UX_PIMA_WITH_MTP_SUPPORT + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_FIRMWARE, + UX_DEVICE_CLASS_PIMA_OFC_WINDOWS_IMAGE_FORMAT, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_AUDIO, + UX_DEVICE_CLASS_PIMA_OFC_WMA, + UX_DEVICE_CLASS_PIMA_OFC_OGG, + UX_DEVICE_CLASS_PIMA_OFC_AAC, + UX_DEVICE_CLASS_PIMA_OFC_AUDIBLE, + UX_DEVICE_CLASS_PIMA_OFC_FLAC, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_VIDEO, + UX_DEVICE_CLASS_PIMA_OFC_WMV, + UX_DEVICE_CLASS_PIMA_OFC_MP4_CONTAINER, + UX_DEVICE_CLASS_PIMA_OFC_MP2, + UX_DEVICE_CLASS_PIMA_OFC_3GP_CONTAINER, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_COLLECTION, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MULTIMEDIA_ALBUM, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_IMAGE_ALBUM, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_AUDIO_ALBUM, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_VIDEO_ALBUM, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_AUDIO_AND_VIDEO_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_CONTACT_GROUP, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MESSAGE_FOLDER, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_CHAPTERED_PRODUCTION, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_AUDIO_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_VIDEO_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MEDIACAST, + UX_DEVICE_CLASS_PIMA_OFC_WPL_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_M3U_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_MPL_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_ASX_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_PLS_PLAYLIST, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_DOCUMENT, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_DOCUMENT, + UX_DEVICE_CLASS_PIMA_OFC_XML_DOCUMENT, + UX_DEVICE_CLASS_PIMA_OFC_MICROSOFT_WORD_DOCUMENT, + UX_DEVICE_CLASS_PIMA_OFC_MHT_COMPILED_HTML_DOCUMENT, + UX_DEVICE_CLASS_PIMA_OFC_MICROSOFT_EXCEL_SPREADSHEET, + UX_DEVICE_CLASS_PIMA_OFC_MICROSOFT_POWERPOINT_PRESENTATION, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_MESSAGE, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_MESSAGE, + UX_DEVICE_CLASS_PIMA_OFC_UNDEFINED_CONTACT, + UX_DEVICE_CLASS_PIMA_OFC_ABSTRACT_CONTACT, + UX_DEVICE_CLASS_PIMA_OFC_VCARD2, +#endif + 0 + }; diff --git a/common/usbx_device_classes/src/ux_device_class_pima_deactivate.c b/common/usbx_device_classes/src/ux_device_class_pima_deactivate.c new file mode 100644 index 0000000..63530ef --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_deactivate.c @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the pima class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort Abort all transfers */ +/* */ +/* CALLED BY */ +/* */ +/* PIMA Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_PIMA *pima; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Store the class instance in the container. */ + pima = (UX_SLAVE_CLASS_PIMA *) class -> ux_slave_class_instance; + + /* Terminate the transactions pending on the endpoints. */ + _ux_device_stack_transfer_all_request_abort(pima -> ux_device_class_pima_bulk_in_endpoint, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(pima -> ux_device_class_pima_bulk_out_endpoint, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(pima -> ux_device_class_pima_interrupt_endpoint, UX_TRANSFER_BUS_RESET); + + /* Session is now closed. */ + pima -> ux_device_class_pima_session_id = 0; + + /* If there is a deactivate function call it. */ + if (pima -> ux_device_class_pima_instance_deactivate != UX_NULL) + { + /* Invoke the application. */ + pima -> ux_device_class_pima_instance_deactivate(pima); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_DEACTIVATE, pima, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(pima); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_device_info_send.c b/common/usbx_device_classes/src/ux_device_class_pima_device_info_send.c new file mode 100644 index 0000000..c9decae --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_device_info_send.c @@ -0,0 +1,309 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_device_info_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the device info structure to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 16-bit value */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_string_to_unicode Ascii string to unicode */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_device_info_send(UX_SLAVE_CLASS_PIMA *pima) +{ +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG device_info_length; +UCHAR *device_info; +UCHAR *device_info_pointer; +ULONG array_field_counter; +USHORT *array_pointer; + + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_DEVICE_INFO_SEND, pima, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. We use the transfer request pre-allocated buffer. */ + device_info = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the data container type. */ + _ux_utility_short_put(device_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(device_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_INFO); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(device_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Allocate the device info pointer to the beginning of the dynamic device info field. */ + device_info_pointer = device_info + UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_DESC; + + /* Fill in the standard version field. */ + _ux_utility_short_put(device_info + UX_DEVICE_CLASS_PIMA_DEVICE_INFO_STANDARD_VERSION, + UX_DEVICE_CLASS_PIMA_STANDARD_VERSION); + + /* Fill in the vendor extension ID field. */ + _ux_utility_long_put(device_info + UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_ID, + UX_DEVICE_CLASS_PIMA_VENDOR_EXTENSION_ID); + + /* Fill in the vendor extension version. */ + _ux_utility_short_put(device_info + UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_VERSION, + UX_DEVICE_CLASS_PIMA_EXTENSION_VERSION); + + /* Fill in the vendor extension description. */ + _ux_utility_string_to_unicode(_ux_device_class_pima_vendor_extension_descriptor, device_info + UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_DESC); + + /* Calculate the address of the dynamic area of the device info. */ + device_info_pointer = device_info + UX_DEVICE_CLASS_PIMA_DEVICE_INFO_VENDOR_EXTENSION_DESC; + + /* Update the device info pointer after the Unicode string. */ + device_info_pointer += (ULONG) *(device_info_pointer) * 2 + 1; + + /* Add the functional mode which is set to standard. */ + _ux_utility_short_put(device_info_pointer, UX_DEVICE_CLASS_PIMA_STANDARD_MODE); + + /* Update the device info pointer. */ + device_info_pointer += sizeof(USHORT); + + /* Fill in the supported operations. This table is defined locally. */ + array_field_counter = 0; + while (_ux_device_class_pima_supported_operations[array_field_counter] != 0) + { + /* Add one element. */ + _ux_utility_short_put(device_info_pointer + sizeof(ULONG) + (sizeof(USHORT) * array_field_counter), + _ux_device_class_pima_supported_operations[array_field_counter]); + + /* Next element. */ + array_field_counter++; + + } + + /* Put the length of this variable array at the beginning of the field. */ + _ux_utility_long_put(device_info_pointer, array_field_counter); + + /* Update the device info pointer. */ + device_info_pointer += sizeof(ULONG) + (sizeof(USHORT) * array_field_counter); + + /* Fill in the supported events. This table is defined locally. */ + array_field_counter = 0; + while (_ux_device_class_pima_supported_events[array_field_counter] != 0) + { + /* Add one element. */ + _ux_utility_short_put(device_info_pointer + sizeof(ULONG) + (sizeof(USHORT) * array_field_counter), + _ux_device_class_pima_supported_events[array_field_counter]); + + /* Next element. */ + array_field_counter++; + + } + + /* Put the length of this variable array at the beginning of the field. */ + _ux_utility_long_put(device_info_pointer, array_field_counter); + + /* Update the device info pointer. */ + device_info_pointer += sizeof(ULONG) + (sizeof(USHORT) * array_field_counter); + + /* Fill in the supported properties. This table can be configured by the user. If it is declared NULL, use the local default one. */ + array_pointer = pima -> ux_device_class_pima_device_properties_list; + + /* Check if array defined. */ + if (array_pointer == UX_NULL) + + /* Use local array. */ + array_pointer = _ux_device_class_pima_device_prop_supported; + + /* Reset counter. */ + array_field_counter = 0; + + /* Parse the table. */ + while (*(array_pointer + array_field_counter) != 0) + { + /* Add one element. */ + _ux_utility_short_put(device_info_pointer + sizeof(ULONG) + (sizeof(USHORT) * array_field_counter), + *(array_pointer + array_field_counter)); + + /* Next element. */ + array_field_counter++; + + } + + /* Put the length of this variable array at the beginning of the field. */ + _ux_utility_long_put(device_info_pointer, array_field_counter); + + /* Update the device info pointer. */ + device_info_pointer += sizeof(ULONG) + (sizeof(USHORT) * array_field_counter); + + /* Fill in the supported capture formats. This table can be configured by the user. If it is declared NULL, use the local default one. */ + array_pointer = pima -> ux_device_class_pima_supported_capture_formats_list; + + /* Check if array defined. */ + if (array_pointer == UX_NULL) + + /* Use local array. */ + array_pointer = _ux_device_class_pima_supported_capture_formats; + + /* Reset counter. */ + array_field_counter = 0; + + /* Parse the table. */ + while (*(array_pointer + array_field_counter) != 0) + { + /* Add one element. */ + _ux_utility_short_put(device_info_pointer + sizeof(ULONG) + (sizeof(USHORT) * array_field_counter), + *(array_pointer + array_field_counter)); + + /* Next element. */ + array_field_counter++; + + } + + /* Put the length of this variable array at the beginning of the field. */ + _ux_utility_long_put(device_info_pointer, array_field_counter); + + /* Update the device info pointer. */ + device_info_pointer += sizeof(ULONG) + (sizeof(USHORT) * array_field_counter); + + /* Fill in the supported image formats. This table can be configured by the user. If it is declared NULL, use the local default one. */ + array_pointer = pima -> ux_device_class_pima_supported_image_formats_list; + + /* Check if array defined. */ + if (array_pointer == UX_NULL) + + /* Use local array. */ + array_pointer = _ux_device_class_pima_supported_image_formats; + + /* Reset counter. */ + array_field_counter = 0; + + /* Parse the table. */ + while (*(array_pointer + array_field_counter) != 0) + { + /* Add one element. */ + _ux_utility_short_put(device_info_pointer + sizeof(ULONG) + (sizeof(USHORT) * array_field_counter), + *(array_pointer + array_field_counter)); + + /* Next element. */ + array_field_counter++; + + } + + /* Put the length of this variable array at the beginning of the field. */ + _ux_utility_long_put(device_info_pointer, array_field_counter); + + /* Update the device info pointer. */ + device_info_pointer += sizeof(ULONG) + (sizeof(USHORT) * array_field_counter); + + /* Fill in the manufacturer string. */ + _ux_utility_string_to_unicode(pima -> ux_device_class_pima_manufacturer, device_info_pointer); + + /* Update the device info pointer. */ + device_info_pointer += (ULONG) (*device_info_pointer * 2) + 1; + + /* Fill in the model string. */ + _ux_utility_string_to_unicode(pima -> ux_device_class_pima_model, device_info_pointer); + + /* Update the device info pointer. */ + device_info_pointer += (ULONG) (*device_info_pointer * 2) + 1; + + /* Fill in the device version string. */ + _ux_utility_string_to_unicode(pima -> ux_device_class_pima_device_version, device_info_pointer); + + /* Update the device info pointer. */ + device_info_pointer += (ULONG) (*device_info_pointer * 2) + 1; + + /* Fill in the device serial number. */ + _ux_utility_string_to_unicode(pima -> ux_device_class_pima_serial_number, device_info_pointer); + + /* Update the device info pointer. */ + device_info_pointer += (ULONG) (*device_info_pointer * 2) + 1; + + /* Compute the overall length of the device info structure. */ + device_info_length = (ULONG) ((ALIGN_TYPE) device_info_pointer - (ALIGN_TYPE) device_info); + + /* Set the transfer data pointer. */ + transfer_request -> ux_slave_transfer_request_data_pointer = device_info; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(device_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + device_info_length); + + /* Send a data payload with the device info data set. */ + status = _ux_device_stack_transfer_request(transfer_request, device_info_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_device_prop_desc_get.c b/common/usbx_device_classes/src/ux_device_class_pima_device_prop_desc_get.c new file mode 100644 index 0000000..173e517 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_device_prop_desc_get.c @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_device_prop_desc_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Return an Device Property Description dataset. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* device_property_code Device Property code for */ +/* which the dataset is obtained */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_short_put Put 16-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_device_prop_desc_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG device_property_code) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *pima_data_buffer; +ULONG device_property_desc_dataset_length; +UCHAR *device_property_desc_dataset; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_GET_DEVICE_PROP_DESC_GET, pima, device_property_code, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + pima_data_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the data container type. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_DESC); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Ask the application to retrieve for us the device prop description. */ + status = pima -> ux_device_class_pima_device_prop_desc_get(pima, device_property_code, &device_property_desc_dataset, &device_property_desc_dataset_length); + + /* Result should always be OK, but to be sure .... */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_DEVICE_PROP_NOT_SUPPORTED, 0, 0, 0, 0); + + else + { + + /* Ensure the application's data can fit in the endpoint's data buffer. */ + if (device_property_desc_dataset_length + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE > UX_SLAVE_REQUEST_DATA_MAX_LENGTH) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_GENERAL_ERROR, 0, 0, 0, 0); + + /* Return overflow error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Copy the property dataset into the local buffer. */ + _ux_utility_memory_copy(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, device_property_desc_dataset, device_property_desc_dataset_length); + + /* Add the header size to the payload. */ + device_property_desc_dataset_length += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + device_property_desc_dataset_length); + + /* Send a data payload with the device prop description dataset. */ + status = _ux_device_stack_transfer_request(transfer_request, device_property_desc_dataset_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_get.c b/common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_get.c new file mode 100644 index 0000000..c4a21a4 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_get.c @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_device_prop_value_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Return an Device Property Value. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* device_property_code Device Property code for */ +/* which the value is obtained. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_short_put Put 16-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_device_prop_value_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG device_property_code) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *pima_data_buffer; +ULONG device_property_value_length; +UCHAR *device_property_value; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_GET_DEVICE_PROP_VALUE, pima, device_property_code, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + pima_data_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the data container type. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_VALUE); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Ask the application to retrieve for us the device prop value. */ + status = pima -> ux_device_class_pima_device_prop_value_get(pima, device_property_code, &device_property_value, &device_property_value_length); + + /* Result should always be OK, but to be sure .... */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0); + + else + { + + /* Copy the property dataset into the local buffer. */ + _ux_utility_memory_copy(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, device_property_value, device_property_value_length); + + /* Add the header size to the payload. */ + device_property_value_length += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + device_property_value_length); + + /* Send a data payload with the device prop value. */ + status = _ux_device_stack_transfer_request(transfer_request, device_property_value_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_set.c b/common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_set.c new file mode 100644 index 0000000..702744b --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_device_prop_value_set.c @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_device_prop_value_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function receives an device object value from the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_device_prop_value_set(UX_SLAVE_CLASS_PIMA *pima, ULONG device_property_code) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *device_property_value; +ULONG device_property_value_length; +UCHAR *device_property_value_pointer; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_GET_DEVICE_PROP_VALUE_SET, pima, device_property_code, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + device_property_value = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the data payload. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_PIMA_DEVICE_PROP_VALUE_BUFFER_SIZE, + UX_DEVICE_CLASS_PIMA_DEVICE_PROP_VALUE_BUFFER_SIZE); + + /* Check if there was an error. If so, stall the endpoint. */ + if (status != UX_SUCCESS) + { + + /* Stall the endpoint. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint); + + /* Return the status. */ + return(status); + + } + + /* Allocate the device info pointer to the beginning of the dynamic object info field. */ + device_property_value_pointer = device_property_value + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Obtain the length of the data payload. */ + device_property_value_length = transfer_request -> ux_slave_transfer_request_actual_length; + + /* Ensure there is some data payload. */ + if (device_property_value_length > UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE) + { + + /* Take out the header. */ + device_property_value_length -= UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Send the object to the application. */ + status = pima -> ux_device_class_pima_device_prop_value_set(pima, device_property_code, device_property_value_pointer, device_property_value_length); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + } + else + { + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0); + + /* Status error. */ + status = UX_ERROR; + + } + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_device_reset.c b/common/usbx_device_classes/src/ux_device_class_pima_device_reset.c new file mode 100644 index 0000000..3a9c162 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_device_reset.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_device_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function informs the application that the device needs to */ +/* be reset. This will close the session. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_device_reset(UX_SLAVE_CLASS_PIMA *pima) +{ + +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_DEVICE_RESET, pima, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Invoke the application callback function. */ + status = pima -> ux_device_class_pima_device_reset(pima); + + /* Check for error. */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, status, 0, 0, 0, 0); + + else + { + + /* We return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Session is now closed. */ + pima -> ux_device_class_pima_session_id = 0; + + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_entry.c b/common/usbx_device_classes/src/ux_device_class_pima_entry.c new file mode 100644 index 0000000..380e987 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_entry.c @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_pima_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the pima class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the pima interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_pima_initialize Initialize pima class */ +/* _ux_device_class_pima_activate Activate pima class */ +/* _ux_device_class_pima_deactivate Deactivate pima class */ +/* _ux_device_class_pima_control_request Request control */ +/* */ +/* CALLED BY */ +/* */ +/* PIMA Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the PIMA class. */ + status = _ux_device_class_pima_initialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_PIMA_CLASS) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the pima thread needs to be activated. */ + status = _ux_device_class_pima_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the pima thread canceled. */ + status = _ux_device_class_pima_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + status = _ux_device_class_pima_control_request(command); + + /* Return the completion status. */ + return(status); + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_event_get.c b/common/usbx_device_classes/src/ux_device_class_pima_event_get.c new file mode 100644 index 0000000..5d83d7d --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_event_get.c @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_event_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if there is an event from the application */ +/* */ +/* INPUT */ +/* */ +/* pima Address of pima class */ +/* event Pointer of the event */ +/* */ +/* OUTPUT */ +/* */ +/* status UX_SUCCESS if there is an */ +/* event */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_event_get(UX_SLAVE_CLASS_PIMA *pima, + UX_SLAVE_CLASS_PIMA_EVENT *pima_event) +{ + +UX_SLAVE_CLASS_PIMA_EVENT *current_pima_event; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_EVENT_GET, pima, pima_event, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Check if the head and the tail of the event array is the same. */ + if (pima -> ux_device_class_pima_event_array_head == + pima -> ux_device_class_pima_event_array_tail) + + /* No event to report. */ + return(UX_ERROR); + + /* There is an event to report, get the current pointer to the event. */ + current_pima_event = pima -> ux_device_class_pima_event_array_tail; + + /* fill in the event structure from the user. */ + pima_event -> ux_device_class_pima_event_code = current_pima_event -> ux_device_class_pima_event_code; + pima_event -> ux_device_class_pima_event_session_id = pima -> ux_device_class_pima_session_id; + pima_event -> ux_device_class_pima_event_transaction_id = pima -> ux_device_class_pima_transaction_id; + pima_event -> ux_device_class_pima_event_parameter_1 = current_pima_event -> ux_device_class_pima_event_parameter_1; + pima_event -> ux_device_class_pima_event_parameter_2 = current_pima_event -> ux_device_class_pima_event_parameter_2; + pima_event -> ux_device_class_pima_event_parameter_3 = current_pima_event -> ux_device_class_pima_event_parameter_3; + + + /* Adjust the tail pointer. Check if we are at the end. */ + if ((current_pima_event + 1) == pima -> ux_device_class_pima_event_array_end) + + /* We are at the end, go back to the beginning. */ + pima -> ux_device_class_pima_event_array_tail = pima -> ux_device_class_pima_event_array; + + else + /* We are not at the end, increment the tail position. */ + pima -> ux_device_class_pima_event_array_tail++; + + + /* Return event status to the user. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_event_set.c b/common/usbx_device_classes/src/ux_device_class_pima_event_set.c new file mode 100644 index 0000000..b360979 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_event_set.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_event_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends an event to the pima class. It is processed */ +/* asynchronously by the interrupt thread. */ +/* */ +/* INPUT */ +/* */ +/* pima Address of pima class */ +/* event Pointer of the event */ +/* */ +/* OUTPUT */ +/* */ +/* status UX_SUCCESS if there is an */ +/* event */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_event_set(UX_SLAVE_CLASS_PIMA *pima, + UX_SLAVE_CLASS_PIMA_EVENT *pima_event) +{ + +UX_SLAVE_CLASS_PIMA_EVENT *current_pima_event; +UX_SLAVE_CLASS_PIMA_EVENT *next_pima_event; +UX_SLAVE_DEVICE *device; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_EVENT_SET, pima, pima_event, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Check the device state. */ + if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_HANDLE_UNKNOWN, device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DEVICE_HANDLE_UNKNOWN); + } + + /* Current position of the head. */ + current_pima_event = pima -> ux_device_class_pima_event_array_head; + + /* If the pointer is NULL, the round robin buffer has not been activated. */ + if (current_pima_event == UX_NULL) + return (UX_ERROR); + + /* Calculate the next position. */ + if ((current_pima_event + 1) == pima -> ux_device_class_pima_event_array_end) + + /* We are at the end, go back to the beginning. */ + next_pima_event = pima -> ux_device_class_pima_event_array; + + else + /* We are not at the end, increment the head position. */ + next_pima_event = current_pima_event + 1; + + + /* Any place left for this event ? */ + if (next_pima_event == pima -> ux_device_class_pima_event_array_tail) + return (UX_ERROR); + + /* Update the head. */ + pima -> ux_device_class_pima_event_array_head = next_pima_event; + + /* There is an event to report, get the current pointer to the event. */ + current_pima_event = pima -> ux_device_class_pima_event_array_tail; + + /* fill in the event structure from the user. */ + current_pima_event -> ux_device_class_pima_event_code = pima_event -> ux_device_class_pima_event_code; + current_pima_event -> ux_device_class_pima_event_parameter_1 = pima_event -> ux_device_class_pima_event_parameter_1; + current_pima_event -> ux_device_class_pima_event_parameter_2 = pima_event -> ux_device_class_pima_event_parameter_2; + current_pima_event -> ux_device_class_pima_event_parameter_3 = pima_event -> ux_device_class_pima_event_parameter_3; + + /* Set a semaphore to wake up the interrupt thread. */ + _ux_utility_semaphore_put(&pima -> ux_device_class_pima_interrupt_thread_semaphore); + + /* Return event status to the user. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_initialize.c b/common/usbx_device_classes/src/ux_device_class_pima_initialize.c new file mode 100644 index 0000000..9afd7c4 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_initialize.c @@ -0,0 +1,215 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB Pima device class */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to pima command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_thread_create Create thread */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; +UX_SLAVE_CLASS_PIMA *pima; +UX_SLAVE_CLASS_PIMA_PARAMETER *pima_parameter; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device pima class. */ + pima = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_PIMA)); + + /* Check for successful allocation. */ + if (pima == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save the address of the PIMA instance inside the PIMA container. */ + class -> ux_slave_class_instance = (VOID *) pima; + + /* Allocate some memory for the thread stack. */ + class -> ux_slave_class_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (class -> ux_slave_class_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + status = UX_SUCCESS; + + /* This instance needs to be running in a different thread. So start + a new thread. We pass a pointer to the class to the new thread. This thread + does not start until we have a instance of the class. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&class -> ux_slave_class_thread, "ux_slave_class_thread", + _ux_device_class_pima_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) class -> ux_slave_class_thread_stack, + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + + UX_THREAD_EXTENSION_PTR_SET(&(class -> ux_slave_class_thread), class) + + /* There is error, free resources and return error. */ + if (status != UX_SUCCESS) + { + + /* The last resource, thread is not created or created error, + no need to free. */ + + if (class -> ux_slave_class_thread_stack) + _ux_utility_memory_free(class -> ux_slave_class_thread_stack); + + /* Detach instance and free memory. */ + class -> ux_slave_class_instance = UX_NULL; + _ux_utility_memory_free(pima); + + /* Return completion status. */ + return(status); + } + + /* Success, complete remaining settings. */ + + /* Get the pointer to the application parameters for the pima class. */ + pima_parameter = command -> ux_slave_class_command_parameter; + + /* Store all the application parameter information about the media. */ + pima -> ux_device_class_pima_manufacturer = pima_parameter -> ux_device_class_pima_parameter_manufacturer; + pima -> ux_device_class_pima_model = pima_parameter -> ux_device_class_pima_parameter_model; + pima -> ux_device_class_pima_device_version = pima_parameter -> ux_device_class_pima_parameter_device_version; + pima -> ux_device_class_pima_serial_number = pima_parameter -> ux_device_class_pima_parameter_serial_number; + + /* Store all the application parameter information about the storage. */ + pima -> ux_device_class_pima_storage_id = pima_parameter -> ux_device_class_pima_parameter_storage_id; + pima -> ux_device_class_pima_storage_type = pima_parameter -> ux_device_class_pima_parameter_storage_type; + pima -> ux_device_class_pima_storage_file_system_type = pima_parameter -> ux_device_class_pima_parameter_storage_file_system_type; + pima -> ux_device_class_pima_storage_access_capability = pima_parameter -> ux_device_class_pima_parameter_storage_access_capability; + pima -> ux_device_class_pima_storage_max_capacity_low = pima_parameter -> ux_device_class_pima_parameter_storage_max_capacity_low; + pima -> ux_device_class_pima_storage_max_capacity_high = pima_parameter -> ux_device_class_pima_parameter_storage_max_capacity_high; + pima -> ux_device_class_pima_storage_free_space_low = pima_parameter -> ux_device_class_pima_parameter_storage_free_space_low; + pima -> ux_device_class_pima_storage_free_space_high = pima_parameter -> ux_device_class_pima_parameter_storage_free_space_high; + pima -> ux_device_class_pima_storage_free_space_image = pima_parameter -> ux_device_class_pima_parameter_storage_free_space_image; + pima -> ux_device_class_pima_storage_description = pima_parameter -> ux_device_class_pima_parameter_storage_description; + pima -> ux_device_class_pima_storage_volume_label = pima_parameter -> ux_device_class_pima_parameter_storage_volume_label; + + /* Update device properties supported. */ + pima -> ux_device_class_pima_device_properties_list = pima_parameter -> ux_device_class_pima_parameter_device_properties_list; + + /* Update the capture formats supported list. */ + pima -> ux_device_class_pima_supported_capture_formats_list = pima_parameter -> ux_device_class_pima_parameter_supported_capture_formats_list; + + /* Update the image formats supported list. */ + pima -> ux_device_class_pima_supported_image_formats_list = pima_parameter -> ux_device_class_pima_parameter_supported_image_formats_list; + +#ifdef UX_PIMA_WITH_MTP_SUPPORT + /* Update the internal pima structure with the object properties. */ + pima -> ux_device_class_pima_object_properties_list = pima_parameter -> ux_device_class_pima_parameter_object_properties_list; + +#endif + + /* Store the callback functions for device. */ + pima -> ux_device_class_pima_device_reset = pima_parameter -> ux_device_class_pima_parameter_device_reset; + pima -> ux_device_class_pima_device_prop_desc_get = pima_parameter -> ux_device_class_pima_parameter_device_prop_desc_get; + pima -> ux_device_class_pima_device_prop_value_get = pima_parameter -> ux_device_class_pima_parameter_device_prop_value_get; + pima -> ux_device_class_pima_device_prop_value_set = pima_parameter -> ux_device_class_pima_parameter_device_prop_value_set; + + /* Store the callback functions for storage. */ + pima -> ux_device_class_pima_storage_format = pima_parameter -> ux_device_class_pima_parameter_storage_format; + pima -> ux_device_class_pima_storage_info_get = pima_parameter -> ux_device_class_pima_parameter_storage_info_get; + + /* Store the callback functions for objects. */ + pima -> ux_device_class_pima_object_number_get = pima_parameter -> ux_device_class_pima_parameter_object_number_get; + pima -> ux_device_class_pima_object_handles_get = pima_parameter -> ux_device_class_pima_parameter_object_handles_get; + pima -> ux_device_class_pima_object_info_get = pima_parameter -> ux_device_class_pima_parameter_object_info_get; + pima -> ux_device_class_pima_object_data_get = pima_parameter -> ux_device_class_pima_parameter_object_data_get; + pima -> ux_device_class_pima_object_info_send = pima_parameter -> ux_device_class_pima_parameter_object_info_send; + pima -> ux_device_class_pima_object_data_send = pima_parameter -> ux_device_class_pima_parameter_object_data_send; + pima -> ux_device_class_pima_object_delete = pima_parameter -> ux_device_class_pima_parameter_object_delete; + + +#ifdef UX_PIMA_WITH_MTP_SUPPORT + /* Add the MTP specific callback functions. */ + pima -> ux_device_class_pima_object_prop_desc_get = pima_parameter -> ux_device_class_pima_parameter_object_prop_desc_get; + pima -> ux_device_class_pima_object_prop_value_get = pima_parameter -> ux_device_class_pima_parameter_object_prop_value_get; + pima -> ux_device_class_pima_object_prop_value_set = pima_parameter -> ux_device_class_pima_parameter_object_prop_value_set; + pima -> ux_device_class_pima_object_references_get = pima_parameter -> ux_device_class_pima_parameter_object_references_get; + pima -> ux_device_class_pima_object_references_set = pima_parameter -> ux_device_class_pima_parameter_object_references_set; +#endif + + /* Store the application owner. */ + pima -> ux_device_class_pima_application = pima_parameter -> ux_device_class_pima_parameter_application; + + /* Store the start and stop signals if needed by the application. */ + pima -> ux_device_class_pima_instance_activate = pima_parameter -> ux_device_class_pima_instance_activate; + pima -> ux_device_class_pima_instance_deactivate = pima_parameter -> ux_device_class_pima_instance_deactivate; + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_interrupt_thread.c b/common/usbx_device_classes/src/ux_device_class_pima_interrupt_thread.c new file mode 100644 index 0000000..1f2f1ae --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_interrupt_thread.c @@ -0,0 +1,175 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_interrupt_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the pima interrupt endpoint */ +/* */ +/* INPUT */ +/* */ +/* pima_class Address of pima class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_device_class_pima_event_get Get PIMA event */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 16-bit value */ +/* _ux_utility_thread_suspend Suspend thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_pima_interrupt_thread(ULONG pima_class) +{ + +UX_SLAVE_CLASS_PIMA *pima; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request_in; +UX_SLAVE_CLASS_PIMA_EVENT pima_event; +UINT status; +UCHAR *buffer; + + /* Get the pima instance from the calling parameter. */ + UX_THREAD_EXTENSION_PTR_GET(pima, UX_SLAVE_CLASS_PIMA, pima_class) + + /* Allocate the event round robin buffer. */ + pima -> ux_device_class_pima_event_array = + _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_PIMA_EVENT), UX_DEVICE_CLASS_PIMA_MAX_EVENTS_QUEUE); + + /* Check for successful allocation. */ + if (pima -> ux_device_class_pima_event_array == UX_NULL) + { + /* Return, no event management. */ + return; + } + + /* Allocate the head\tail and end of the round robin buffer. */ + pima -> ux_device_class_pima_event_array_head = pima -> ux_device_class_pima_event_array; + pima -> ux_device_class_pima_event_array_tail = pima -> ux_device_class_pima_event_array; + pima -> ux_device_class_pima_event_array_end = pima -> ux_device_class_pima_event_array + UX_DEVICE_CLASS_PIMA_MAX_EVENTS_QUEUE; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* All PIMA events are on the interrupt endpoint IN, from the host. */ + transfer_request_in = &pima -> ux_device_class_pima_interrupt_endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Wait until something has awaken us. */ + status = _ux_utility_semaphore_get(&pima -> ux_device_class_pima_interrupt_thread_semaphore, UX_WAIT_FOREVER); + + /* Check the completion code. */ + if (status != UX_SUCCESS) + + /* Do not proceed. */ + return; + + /* Check if we have an event to report. */ + status = _ux_device_class_pima_event_get(pima, &pima_event); + + /* We may have an event to report on the interrupt pipe. */ + if(status == UX_SUCCESS) + { + + /* Prepare the event data payload from the pima event structure. Get a pointer to the buffer area. */ + buffer = transfer_request_in -> ux_slave_transfer_request_data_pointer; + + /* Put the length of the entire event payload. */ + _ux_utility_long_put(buffer + UX_DEVICE_CLASS_PIMA_AEI_DATA_LENGTH, UX_DEVICE_CLASS_PIMA_AEI_MAX_LENGTH); + + /* Put the type of packet (Event) */ + _ux_utility_short_put(buffer + UX_DEVICE_CLASS_PIMA_AEI_TYPE, UX_DEVICE_CLASS_PIMA_CT_EVENT_BLOCK); + + /* Put the type of event. */ + _ux_utility_short_put(buffer + UX_DEVICE_CLASS_PIMA_AEI_EVENT_CODE, (USHORT)pima_event.ux_device_class_pima_event_code); + + /* Put the transaction ID. */ + _ux_utility_long_put(buffer + UX_DEVICE_CLASS_PIMA_AEI_TRANSACTION_ID, 0xFFFFFFFF); + + /* Put the value of parameter 1. */ + _ux_utility_long_put(buffer + UX_DEVICE_CLASS_PIMA_AEI_PARAMETER_1, pima_event.ux_device_class_pima_event_parameter_1); + + /* Put the value of parameter 2. */ + _ux_utility_long_put(buffer + UX_DEVICE_CLASS_PIMA_AEI_PARAMETER_2, pima_event.ux_device_class_pima_event_parameter_3); + + /* Put the value of parameter 3. */ + _ux_utility_long_put(buffer + UX_DEVICE_CLASS_PIMA_AEI_PARAMETER_2, pima_event.ux_device_class_pima_event_parameter_3); + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request_in, UX_DEVICE_CLASS_PIMA_AEI_MAX_LENGTH + 1, UX_DEVICE_CLASS_PIMA_AEI_MAX_LENGTH); + + /* Check error code. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&pima -> ux_device_class_pima_interrupt_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_add.c b/common/usbx_device_classes/src/ux_device_class_pima_object_add.c new file mode 100644 index 0000000..cfb604c --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_add.c @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_add PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends an event to the host to inform that an object */ +/* has been added. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_handle Handle of object to delete */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_pima_event_set Add PIMA event to queue */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_add(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle) +{ + +UINT status; +UX_SLAVE_CLASS_PIMA_EVENT pima_event; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_ADD, pima, object_handle, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Set the pima code for object added. */ + pima_event.ux_device_class_pima_event_code = UX_DEVICE_CLASS_PIMA_EC_REQUEST_OBJECT_TRANSFER; + + /* Set the object handle. */ + pima_event.ux_device_class_pima_event_parameter_1 = object_handle; + + /* Other parameters are not used. */ + pima_event.ux_device_class_pima_event_parameter_2 = 0; + pima_event.ux_device_class_pima_event_parameter_3 = 0; + + /* Add the event. */ + status = _ux_device_class_pima_event_set(pima, &pima_event); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_data_get.c b/common/usbx_device_classes/src/ux_device_class_pima_object_data_get.c new file mode 100644 index 0000000..4786563 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_data_get.c @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_data_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the object data to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_data_get(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_CLASS_PIMA_OBJECT *object; +UCHAR *object_data; +ULONG object_offset; +ULONG object_length; +ULONG total_length; +ULONG object_length_demanded; +ULONG object_length_received; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_DATA_GET, pima, object_handle, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the object info from the application. */ + status = pima -> ux_device_class_pima_object_info_get(pima, object_handle, &object); + + /* Check for error. */ + if (status == UX_SUCCESS) + { + + /* Set the object length. */ + object_length = object -> ux_device_class_pima_object_compressed_size; + /* Set the total length to be sent. */ + total_length = object_length + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Reset the offset. */ + object_offset = 0; + + /* Obtain the pointer to the transfer request of the bulk in endpoint. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_data = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the total length to be sent (header + payload. */ + _ux_utility_long_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + total_length); + + /* Fill in the data container type. */ + _ux_utility_short_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Assuming the host will ask for the entire object. */ + while (object_length != 0) + { + + /* If this is the first packet, we have to take into account the + header. */ + if (object_offset == 0) + { + + /* Calculate the maximum length for the first packet. */ + object_length_demanded = UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE - + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Can we get that much from the application ? */ + if (object_length_demanded > object_length) + + /* We ask too much. */ + object_length_demanded = object_length; + + /* Obtain some data from the application. */ + status = pima -> ux_device_class_pima_object_data_get(pima, object_handle, object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, + object_offset, + object_length_demanded, + &object_length_received); + + /* Check status, if we have a problem, we abort. */ + if (status != UX_SUCCESS) + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + } + else + { + /* Adjust the length of the object. */ + object_length -= object_length_received; + + /* Adjust the offset within the object data. */ + object_offset += object_length_received; + + /* Adjust the length to be sent. */ + object_length_received += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + } + + } + else + { + + /* Calculate the maximum length for the first packet. */ + object_length_demanded = UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE; + + /* Can we get that much from the application ? */ + if (object_length_demanded > object_length) + + /* We ask too much. */ + object_length_demanded = object_length; + + /* Obtain some data from the application. */ + status = pima -> ux_device_class_pima_object_data_get(pima, object_handle, object_data, object_offset, + object_length_demanded, + &object_length_received); + + + + /* Check status, if we have a problem, we abort. */ + if (status != UX_SUCCESS) + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + + } + + else + { + + /* Adjust the length of the object. */ + object_length -= object_length_received; + /* Adjust the offset within the object data. */ + object_offset += object_length_received; + + } + + } + + /* Check if it is the last transfer. */ + if (object_length > 0) + + /* Not the last transfer, just send the object data to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, object_length_received, object_length_received); + + else + + /* It's the last transfer, we need to send a ZLP if the last packet is a full packet. */ + status = _ux_device_stack_transfer_request(transfer_request, object_length_received, 0); + + /* Check for the status. We may have had a request to cancel the transaction from the host. */ + if (status != UX_SUCCESS) + { + + /* Check the completion code for transfer abort from the host. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_ABORT) + { + + /* Do not proceed. */ + return(UX_ERROR); + + } + + else + + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + + } + } + + else + + { + + /* Do some sanity check. */ + if (total_length < object_length_received) + { + /* We have an overflow. Do not proceed. */ + status = UX_ERROR; + break; + + } + + + + /* Update the total length to be sent. */ + total_length -= object_length_received; + + } + } + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + } + + /* Check if status is OK. */ + if (status != UX_SUCCESS) + + /* We need to stall the bulk in pipe. This is the method used by Pima devices to + cancel a transaction. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_in_endpoint); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_data_send.c b/common/usbx_device_classes/src/ux_device_class_pima_object_data_send.c new file mode 100644 index 0000000..50472ab --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_data_send.c @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_data_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function accepts an object data from the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_data_send(UX_SLAVE_CLASS_PIMA *pima) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG transfer_length; +UX_SLAVE_CLASS_PIMA_OBJECT *object; +ULONG object_handle; +UCHAR *object_data; +ULONG object_offset; +ULONG object_length; +ULONG total_length; + + /* Get the last object handle. The handle used is the last handle used when he host performed a OBJECT_INFO_SEND. */ + object_handle = pima -> ux_device_class_pima_current_object_handle; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_DATA_SEND, pima, object_handle, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the object info from the application. */ + status = pima -> ux_device_class_pima_object_info_get(pima, object_handle, &object); + + /* Check for error. */ + if (status == UX_SUCCESS) + { + + /* Set the object length. */ + object_length = object -> ux_device_class_pima_object_compressed_size; + + /* Set the total length to be received. */ + total_length = object_length + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Reset the offset. */ + object_offset = 0; + + /* Obtain the pointer to the transfer request of the bulk out endpoint. */ + transfer_request = &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_data = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Assume the host will send all the data. */ + while (total_length != 0) + { + + /* Get a data payload. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE, UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE); + + /* Check for the status. We may have had a request to cancel the transaction from the host. */ + if (status != UX_SUCCESS) + { + + /* Check the completion code for transfer abort from the host. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_ABORT) + { + + /* Do not proceed. */ + return(UX_ERROR); + + } + + else + { + /* We have a transmission error. Do not proceed. */ + status = UX_ERROR; + break; + + } + + } + + /* Obtain the length of the transaction. */ + transfer_length = transfer_request -> ux_slave_transfer_request_actual_length; + + /* If this is the first packet, we have to take into account the + header. */ + if (object_offset == 0) + { + + /* Do some sanity check. */ + if (object_length < (transfer_length - UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE)) + { + /* We have an overflow. Do not proceed. */ + status = UX_ERROR; + break; + + } + + /* Send the object data to the application. */ + status = pima -> ux_device_class_pima_object_data_send(pima, object_handle, UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_ACTIVE, + object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, + object_offset, + (transfer_length - UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE)); + + /* Check status, if we have a problem, we abort. */ + if (status != UX_SUCCESS) + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + } + else + { + + /* Adjust the remaining length of the object. */ + total_length -= transfer_length; + + /* Adjust the length to be sent. */ + object_offset += (transfer_length - UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE); + + } + + } + else + { + + /* Do some sanity check. */ + if (object_length < (transfer_length - UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE)) + { + /* We have an overflow. Do not proceed. */ + status = UX_ERROR; + break; + + } + + /* This is not the first packet, send the object data to the application. */ + status = pima -> ux_device_class_pima_object_data_send(pima, object_handle, UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_ACTIVE, + object_data, + object_offset, + transfer_length); + + /* Check status, if we have a problem, we abort. */ + if (status != UX_SUCCESS) + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + } + else + { + + /* Adjust the remaining length of the total object. */ + total_length -= transfer_length; + + /* Adjust the length to be sent. */ + object_offset += transfer_length; + + } + + } + } + } + + /* Check for status. */ + if (status == UX_SUCCESS) + { + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Make the object transfer completed. */ + status = pima -> ux_device_class_pima_object_data_send(pima, object_handle, UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_COMPLETED, + UX_NULL, 0, 0); + } + else + { + + /* We need to stall the bulk out pipe. This is the method used by Pima devices to + cancel a transaction. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_in_endpoint); + + /* Make the object transfer non completed. */ + status = pima -> ux_device_class_pima_object_data_send(pima, object_handle, UX_DEVICE_CLASS_PIMA_OBJECT_TRANSFER_PHASE_COMPLETED_ERROR, + UX_NULL, 0, 0); + + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_delete.c b/common/usbx_device_classes/src/ux_device_class_pima_object_delete.c new file mode 100644 index 0000000..e5b9519 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_delete.c @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function informs the application that an object is to be */ +/* deleted. The handle points to an object or all objects if -1 */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_handle Handle of object to delete */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_delete(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle) +{ + +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_DELETE, pima, object_handle, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Invoke the application callback function. */ + status = pima -> ux_device_class_pima_object_delete(pima, object_handle); + + /* Check for error. */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_HANDLE, 0, 0, 0, 0); + + else + + /* We return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_handles_send.c b/common/usbx_device_classes/src/ux_device_class_pima_object_handles_send.c new file mode 100644 index 0000000..60ee0f9 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_handles_send.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_handles_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the object handle array to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* storage_id StorageID */ +/* object_format_code Format code filter */ +/* object_association Object Handle Association */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_memory_set Set memory */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_handles_send(UX_SLAVE_CLASS_PIMA *pima, + ULONG storage_id, + ULONG object_format_code, + ULONG object_association) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG object_handles_array_length; +UCHAR *object_handles_array; + + UX_PARAMETER_NOT_USED(storage_id); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_HANDLES_SEND, pima, storage_id, object_format_code, object_association, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_handles_array = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the data container type. */ + _ux_utility_short_put(object_handles_array + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(object_handles_array + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_HANDLES); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(object_handles_array + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Get the array from the application. */ + status = pima -> ux_device_class_pima_object_handles_get(pima, object_format_code, + object_association, (ULONG *) (object_handles_array + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE), + (UX_DEVICE_CLASS_PIMA_ARRAY_BUFFER_SIZE / sizeof(ULONG)) -1); + + /* Result should always be OK, but to be sure .... */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_FORMAT_CODE, 0, 0, 0, 0); + + else + { + + /* Compute the overall length of the handle arrays. */ + object_handles_array_length = (_ux_utility_long_get(object_handles_array + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE) +1) * (ULONG)sizeof(ULONG); + + /* Add the header size. */ + object_handles_array_length += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(object_handles_array + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + object_handles_array_length); + + /* Send a data payload with the object handles array. */ + status = _ux_device_stack_transfer_request(transfer_request, object_handles_array_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_info_get.c b/common/usbx_device_classes/src/ux_device_class_pima_object_info_get.c new file mode 100644 index 0000000..fe6fe48 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_info_get.c @@ -0,0 +1,188 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_info_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the object info structure to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_descriptor_pack Pack descriptor */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_info_get(UX_SLAVE_CLASS_PIMA *pima, ULONG object_handle) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_CLASS_PIMA_OBJECT *object; +ULONG object_info_length; +UCHAR *object_info; +UCHAR *object_info_pointer; +ULONG unicode_string_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_INFO_GET, pima, object_handle, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the object info from the application. */ + status = pima -> ux_device_class_pima_object_info_get(pima, object_handle, &object); + + /* Check for error. */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_HANDLE, 0, 0, 0, 0); + + else + { + + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_info = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the data container type. */ + _ux_utility_short_put(object_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(object_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_INFO); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(object_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Allocate the device info pointer to the beginning of the dynamic object info field. */ + object_info_pointer = object_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* The object info structure coming from the application needs to be packed. */ + _ux_utility_descriptor_pack((UCHAR *) object, + _ux_system_class_pima_object_structure, + UX_DEVICE_CLASS_PIMA_OBJECT_ENTRIES, + object_info_pointer); + + /* Copy the object filename field. Point to the beginning of the object description string. */ + object_info_pointer += UX_DEVICE_CLASS_PIMA_OBJECT_VARIABLE_OFFSET; + + /* Get the unicode string length for the filename. */ + unicode_string_length = ((ULONG) *object -> ux_device_class_pima_object_filename * 2 ) + 1; + + /* Copy that string into the object description field. */ + _ux_utility_memory_copy(object_info_pointer, object -> ux_device_class_pima_object_filename, unicode_string_length); + + /* Point to the next field. */ + object_info_pointer += unicode_string_length; + + /* Get the unicode string length of the capture date. */ + unicode_string_length = ((ULONG) *object -> ux_device_class_pima_object_capture_date *2 ) + 1; + + /* Copy that string into the capture date field. */ + _ux_utility_memory_copy(object_info_pointer, object -> ux_device_class_pima_object_capture_date, unicode_string_length); + + /* Point to the next field. */ + object_info_pointer += unicode_string_length; + + + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object -> ux_device_class_pima_object_modification_date * 2 ) + 1; + + /* Copy that string into the modification date field. */ + _ux_utility_memory_copy(object_info_pointer, object -> ux_device_class_pima_object_modification_date, unicode_string_length); + + /* Point to the next field. */ + object_info_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object -> ux_device_class_pima_object_keywords * 2 ) +1; + + /* Copy that string into the keywords field. */ + _ux_utility_memory_copy(object_info_pointer, object -> ux_device_class_pima_object_keywords, unicode_string_length); + + /* Point to the end of the variable length. */ + object_info_pointer += unicode_string_length; + + /* Compute the overall length of the device info structure. */ + object_info_length = (ULONG) ((ALIGN_TYPE) object_info_pointer - (ALIGN_TYPE) object_info); + + /* Fill in the size of the response header. */ + _ux_utility_long_put(object_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + object_info_length); + + /* Send a data payload with the object info data set. */ + status = _ux_device_stack_transfer_request(transfer_request, object_info_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_info_send.c b/common/usbx_device_classes/src/ux_device_class_pima_object_info_send.c new file mode 100644 index 0000000..1ff436d --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_info_send.c @@ -0,0 +1,244 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_info_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function receives an object info structure from the host */ +/* before receiving the actual data. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_info_send(UX_SLAVE_CLASS_PIMA *pima, ULONG storage_id, ULONG parent_object_handle) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_CLASS_PIMA_OBJECT *object; +UCHAR *object_info; +UCHAR *object_info_pointer; +ULONG unicode_string_length; +ULONG object_handle; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECT_INFO_SEND, pima, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_info = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the data payload. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE, + UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE); + + /* Check if there was an error. If so, stall the endpoint. */ + if (status != UX_SUCCESS) + { + + /* Stall the endpoint. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint); + + /* Return the status. */ + return(status); + + } + + /* Allocate some memory for the object. */ + object = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_PIMA_OBJECT)); + + /* Check for successful allocation. */ + if (object == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Allocate the device info pointer to the beginning of the dynamic object info field. */ + object_info_pointer = object_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Uncompress the object descriptor, at least the fixed part. */ + _ux_utility_descriptor_parse(object_info_pointer, + _ux_system_class_pima_object_structure, + UX_DEVICE_CLASS_PIMA_OBJECT_ENTRIES, + (UCHAR *) object); + + /* Copy the object filename field. Point to the beginning of the object description string. */ + object_info_pointer = object_info_pointer + UX_DEVICE_CLASS_PIMA_OBJECT_VARIABLE_OFFSET; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_info_pointer * 2) + 1; + + /* Ensure there's enough space for this string. */ + if (unicode_string_length > UX_DEVICE_CLASS_PIMA_UNICODE_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the object description field. */ + _ux_utility_memory_copy(object -> ux_device_class_pima_object_filename, object_info_pointer, unicode_string_length); + + /* Point to the next field. */ + object_info_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_info_pointer * 2) + 1; + + /* Ensure there's enough space for this string. */ + if (unicode_string_length > UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the capture date field. */ + _ux_utility_memory_copy(object -> ux_device_class_pima_object_capture_date, object_info_pointer, unicode_string_length); + + /* Point to the next field. */ + object_info_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_info_pointer * 2) + 1; + + /* Ensure there's enough space for this string. */ + if (unicode_string_length > UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the modification date field. */ + _ux_utility_memory_copy(object -> ux_device_class_pima_object_modification_date, object_info_pointer, unicode_string_length); + + /* Point to the next field. */ + object_info_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_info_pointer * 2) + 1; + + /* Ensure there's enough space for this string. */ + if (unicode_string_length > UX_DEVICE_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the keywords field. */ + _ux_utility_memory_copy(object -> ux_device_class_pima_object_keywords, object_info_pointer, unicode_string_length); + + /* Reset the rest of the other parameters. */ + object -> ux_device_class_pima_object_state = 0; + object -> ux_device_class_pima_object_offset = 0; + object -> ux_device_class_pima_object_transfer_status = 0; + object -> ux_device_class_pima_object_handle_id = 0; + object -> ux_device_class_pima_object_length = 0; + + /* Send the object to the application. */ + status = pima -> ux_device_class_pima_object_info_send(pima, object, storage_id, parent_object_handle, &object_handle); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 3, pima -> ux_device_class_pima_storage_id, + parent_object_handle, object_handle); + + /* Store the object handle. It will be used for the OBJECT_SEND command. */ + pima -> ux_device_class_pima_current_object_handle = object_handle; + } + else + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_GENERAL_ERROR, 0, 0, 0, 0); + } + + /* Free the resources. */ + _ux_utility_memory_free(object); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_prop_desc_get.c b/common/usbx_device_classes/src/ux_device_class_pima_object_prop_desc_get.c new file mode 100644 index 0000000..374b1ce --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_prop_desc_get.c @@ -0,0 +1,169 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_prop_desc_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Return an Object Property Description dataset for a format code and */ +/* a specific object property. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_property Object Property */ +/* object_format_code Object format for which the */ +/* properties supported are. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_prop_desc_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_property, + ULONG object_format_code) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *object_prop_dataset; +ULONG object_prop_dataset_length; +UCHAR *object_props_desc; +UCHAR *object_props_desc_end; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_GET_OBJECT_PROP_DESC, pima, object_property, object_format_code, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_props_desc = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Save the end of the object info. */ + object_props_desc_end = object_props_desc + UX_SLAVE_REQUEST_DATA_MAX_LENGTH; + + /* Fill in the data container type. */ + _ux_utility_short_put(object_props_desc + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(object_props_desc + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_DESC); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(object_props_desc + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Call the application to retrieve the property description dataset. */ + status = pima -> ux_device_class_pima_object_prop_desc_get(pima, object_property, object_format_code, &object_prop_dataset, &object_prop_dataset_length); + + /* See if we have the right format code and object property. */ + if (status == UX_SUCCESS) + { + + /* We have found the object property for the format code requested, retrieve the dataset. */ + + /* Do we have enough space? */ + if (object_props_desc + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + object_prop_dataset_length <= object_props_desc_end) + { + + /* Copy the object property array. */ + _ux_utility_memory_copy(object_props_desc + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, + object_prop_dataset, object_prop_dataset_length); + + /* Add the header size. */ + object_prop_dataset_length += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(object_props_desc + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + object_prop_dataset_length); + + /* Send a data payload with the object props array. */ + status = _ux_device_stack_transfer_request(transfer_request, object_prop_dataset_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Return status. */ + return(status); + } + else + { + + /* The dataset is too large for our buffer. */ + + /* Report the error to the application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* Report the error. */ + status = UX_MEMORY_INSUFFICIENT; + } + } + + /* We get here when we did not find the object format code or the dataset was too large. */ + + /* Now we return a response with error code. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OBJECT_PROP_NOT_SUPPORTED, 0, 0, 0, 0); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_get.c b/common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_get.c new file mode 100644 index 0000000..0683fab --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_get.c @@ -0,0 +1,169 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_prop_value_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Return an Object Property Value. The value is fetched by calling the */ +/* application which initialized a call back pointer. The application */ +/* will copy the value at the specified address. The length of the */ +/* value of the object will be returned to compute the length of the */ +/* payload packet. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_handle Object Handle */ +/* object_property_code Object Property code for */ +/* which the value is obtained. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_prop_value_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG object_property_code) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *pima_data_buffer; +UCHAR *pima_data_buffer_end; +UCHAR *object_property_value; +ULONG object_property_value_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_GET_OBJECT_PROP_VALUE, pima, object_handle, object_property_code, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + pima_data_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Save the end of the buffer. */ + pima_data_buffer_end = pima_data_buffer + UX_SLAVE_REQUEST_DATA_MAX_LENGTH; + + /* Fill in the data container type. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_VALUE); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Ask the application to retrieve for us the object prop value. */ + status = pima -> ux_device_class_pima_object_prop_value_get(pima, object_handle, object_property_code, &object_property_value, &object_property_value_length); + + /* Result should always be OK, but to be sure .... */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0); + + else + { + + /* Check if the prop value will fit. */ + if (pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + object_property_value_length <= pima_data_buffer_end) + { + + /* Copy the property dataset into the local buffer. */ + _ux_utility_memory_copy(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, object_property_value, object_property_value_length); + + /* Add the header size to the payload. */ + object_property_value_length += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + object_property_value_length); + + /* Send a data payload with the object props array. */ + status = _ux_device_stack_transfer_request(transfer_request, object_property_value_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + } + else + { + + /* The prop value doesn't fit. */ + + /* Report the error to the application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* Return response code to host. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_GENERAL_ERROR, 0, 0, 0, 0); + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_set.c b/common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_set.c new file mode 100644 index 0000000..0ac6a35 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_prop_value_set.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_prop_value_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Return an Object Property Value. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_handle Object Handle */ +/* object_property_code Object Property code for */ +/* which the value is obtained. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_prop_value_set(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG object_property_code) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *pima_data_buffer; +UCHAR *object_property_value; +ULONG object_property_value_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_SET_OBJECT_PROP_VALUE, pima, object_handle, object_property_code, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + pima_data_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the data payload. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_PIMA_OBJECT_PROP_VALUE_BUFFER_SIZE, + UX_DEVICE_CLASS_PIMA_OBJECT_PROP_VALUE_BUFFER_SIZE); + + /* Check if there was an error. If so, stall the endpoint. */ + if (status != UX_SUCCESS) + { + + /* Stall the endpoint. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint); + + /* Return the status. */ + return(status); + + } + + /* Allocate the device info pointer to the beginning of the dynamic object info field. */ + object_property_value = pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Obtain the length of the data payload. */ + object_property_value_length = transfer_request -> ux_slave_transfer_request_actual_length; + + /* Ensure there is some data payload. */ + if (object_property_value_length > UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE) + { + + /* Take out the header. */ + object_property_value_length -= UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Send the object to the application. */ + status = pima -> ux_device_class_pima_object_prop_value_set(pima, object_handle, object_property_code, object_property_value, object_property_value_length); + + /* Check error code from application. */ + if (status == UX_SUCCESS) + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + else + + /* We return an error. The code is passed by the application. */ + _ux_device_class_pima_response_send(pima, status, 0, 0, 0, 0); + + } + else + { + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0); + + /* Status error. */ + status = UX_ERROR; + + } + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_props_supported_get.c b/common/usbx_device_classes/src/ux_device_class_pima_object_props_supported_get.c new file mode 100644 index 0000000..3b4289b --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_props_supported_get.c @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_prop_supported_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Return an Object Property Code array of supported object properties */ +/* in the first parameter. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_format_code Object format for which the */ +/* properties supported are. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_props_supported_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_format_code) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG object_props_list_items; +ULONG object_props_list_length; +UCHAR *object_props_list; +USHORT *object_props_list_pointer; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECTS_PROPS_SUPPORTED_GET, pima, object_format_code, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_props_list = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the data container type. */ + _ux_utility_short_put(object_props_list + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(object_props_list + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROPS_SUPPORTED); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(object_props_list + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Get the pointer to the list of arrays. */ + object_props_list_pointer = pima -> ux_device_class_pima_object_properties_list; + + /* Find the object format within the object props arrays. */ + while (*object_props_list_pointer != 0) + { + /* See what the format code is. */ + if (*object_props_list_pointer == (USHORT) object_format_code) + { + + /* We have found the format code requested, retrieve the array of props. */ + /* Retrieve the number of properties for this format code. */ + object_props_list_items = (ULONG) *(object_props_list_pointer + 1); + + /* Compute the overall length of the object properties array = number of properties + size of array in USHORT. */ + object_props_list_length = (object_props_list_items * (ULONG)sizeof(USHORT)) + (ULONG)sizeof(ULONG); + + /* Add the header size. */ + object_props_list_length += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(object_props_list + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + object_props_list_length); + + /* Update the destination pointer. */ + object_props_list += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Insert the number of elements in the array. */ + _ux_utility_long_put(object_props_list, object_props_list_items); + + /* Add the length of the array. */ + object_props_list += sizeof(ULONG); + + /* Point the object_props_list_pointer to the props supported. Skip the object format and the number + of properties. */ + object_props_list_pointer += 2; + + /* Create the array in little endian format for transfer to host. */ + while(object_props_list_items--) + { + + /* Insert the object property in little endian. */ + _ux_utility_short_put(object_props_list, *object_props_list_pointer); + + /* Update target pointer. */ + object_props_list += sizeof(SHORT); + + /* Update source pointer. */ + object_props_list_pointer++; + + } + + /* Send a data payload with the object props array. */ + status = _ux_device_stack_transfer_request(transfer_request, object_props_list_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Return status. */ + return(status); + } + else + { + + /* We need to skip this object format and point to the next. */ + /* Retrieve the number of properties for this format code. */ + object_props_list_items = *(object_props_list_pointer + 1); + + /* Add the number of items to skip to the current list pointer. */ + object_props_list_pointer += object_props_list_items + 2; + + } + } + + /* We get here when we did not find the object format code. */ + status = _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_OBJECT_FORMAT_CODE, 0, 0, 0, 0); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_references_get.c b/common/usbx_device_classes/src/ux_device_class_pima_object_references_get.c new file mode 100644 index 0000000..dfe537d --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_references_get.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_references_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Return object references associated with an object handle. */ +/* The pima class will call the application to get the array of */ +/* references. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_handle Object Handle for which */ +/* references are obtained. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_references_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *pima_data_buffer; +UCHAR *pima_data_buffer_end; +UCHAR *object_references; +ULONG object_references_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_GET_OBJECT_REFERENCES, pima, object_handle, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + pima_data_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Save end of buffer. */ + pima_data_buffer_end = pima_data_buffer + UX_SLAVE_REQUEST_DATA_MAX_LENGTH; + + /* Fill in the data container type. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_REFERENCES); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Ask the application to retrieve for us the object references. */ + status = pima -> ux_device_class_pima_object_references_get(pima, object_handle, &object_references, &object_references_length); + + /* Result should always be OK, but to be sure .... */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0); + + else + { + + /* Check if there enough space in our buffer. */ + if (pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + object_references_length <= pima_data_buffer_end) + { + + /* Copy the property dataset into the local buffer. */ + _ux_utility_memory_copy(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, object_references, object_references_length); + + /* Add the header size to the payload. */ + object_references_length += UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + object_references_length); + + /* Send a data payload with the object references array. */ + status = _ux_device_stack_transfer_request(transfer_request, object_references_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + } + else + { + + /* Report the error to the application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* We return an error code to the host. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_GENERAL_ERROR, 0, 0, 0, 0); + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_object_references_set.c b/common/usbx_device_classes/src/ux_device_class_pima_object_references_set.c new file mode 100644 index 0000000..e569dc8 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_object_references_set.c @@ -0,0 +1,145 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_references_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* Set object references associated with an object handle. */ +/* The pima class will call the application to set the array of */ +/* references. */ +/* */ +/* */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* object_handle Object Handle */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_object_references_set(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *pima_data_buffer; +UCHAR *object_references; +ULONG object_references_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_SET_OBJECT_REFERENCES, pima, object_handle, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + pima_data_buffer = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the data payload. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_PIMA_OBJECT_PROP_VALUE_BUFFER_SIZE, + UX_DEVICE_CLASS_PIMA_OBJECT_PROP_VALUE_BUFFER_SIZE); + + /* Check if there was an error. If so, stall the endpoint. */ + if (status != UX_SUCCESS) + { + + /* Stall the endpoint. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint); + + /* Return the status. */ + return(status); + + } + + /* Allocate the device info pointer to the beginning of the dynamic object info field. */ + object_references = pima_data_buffer + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Obtain the length of the data payload. */ + object_references_length = transfer_request -> ux_slave_transfer_request_actual_length; + + /* Ensure there is some data payload. */ + if (object_references_length > UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE) + { + + /* Take out the header. */ + object_references_length -= UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Send the object references to the application. */ + status = pima -> ux_device_class_pima_object_references_set(pima, object_handle, object_references, object_references_length); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + } + else + { + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0); + + /* Status error. */ + status = UX_ERROR; + + } + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_objects_number_send.c b/common/usbx_device_classes/src/ux_device_class_pima_objects_number_send.c new file mode 100644 index 0000000..16a4d1e --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_objects_number_send.c @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_object_number_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the number of objects in the system. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_objects_number_send(UX_SLAVE_CLASS_PIMA *pima, + ULONG storage_id, + ULONG object_format_code, + ULONG object_association) +{ + +UINT status; +ULONG object_number; + + UX_PARAMETER_NOT_USED(storage_id); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_OBJECTS_NUMBER_SEND, pima, storage_id, object_format_code, object_association, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the number of objects from the application. */ + status = pima -> ux_device_class_pima_object_number_get(pima, object_format_code, object_association, &object_number); + + /* Result should always be OK, but to be sure .... */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0); + + else + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 1, object_number, 0, 0); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_partial_object_data_get.c b/common/usbx_device_classes/src/ux_device_class_pima_partial_object_data_get.c new file mode 100644 index 0000000..84a22f5 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_partial_object_data_get.c @@ -0,0 +1,307 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_partial_object_data_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the partial object data to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_partial_object_data_get(UX_SLAVE_CLASS_PIMA *pima, + ULONG object_handle, + ULONG offset_requested, + ULONG length_requested) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_CLASS_PIMA_OBJECT *object; +UCHAR *object_data; +ULONG object_offset; +ULONG object_length; +ULONG total_length; +ULONG object_length_demanded; +ULONG object_length_received; +ULONG object_length_transfer; +ULONG object_bytes_sent; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_PARTIAL_OBJECT_DATA_GET, pima, object_handle, offset_requested, length_requested, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the object info from the application. */ + status = pima -> ux_device_class_pima_object_info_get(pima, object_handle, &object); + + /* Check for error. */ + if (status == UX_SUCCESS) + { + + /* Get the object length. */ + object_length = object -> ux_device_class_pima_object_length; + + /* Check how much to return. */ + if ((object_length - offset_requested) > length_requested) + { + + /* Return the maximum demanded. */ + total_length = length_requested + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Set the number of bytes we will send back. */ + object_bytes_sent = length_requested; + } + + else + { + + /* Return the maximum we can. */ + total_length = (object_length - offset_requested) + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Set the number of bytes we will send back. */ + object_bytes_sent = object_length - offset_requested; + + } + + /* Reset the offset. */ + object_offset = 0; + + /* Obtain the pointer to the transfer request of the bulk in endpoint. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + object_data = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the total length to be sent (header + payload. */ + _ux_utility_long_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + total_length); + + /* Fill in the data container type. */ + _ux_utility_short_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_PARTIAL_OBJECT); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Assuming the host will ask for the entire object. */ + while (total_length != 0) + { + + /* If this is the first packet, we have to take into account the + header. */ + if (object_offset == 0) + { + + /* Calculate the maximum length for the first packet. */ + object_length_demanded = UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE - + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Can we get that much from the application ? */ + if (object_length_demanded > object_bytes_sent) + + /* We ask too much. */ + object_length_demanded = object_bytes_sent; + + /* Obtain some data from the application. */ + status = pima -> ux_device_class_pima_object_data_get(pima, object_handle, object_data + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, + object_offset + offset_requested, + object_length_demanded, + &object_length_received); + + /* Check status, if we have a problem, we abort. */ + if (status != UX_SUCCESS) + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + } + else + { + /* Adjust the length of the object. */ + object_length -= object_length_received; + + /* Adjust the length to be sent. */ + object_length_transfer = object_length_received + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE; + + } + + } + else + { + + /* Calculate the maximum length for the first packet. */ + object_length_demanded = UX_DEVICE_CLASS_PIMA_OBJECT_INFO_BUFFER_SIZE; + + /* Can we get that much from the application ? */ + if (object_length_demanded > total_length) + + /* We ask too much. */ + object_length_demanded = total_length; + + /* Obtain some data from the application. */ + status = pima -> ux_device_class_pima_object_data_get(pima, object_handle, object_data, object_offset + offset_requested, + object_length_demanded, + &object_length_received); + + /* Check status, if we have a problem, we abort. */ + if (status != UX_SUCCESS) + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + + } + + else + { + /* Adjust the length of the object. */ + object_length -= object_length_received; + + /* Adjust the length to be sent. */ + object_length_transfer = object_length_received; + } + + } + + /* Check if it is the last transfer. */ + if (object_length_transfer != total_length) + + /* Not the last transfer, just send the object data to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, object_length_transfer, object_length_transfer); + + else + + /* It's the last transfer, we need to send a ZLP if the last packet is a full packet. */ + status = _ux_device_stack_transfer_request(transfer_request, object_length_transfer, 0); + + /* Check for the status. We may have had a request to cancel the transaction from the host. */ + if (status != UX_SUCCESS) + { + + /* Check the completion code for transfer abort from the host. */ + if (transfer_request -> ux_slave_transfer_request_status == UX_TRANSFER_STATUS_ABORT) + { + + /* Do not proceed. */ + return(UX_ERROR); + + } + + else + + { + + /* We need to inform the host of an error. */ + status = UX_ERROR; + break; + + } + } + + else + + { + + /* Do some sanity check. */ + if (total_length < object_length_received) + { + /* We have an overflow. Do not proceed. */ + status = UX_ERROR; + break; + + } + + /* Adjust the offset within the object data. */ + object_offset += object_length_received; + + /* Update the total length to be sent. */ + total_length -= object_length_transfer; + + } + + } + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 1, object_bytes_sent, 0, 0); + + } + + /* Check if status is OK. */ + if (status != UX_SUCCESS) + + /* We need to stall the bulk in pipe. This is the method used by Pima devices to + cancel a transaction. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_in_endpoint); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_response_send.c b/common/usbx_device_classes/src/ux_device_class_pima_response_send.c new file mode 100644 index 0000000..02ec295 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_response_send.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_response_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a response to a pima command to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* response_code Response code */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_response_send(UX_SLAVE_CLASS_PIMA *pima, ULONG response_code, + ULONG number_parameters, + ULONG pima_parameter_1, + ULONG pima_parameter_2, + ULONG pima_parameter_3) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *response; +ULONG header_size; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_RESPONSE_SEND, pima, response_code, number_parameters, pima_parameter_1, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Calculate the header size. */ + header_size = UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + (ULONG)(sizeof(ULONG) * number_parameters); + + /* Obtain memory for this response. Use the transfer request pre-allocated memory. */ + response = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the size of the response header. */ + _ux_utility_long_put(response + UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_LENGTH, header_size); + + /* Fill in the response container type. */ + _ux_utility_short_put(response + UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_RESPONSE_BLOCK); + + /* Fill in the response code. */ + _ux_utility_short_put(response + UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_CODE, + (USHORT)response_code); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(response + UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Parse each parameter and insert it if needed. */ + switch (number_parameters) + { + + case 3 : + _ux_utility_long_put(response + UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_PARAMETERS + (sizeof(ULONG) * 2), + pima_parameter_3); + /* Intentionally fallthrough to "case 2" */ + /* fall through */ + case 2 : + _ux_utility_long_put(response + UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_PARAMETERS + (sizeof(ULONG) * 1), + pima_parameter_2); + /* Intentionally fallthrough to "case 1" */ + /* fall through */ + case 1 : + _ux_utility_long_put(response + UX_DEVICE_CLASS_PIMA_RESPONSE_HEADER_PARAMETERS , + pima_parameter_1); + /* Intentionally fallthrough to "default" */ + /* fall through */ + default : + break; + + } + + /* Send the response block. */ + status = _ux_device_stack_transfer_request(transfer_request, header_size, 0); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_storage_format.c b/common/usbx_device_classes/src/ux_device_class_pima_storage_format.c new file mode 100644 index 0000000..f9d04a0 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_storage_format.c @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_storage_format PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function informs the application that the storage media */ +/* should be reformatted. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* storage_id Storage ID */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_storage_format(UX_SLAVE_CLASS_PIMA *pima, ULONG storage_id) +{ + +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_STORAGE_FORMAT, pima, storage_id, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Invoke the application callback function. */ + status = pima -> ux_device_class_pima_storage_format(pima, storage_id); + + /* Check for error. */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, status, 0, 0, 0, 0); + + else + + /* We return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_storage_id_send.c b/common/usbx_device_classes/src/ux_device_class_pima_storage_id_send.c new file mode 100644 index 0000000..8ab1ab6 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_storage_id_send.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_storage_id_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the storage id array to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Pima Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_storage_id_send(UX_SLAVE_CLASS_PIMA *pima) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG storage_id_length; +UCHAR *storage_id; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_STORAGE_ID_SEND, pima, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object. Use the transfer request pre-allocated memory. */ + storage_id = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Fill in the data container type. */ + _ux_utility_short_put(storage_id + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(storage_id + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_IDS); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(storage_id + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* We have one element only to report. */ + _ux_utility_long_put(storage_id + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE, UX_DEVICE_CLASS_PIMA_MAX_STORAGE_IDS); + + /* Insert the element ID. */ + _ux_utility_long_put(storage_id + UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + sizeof(ULONG), pima -> ux_device_class_pima_storage_id); + + /* Compute the overall length of the device info structure. */ + storage_id_length = UX_DEVICE_CLASS_PIMA_DATA_HEADER_SIZE + sizeof(ULONG) + (sizeof(ULONG) * UX_DEVICE_CLASS_PIMA_MAX_STORAGE_IDS); + + /* Fill in the size of the response header. */ + _ux_utility_long_put(storage_id + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + storage_id_length); + + /* Send a data payload with the storage id data set. */ + status = _ux_device_stack_transfer_request(transfer_request, storage_id_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_storage_info_get.c b/common/usbx_device_classes/src/ux_device_class_pima_storage_info_get.c new file mode 100644 index 0000000..47581d9 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_storage_info_get.c @@ -0,0 +1,181 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_storage_info_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the storage info structure to the host. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 32-bit value */ +/* _ux_utility_string_to_unicode Ascii string to unicode */ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_pima_storage_info_get(UX_SLAVE_CLASS_PIMA *pima, ULONG storage_id) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG storage_info_length; +UCHAR *storage_info; +UCHAR *storage_info_pointer; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_PIMA_STORAGE_INFO_SEND, pima, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &pima -> ux_device_class_pima_bulk_in_endpoint -> ux_slave_endpoint_transfer_request; + + /* Obtain memory for this object info. Use the transfer request pre-allocated memory. */ + storage_info = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Update the storage information. We get the volatile parameters from the application. */ + status = pima -> ux_device_class_pima_storage_info_get(pima, storage_id); + + /* Check for error. */ + if (status != UX_SUCCESS) + + /* We return an error. */ + _ux_device_class_pima_response_send(pima, status, 0, 0, 0, 0); + + else + { + + /* Fill in the data container type. */ + _ux_utility_short_put(storage_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TYPE, + UX_DEVICE_CLASS_PIMA_CT_DATA_BLOCK); + + /* Fill in the data code. */ + _ux_utility_short_put(storage_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_CODE, + UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_INFO); + + /* Fill in the Transaction ID. */ + _ux_utility_long_put(storage_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_device_class_pima_transaction_id); + + /* Allocate the device info pointer to the beginning of the dynamic storage info field. */ + storage_info_pointer = storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_FREE_STORAGE_DESCRIPTION; + + /* Fill in the storage type. */ + _ux_utility_short_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_TYPE, + (USHORT)pima -> ux_device_class_pima_storage_type); + + /* Fill in the file system type. */ + _ux_utility_short_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_FILE_SYSTEM_TYPE, + (USHORT)pima -> ux_device_class_pima_storage_file_system_type); + + /* Fill in the access capability. */ + _ux_utility_short_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_ACCESS_CAPABILITY, + (USHORT)pima -> ux_device_class_pima_storage_access_capability); + + /* Fill in the low dword of max capacity. */ + _ux_utility_long_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_MAX_CAPACITY_LOW, + pima -> ux_device_class_pima_storage_max_capacity_low); + + /* Fill in the high dword of max capacity. */ + _ux_utility_long_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_MAX_CAPACITY_HIGH, + pima -> ux_device_class_pima_storage_max_capacity_high); + + /* Fill in the low dword of free space. */ + _ux_utility_long_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_FREE_SPACE_LOW, + pima -> ux_device_class_pima_storage_free_space_low); + + /* Fill in the high dword of free space. */ + _ux_utility_long_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_FREE_SPACE_HIGH, + pima -> ux_device_class_pima_storage_free_space_high); + + /* Fill in the free space in image. */ + _ux_utility_long_put(storage_info + UX_DEVICE_CLASS_PIMA_STORAGE_FREE_SPACE_IMAGE, + pima -> ux_device_class_pima_storage_free_space_image); + + /* Fill in the storage description string. */ + _ux_utility_string_to_unicode(pima -> ux_device_class_pima_storage_description, storage_info_pointer); + + /* Update the storage info pointer. */ + storage_info_pointer += (ULONG) (*storage_info_pointer * 2) + 1; + + /* Fill in the volume label string. */ + _ux_utility_string_to_unicode(pima -> ux_device_class_pima_storage_volume_label, storage_info_pointer); + + /* Update the storage info pointer. */ + storage_info_pointer += (ULONG) (*storage_info_pointer* 2) + 1; + + /* Compute the overall length of the storage info structure. */ + storage_info_length = (ULONG) ((ALIGN_TYPE) storage_info_pointer - (ALIGN_TYPE) storage_info); + + /* Fill in the size of the response header. */ + _ux_utility_long_put(storage_info + UX_DEVICE_CLASS_PIMA_DATA_HEADER_LENGTH, + storage_info_length); + + /* Send a data payload with the storage info data set. */ + status = _ux_device_stack_transfer_request(transfer_request, storage_info_length, 0); + + /* Now we return a response with success. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + } + + /* Return completion status. */ + return(status); +} + + diff --git a/common/usbx_device_classes/src/ux_device_class_pima_thread.c b/common/usbx_device_classes/src/ux_device_class_pima_thread.c new file mode 100644 index 0000000..d1a4a19 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_pima_thread.c @@ -0,0 +1,465 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_pima.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_pima_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the pima class. */ +/* */ +/* INPUT */ +/* */ +/* pima_class Address of pima class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_suspend Suspend thread */ +/* _ux_utility_short_get Get 16-bit value */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_device_class_pima_device_info_send */ +/* Send PIMA device info */ +/* _ux_device_class_pima_storage_id_send Send PIMA storage ID */ +/* _ux_device_class_pima_storage_info_get */ +/* Get PIMA storage info get */ +/* _ux_device_class_pima_objects_number_send */ +/* Send number of PIMA objects */ +/* _ux_device_class_pima_object_handles_send */ +/* Send PIMA object handlers */ +/* _ux_device_class_pima_object_info_get Get PIMA object info */ +/* _ux_device_class_pima_object_data_get Get PIMA object data */ +/* _ux_device_class_pima_object_delete Delete PIMA object */ +/* _ux_device_class_pima_object_info_send */ +/* Send PIMA object info */ +/* _ux_device_class_pima_object_data_send */ +/* Send PIMA object data */ +/* _ux_device_class_pima_storage_format Format storage */ +/* _ux_device_class_pima_device_reset Reset device */ +/* _ux_device_class_pima_object_props_supported_get */ +/* Get support PIMA object */ +/* properties */ +/* _ux_device_class_pima_object_prop_desc_get */ +/* Get PIMA object property */ +/* descriptor */ +/* _ux_device_class_pima_object_prop_value_get */ +/* Get PIMA object property value*/ +/* _ux_device_class_pima_object_prop_value_set */ +/* Set PIMA object property value*/ +/* _ux_device_class_pima_object_references_get */ +/* Get PIMA object references */ +/* _ux_device_class_pima_device_prop_desc_get */ +/* Get PIMA device property */ +/* descriptor */ +/* _ux_device_class_pima_device_prop_value_get */ +/* Get PIMA device property value*/ +/* _ux_device_class_pima_device_prop_value_set */ +/* Set PIMA device property value*/ +/* _ux_device_class_pima_response_send Send PIMA response */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_pima_thread(ULONG pima_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_PIMA *pima; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *pima_command; +ULONG pima_command_code; +ULONG pima_parameter_1; +ULONG pima_parameter_2; +ULONG pima_parameter_3; +UINT status; + + + /* Cast properly the pima instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, pima_class) + + /* Get the pima instance from this class container. */ + pima = (UX_SLAVE_CLASS_PIMA *) class -> ux_slave_class_instance; + + /* Allocate some memory for the thread stack. */ + pima -> ux_device_class_pima_interrupt_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (pima -> ux_device_class_pima_interrupt_thread_stack == UX_NULL) + { + + /* Do not proceed. */ + return; + } + + /* Allocate a semaphore to this thread. */ + status = _ux_utility_semaphore_create(&pima -> ux_device_class_pima_interrupt_thread_semaphore, + "ux_device_class_interrupt_thread_semaphore", 0); + + /* Check completion status. */ + if (status != UX_SUCCESS) + { + + /* Do not proceed. */ + return; + } + + /* The Pima device class needs 2 threads, one is activated by default for the command\response and one needs to be + created here for the interrupt pipe event. */ + status = _ux_utility_thread_create(&pima -> ux_device_class_pima_interrupt_thread, "ux_slave_class_thread_pima_interrupt", + _ux_device_class_pima_interrupt_thread, + (ULONG) (ALIGN_TYPE) pima, (VOID *) pima -> ux_device_class_pima_interrupt_thread_stack, + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + { + + /* Do not proceed. */ + return; + } + + UX_THREAD_EXTENSION_PTR_SET(&(pima -> ux_device_class_pima_interrupt_thread), pima) + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* All PIMA commands are on the endpoint OUT, from the host. */ + transfer_request = &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, 64, 64); + + /* Check the status */ + if (status == UX_SUCCESS) + { + + /* Obtain the buffer address containing the PIMA command. */ + pima_command = transfer_request -> ux_slave_transfer_request_data_pointer; + + + /* Check to make sure we have a command block. */ + if (_ux_utility_short_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_TYPE) == UX_DEVICE_CLASS_PIMA_CT_COMMAND_BLOCK) + { + + /* Save the transaction ID. */ + pima -> ux_device_class_pima_transaction_id = _ux_utility_long_get(pima_command + + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID); + + /* Retrieve the command stored in the command block. */ + pima_command_code = _ux_utility_short_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_CODE); + + /* Retrieve the parameter 1. */ + pima_parameter_1 = _ux_utility_long_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1); + + /* Retrieve the parameter 2. */ + pima_parameter_2 = _ux_utility_long_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_2); + + /* Retrieve the parameter 3. */ + pima_parameter_3 = _ux_utility_long_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_3); + + /* We check first if this is a GET_DEVICE_INFO as this is the only command which does not require + a session to be opened. */ + + switch (pima_command_code) + { + + case UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_INFO : + + /* Return the device info to the host. */ + status = _ux_device_class_pima_device_info_send(pima); + break; + + + case UX_DEVICE_CLASS_PIMA_OC_OPEN_SESSION : + + /* Check if session is already opened. */ + if (pima -> ux_device_class_pima_session_id == 0) + { + + /* Session can be opened. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Store the session number. */ + pima -> ux_device_class_pima_session_id = pima_parameter_1; + + break; + + + default : + + + /* Check if a session is opened. */ + if (pima -> ux_device_class_pima_session_id == 0) + { + + /* We cannot proceed since the session is not opened. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_SESSION_NOT_OPEN, 0, 0, 0, 0); + + } + + else + { + + /* Analyze the command stored in the command block. */ + switch (pima_command_code) + { + + case UX_DEVICE_CLASS_PIMA_OC_CLOSE_SESSION : + + /* We close the session. Return OK. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0); + + /* Session is now closed. */ + pima -> ux_device_class_pima_session_id = 0; + + break; + case UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_IDS : + + /* Return the array of storage IDs to the host. In this version, we support + only one storage media. */ + status = _ux_device_class_pima_storage_id_send(pima); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_INFO : + + /* Return the storage info to the host. */ + status = _ux_device_class_pima_storage_info_get(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_NUM_OBJECTS : + + /* Return the number of objects found in the system. */ + status = _ux_device_class_pima_objects_number_send(pima, + pima_parameter_1, pima_parameter_2, pima_parameter_3); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_HANDLES : + + /* Return the object handles found in the system. */ + status = _ux_device_class_pima_object_handles_send(pima, + pima_parameter_1, pima_parameter_2, pima_parameter_3); + + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_INFO : + + /* Return the object info data set. */ + status = _ux_device_class_pima_object_info_get(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT : + + /* Return the object data. */ + status = _ux_device_class_pima_object_data_get(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_DELETE_OBJECT : + + /* Delete one or more objects. */ + status = _ux_device_class_pima_object_delete(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT_INFO : + + /* Accept an object info data set. */ + status = _ux_device_class_pima_object_info_send(pima, pima_parameter_1, pima_parameter_2); + break; + + case UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT : + /* Accept the object data. */ + status = _ux_device_class_pima_object_data_send(pima); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_PARTIAL_OBJECT : + + /* Return the partial object data. */ + status = _ux_device_class_pima_partial_object_data_get(pima, pima_parameter_1, pima_parameter_2, pima_parameter_3); + break; + + case UX_DEVICE_CLASS_PIMA_OC_FORMAT_STORE : + + /* Format the storage device. This calls the application to reset all object handles stored + on the media. */ + status = _ux_device_class_pima_storage_format(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE : + + /* Reset the device. This calls the application to reset the device. The session is closed + but all objects retain their properties. */ + status = _ux_device_class_pima_device_reset(pima); + break; + + + case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROPS_SUPPORTED : + + /* Return an Object Property Code array of supported object properties for the object format that is indicated + in the first parameter. */ + status = _ux_device_class_pima_object_props_supported_get(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_DESC : + + /* Returns the appropriate property that describes the dataset that is indicated in the first parameter. */ + status = _ux_device_class_pima_object_prop_desc_get(pima, pima_parameter_1, pima_parameter_2); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_VALUE : + + /* Returns the Object property value. */ + status = _ux_device_class_pima_object_prop_value_get(pima, pima_parameter_1, pima_parameter_2); + break; + + case UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_PROP_VALUE : + + /* Sets the current value of the object property. */ + status = _ux_device_class_pima_object_prop_value_set(pima, pima_parameter_1, pima_parameter_2); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_REFERENCES : + + /* Returns the object handle references. */ + status = _ux_device_class_pima_object_references_get(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_REFERENCES : + + /* Set the object handle references. */ + status = _ux_device_class_pima_object_references_set(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_DESC : + + /* Returns the appropriate device property. */ + status = _ux_device_class_pima_device_prop_desc_get(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_VALUE : + + /* Returns the device property value. */ + status = _ux_device_class_pima_device_prop_value_get(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_SET_DEVICE_PROP_VALUE : + + /* Sets the current value of the device property. */ + status = _ux_device_class_pima_device_prop_value_set(pima, pima_parameter_1); + break; + + case UX_DEVICE_CLASS_PIMA_OC_INITIATE_OPEN_CAPTURE : + case UX_DEVICE_CLASS_PIMA_OC_GET_THUMB : + case UX_DEVICE_CLASS_PIMA_OC_INITIATE_CAPTURE : + case UX_DEVICE_CLASS_PIMA_OC_SELF_TEST : + case UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_PROTECTION : + case UX_DEVICE_CLASS_PIMA_OC_POWER_DOWN : + case UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE_PROP_VALUE : + case UX_DEVICE_CLASS_PIMA_OC_TERMINATE_OPEN_CAPTURE : + case UX_DEVICE_CLASS_PIMA_OC_MOVE_OBJECT : + case UX_DEVICE_CLASS_PIMA_OC_COPY_OBJECT : + + /* Functions not yet supported. */ + _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OPERATION_NOT_SUPPORTED, 0, 0, 0, 0); + + /* Set error code. */ + status = UX_FUNCTION_NOT_SUPPORTED; + + break; + + default: + + /* The command is unknown, so we stall the endpoint. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint); + } + + /* Check error code. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + } + } + } + + /* Check error code. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + else + + /* We have a wrong buffer format. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint); + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&class -> ux_slave_class_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_activate.c b/common/usbx_device_classes/src/ux_device_class_rndis_activate.c new file mode 100644 index 0000000..31c0c17 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_activate.c @@ -0,0 +1,208 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates the USB RNDIS device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to rndis command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_network_driver_activate Activate NetX USB interface */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_thread_resume Resume thread */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_RNDIS *rndis; +UX_SLAVE_CLASS *class; +UX_SLAVE_ENDPOINT *endpoint; +ULONG physical_address_msw; +ULONG physical_address_lsw; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + rndis = (UX_SLAVE_CLASS_RNDIS *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Check if this is the Control or Data interface. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_RNDIS_CLASS_COMMUNICATION_CONTROL) + { + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)rndis; + + /* Now the opposite, store the interface in the class instance. */ + rndis -> ux_slave_class_rndis_interface = interface; + + /* If there is a activate function call it. */ + if (rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_activate != UX_NULL) + + /* Invoke the application. */ + rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_activate(rndis); + } + else + + /* This is the DATA Class, only store the rndis instance in the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)rndis; + + /* Locate the endpoints. Interrupt for Control and Bulk in/out for Data. */ + endpoint = interface -> ux_slave_interface_first_endpoint; + + /* Parse all endpoints. */ + while (endpoint != UX_NULL) + { + + /* Check the endpoint direction, and type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) + { + + /* Look at type. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT) + + /* We have found the interrupt endpoint, save it. */ + rndis -> ux_slave_class_rndis_interrupt_endpoint = endpoint; + + else + + /* We have found the bulk in endpoint, save it. */ + rndis -> ux_slave_class_rndis_bulkin_endpoint = endpoint; + + } + else + { + /* Look at type for out endpoint. */ + if ((endpoint -> ux_slave_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT) + + /* We have found the bulk out endpoint, save it. */ + rndis -> ux_slave_class_rndis_bulkout_endpoint = endpoint; + } + + /* Next endpoint. */ + endpoint = endpoint -> ux_slave_endpoint_next_endpoint; + } + + /* Check if this is the Control or Data interface. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_RNDIS_CLASS_COMMUNICATION_DATA) + { + + /* Now check if all endpoints have been found. */ + if (rndis -> ux_slave_class_rndis_bulkout_endpoint == UX_NULL || rndis -> ux_slave_class_rndis_bulkin_endpoint == UX_NULL || + rndis -> ux_slave_class_rndis_interrupt_endpoint == UX_NULL) + + /* Not all endpoints have been found. Major error, do not proceed. */ + return(UX_ERROR); + + /* Declare the link to be up. That may need to change later to make it dependent on the + WAN/Wireless modem. */ + rndis -> ux_slave_class_rndis_link_state = UX_DEVICE_CLASS_RNDIS_LINK_STATE_UP; + + /* Setup the physical address of this IP instance. */ + physical_address_msw = (ULONG)((rndis -> ux_slave_class_rndis_local_node_id[0] << 8) | (rndis -> ux_slave_class_rndis_local_node_id[1])); + physical_address_lsw = (ULONG)((rndis -> ux_slave_class_rndis_local_node_id[2] << 24) | (rndis -> ux_slave_class_rndis_local_node_id[3] << 16) | + (rndis -> ux_slave_class_rndis_local_node_id[4] << 8) | (rndis -> ux_slave_class_rndis_local_node_id[5])); + + /* Register this interface to the NetX USB interface broker. */ + _ux_network_driver_activate((VOID *) rndis, _ux_device_class_rndis_write, + &rndis -> ux_slave_class_rndis_network_handle, + physical_address_msw, + physical_address_lsw); + + /* Reset the endpoint buffers. */ + _ux_utility_memory_set(rndis -> ux_slave_class_rndis_bulkout_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + _ux_utility_memory_set(rndis -> ux_slave_class_rndis_bulkin_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + _ux_utility_memory_set(rndis -> ux_slave_class_rndis_interrupt_endpoint -> ux_slave_endpoint_transfer_request. + ux_slave_transfer_request_data_pointer, 0, UX_SLAVE_REQUEST_DATA_MAX_LENGTH); + + /* Resume the endpoint threads. */ + _ux_utility_thread_resume(&rndis -> ux_slave_class_rndis_interrupt_thread); + _ux_utility_thread_resume(&rndis -> ux_slave_class_rndis_bulkout_thread); + _ux_utility_thread_resume(&rndis -> ux_slave_class_rndis_bulkin_thread); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_ACTIVATE, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, rndis, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); + + } + else + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_ACTIVATE, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, rndis, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_bulkin_thread.c b/common/usbx_device_classes/src/ux_device_class_rndis_bulkin_thread.c new file mode 100644 index 0000000..2347b0c --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_bulkin_thread.c @@ -0,0 +1,223 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_bulkin_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the rndis bulkin endpoint. The bulk */ +/* IN endpoint is used when the device wants to write data to be sent */ +/* to the host. */ +/* */ +/* INPUT */ +/* */ +/* rndis_class Address of rndis class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_event_flags_get Get event flags */ +/* _ux_utility_mutex_on Take mutex */ +/* _ux_utility_mutex_off Release mutex */ +/* _ux_utility_long_put Put 32-bit value */ +/* nx_packet_transmit_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_rndis_bulkin_thread(ULONG rndis_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_RNDIS *rndis; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +ULONG actual_flags; +NX_PACKET *current_packet; +UCHAR *packet_header; +ULONG transfer_length; + + /* Cast properly the rndis instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, rndis_class) + + /* Get the rndis instance from this class container. */ + rndis = (UX_SLAVE_CLASS_RNDIS *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* Get the transfer request for the bulk IN pip. */ + transfer_request = &rndis -> ux_slave_class_rndis_bulkin_endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Wait until we have a event sent by the application. We do not treat yet the case where a timeout based + on the interrupt pipe frequency or a change in the idle state forces us to send an empty report. */ + status = _ux_utility_event_flags_get(&rndis -> ux_slave_class_rndis_event_flags_group, (UX_DEVICE_CLASS_RNDIS_NEW_BULKIN_EVENT | + UX_DEVICE_CLASS_RNDIS_NEW_DEVICE_STATE_CHANGE_EVENT), + TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); + + /* Check the completion code and the actual flags returned. */ + if (status == UX_SUCCESS && (actual_flags & UX_DEVICE_CLASS_RNDIS_NEW_DEVICE_STATE_CHANGE_EVENT) == 0) + { + + /* Parse all packets. */ + while(rndis -> ux_slave_class_rndis_xmit_queue != UX_NULL) + { + + /* Protect this thread. */ + _ux_utility_mutex_on(&rndis -> ux_slave_class_rndis_mutex); + + /* Get the current packet in the list. */ + current_packet = rndis -> ux_slave_class_rndis_xmit_queue; + + /* Set the next packet (or a NULL value) as the head of the xmit queue. */ + rndis -> ux_slave_class_rndis_xmit_queue = current_packet -> nx_packet_queue_next; + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&rndis -> ux_slave_class_rndis_mutex); + + /* If the link is down no need to rearm a packet. */ + if (rndis -> ux_slave_class_rndis_link_state == UX_DEVICE_CLASS_RNDIS_LINK_STATE_UP) + { + + /* Load the address of the current packet header at the physical header. */ + packet_header = current_packet -> nx_packet_prepend_ptr; + + /* Calculate the transfer length. */ + transfer_length = current_packet -> nx_packet_length + UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH; + + /* Is there enough space for this packet in the transfer buffer? */ + if (transfer_length <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH) + { + + /* Copy the packet in the transfer descriptor buffer. */ + _ux_utility_memory_copy (transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH, packet_header, current_packet -> nx_packet_length); + + /* Add the RNDIS header to this packet. */ + _ux_utility_long_put(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_MESSAGE_TYPE, UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_MSG); + _ux_utility_long_put(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_MESSAGE_LENGTH, transfer_length); + _ux_utility_long_put(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_DATA_OFFSET, + UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH - UX_DEVICE_CLASS_RNDIS_PACKET_DATA_OFFSET); + _ux_utility_long_put(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_DATA_LENGTH, current_packet -> nx_packet_length); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_PACKET_TRANSMIT, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, transfer_length, UX_DEVICE_CLASS_RNDIS_ETHERNET_PACKET_SIZE); + + /* Check for error. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + else + { + + /* No, there is not enough space. */ + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + } + } + + /* Free the packet that was just sent. First do some housekeeping. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_DEVICE_CLASS_RNDIS_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_DEVICE_CLASS_RNDIS_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + } + } + else + { + + /* We get here when the link is down or the last transmission failed. All packets pending must be freed. */ + while(rndis -> ux_slave_class_rndis_xmit_queue != UX_NULL) + { + + /* Protect the chain of packets. */ + _ux_utility_mutex_on(&rndis -> ux_slave_class_rndis_mutex); + + /* Get the current packet in the list. */ + current_packet = rndis -> ux_slave_class_rndis_xmit_queue; + + /* Set the next packet (or a NULL value) as the head of the xmit queue. */ + rndis -> ux_slave_class_rndis_xmit_queue = current_packet -> nx_packet_queue_next; + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&rndis -> ux_slave_class_rndis_mutex); + + /* Free the packet */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_DEVICE_CLASS_RNDIS_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_DEVICE_CLASS_RNDIS_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + } + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&rndis -> ux_slave_class_rndis_bulkin_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_bulkout_thread.c b/common/usbx_device_classes/src/ux_device_class_rndis_bulkout_thread.c new file mode 100644 index 0000000..9bd016a --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_bulkout_thread.c @@ -0,0 +1,210 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_bulkout_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the rndis bulk out endpoint. It */ +/* is waiting for the host to send data on the bulk out endpoint to */ +/* the device. */ +/* */ +/* INPUT */ +/* */ +/* rndis_class Address of rndis class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_network_driver_packet_received Process received packet */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_thread_suspend Suspend thread */ +/* nx_packet_allocate Allocate NetX packet */ +/* nx_packet_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_rndis_bulkout_thread(ULONG rndis_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_RNDIS *rndis; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +NX_PACKET *packet; +ULONG ip_given_length; +ULONG packet_payload; + + /* Cast properly the rndis instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, rndis_class) + + /* Get the rndis instance from this class container. */ + rndis = (UX_SLAVE_CLASS_RNDIS *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* Select the transfer request associated with BULK OUT endpoint. */ + transfer_request = &rndis -> ux_slave_class_rndis_bulkout_endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* We can accept new reception. Get a NX Packet. */ + status = nx_packet_allocate(&rndis -> ux_slave_class_rndis_packet_pool, &packet, + NX_RECEIVE_PACKET, MS_TO_TICK(UX_DEVICE_CLASS_RNDIS_PACKET_POOL_WAIT)); + + if (status == NX_SUCCESS) + { + + /* And length. */ + transfer_request -> ux_slave_transfer_request_requested_length = UX_DEVICE_CLASS_RNDIS_MAX_PACKET_LENGTH; + transfer_request -> ux_slave_transfer_request_actual_length = 0; + + /* Memorize this packet at the beginning of the queue. */ + rndis -> ux_slave_class_rndis_receive_queue = packet; + + /* Reset the queue pointer of this packet. */ + packet -> nx_packet_queue_next = UX_NULL; + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_RNDIS_MAX_PACKET_LENGTH + UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH, + UX_DEVICE_CLASS_RNDIS_MAX_PACKET_LENGTH + UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH); + + /* Check the completion code. */ + if (status == UX_SUCCESS) + { + + /* We only proceed with packets that are received OK, if error, ignore the packet. */ + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_PACKET_RECEIVE, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. Also ensure the header has a valid ID of 1. */ + if (_ux_utility_long_get(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_MESSAGE_TYPE) == UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_MSG) + { + + /* Ensure this packet is at least larger than the header. */ + if (transfer_request -> ux_slave_transfer_request_actual_length > UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH) + { + + /* Get the size of the payload. */ + packet_payload = _ux_utility_long_get(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_DATA_LENGTH); + + /* Ensure the length reported in the RNDIS header is not larger than it actually is. + The reason we can't check to see if the length reported in the header and the + actual length are exactly equal is because there might other data after the payload + (padding, or even a message). */ + if (packet_payload <= transfer_request -> ux_slave_transfer_request_actual_length - UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH) + { + + /* Adjust the prepend pointer to take into account the non 3 bit alignment of the ethernet header. */ + packet -> nx_packet_prepend_ptr += sizeof(USHORT); + + /* Adjust the prepend, length, and append fields. */ + packet -> nx_packet_length = packet_payload; + packet -> nx_packet_append_ptr = packet -> nx_packet_prepend_ptr + packet_payload; + + + /* Copy the received packet in the IP packet data area. */ + _ux_utility_memory_copy(packet -> nx_packet_prepend_ptr, transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_BUFFER, packet_payload); + + /* Calculate the accurate packet length from ip header */ + if((*(packet -> nx_packet_prepend_ptr + 12) == 0x08) && + (*(packet -> nx_packet_prepend_ptr + 13) == 0)) + { + + ip_given_length = _ux_utility_short_get_big_endian(packet -> nx_packet_prepend_ptr + 16) + UX_DEVICE_CLASS_RNDIS_ETHERNET_SIZE; + packet -> nx_packet_length = ip_given_length ; + packet -> nx_packet_append_ptr = packet -> nx_packet_prepend_ptr + ip_given_length; + } + + /* Send that packet to the NetX USB broker. */ + _ux_network_driver_packet_received(rndis -> ux_slave_class_rndis_network_handle, packet); + } + else + + /* We received a malformed packet. Report to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR); + } + else + + /* We received a malformed packet. Report to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR); + } + } + else + + /* Free the packet that was not successfully received. */ + nx_packet_release(packet); + } + else + { + + /* Packet allocation timed out. Note that the timeout value is + configurable. */ + + /* Error trap. No need for trace, since NetX does it. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&rndis -> ux_slave_class_rndis_bulkout_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_control_request.c b/common/usbx_device_classes/src/ux_device_class_rndis_control_request.c new file mode 100644 index 0000000..b7ed5b1 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_control_request.c @@ -0,0 +1,200 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* rndis Pointer to rndis class */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_endpoint_stall Endpoint stall */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_event_flags_set Set event flags */ +/* _ux_device_class_rndis_msg_initialize Command initialize */ +/* _ux_device_class_rndis_msg_query Command query */ +/* _ux_device_class_rndis_msg_set Command set */ +/* _ux_device_class_rndis_msg_reset Command reset */ +/* _ux_device_class_rndis_msg_keep_alive Command keep alive */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_TRANSFER *transfer_request_in; +UX_SLAVE_DEVICE *device; +ULONG request; +ULONG request_length; +ULONG rndis_command; +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_RNDIS *rndis; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Extract all necessary fields of the request. */ + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + request_length = _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the rndis instance from this class container. */ + rndis = (UX_SLAVE_CLASS_RNDIS *) class -> ux_slave_class_instance; + + /* Here we proceed only the standard request we know of at the device level. */ + switch (request) + { + + + case UX_DEVICE_CLASS_RNDIS_SEND_ENCAPSULATED_COMMAND : + + + /* We have received a command. Check if the command is valid and dispatch it. */ + rndis_command = _ux_utility_long_get(transfer_request -> ux_slave_transfer_request_data_pointer); + + switch (rndis_command) + { + + case UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE : + + _ux_device_class_rndis_msg_initialize(rndis, transfer_request); + break; + + case UX_DEVICE_CLASS_RNDIS_MSG_HALT : + break; + + + case UX_DEVICE_CLASS_RNDIS_MSG_QUERY : + _ux_device_class_rndis_msg_query(rndis, transfer_request); + break; + + + case UX_DEVICE_CLASS_RNDIS_MSG_SET : + _ux_device_class_rndis_msg_set(rndis, transfer_request); + break; + + + case UX_DEVICE_CLASS_RNDIS_MSG_RESET : + _ux_device_class_rndis_msg_reset(rndis, transfer_request); + break; + + + case UX_DEVICE_CLASS_RNDIS_MSG_INDICATE_STATUS : + break; + + + case UX_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE : + _ux_device_class_rndis_msg_keep_alive(rndis, transfer_request); + break; + + default : + + /* Unknown function. Stall the endpoint. */ + _ux_device_stack_endpoint_stall(&device -> ux_slave_device_control_endpoint); + break; + + } + + /* Check the return status. If no error, we set the interrupt pipe to reply response available. + All RNDIS events are on the interrupt endpoint IN, from the host. */ + transfer_request_in = &rndis -> ux_slave_class_rndis_interrupt_endpoint -> ux_slave_endpoint_transfer_request; + + /* Reset the buffer. */ + _ux_utility_memory_set(transfer_request_in -> ux_slave_transfer_request_data_pointer, 0, UX_DEVICE_CLASS_RNDIS_INTERRUPT_RESPONSE_LENGTH); + + /* Set the buffer of this transfer request with the flag for response available. */ + transfer_request_in -> ux_slave_transfer_request_data_pointer[0] = UX_DEVICE_CLASS_RNDIS_INTERRUPT_RESPONSE_AVAILABLE_FLAG; + + /* Set an event to wake up the interrupt thread. */ + _ux_utility_event_flags_set(&rndis -> ux_slave_class_rndis_event_flags_group, UX_DEVICE_CLASS_RNDIS_NEW_INTERRUPT_EVENT, TX_OR); + + break; + + case UX_DEVICE_CLASS_RNDIS_GET_ENCAPSULATED_RESPONSE : + + + /* Copy the response into the request data buffer. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, rndis -> ux_slave_class_rndis_response, + rndis -> ux_slave_class_rndis_response_length); + + /* Set the phase of the transfer to data out. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* We can return the RNDIS response. */ + _ux_device_stack_transfer_request(transfer_request, rndis -> ux_slave_class_rndis_response_length, request_length); + + break ; + + default: + + /* Unknown function. It's not handled. */ + return(UX_ERROR); + } + + /* It's handled. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_deactivate.c b/common/usbx_device_classes/src/ux_device_class_rndis_deactivate.c new file mode 100644 index 0000000..c47748c --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_deactivate.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the rndis class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort */ +/* Abort all transfers */ +/* _ux_utility_event_flags_set Set event flags */ +/* _ux_network_driver_deactivate Deactivate NetX USB interface */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_RNDIS *rndis; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + rndis = (UX_SLAVE_CLASS_RNDIS *) class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. Normally the interface can be derived + from the class instance but since RNDIS has 2 interfaces and we only store the Control + interface in the class container, we used the class_command pointer to retrieve the + correct interface which issued the deactivation. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Check if this is the Control or Data interface. We only need to dismount the link and abort the + transfer once for the 2 classes. */ + if (interface -> ux_slave_interface_descriptor.bInterfaceClass == UX_DEVICE_CLASS_RNDIS_CLASS_COMMUNICATION_CONTROL) + { + + /* Declare the link to be down. That may nd to change later to make it dependant on the + WAN/Wireless modem. */ + rndis -> ux_slave_class_rndis_link_state = UX_DEVICE_CLASS_RNDIS_LINK_STATE_DOWN; + + /* Terminate the transactions pending on the endpoints (interrupt, bulk in, bulk out). */ + _ux_device_stack_transfer_all_request_abort(rndis -> ux_slave_class_rndis_interrupt_endpoint, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(rndis -> ux_slave_class_rndis_bulkin_endpoint, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(rndis -> ux_slave_class_rndis_bulkout_endpoint, UX_TRANSFER_BUS_RESET); + + /* We have 2 threads waiting for an event, interrupt and bulk in. We wake them up with + a DEVICE_STATE_CHANGE event. In turn they will release the NetX resources used and suspend. */ + _ux_utility_event_flags_set(&rndis -> ux_slave_class_rndis_event_flags_group, UX_DEVICE_CLASS_RNDIS_NEW_DEVICE_STATE_CHANGE_EVENT, TX_OR); + + /* If there is a deactivate function call it. */ + if (rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_deactivate != UX_NULL) + + /* Invoke the application. */ + rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_deactivate(rndis); + + /* Deregister this interface to the NetX USB interface broker. */ + _ux_network_driver_deactivate((VOID *) rndis, rndis -> ux_slave_class_rndis_network_handle); + + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_DEACTIVATE, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(rndis); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_entry.c b/common/usbx_device_classes/src/ux_device_class_rndis_entry.c new file mode 100644 index 0000000..c8199b9 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_entry.c @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_rndis_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the rndis class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the rndis interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_rndis_initialize Initialize rndis class */ +/* _ux_device_class_rndis_activate Activate rndis class */ +/* _ux_device_class_rndis_deactivate Deactivate rndis class */ +/* _ux_device_class_rndis_control_request Request control */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the RNDIS class. */ + status = _ux_device_class_rndis_initialize(command); + + /* Return the completion status. */ + return(status); + + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_DEVICE_CLASS_RNDIS_CLASS_COMMUNICATION_CONTROL || + command -> ux_slave_class_command_class == UX_DEVICE_CLASS_RNDIS_CLASS_COMMUNICATION_DATA) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the rndis thread needs to be activated. */ + status = _ux_device_class_rndis_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the rndis thread canceled. */ + status = _ux_device_class_rndis_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + status = _ux_device_class_rndis_control_request(command); + + /* Return the completion status. */ + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_initialize.c b/common/usbx_device_classes/src/ux_device_class_rndis_initialize.c new file mode 100644 index 0000000..43eab26 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_initialize.c @@ -0,0 +1,367 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + +UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT + +/* Define list of supported OIDs (ends with zero-terminator) */ +ULONG ux_device_class_rndis_oid_supported_list[UX_DEVICE_CLASS_RNDIS_OID_SUPPORTED_LIST_LENGTH + 1] = +{ + /* Mandatory general OIDs. */ + UX_DEVICE_CLASS_RNDIS_OID_GEN_SUPPORTED_LIST, + UX_DEVICE_CLASS_RNDIS_OID_GEN_HARDWARE_STATUS, + UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_SUPPORTED, + UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_IN_USE, + UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, + UX_DEVICE_CLASS_RNDIS_OID_GEN_LINK_SPEED, + UX_DEVICE_CLASS_RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE, + UX_DEVICE_CLASS_RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE, + UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_ID, + UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_DESCRIPTION, + UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_DRIVER_VERSION, + UX_DEVICE_CLASS_RNDIS_OID_GEN_CURRENT_PACKET_FILTER, + UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE, + UX_DEVICE_CLASS_RNDIS_OID_GEN_MAC_OPTIONS, + UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + UX_DEVICE_CLASS_RNDIS_OID_GEN_PHYSICAL_MEDIUM, + UX_DEVICE_CLASS_RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER, + + /* Mandatory statistical OIDs. */ + UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_OK, + UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_OK, + UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_ERROR, + UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_ERROR, + UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_NO_BUFFER, + + /* Mandatory 802.3 OIDs. */ + UX_DEVICE_CLASS_RNDIS_OID_802_3_PERMANENT_ADDRESS, + UX_DEVICE_CLASS_RNDIS_OID_802_3_CURRENT_ADDRESS, + UX_DEVICE_CLASS_RNDIS_OID_802_3_MULTICAST_LIST, + UX_DEVICE_CLASS_RNDIS_OID_802_3_MAC_OPTIONS, + UX_DEVICE_CLASS_RNDIS_OID_802_3_MAXIMUM_LIST_SIZE, + + /* Mandatory 802.3 statistical OIDs. */ + UX_DEVICE_CLASS_RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT, + UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_ONE_COLLISION, + UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_MORE_COLLISIONS, + + 0, +}; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB RNDIS device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to rndis 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_event_flags_create Create Flag group */ +/* _ux_utility_event_flags_delete Delete Flag group */ +/* _ux_utility_mutex_create Create mutex */ +/* _ux_utility_mutex_delete Delete mutex */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_delete Delete thread */ +/* nx_packet_pool_create Create NetX packet pool */ +/* nx_packet_pool_delete Delete NetX packet pool */ +/* */ +/* CALLED BY */ +/* */ +/* USBX Source Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_RNDIS *rndis; +UX_SLAVE_CLASS_RNDIS_PARAMETER *rndis_parameter; +UX_SLAVE_CLASS *class; +UINT status; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device rndis class. */ + rndis = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_RNDIS)); + + /* Check for successful allocation. */ + if (rndis == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save the address of the RNDIS instance inside the RNDIS container. */ + class -> ux_slave_class_instance = (VOID *) rndis; + + /* Get the pointer to the application parameters for the rndis class. */ + rndis_parameter = command -> ux_slave_class_command_parameter; + + /* Store the start and stop signals if needed by the application. */ + rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_activate = rndis_parameter -> ux_slave_class_rndis_instance_activate; + rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_deactivate = rndis_parameter -> ux_slave_class_rndis_instance_deactivate; + + /* Save the Netx IP passed by the application. */ + rndis -> ux_slave_class_rndis_nx_ip = rndis_parameter -> ux_slave_class_rndis_parameter_nx_ip; + + /* Save the Netx IP address passed by the application. */ + rndis -> ux_slave_class_rndis_nx_ip_address = rndis_parameter -> ux_slave_class_rndis_parameter_nx_ip_address; + + /* Save the Netx IP address network mask passed by the application. */ + rndis -> ux_slave_class_rndis_nx_ip_network_mask = rndis_parameter -> ux_slave_class_rndis_parameter_nx_ip_network_mask; + + /* Copy the local node ID. */ + _ux_utility_memory_copy(rndis -> ux_slave_class_rndis_local_node_id, rndis_parameter -> ux_slave_class_rndis_parameter_local_node_id, + UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH); + + /* Copy the remote node ID. */ + _ux_utility_memory_copy(rndis -> ux_slave_class_rndis_remote_node_id, rndis_parameter -> ux_slave_class_rndis_parameter_remote_node_id, + UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH); + + /* Store the rest of the parameters as they are in the local instance. */ + _ux_utility_memory_copy(&rndis -> ux_slave_class_rndis_parameter, rndis_parameter, sizeof (UX_SLAVE_CLASS_RNDIS_PARAMETER)); + + /* Create a mutex to protect the RNDIS thread and the application messing up the transmit queue. */ + status = _ux_utility_mutex_create(&rndis -> ux_slave_class_rndis_mutex, "ux_slave_class_rndis_mutex"); + if (status != UX_SUCCESS) + status = UX_MUTEX_ERROR; + + /* Allocate some memory for the interrupt thread stack. */ + if (status == UX_SUCCESS) + { + rndis -> ux_slave_class_rndis_interrupt_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (rndis -> ux_slave_class_rndis_interrupt_thread_stack == UX_NULL) + + /* Set status to memory insufficient. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Interrupt endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the rndis instance to the new thread. This thread + does not start until we have a instance of the class. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&rndis -> ux_slave_class_rndis_interrupt_thread , "ux_slave_class_rndis_interrupt_thread", + _ux_device_class_rndis_interrupt_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) rndis -> ux_slave_class_rndis_interrupt_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + + UX_THREAD_EXTENSION_PTR_SET(&(rndis -> ux_slave_class_rndis_interrupt_thread), class) + + /* Allocate some memory for the bulk out thread stack. */ + if (status == UX_SUCCESS) + { + rndis -> ux_slave_class_rndis_bulkout_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (rndis -> ux_slave_class_rndis_bulkout_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Bulk endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the rndis instance to the new thread. This thread + does not start until we have a instance of the class. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&rndis -> ux_slave_class_rndis_bulkout_thread , "ux_slave_class_rndis_bulkout_thread", + _ux_device_class_rndis_bulkout_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) rndis -> ux_slave_class_rndis_bulkout_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + + UX_THREAD_EXTENSION_PTR_SET(&(rndis -> ux_slave_class_rndis_bulkout_thread), class) + + /* Allocate some memory for the bulk in thread stack. */ + if (status == UX_SUCCESS) + { + rndis -> ux_slave_class_rndis_bulkin_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check for successful allocation. */ + if (rndis -> ux_slave_class_rndis_bulkin_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Bulk endpoint treatment needs to be running in a different thread. So start + a new thread. We pass a pointer to the rndis instance to the new thread. This thread + does not start until we have a instance of the class. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&rndis -> ux_slave_class_rndis_bulkin_thread , "ux_slave_class_rndis_bulkin_thread", + _ux_device_class_rndis_bulkin_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) rndis -> ux_slave_class_rndis_bulkin_thread_stack , + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + + /* Check the creation of this thread. */ + if (status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + + UX_THREAD_EXTENSION_PTR_SET(&(rndis -> ux_slave_class_rndis_bulkin_thread), class) + + /* Create a event flag group for the rndis class to synchronize with the event interrupt thread. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_event_flags_create(&rndis -> ux_slave_class_rndis_event_flags_group, "ux_device_class_rndis_event_flag"); + + /* Check status. */ + if (status != UX_SUCCESS) + status = UX_EVENT_ERROR; + } + + /* Allocate some packet pool for reception. */ + if (status == UX_SUCCESS) + { + + /* UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE overflow has been checked by + * UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT outside of function. + */ + rndis -> ux_slave_class_rndis_pool_memory = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE); + + /* Check the completion status. */ + if (rndis -> ux_slave_class_rndis_pool_memory == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Create a packet pool. */ + if (status == UX_SUCCESS) + { + status = nx_packet_pool_create(&rndis -> ux_slave_class_rndis_packet_pool, "Rndis Device Packet Pool", + UX_DEVICE_CLASS_RNDIS_NX_PAYLOAD_SIZE, rndis -> ux_slave_class_rndis_pool_memory, UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE); + + /* Check for pool creation error. */ + if (status != UX_SUCCESS) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Create a semaphore for protecting the driver entry. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&rndis -> ux_slave_class_rndis_semaphore, "ux_device_class_rndis_semaphore", 1); + if (status == UX_SUCCESS) + + /* Return success. */ + return(UX_SUCCESS); + + /* Semaphore creation error. */ + status = UX_SEMAPHORE_ERROR; + } + + /* Failed! Free up resources. */ + + /* Delete semaphore for protecting the driver entry. */ + if (rndis -> ux_slave_class_rndis_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&rndis -> ux_slave_class_rndis_semaphore); + + /* Delete the packet pool. */ + if (rndis -> ux_slave_class_rndis_packet_pool.nx_packet_pool_id != 0) + nx_packet_pool_delete(&rndis -> ux_slave_class_rndis_packet_pool); + + /* Free rndis -> ux_slave_class_rndis_pool_memory. */ + if (rndis -> ux_slave_class_rndis_pool_memory) + _ux_utility_memory_free(rndis -> ux_slave_class_rndis_pool_memory); + + /* Delete rndis -> ux_slave_class_rndis_event_flags_group. */ + if (rndis -> ux_slave_class_rndis_event_flags_group.tx_event_flags_group_id != 0) + _ux_utility_event_flags_delete(&rndis -> ux_slave_class_rndis_event_flags_group); + + /* Delete rndis -> ux_slave_class_rndis_bulkin_thread. */ + if (rndis -> ux_slave_class_rndis_bulkin_thread.tx_thread_id != 0) + _ux_utility_thread_delete(&rndis -> ux_slave_class_rndis_bulkin_thread); + + /* Free rndis -> ux_slave_class_rndis_bulkin_thread_stack. */ + if (rndis -> ux_slave_class_rndis_bulkin_thread_stack) + _ux_utility_memory_free(rndis -> ux_slave_class_rndis_bulkin_thread_stack); + + /* Delete rndis -> ux_slave_class_rndis_bulkout_thread. */ + if (rndis -> ux_slave_class_rndis_bulkout_thread.tx_thread_id != 0) + _ux_utility_thread_delete(&rndis -> ux_slave_class_rndis_bulkout_thread); + + /* Free rndis -> ux_slave_class_rndis_bulkout_thread_stack. */ + if (rndis -> ux_slave_class_rndis_bulkout_thread_stack) + _ux_utility_memory_free(rndis -> ux_slave_class_rndis_bulkout_thread_stack); + + /* Delete rndis -> ux_slave_class_rndis_interrupt_thread. */ + if (rndis -> ux_slave_class_rndis_interrupt_thread.tx_thread_id != 0) + _ux_utility_thread_delete(&rndis -> ux_slave_class_rndis_interrupt_thread); + + /* Free rndis -> ux_slave_class_rndis_interrupt_thread_stack. */ + if (rndis -> ux_slave_class_rndis_interrupt_thread_stack) + _ux_utility_memory_free(rndis -> ux_slave_class_rndis_interrupt_thread_stack); + + /* Delete rndis -> ux_slave_class_rndis_mutex. */ + if (rndis -> ux_slave_class_rndis_mutex.tx_mutex_id != 0) + _ux_utility_mutex_delete(&rndis -> ux_slave_class_rndis_mutex); + + /* Free memory for rndis instance. */ + _ux_utility_memory_free(rndis); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_interrupt_thread.c b/common/usbx_device_classes/src/ux_device_class_rndis_interrupt_thread.c new file mode 100644 index 0000000..01e3463 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_interrupt_thread.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_interrupt_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the rndis interrupt endpoint */ +/* */ +/* INPUT */ +/* */ +/* rndis_class Address of rndis class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Request transfer */ +/* _ux_utility_event_flags_get Get event flags */ +/* _ux_utility_thread_suspend Suspend thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_rndis_interrupt_thread(ULONG rndis_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_RNDIS *rndis; +UX_SLAVE_DEVICE *device; +UX_SLAVE_TRANSFER *transfer_request; +UINT status; +ULONG actual_flags; + + /* Cast properly the rndis instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, rndis_class) + + /* Get the rndis instance from this class container. */ + rndis = (UX_SLAVE_CLASS_RNDIS *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* All RNDIS events are on the interrupt endpoint IN, from the host. */ + transfer_request = &rndis -> ux_slave_class_rndis_interrupt_endpoint -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + + /* Wait until we have a event sent by the application. We do not treat yet the case where a timeout based + on the interrupt pipe frequency or a change in the idle state forces us to send an empty report. */ + status = _ux_utility_event_flags_get(&rndis -> ux_slave_class_rndis_event_flags_group, (UX_DEVICE_CLASS_RNDIS_NEW_INTERRUPT_EVENT | + UX_DEVICE_CLASS_RNDIS_NEW_DEVICE_STATE_CHANGE_EVENT), + TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); + + /* If error log is enabled, insert this error message into the log buffer. */ + + /* Check the completion code. */ + if (status == UX_SUCCESS && (actual_flags & UX_DEVICE_CLASS_RNDIS_NEW_DEVICE_STATE_CHANGE_EVENT) == 0) + { + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_RNDIS_INTERRUPT_RESPONSE_LENGTH, + UX_DEVICE_CLASS_RNDIS_INTERRUPT_RESPONSE_LENGTH); + + /* Check error code. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + } + + /* We need to suspend ourselves. We will be resumed by the device enumeration module. */ + _ux_utility_thread_suspend(&rndis -> ux_slave_class_rndis_interrupt_thread); + + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_msg_initialize.c b/common/usbx_device_classes/src/ux_device_class_rndis_msg_initialize.c new file mode 100644 index 0000000..6e1c7c3 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_msg_initialize.c @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_msg_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function analyzes and replies to the MSG INITIALIZE */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* rndis Pointer to rndis class */ +/* transfer_request Pointer to the transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_msg_initialize(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request) +{ + +UCHAR *rndis_msg; +UCHAR *rndis_response; + + /* Get the pointer to the RNDIS message. */ + rndis_msg = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the request ID and keep it for the response. */ + rndis -> ux_slave_class_rndis_request_id = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_REQUEST_ID); + + /* Get the major version and store it into the RNDIS instance. */ + rndis -> ux_slave_class_rndis_major_version = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MAJOR_VERSION); + + /* Get the minor version and store it into the RNDIS instance. */ + rndis -> ux_slave_class_rndis_minor_version = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MINOR_VERSION); + + /* Get the max transfer size and store it into the RNDIS instance. */ + rndis -> ux_slave_class_rndis_max_transfer_size = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_INITIALIZE_MAX_TRANSFER_SIZE); + + /* Store the state machine to initialized. */ + rndis -> ux_slave_class_rndis_state = UX_DEVICE_CLASS_RNDIS_STATE_INITIALIZED; + + /* Now prepare the response. */ + rndis_response = rndis -> ux_slave_class_rndis_response; + + /* First store the command. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MESSAGE_TYPE, UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE); + + /* Then the length of the response. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MESSAGE_LENGTH, UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_RESPONSE_LENGTH); + + /* Store the request ID. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_REQUEST_ID, rndis -> ux_slave_class_rndis_request_id); + + /* Force the status to SUCCESS. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_STATUS, UX_DEVICE_CLASS_RNDIS_STATUS_SUCCESS); + + /* Set the major version of the device */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MAJOR_VERSION, UX_DEVICE_CLASS_RNDIS_VERSION_MAJOR); + + /* Set the minor version of the device */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MINOR_VERSION, UX_DEVICE_CLASS_RNDIS_VERSION_MINOR); + + /* Set the type of connection supported. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_DEVICE_FLAGS, UX_DEVICE_CLASS_RNDIS_DF_CONNECTION_SUPPORTED); + + /* Set the type of media supported. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MEDIUM, UX_DEVICE_CLASS_RNDIS_MEDIUM_SUPPORTED); + + /* Set the max packet per transfer. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MAX_PACKETS_PER_TRANSFER, UX_DEVICE_CLASS_RNDIS_MAX_PACKET_PER_TRANSFER); + + /* Set the max transfer size. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_MAX_TRANSFER_SIZE, UX_DEVICE_CLASS_RNDIS_MAX_PACKET_TRANSFER_SIZE); + + /* Set the packet alignment factor. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_PACKET_ALIGNMENT, UX_DEVICE_CLASS_RNDIS_PACKET_ALIGNEMENT_FACTOR); + + /* Set AFListOffset and AFListSize fields to 0. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_AFL_LIST_OFFSET, 0); + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_AFL_LIST_SIZE, 0); + + /* Set the response length. */ + rndis -> ux_slave_class_rndis_response_length = UX_DEVICE_CLASS_RNDIS_CMPLT_INITIALIZE_RESPONSE_LENGTH; + + /* We are done. Return UX_SUCCESS. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_msg_keep_alive.c b/common/usbx_device_classes/src/ux_device_class_rndis_msg_keep_alive.c new file mode 100644 index 0000000..ad0abd0 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_msg_keep_alive.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_msg_keep_alive PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function analyzes and replies to the MSG KEEP_ALIVE */ +/* */ +/* INPUT */ +/* */ +/* rndis Pointer to rndis class */ +/* transfer_request Pointer to the transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_msg_keep_alive(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request) +{ + +UCHAR *rndis_msg; +UCHAR *rndis_response; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Get the pointer to the RNDIS message. */ + rndis_msg = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the request ID and keep it for the response. */ + rndis -> ux_slave_class_rndis_request_id = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_KEEP_ALIVE_REQUEST_ID); + + /* Now prepare the response. */ + rndis_response = rndis -> ux_slave_class_rndis_response; + + /* First store the command. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_MESSAGE_TYPE, UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE); + + /* Then the length of the response. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_MESSAGE_LENGTH, UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_RESPONSE_LENGTH); + + /* Store the request ID. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_REQUEST_ID, rndis -> ux_slave_class_rndis_request_id); + + /* Force the status to SUCCESS. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_STATUS, UX_DEVICE_CLASS_RNDIS_STATUS_SUCCESS); + + /* Set the response length. */ + rndis -> ux_slave_class_rndis_response_length = UX_DEVICE_CLASS_RNDIS_CMPLT_KEEP_ALIVE_RESPONSE_LENGTH; + + /* We are done. Return UX_SUCCESS. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_msg_query.c b/common/usbx_device_classes/src/ux_device_class_rndis_msg_query.c new file mode 100644 index 0000000..a2b6b33 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_msg_query.c @@ -0,0 +1,357 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_msg_query PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function analyzes and replies to the MSG QUERY */ +/* */ +/* INPUT */ +/* */ +/* rndis Pointer to rndis class */ +/* transfer_request Pointer to the transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_string_length_check Check and return C string length */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_msg_query(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request) +{ + +UCHAR *rndis_msg; +UCHAR *rndis_response; +ULONG rndis_oid; +ULONG rndis_response_length; +UINT rndis_response_string_length = 0; +ULONG oid_index; +ULONG status; + + /* Get the pointer to the RNDIS message. */ + rndis_msg = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the request ID and keep it for the response. */ + rndis -> ux_slave_class_rndis_request_id = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_QUERY_REQUEST_ID); + + /* Get the OID. */ + rndis_oid = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_QUERY_OID); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_MSG_QUERY, rndis, rndis_oid, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Now prepare the response. */ + rndis_response = rndis -> ux_slave_class_rndis_response; + + /* First store the command. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_MESSAGE_TYPE, UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY); + + /* Store the request ID. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_REQUEST_ID, rndis -> ux_slave_class_rndis_request_id); + + /* By default the function will succeed. */ + status = UX_DEVICE_CLASS_RNDIS_STATUS_SUCCESS; + + /* What OID are we dealing here ? */ + switch (rndis_oid) + { + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_SUPPORTED_LIST : + + /* Supported List of OIDs. Parse each OID until the end and store it in the response buffer. */ + oid_index = 0; + while (ux_device_class_rndis_oid_supported_list[oid_index] != 0) + { + + /* We have found a valid OID to return. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER + (oid_index * sizeof(ULONG)), + ux_device_class_rndis_oid_supported_list[oid_index]); + + /* Next OID index. */ + oid_index++; + + } + + /* Set the total response length. */ + rndis_response_length = oid_index * (ULONG)sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE : + + /* Set the maximum frame size. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_MAX_FRAME_SIZE); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_SUPPORTED : + + /* Set the media supported. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_MEDIA_802_3); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_IN_USE : + + /* Set the media in use. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_MEDIA_802_3); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_HARDWARE_STATUS : + + /* Set the hardware status. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_OID_HW_STATUS_READY); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_PHYSICAL_MEDIUM : + + /* Set the physical medium. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, 0); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE : + + /* Set the physical medium. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_MAX_PACKET_LENGTH); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_LINK_SPEED : + + /* Set the link speed. For now we assume a full speed device. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_LINK_SPEED_FS); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_CONNECT_STATUS : + + /* Set the media connection status. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_MEDIA_CONNECTED); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MAC_OPTIONS : + + /* Set the MAC options. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, UX_DEVICE_CLASS_RNDIS_MAC_OPTIONS); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_ID : + + /* Set the vendor ID. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_parameter_vendor_id); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_DRIVER_VERSION : + + /* Set the driver version. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_parameter_driver_version); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_DESCRIPTION : + + /* Get the string length for the vendor description. */ + status = _ux_utility_string_length_check(rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_parameter_vendor_description, &rndis_response_string_length, UX_DEVICE_CLASS_RNDIS_VENDOR_DESCRIPTION_MAX_LENGTH); + if (status) + return(status); + + /* Set the total response length. */ + rndis_response_length = (ULONG) rndis_response_string_length; + + /* Copy the vendor description. */ + _ux_utility_memory_copy(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_parameter_vendor_description, rndis_response_length); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_OK : + + /* Set the appropriate statistic value. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_statistics_xmit_ok); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_OK : + + /* Set the appropriate statistic value. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_statistics_rcv_ok); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_ERROR : + + /* Set the appropriate statistic value. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_statistics_xmit_error); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_ERROR : + + /* Set the appropriate statistic value. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_statistics_rcv_error); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_NO_BUFFER : + + /* Set the appropriate statistic value. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, rndis -> ux_slave_class_rndis_statistics_rcv_no_buffer); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + case UX_DEVICE_CLASS_RNDIS_OID_802_3_CURRENT_ADDRESS : + + + /* Save the Hardware address in the return message. */ + _ux_utility_memory_copy(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, + rndis -> ux_slave_class_rndis_remote_node_id, UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH); + + /* Set the total response length. */ + rndis_response_length = UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH; + + break; + + + default : + + /* Just return zero ULONG field. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER, 0); + + /* Set the total response length. */ + rndis_response_length = sizeof(ULONG); + + break; + + } + + /* Set the status field. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_STATUS, status); + + /* Set the buffer offset value. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER_OFFSET, + (UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER - UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_REQUEST_ID)); + + /* Store the length of the buffer. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER_LENGTH, rndis_response_length); + + /* Update the response length to add the header. */ + rndis_response_length += UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_INFO_BUFFER; + + /* Set the response length. */ + rndis -> ux_slave_class_rndis_response_length = rndis_response_length; + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_QUERY_MESSAGE_LENGTH, rndis_response_length); + + /* We are done. Return UX_SUCCESS. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_msg_reset.c b/common/usbx_device_classes/src/ux_device_class_rndis_msg_reset.c new file mode 100644 index 0000000..fbb4319 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_msg_reset.c @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_msg_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function analyzes and replies to the MSG RESET */ +/* */ +/* INPUT */ +/* */ +/* rndis Pointer to rndis class */ +/* transfer_request Pointer to the transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_msg_reset(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request) +{ + +UCHAR *rndis_response; + + UX_PARAMETER_NOT_USED(transfer_request); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_MSG_RESET, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Now prepare the response. */ + rndis_response = rndis -> ux_slave_class_rndis_response; + + /* First store the command. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_MESSAGE_TYPE, UX_DEVICE_CLASS_RNDIS_CMPLT_RESET); + + /* Then the length of the response. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_MESSAGE_LENGTH, UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_RESPONSE_LENGTH); + + /* Force the status to SUCCESS. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_STATUS, UX_DEVICE_CLASS_RNDIS_STATUS_SUCCESS); + + /* Set Addressing Reset field: no need to resend multicast address list. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_ADDRESSING_RESET, 0); + + /* Set the response length. */ + rndis -> ux_slave_class_rndis_response_length = UX_DEVICE_CLASS_RNDIS_CMPLT_RESET_RESPONSE_LENGTH; + + /* We are done. Return UX_SUCCESS. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_msg_set.c b/common/usbx_device_classes/src/ux_device_class_rndis_msg_set.c new file mode 100644 index 0000000..1f5faa7 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_msg_set.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_msg_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function analyzes and replies to the MSG SET */ +/* */ +/* INPUT */ +/* */ +/* rndis Pointer to rndis class */ +/* transfer_request Pointer to the transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* RNDIS Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_msg_set(UX_SLAVE_CLASS_RNDIS *rndis, UX_SLAVE_TRANSFER *transfer_request) +{ + +UCHAR *rndis_msg; +UCHAR *rndis_response; +ULONG rndis_oid; +ULONG status; + + /* Get the pointer to the RNDIS message. */ + rndis_msg = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Get the request ID and keep it for the response. */ + rndis -> ux_slave_class_rndis_request_id = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_SET_REQUEST_ID); + + /* Get the OID. */ + rndis_oid = _ux_utility_long_get(rndis_msg + UX_DEVICE_CLASS_RNDIS_MSG_SET_OID); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_MSG_SET, rndis, rndis_oid, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Now prepare the response. */ + rndis_response = rndis -> ux_slave_class_rndis_response; + + /* First store the command. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_SET_MESSAGE_TYPE, UX_DEVICE_CLASS_RNDIS_CMPLT_SET); + + /* Store the request ID. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_SET_REQUEST_ID, rndis -> ux_slave_class_rndis_request_id); + + /* By default the function will succeed. */ + status = UX_DEVICE_CLASS_RNDIS_STATUS_SUCCESS; + + /* What OID are we dealing here ? No need to treat OIDs but not sure so leave the code as is for now. */ + switch (rndis_oid) + { + + case UX_DEVICE_CLASS_RNDIS_OID_GEN_SUPPORTED_LIST : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_SUPPORTED : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_IN_USE : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_HARDWARE_STATUS : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_PHYSICAL_MEDIUM : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_LINK_SPEED : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_CONNECT_STATUS : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_OK : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_OK : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_ERROR : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_ERROR : + case UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_NO_BUFFER : + default : + break; + + } + + /* Set the status field. */ + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_SET_STATUS, status); + + /* Set the response length. */ + rndis -> ux_slave_class_rndis_response_length = UX_DEVICE_CLASS_RNDIS_CMPLT_SET_RESPONSE_LENGTH; + _ux_utility_long_put(rndis_response + UX_DEVICE_CLASS_RNDIS_CMPLT_SET_MESSAGE_LENGTH, UX_DEVICE_CLASS_RNDIS_CMPLT_SET_RESPONSE_LENGTH); + + /* We are done. Return UX_SUCCESS. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_rndis_write.c b/common/usbx_device_classes/src/ux_device_class_rndis_write.c new file mode 100644 index 0000000..a21615f --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_rndis_write.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device RNDIS Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_rndis.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_rndis_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a packet into a queue for later thread */ +/* processing. */ +/* */ +/* INPUT */ +/* */ +/* rndis Address of rndis class */ +/* instance */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_mutex_on Take mutex */ +/* _ux_utility_mutex_off Free mutex */ +/* _ux_utility_event_flags_set Set event flags */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_rndis_write(VOID *rndis_class, NX_PACKET *packet) +{ + +NX_PACKET *current_packet; +NX_PACKET *next_packet; +UX_SLAVE_CLASS_RNDIS *rndis; + + /* Proper class casting. */ + rndis = (UX_SLAVE_CLASS_RNDIS *) rndis_class; + + /* Protect this thread. */ + _ux_utility_mutex_on(&rndis -> ux_slave_class_rndis_mutex); + + /* Check the queue. See if there is something that is being sent. */ + if (rndis -> ux_slave_class_rndis_xmit_queue == UX_NULL) + + /* Memorize this packet at the beginning of the queue. */ + rndis -> ux_slave_class_rndis_xmit_queue = packet; + + else + + { + + /* We get here when there is something in the queue. */ + current_packet = rndis -> ux_slave_class_rndis_xmit_queue; + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + /* Parse the current chain for the end. */ + while (next_packet != NX_NULL) + { + /* Remember the current packet. */ + current_packet = next_packet; + + /* See what the next packet in the chain is. */ + next_packet = current_packet -> nx_packet_queue_next; + } + + /* Memorize the packet to be sent. */ + current_packet -> nx_packet_queue_next = packet; + + } + + /* Free Mutex resource. */ + _ux_utility_mutex_off(&rndis -> ux_slave_class_rndis_mutex); + + /* The packet to be sent is the last in the chain. */ + packet -> nx_packet_queue_next = NX_NULL; + + /* Set an event to wake up the bulkin thread. */ + _ux_utility_event_flags_set(&rndis -> ux_slave_class_rndis_event_flags_group, UX_DEVICE_CLASS_RNDIS_NEW_BULKIN_EVENT, TX_OR); + + /* We are done here. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_activate.c b/common/usbx_device_classes/src/ux_device_class_storage_activate.c new file mode 100644 index 0000000..1429e09 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_activate.c @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates the USB storage device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to storage command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_thread_resume Resume thread */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_activate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_STORAGE *storage; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + storage = (UX_SLAVE_CLASS_STORAGE *)class -> ux_slave_class_instance; + + /* Get the interface that owns this instance. */ + interface = (UX_SLAVE_INTERFACE *) command -> ux_slave_class_command_interface; + + /* Store the class instance into the interface. */ + interface -> ux_slave_interface_class_instance = (VOID *)storage; + + /* Now the opposite, store the interface in the class instance. */ + storage -> ux_slave_class_storage_interface = interface; + + /* Resume thread. */ + status = _ux_utility_thread_resume(&class -> ux_slave_class_thread); + + /* If there is a activate function call it. */ + if (storage -> ux_slave_class_storage_instance_activate != UX_NULL) + { + /* Invoke the application. */ + storage -> ux_slave_class_storage_instance_activate(storage); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_ACTIVATE, storage, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_DEVICE_OBJECT_TYPE_INTERFACE, storage, 0, 0, 0) + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_control_request.c b/common/usbx_device_classes/src/ux_device_class_storage_control_request.c new file mode 100644 index 0000000..895e674 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_control_request.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_control_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function manages the based sent by the host on the control */ +/* endpoints with a CLASS or VENDOR SPECIFIC type. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_transfer_abort Abort Transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_control_request(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_CLASS *class; +ULONG request; +UX_SLAVE_CLASS_STORAGE *storage; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; + + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* Get the pointer to the transfer request associated with the control endpoint. */ + transfer_request = &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request; + + /* Extract the request type from the SETUP packet.. */ + request = *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST); + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the storage instance from this class container. */ + storage = (UX_SLAVE_CLASS_STORAGE *) class -> ux_slave_class_instance; + + /* Here we proceed only the standard request we know of at the device level. */ + switch (request) + { + + case UX_SLAVE_CLASS_STORAGE_RESET: + + /* We need the interface to the class. */ + interface = storage -> ux_slave_class_storage_interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + } + + /* First cancel any transfer on the endpoint OUT, from the host. */ + transfer_request = &endpoint_out -> ux_slave_endpoint_transfer_request; + _ux_device_stack_transfer_abort(transfer_request, UX_TRANSFER_APPLICATION_RESET); + + /* Then cancel any transfer on the endpoint IN, from the host. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + _ux_device_stack_transfer_abort(transfer_request, UX_TRANSFER_APPLICATION_RESET); + + /* Reset phase error. */ + storage -> ux_slave_class_storage_phase_error = UX_FALSE; + + break; + + case UX_SLAVE_CLASS_STORAGE_GET_MAX_LUN: + + /* Set the value of the number of LUN in the buffer. The max number of LUN is the + number of declared LUN - 1. */ + *transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR)(storage -> ux_slave_class_storage_number_lun -1); + + /* Set the phase of the transfer to data out. */ + transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; + + /* We can return the LUN number. */ + _ux_device_stack_transfer_request(transfer_request, 1, 1); + break; + + default: + + /* Unknown function. It's not handled. */ + return(UX_ERROR); + } + + /* It's handled. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_device_classes/src/ux_device_class_storage_csw_send.c b/common/usbx_device_classes/src/ux_device_class_storage_csw_send.c new file mode 100644 index 0000000..9df0fa6 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_csw_send.c @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_csw_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends the status phase of SCSI transaction. Note that */ +/* dCSWDataResidue is always set to 0 because we either transfer all */ +/* the data, or report command failure. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* status Status of CSW */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_put Put long word */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_set Set memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_csw_send(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, UCHAR csw_status) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR csw_buffer[UX_SLAVE_CLASS_STORAGE_CSW_LENGTH]; + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Ensure it is cleaned. */ + _ux_utility_memory_set(csw_buffer, 0, UX_SLAVE_CLASS_STORAGE_CSW_LENGTH); + + /* Store the signature of the CSW. */ + _ux_utility_long_put(&csw_buffer[UX_SLAVE_CLASS_STORAGE_CSW_SIGNATURE], UX_SLAVE_CLASS_STORAGE_CSW_SIGNATURE_MASK); + + /* Store the SCSI tag from the CBW. */ + _ux_utility_long_put(&csw_buffer[UX_SLAVE_CLASS_STORAGE_CSW_TAG], storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_scsi_tag); + + /* Store the status of the previous operation. */ + csw_buffer[UX_SLAVE_CLASS_STORAGE_CSW_STATUS] = csw_status; + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + csw_buffer, UX_SLAVE_CLASS_STORAGE_CSW_LENGTH); + + /* We may be in a special state machine condition where the endpoint is stalled waiting for + a CLEAR_FEATURE. We will wait until the host clears the endpoint. + The transfer_request function does that. */ + /* Send the CSW back to the host. */ + status = _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_STORAGE_CSW_LENGTH, + UX_SLAVE_CLASS_STORAGE_CSW_LENGTH); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_deactivate.c b/common/usbx_device_classes/src/ux_device_class_storage_deactivate.c new file mode 100644 index 0000000..ef20dd0 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_deactivate.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deactivate an instance of the storage class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to a class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_all_request_abort Abort all transfers */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_deactivate(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_CLASS_STORAGE *storage; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + storage = (UX_SLAVE_CLASS_STORAGE *)class -> ux_slave_class_instance; + + /* We need the interface to the class. */ + interface = storage -> ux_slave_class_storage_interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + } + + /* Terminate the transactions pending on the endpoints. */ + _ux_device_stack_transfer_all_request_abort(endpoint_in, UX_TRANSFER_BUS_RESET); + _ux_device_stack_transfer_all_request_abort(endpoint_out, UX_TRANSFER_BUS_RESET); + + /* If there is a deactivate function call it. */ + if (storage -> ux_slave_class_storage_instance_deactivate != UX_NULL) + { + + /* Invoke the application. */ + storage -> ux_slave_class_storage_instance_deactivate(storage); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_DEACTIVATE, storage, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(storage); + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_entry.c b/common/usbx_device_classes/src/ux_device_class_storage_entry.c new file mode 100644 index 0000000..97cb88f --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_entry.c @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_class_device_storage_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the device storage class. It */ +/* will be called by the device stack enumeration module when the */ +/* host has sent a SET_CONFIGURATION command and the storage interface */ +/* needs to be mounted. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_initialize Initialize storage class */ +/* _ux_device_class_storage_uninitialize Uninitialize storage class */ +/* _ux_device_class_storage_activate Activate storage class */ +/* _ux_device_class_storage_deactivate Deactivate storage class */ +/* _ux_device_class_storage_control_request */ +/* Request control */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_entry(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_slave_class_command_request) + { + + case UX_SLAVE_CLASS_COMMAND_INITIALIZE: + + /* Call the init function of the Storage class. */ + status = _ux_device_class_storage_initialize(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_UNINITIALIZE: + + /* Call the init function of the Storage class. */ + status = _ux_device_class_storage_uninitialize(command); + + /* Return the completion status. */ + return(status); + + + case UX_SLAVE_CLASS_COMMAND_QUERY: + + /* Check the CLASS definition in the interface descriptor. */ + if (command -> ux_slave_class_command_class == UX_SLAVE_CLASS_STORAGE_CLASS) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_SLAVE_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the host has sent a SET_CONFIGURATION command + and this interface has to be mounted. Both Bulk endpoints have to be mounted + and the storage thread needs to be activated. */ + status = _ux_device_class_storage_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted. + The device endpoints have to be dismounted and the storage thread canceled. */ + status = _ux_device_class_storage_deactivate(command); + + /* Return the completion status. */ + return(status); + + case UX_SLAVE_CLASS_COMMAND_REQUEST: + + /* The request command is used when the host sends a command on the control endpoint. */ + status = _ux_device_class_storage_control_request(command); + + /* Return the completion status. */ + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_format.c b/common/usbx_device_classes/src/ux_device_class_storage_format.c new file mode 100644 index 0000000..66a83a5 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_format.c @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_format PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function formats the local media. It is not supported in this */ +/* release. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_endpoint_stall Endpoint stall */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_format(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb) +{ + + UX_PARAMETER_NOT_USED(cbwcb); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_FORMAT, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* This command is not yet supported. So Stall the endpoint. We stall the + OUT endpoint because we expect the host to send parameters. */ + _ux_device_stack_endpoint_stall(endpoint_out); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x05; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x26; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x01; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return not supported error. */ + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_get_configuration.c b/common/usbx_device_classes/src/ux_device_class_storage_get_configuration.c new file mode 100644 index 0000000..8b50ab9 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_get_configuration.c @@ -0,0 +1,508 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_PROFILE_LENGTH 228 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_HEADER_LENGTH 8 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_DESCRIPTOR_LENGTH 32 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_HEADER_LENGTH 4 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_LENGTH (USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_PROFILE_LENGTH - USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_DESCRIPTOR_LENGTH - USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_HEADER_LENGTH) +UCHAR usbx_device_class_storage_configuration_profile[] = { + + /* Feature Header */ + 0x00, 0x00, /* Entire length of profile filled by firmware */ + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, /* Current profile */ + + /* Feature Descriptor */ + 0x00, 0x00, /* Feature Code : profile list */ + 0x00, /* Persistent/current */ + 0x1c, /* Additional Length */ + + 0x00, 0x12, 0x00, 0x00, /* DVD-RAM */ + 0x00, 0x11, 0x00, 0x00, /* DVD-R */ + 0x00, 0x10, 0x00, 0x00, /* DVD-ROM */ + 0x00, 0x0A, 0x00, 0x00, /* CD-RW */ + 0x00, 0x09, 0x00, 0x00, /* CD-R */ + 0x00, 0x08, 0x00, 0x00, /* CD-ROM */ + 0x00, 0x02, 0x00, 0x00, /* Writable capable with removable media. */ + + + /* Feature Descriptor */ + 0x00, 0x01, /* Feature Code : core feature */ + 0x0b, /* Persistent/current */ + 0x08, /* Additional Length */ + + 0x00, 0x00, 0x00, 0x07, /* Physical Interface Standard */ + 0x01, 0x00, 0x00, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x02, /* Feature Code : morphing (Ability to notify initiator about + operational changes and accept initiator requests to prevent + operational changes) */ + 0x07, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x02, 0x00, 0x00, 0x00, /* Physical Interface Standard */ + + /* Feature Descriptor */ + 0x00, 0x03, /* Feature Code : Removable Medium (The medium may be removed + from the device ) */ + 0x0b, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x2b, 0x00, 0x00, 0x00, /* Physical Interface Standard */ + + /* Feature Descriptor */ + 0x00, 0x10, /* Feature Code : Random Readable (Read ability for storage devices + with random addressing) */ + 0x00, /* Persistent/current */ + 0x08, /* Additional Length */ + + 0x00, 0x00, 0x08, 0x00, /* */ + 0x00, 0x01, 0x01, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x1d, /* Feature Code : MultiRead (The logical unit can read all CD media types) */ + 0x00, /* Persistent/current */ + 0x00, /* Additional Length */ + + + /* Feature Descriptor */ + 0x00, 0x1e, /* Feature Code : CD Read (The ability to read CD specific structures) */ + 0x08, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x03, 0x00, 0x00, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x1f, /* Feature Code : DVD Read (The ability to read DVD specific structures) */ + 0x08, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x01, 0x00, 0x01, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x20, /* Feature Code : Random Writable (Write support for randomly addressed writes) */ + 0x04, /* Persistent/current */ + 0x0c, /* Additional Length */ + + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x08, 0x00, /* */ + 0x00, 0x00, 0x01, 0x00, /* */ + + + /* Feature Descriptor */ + 0x00, 0x21, /* Feature Code : Incremental Streaming Writable (Write support for sequential recording) */ + 0x0C, /* Persistent/current */ + 0x08, /* Additional Length */ + + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x23, /* Feature Code : Formattable (Support for formatting of media.) */ + 0x08, /* Persistent/current */ + 0x08, /* Additional Length */ + + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x00, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x24, /* Feature Code : Defect Management (Ability of the drive/media system to provide an + apparently defect-free space.) */ + 0x04, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x80, 0x00, 0x00, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x26, /* Feature Code : Restricted Overwrite (Write support for media that must be written + in multiples of logical blocks.) */ + 0x00, /* Persistent/current */ + 0x00, /* Additional Length */ + + + /* Feature Descriptor */ + 0x00, 0x2d, /* Feature Code : CD Track at Once (Ability to write CD with Track at Once recording) */ + 0x08, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x46, 0x00, 0x3f, 0x0f, /* */ + + /* Feature Descriptor */ + 0x00, 0x2e, /* Feature Code : CD Mastering (The ability to write CD with Session at Once or Raw write methods.) */ + 0x04, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x7f, 0x00, 0x0d, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x2f, /* Feature Code : DVD-R Write (The ability to write DVD specific structures) */ + 0x08, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x4e, 0x00, 0x00, 0x00, /* */ + + + 0x01, 0x00, /* Feature Code : Power Management (Initiator and device directed power management) */ + 0x07, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x00, 0x00, 0x00, 0x00, /* */ + + + 0x01, 0x01, /* Feature Code : S.M.A.R.T. (Self Monitoring Analysis and Reporting Technology (Failure prediction)) */ + 0x00, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x00, 0x00, 0x00, 0x00, /* */ + + 0x01, 0x08, /* Feature Code : Logical Unit serial number (Logical unit has a unique identifier) */ + 0x03, /* Persistent/current */ + 0x10, /* Additional Length */ + + 0x53, 0x31, 0x33, 0x36, /* Serial Number */ + 0x36, 0x59, 0x42, 0x46, + 0x37, 0x30, 0x30, 0x39, + 0x45, 0x48, 0x20, 0x20, + + 0x01, 0x0a, /* Feature Code : Not sure : says FDC STC TOC */ + 0x00, /* Persistent/current */ + 0x0c, /* Additional Length */ + + 0x46, 0x44, 0x43, 0x00, + 0x53, 0x54, 0x43, 0x00, + 0x54, 0x4F, 0x43, 0x00, + }; + + +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_PROFILE_LENGTH 112 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_HEADER_LENGTH 8 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_FEATURE_DESCRIPTOR_LENGTH 32 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_FEATURE_HEADER_LENGTH 4 +#define USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_FEATURE_LENGTH (USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_PROFILE_LENGTH - USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_FEATURE_DESCRIPTOR_LENGTH - USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_HEADER_LENGTH) +UCHAR usbx_device_class_storage_configuration_active_profile[] = { + + /* Feature Header */ + 0x00, 0x00, /* Entire length of profile filled by firmware */ + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x08, /* Current profile is CD-ROM*/ + + /* Feature Descriptor */ + 0x00, 0x00, /* Feature Code : profile list */ + 0x00, /* Persistent/current */ + 0x1c, /* Additional Length */ + + 0x00, 0x12, 0x00, 0x00, /* DVD-RAM */ + 0x00, 0x11, 0x00, 0x00, /* DVD-R */ + 0x00, 0x10, 0x00, 0x00, /* DVD-ROM */ + 0x00, 0x0A, 0x00, 0x00, /* CD-RW */ + 0x00, 0x09, 0x00, 0x00, /* CD-R */ + 0x00, 0x08, 0x01, 0x00, /* CD-ROM : active profile */ + 0x00, 0x02, 0x00, 0x00, /* Writable capable with removable media. */ + + + /* Feature Descriptor */ + 0x00, 0x01, /* Feature Code : core feature */ + 0x0b, /* Persistent/current */ + 0x08, /* Additional Length */ + + 0x00, 0x00, 0x00, 0x07, /* Physical Interface Standard */ + 0x01, 0x00, 0x00, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x02, /* Feature Code : morphing (Ability to notify initiator about + operational changes and accept initiator requests to prevent + operational changes) */ + 0x07, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x02, 0x00, 0x00, 0x00, /* Physical Interface Standard */ + + /* Feature Descriptor */ + 0x00, 0x03, /* Feature Code : Removable Medium (The medium may be removed + from the device ) */ + 0x0b, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x2b, 0x00, 0x00, 0x00, /* Physical Interface Standard */ + + /* Feature Descriptor */ + 0x00, 0x10, /* Feature Code : Random Readable (Read ability for storage devices + with random addressing) */ + 0x01, /* Persistent/current */ + 0x08, /* Additional Length */ + + 0x00, 0x00, 0x08, 0x00, /* */ + 0x00, 0x01, 0x01, 0x00, /* */ + + /* Feature Descriptor */ + 0x00, 0x1d, /* Feature Code : MultiRead (The logical unit can read all CD media types) */ + 0x01, /* Persistent/current */ + 0x00, /* Additional Length */ + + + /* Feature Descriptor */ + 0x00, 0x1e, /* Feature Code : CD Read (The ability to read CD specific structures) */ + 0x09, /* Persistent/current */ + 0x04, /* Additional Length */ + + 0x03, 0x00, 0x00, 0x00, /* */ + + + 0x01, 0x08, /* Feature Code : Logical Unit serial number (Logical unit has a unique identifier) */ + 0x03, /* Persistent/current */ + 0x10, /* Additional Length */ + + 0x53, 0x31, 0x33, 0x36, /* Serial Number */ + 0x36, 0x59, 0x42, 0x46, + 0x37, 0x30, 0x30, 0x39, + 0x45, 0x48, 0x20, 0x20, + + }; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_get_configuration PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a GET_CONFIGURATION command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* _ux_utility_long_put_big_endian Put 32-bit big endian */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_get_configuration(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status = 0; +UX_SLAVE_TRANSFER *transfer_request; +ULONG starting_feature; +ULONG allocation_length; +ULONG additional_length; +ULONG profile_counter; +UCHAR *profile_pointer; +ULONG feature; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_GET_CONFIGURATION, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Initialize the length of the configuration profile. */ + _ux_utility_long_put_big_endian(usbx_device_class_storage_configuration_profile, (USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_PROFILE_LENGTH - 4)); + + /* Initialize the length of the active configuration profile. */ + _ux_utility_long_put_big_endian(usbx_device_class_storage_configuration_active_profile, (USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_PROFILE_LENGTH - 4)); + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Get the Starting Feature. */ + starting_feature = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_STARTING_FEATURE); + + /* Get the allocation length. */ + allocation_length = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_ALLOCATION_LENGTH); + + /* Is the request demanding all the features ? */ + if (starting_feature == 0) + { + + /* Is the requester demanding the active profile ? */ + if ((*(cbwcb + UX_SLAVE_CLASS_STORAGE_GET_CONFIGURATION_RT) & 3) == 1) + { + + /* We get the active profile. */ + /* Can we send all the activeconfiguration profile ? If not, the host may demand the first part of the configuration to get the entire length. + In this case, return the length demanded by the host. */ + if (allocation_length >= USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_PROFILE_LENGTH) + + /* Adjust allocation length to maximum allowed. */ + allocation_length = USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_ACTIVE_PROFILE_LENGTH; + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + usbx_device_class_storage_configuration_active_profile, + allocation_length); + } + else + { + + /* We get the whole profile. */ + /* Can we send all the configuration profile ? If not, the host may demand the first part of the configuration to get the entire length. + In this case, return the length demanded by the host. */ + if (allocation_length >= USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_PROFILE_LENGTH) + + /* Adjust allocation length to maximum allowed. */ + allocation_length = USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_PROFILE_LENGTH; + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + usbx_device_class_storage_configuration_profile, + allocation_length); + + } + + /* Send a data payload with the configuration profile response buffer. */ + _ux_device_stack_transfer_request(transfer_request, + allocation_length, + allocation_length); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + } + else + { + + /* The caller has demanded a specific feature. Scan our configuration profile. Jump over the beginning sections. */ + profile_pointer = usbx_device_class_storage_configuration_profile + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_HEADER_LENGTH + + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_DESCRIPTOR_LENGTH; + profile_counter = USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_LENGTH; + + /* Scan our configuration profile. */ + while (profile_counter != 0) + { + + /* Extract the feature from the configuration profile. */ + feature = _ux_utility_short_get_big_endian(profile_pointer + USBX_DEVICE_CLASS_STORAGE_FEATURE_DESCRIPTOR_FEATURE_CODE); + + /* Extract the feature length from the configuration profile. */ + additional_length = (ULONG ) *(profile_pointer + USBX_DEVICE_CLASS_STORAGE_FEATURE_DESCRIPTOR_FEATURE_ADD_LENGTH); + + /* Compare the Feature extracted with the one demanded. */ + if (feature == starting_feature) + { + + /* We found the feature, we check if the requester has enough space for us to return it. */ + if (allocation_length >= (additional_length + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_HEADER_LENGTH + + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_HEADER_LENGTH)) + + /* Need to adjust the allocation length. */ + allocation_length = additional_length + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_HEADER_LENGTH + + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_HEADER_LENGTH; + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + profile_pointer, + allocation_length); + + /* Send a data payload with the read_capacity response buffer. */ + _ux_device_stack_transfer_request(transfer_request, + allocation_length, + allocation_length); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Get out of the loop. */ + break; + } + else + { + + /* We have not yet found the feature, keep parsing. */ + if (profile_counter - additional_length - USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_HEADER_LENGTH <= 0) + { + + /* We are either at the end of the profile or the profile is corrupted. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = UX_SLAVE_CLASS_STORAGE_SENSE_KEY_ILLEGAL_REQUEST; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x26; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x02; + + /* Set status to error. */ + status = UX_ERROR; + + /* Get out of the loop. */ + break; + + } + else + { + + /* Update the profile pointer. */ + profile_pointer += additional_length + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_HEADER_LENGTH; + + /* Update the length remaining to parse in profile. */ + profile_counter -= additional_length + USBX_DEVICE_CLASS_STORAGE_CONFIGURATION_FEATURE_HEADER_LENGTH; + } + } + } + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_get_performance.c b/common/usbx_device_classes/src/ux_device_class_storage_get_performance.c new file mode 100644 index 0000000..b50025b --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_get_performance.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +#define USBX_DEVICE_CLASS_STORAGE_GET_PERFORMANCE_0_LENGTH 16 +UCHAR usbx_device_class_storage_performance[] = { + + 0x08, 0x00, 0x00, 0x00, 0x00, 0x23, 0x12, 0x80, + 0x00, 0x00, 0x10, 0x89, 0x00, 0x00, 0x10, 0x89 +}; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_get_performance PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a GET_PERFORMANCE SCSI command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_long_put_big_endian Put 32-bit big endian */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_get_performance(UX_SLAVE_CLASS_STORAGE *storage, + ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, + UCHAR *cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG performance_page; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_OTHER, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Ensure memory buffer cleaned. */ + _ux_utility_memory_set(transfer_request -> ux_slave_transfer_request_data_pointer, 0, 64); + + /* Get the performance page code. */ + performance_page = (ULONG) *(cbwcb + UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAGE); + + /* Filter it as the response depends on it. */ + switch (performance_page) + { + + case UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAGE_14 : + + /* Put the length to be returned. */ + _ux_utility_long_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer, + UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAYLOAD_LENGTH); + + /* Put the payload to be returned. */ + _ux_utility_long_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer + 4, + UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAYLOAD); + + /* Send a data payload with the read_capacity response buffer. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_RESPONSE_LENGTH, UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_RESPONSE_LENGTH); + break; + + case UX_SLAVE_CLASS_STORAGE_GET_PERFORMANCE_PAGE_0 : + + /* Put the length to be returned. */ + _ux_utility_long_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer, + USBX_DEVICE_CLASS_STORAGE_GET_PERFORMANCE_0_LENGTH + 8); + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer + 8, + usbx_device_class_storage_performance, USBX_DEVICE_CLASS_STORAGE_GET_PERFORMANCE_0_LENGTH); + + /* Send a data payload with the read_capacity response buffer. */ + _ux_device_stack_transfer_request(transfer_request, USBX_DEVICE_CLASS_STORAGE_GET_PERFORMANCE_0_LENGTH + 8, USBX_DEVICE_CLASS_STORAGE_GET_PERFORMANCE_0_LENGTH + 8); + break; + } + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_get_status_notification.c b/common/usbx_device_classes/src/ux_device_class_storage_get_status_notification.c new file mode 100644 index 0000000..aaffd92 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_get_status_notification.c @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_get_status_notification PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a GET_STATUS_NOTIFICATION command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_short_put_big_endian Put 16-bit big endian */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_get_status_notification(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR *media_notification; +ULONG media_notification_length; +ULONG notification_class; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_READ_CAPACITY, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Ensure the callback has been initialized. */ + if (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_notification == UX_NULL) + { + + /* We need to STALL the IN endpoint. The endpoint will be reset by the host. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Now we return a CSW with Error. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Return error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } + + /* Extract the notification from the cbwcb. */ + notification_class = (ULONG) *(cbwcb + UX_SLAVE_CLASS_STORAGE_EVENT_NOTIFICATION_CLASS_REQUEST); + + /* Obtain the notification of the device. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_notification(storage, lun, + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, + notification_class, + &media_notification, + &media_notification_length); + + /* Check the status for error. */ + if (status != UX_SUCCESS) + { + + /* We need to STALL the IN endpoint. The endpoint will be reset by the host. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Now we return a CSW with Error. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + } + else + { + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Put the length of the notification length in the buffer. */ + _ux_utility_short_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer, (USHORT)media_notification_length); + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer + sizeof (USHORT), + media_notification, + media_notification_length); + + /* Update the notification length. */ + media_notification_length += (ULONG)sizeof (USHORT); + + /* Send a data payload with the notification buffer. */ + _ux_device_stack_transfer_request(transfer_request, + media_notification_length, + media_notification_length); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_initialize.c b/common/usbx_device_classes/src/ux_device_class_storage_initialize.c new file mode 100644 index 0000000..db5704d --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_initialize.c @@ -0,0 +1,205 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +/* Define the Slave Storage Class Inquiry data : DO NOT CHANGE THE LENGTH OF THESE ITEMS */ + +UCHAR _ux_system_slave_class_storage_vendor_id[] = "ExpressL"; +UCHAR _ux_system_slave_class_storage_product_id[] = "USBX storage dev"; +UCHAR _ux_system_slave_class_storage_product_rev[] = "2000"; +UCHAR _ux_system_slave_class_storage_product_serial[] = "12345678901234567890"; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB storage device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to storage command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_delete Delete thread */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_initialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UINT status; +UX_SLAVE_CLASS_STORAGE *storage; +UX_SLAVE_CLASS_STORAGE_PARAMETER *storage_parameter; +UX_SLAVE_CLASS *class; +ULONG lun_index; + + /* Get the pointer to the application parameters for the storage class. */ + storage_parameter = command -> ux_slave_class_command_parameter; + + /* Ensure the number of LUN declared by the caller does not exceed the + max number allowed for LUN storage. */ + if (storage_parameter -> ux_slave_class_storage_parameter_number_lun > UX_MAX_SLAVE_LUN) + return UX_ERROR; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Create an instance of the device storage class. */ + storage = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_STORAGE)); + + /* Check for successful allocation. */ + if (storage == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Allocate some memory for the thread stack. */ + class -> ux_slave_class_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* If it's OK, create thread. */ + if (class -> ux_slave_class_thread_stack != UX_NULL) + + /* This instance needs to be running in a different thread. So start + a new thread. We pass a pointer to the class to the new thread. This thread + does not start until we have a instance of the class. */ + status = _ux_utility_thread_create(&class -> ux_slave_class_thread, "ux_slave_storage_thread", + _ux_device_class_storage_thread, + (ULONG) (ALIGN_TYPE) class, (VOID *) class -> ux_slave_class_thread_stack, + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, TX_DONT_START); + else + status = UX_MEMORY_INSUFFICIENT; + + /* If thread resources allocated, go on. */ + if (status == UX_SUCCESS) + { + + UX_THREAD_EXTENSION_PTR_SET(&(class -> ux_slave_class_thread), class) + + /* Store the number of LUN declared. */ + storage -> ux_slave_class_storage_number_lun = storage_parameter -> ux_slave_class_storage_parameter_number_lun; + + /* Copy each individual LUN parameters. */ + for (lun_index = 0; lun_index < storage -> ux_slave_class_storage_number_lun; lun_index++) + { + + /* Check block length size. */ + if (storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_block_length > UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE) + { + /* Cannot proceed. */ + status = (UX_MEMORY_INSUFFICIENT); + break; + } + + /* Store all the application parameter information about the media. */ + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_last_lba = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_last_lba; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_block_length = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_block_length; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_type = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_type; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_removable_flag = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_removable_flag; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_read_only_flag = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_read_only_flag; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_read = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_read; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_flush = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_flush; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_write = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_write; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_status = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_status; + storage -> ux_slave_class_storage_lun[lun_index].ux_slave_class_storage_media_notification = storage_parameter -> ux_slave_class_storage_parameter_lun[lun_index].ux_slave_class_storage_media_notification; + } + + /* If it's OK, complete it. */ + if (status == UX_SUCCESS) + { + + /* Store the start and stop signals if needed by the application. */ + storage -> ux_slave_class_storage_instance_activate = storage_parameter -> ux_slave_class_storage_instance_activate; + storage -> ux_slave_class_storage_instance_deactivate = storage_parameter -> ux_slave_class_storage_instance_deactivate; + + /* Store the vendor id, product id, product revision and product serial. */ + if (storage_parameter -> ux_slave_class_storage_parameter_vendor_id) + storage -> ux_slave_class_storage_vendor_id = storage_parameter -> ux_slave_class_storage_parameter_vendor_id; + else + storage -> ux_slave_class_storage_vendor_id = _ux_system_slave_class_storage_vendor_id; + + if (storage_parameter -> ux_slave_class_storage_parameter_product_id) + storage -> ux_slave_class_storage_product_id = storage_parameter -> ux_slave_class_storage_parameter_product_id; + else + storage -> ux_slave_class_storage_product_id = _ux_system_slave_class_storage_product_id; + + if (storage_parameter -> ux_slave_class_storage_parameter_product_rev) + storage -> ux_slave_class_storage_product_rev = storage_parameter -> ux_slave_class_storage_parameter_product_rev; + else + storage -> ux_slave_class_storage_product_rev = _ux_system_slave_class_storage_product_rev; + + if (storage_parameter -> ux_slave_class_storage_parameter_product_serial) + storage -> ux_slave_class_storage_product_serial = storage_parameter -> ux_slave_class_storage_parameter_product_serial; + else + storage -> ux_slave_class_storage_product_serial = _ux_system_slave_class_storage_product_serial; + + /* Save the address of the STORAGE instance inside the STORAGE container. */ + class -> ux_slave_class_instance = (VOID *) storage; + + return(UX_SUCCESS); + } + + /* Free thread resources. */ + _ux_utility_thread_delete(&class -> ux_slave_class_thread); + } + + if (class -> ux_slave_class_thread_stack != UX_NULL) + _ux_utility_memory_free(&class -> ux_slave_class_thread_stack); + + /* Free instance. */ + _ux_utility_memory_free(storage); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_inquiry.c b/common/usbx_device_classes/src/ux_device_class_storage_inquiry.c new file mode 100644 index 0000000..aa77c82 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_inquiry.c @@ -0,0 +1,207 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_inquiry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a INQUIRY command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_short_put_big_endian Put 16-bit big endian */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_inquiry(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR inquiry_page_code; +UCHAR inquiry_length; +UCHAR inquiry_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH]; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_INQUIRY, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* From the SCSI Inquiry payload, get the page code. */ + inquiry_page_code = *(cbwcb + UX_SLAVE_CLASS_STORAGE_INQUIRY_PAGE_CODE); + + /* And the length to be returned. */ + inquiry_length = *(cbwcb + UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_ADDITIONAL_LENGTH); + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Ensure the data buffer is cleaned. */ + _ux_utility_memory_set(inquiry_buffer, 0, UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH); + + /* Check for the maximum length to be returned. */ + if (inquiry_length > UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH) + inquiry_length = UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH; + + /* Ensure we know about the page code. */ + switch (inquiry_page_code) + { + + case UX_SLAVE_CLASS_STORAGE_INQUIRY_PAGE_CODE_STANDARD: + + /* Store the product type. */ + inquiry_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_PERIPHERAL_TYPE] = (UCHAR)storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_type; + + /* Store the Media Removable bit. */ + inquiry_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_REMOVABLE_MEDIA] = (UCHAR)storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_removable_flag; + + /* Store the Data Format bit. */ + if (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_type == UX_SLAVE_CLASS_STORAGE_MEDIA_CDROM) + inquiry_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_DATA_FORMAT] = 0x32; + else + inquiry_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_DATA_FORMAT] = 0x00; + + /* Store the length of the response. There is a hack here. For CD-ROM, the data lg is fixed to 0x5B ! */ + if (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_type != UX_SLAVE_CLASS_STORAGE_MEDIA_CDROM) + inquiry_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_ADDITIONAL_LENGTH] = UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH; + else + inquiry_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_ADDITIONAL_LENGTH] = UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH_CD_ROM; + + /* Fill in the storage vendor ID. */ + _ux_utility_memory_copy(inquiry_buffer + UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_VENDOR_INFORMATION, + storage -> ux_slave_class_storage_vendor_id, 8); + + /* Fill in the product vendor ID. */ + _ux_utility_memory_copy(inquiry_buffer + UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_PRODUCT_ID, + storage -> ux_slave_class_storage_product_id, 16); + + /* Fill in the product revision number. */ + _ux_utility_memory_copy(inquiry_buffer + UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_PRODUCT_REVISION, + storage -> ux_slave_class_storage_product_rev, 4); + + /* Copy the Inquiry Buffer into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, inquiry_buffer, inquiry_length); + + /* Send a data payload with the inquiry response buffer. */ + _ux_device_stack_transfer_request(transfer_request, inquiry_length, inquiry_length); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + break; + + case UX_SLAVE_CLASS_STORAGE_INQUIRY_PAGE_CODE_SERIAL: + + /* Initialize the page code in response buffer. */ + _ux_utility_short_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer, UX_SLAVE_CLASS_STORAGE_INQUIRY_PAGE_CODE_SERIAL); + + /* Initialize the length of the serial number in response buffer. */ + _ux_utility_short_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer + 2, 20); + + /* Copy the serial number buffer into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer + 4, storage -> ux_slave_class_storage_product_serial, 20); + + /* Send a data payload with the inquiry response buffer. */ + _ux_device_stack_transfer_request(transfer_request, 24, 24); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + break; + + default: + + /* The page code is not supported. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x05; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x26; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x01; + + /* Return error. */ + status = UX_ERROR; + + break; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_mode_select.c b/common/usbx_device_classes/src/ux_device_class_storage_mode_select.c new file mode 100644 index 0000000..f314a52 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_mode_select.c @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_mode_select PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a MODE_SELECT SCSI command. It is not */ +/* supported in this release. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_mode_select(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + + UX_PARAMETER_NOT_USED(cbwcb); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_MODE_SELECT, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* This command is not yet supported. So Stall the endpoint. */ + _ux_device_stack_endpoint_stall(endpoint_out); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x05; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x26; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x01; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return not supported error! */ + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_mode_sense.c b/common/usbx_device_classes/src/ux_device_class_storage_mode_sense.c new file mode 100644 index 0000000..53934c5 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_mode_sense.c @@ -0,0 +1,261 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +#ifdef UX_SLAVE_CLASS_STORAGE_INCLUDE_MMC +#define USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CDROM_LENGTH (0x42 + 2) +UCHAR usbx_device_class_storage_mode_sense_page_cdrom[USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CDROM_LENGTH] = { + + 0x2A, 0x42, 0x3F, 0x37, 0xF1, 0x77, 0x29, 0x23, + 0x10, 0x89, 0x01, 0x00, 0x02, 0x00, 0x05, 0x84, + 0x00, 0x10, 0x10, 0x89, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + +}; +#else +#define USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CDROM_LENGTH (0) +#endif +#define USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CACHE_LENGTH (0x12 + 2) +#define USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_IEC_LENGTH (0x0A + 2) + +/* Ensure sense pages can fit in the bulk in endpoint's transfer buffer. */ +#define USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_ALL_RESPONSE_LENGTH ( \ + UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_HEADER_LENGTH_10 + \ + USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CDROM_LENGTH + \ + USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CACHE_LENGTH + \ + USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_IEC_LENGTH) +#if USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_ALL_RESPONSE_LENGTH > UX_SLAVE_REQUEST_DATA_MAX_LENGTH +#error "The maximum-sized MODE_SENSE response cannot fit inside the bulk in endpoint's data buffer." +#endif + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_mode_sense PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a MODE_SENSE SCSI command. It supports */ +/* the standard page for the CD-ROM. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* _ux_utility_short_put_big_endian Put 16-bit big endian */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_mode_sense(UX_SLAVE_CLASS_STORAGE *storage, + ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, + UCHAR *cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG mode_sense_reply_length; +ULONG page_code; +ULONG mode_sense_command; +UCHAR read_only_flag; +ULONG response_header_length; +ULONG medium_type_index; +ULONG mode_data_length; +UCHAR *page_pointer; +ULONG page_length; + + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_MODE_SENSE, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Get the command format : we have 1a and 5a. */ + mode_sense_command = (ULONG) *(cbwcb + UX_SLAVE_CLASS_STORAGE_MODE_SENSE_OPERATION); + + /* Extract the notification from the cbwcb. */ + page_code = (ULONG) *(cbwcb + UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PC_PAGE_CODE); + + /* Check the command. */ + if (mode_sense_command == UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT) + { + + /* Extract the length to be returned by the cbwcb. */ + mode_sense_reply_length = (ULONG) *(cbwcb + UX_SLAVE_CLASS_STORAGE_MODE_SENSE_ALLOCATION_LENGTH_6); + medium_type_index = UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_MEDIUM_TYPE_6; + response_header_length = UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_HEADER_LENGTH_6; + } + + else + { + + /* Extract the length to be returned by the cbwcb. */ + mode_sense_reply_length = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_MODE_SENSE_ALLOCATION_LENGTH_10); + medium_type_index = UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_MEDIUM_TYPE_10; + response_header_length = UX_SLAVE_CLASS_STORAGE_MODE_SENSE_PARAMETER_HEADER_LENGTH_10; + } + + /* Ensure reply not exceed storage buffer. */ + if (mode_sense_reply_length > UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE) + mode_sense_reply_length = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE; + + /* Ensure memory buffer cleaned. */ + _ux_utility_memory_set(transfer_request -> ux_slave_transfer_request_data_pointer, 0, mode_sense_reply_length); + + /* Establish READ ONLY flag. */ + if (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_read_only_flag == UX_TRUE) + + /* This device is Read Only. */ + read_only_flag = 0x80; + + else + + /* This device can be written to. */ + read_only_flag = 0; + + /* Build response based on expected page codes. */ + + /* Initialize length and page pointer. */ + mode_data_length = response_header_length; + page_pointer = transfer_request -> ux_slave_transfer_request_data_pointer + response_header_length; + +#ifdef UX_SLAVE_CLASS_STORAGE_INCLUDE_MMC + /* CD Capabilities and Mechanical Status mode page. */ + if(page_code == UX_SLAVE_CLASS_STORAGE_MMC2_PAGE_CODE_CDROM || + page_code == UX_SLAVE_CLASS_STORAGE_PAGE_CODE_ALL) + { + page_length = USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CDROM_LENGTH; + + /* Copy page data. */ + _ux_utility_memory_copy(page_pointer, usbx_device_class_storage_mode_sense_page_cdrom, page_length); + + /* Update pointer and length. */ + mode_data_length += page_length; + page_pointer += page_length; + } +#endif + + /* Caching mode page is returned if cache flush callback implemented. */ + if (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_flush != UX_NULL && + (page_code == UX_SLAVE_CLASS_STORAGE_PAGE_CODE_CACHE || + page_code == UX_SLAVE_CLASS_STORAGE_PAGE_CODE_ALL)) + { + page_length = USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_CACHE_LENGTH; + + /* Store page code. */ + *(page_pointer) = UX_SLAVE_CLASS_STORAGE_PAGE_CODE_CACHE; + + /* Store the length of the page data. */ + *(page_pointer + 1) = 0x12; + + /* Set the Write Cache Enabled (WCE) bit. */ + *(page_pointer + 2) |= 0x04; + + mode_data_length += page_length; + page_pointer += page_length; + } + + /* Informational Exceptions Control mode page. */ + if (page_code == UX_SLAVE_CLASS_STORAGE_PAGE_CODE_IEC || + page_code == UX_SLAVE_CLASS_STORAGE_PAGE_CODE_ALL) + { + page_length = USBX_DEVICE_CLASS_STORAGE_MODE_SENSE_PAGE_IEC_LENGTH; + + /* Store page code. */ + *(page_pointer) = UX_SLAVE_CLASS_STORAGE_PAGE_CODE_IEC; + + /* Store the length of the page data. */ + *(page_pointer + 1) = 0x0A; + + mode_data_length += page_length; + page_pointer += page_length; + } + + /* Put the payload length in the header. */ + if (mode_sense_command == UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT) + * transfer_request -> ux_slave_transfer_request_data_pointer = (UCHAR)(mode_data_length); + else + _ux_utility_short_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer, (USHORT)mode_data_length); + + /* Store the write protection flag. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + medium_type_index + 1) = read_only_flag; + + /* Send a payload with the response buffer. */ + _ux_device_stack_transfer_request(transfer_request, mode_sense_reply_length, mode_sense_reply_length); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_prevent_allow_media_removal.c b/common/usbx_device_classes/src/ux_device_class_storage_prevent_allow_media_removal.c new file mode 100644 index 0000000..903922e --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_prevent_allow_media_removal.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_prevent_allow_media_removal PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function allows or prevents the removal of the media. We don't */ +/* do anything here, just the CSW is returned with a SUCCESS code. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_prevent_allow_media_removal(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + + UX_PARAMETER_NOT_USED(cbwcb); + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_PREVENT_ALLOW_MEDIA_REMOVAL, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* We return a CSW with success. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_read.c b/common/usbx_device_classes/src/ux_device_class_storage_read.c new file mode 100644 index 0000000..4fb48dd --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_read.c @@ -0,0 +1,226 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a READ command in 32 or 16 bits. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* scsi_command SCSI command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_storage_media_read) Read from media */ +/* (ux_slave_class_storage_media_status) Get media status */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_get_big_endian Get 32-bit big endian */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_read(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb, UCHAR scsi_command) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG lba; +ULONG total_number_blocks; +ULONG number_blocks; +ULONG media_status; +ULONG total_length; +ULONG transfer_length; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* Get the LBA from the CBWCB. */ + lba = _ux_utility_long_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_LBA); + + /* The type of commands will tell us the width of the field containing the number + of sectors to read. */ + if (scsi_command == UX_SLAVE_CLASS_STORAGE_SCSI_READ16) + + /* Get the number of blocks from the CBWCB in 16 bits. */ + total_number_blocks = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_TRANSFER_LENGTH_16); + + else + + /* Get the number of blocks from the CBWCB in 32 bits. */ + total_number_blocks = _ux_utility_long_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_TRANSFER_LENGTH_32); + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Compute the total length to transfer and how much remains. */ + total_length = total_number_blocks * storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length; + + /* It may take several transfers to send the requested data. */ + while (total_number_blocks) + { + + /* Obtain the status of the device. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_status(storage, lun, + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, &media_status); + + /* Update the request sense. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (UCHAR) (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = (UCHAR) ((media_status >> 8 ) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = (UCHAR) ((media_status >> 16 ) & 0xff); + + /* If there is a problem, return a failed command. */ + if (status != UX_SUCCESS) + { + + /* We have a problem, media status error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Return an error. */ + return(UX_ERROR); + } + + /* How much can we send in this transfer? */ + if (total_length > UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE) + + /* Compute the transfer length based on the maximum allowed. */ + transfer_length = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE; + + else + + /* Compute the transfer length based on what is left to transfer. */ + transfer_length = total_length; + + /* Compute the number of blocks to transfer. */ + number_blocks = transfer_length / storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_READ, storage, lun, transfer_request -> ux_slave_transfer_request_data_pointer, + number_blocks, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Execute the read command from the local media. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_read(storage, lun, + transfer_request -> ux_slave_transfer_request_data_pointer, number_blocks, lba, &media_status); + + /* If there is a problem, return a failed command. */ + if (status != UX_SUCCESS) + { + + /* We have a problem, request error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (UCHAR) (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = (UCHAR) ((media_status >> 8 ) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = (UCHAR) ((media_status >> 16 ) & 0xff); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Return an error. */ + return(UX_ERROR); + } + + /* Sends the data payload back to the caller. */ + status = _ux_device_stack_transfer_request(transfer_request, transfer_length, transfer_length); + + /* Check the status. */ + if(status != UX_SUCCESS) + { + + /* We have a problem, request error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x02; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x54; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Return an error. */ + return(UX_ERROR); + + } + + /* Update the LBA address. */ + lba += number_blocks; + + /* Update the length to remain. */ + total_length -= transfer_length; + + /* Update the number of blocks to read. */ + total_number_blocks -= number_blocks; + } + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); +} diff --git a/common/usbx_device_classes/src/ux_device_class_storage_read_capacity.c b/common/usbx_device_classes/src/ux_device_class_storage_read_capacity.c new file mode 100644 index 0000000..cd3a63a --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_read_capacity.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_read_capacity PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a READ_CAPACITY command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_long_put_big_endian Put 32-bit big endian */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_set Set memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_read_capacity(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +ULONG media_status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR read_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH]; + + UX_PARAMETER_NOT_USED(cbwcb); + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_READ_CAPACITY, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the status of the device. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_status(storage, lun, + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, &media_status); + + /* Update the request sense. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (UCHAR) (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = (UCHAR) ((media_status >> 8 ) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = (UCHAR) ((media_status >> 16 ) & 0xff); + + /* Check the status for error. */ + if (status != UX_SUCCESS) + { + + /* We need to STALL the IN endpoint. The endpoint will be reset by the host. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Now we return a CSW with Error. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + } + else + { + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Ensure it is cleaned. */ + _ux_utility_memory_set(read_capacity_buffer, 0, UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH); + + /* Insert the last LBA address in the response. */ + _ux_utility_long_put_big_endian(&read_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LAST_LBA], + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_last_lba); + + /* Insert the block length in the response. */ + _ux_utility_long_put_big_endian(&read_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_BLOCK_SIZE], + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length); + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + read_capacity_buffer, UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH); + + /* Send a data payload with the read_capacity response buffer. */ + _ux_device_stack_transfer_request(transfer_request, + UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH, + UX_SLAVE_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_read_disk_information.c b/common/usbx_device_classes/src/ux_device_class_storage_read_disk_information.c new file mode 100644 index 0000000..ad52508 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_read_disk_information.c @@ -0,0 +1,159 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +#define USBX_DEVICE_CLASS_STORAGE_DISK_INFORMATION_LENGTH 34 +UCHAR usbx_device_class_storage_disk_information[] = { + + 0x00, 0x00, /* Entire length of disk_information */ + 0x0e, /* Erasable/state of last session/disk status ...*/ + 0x01, /* Number of first track on disk. */ + 0x01, /* Number of sessions. */ + 0x01, /* First track number in last session. */ + 0x01, /* Last track number in last session. */ + + 0x00, /* DID_V, DBC_V, URU .... */ + 0x00, /* Disk type */ + 0x00, /* Number of sessions */ + 0x00, /* First Track Number in last session */ + 0x00, /* Last Track Number in last session */ + + 0x00, 0x00, 0x00, 0x00, /* Disk Identification */ + 0xff, 0xff, 0xff, 0xff, /* Last session lead in start time */ + 0xff, 0xff, 0xff, 0xff, /* Last possible start time for SOLO */ + 0x00, 0x00, 0x00, 0x00, /* Disk Bar Code */ + 0x00, /* Reserved */ + 0x00, /* Number of OPC table entries. */ + 0x00, 0x00, 0x00, 0x00, /* Some padding ? */ + 0x00, 0x00 /* */ + + }; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_read_disk_information PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a READ_DISK_INFORMATION command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_short_put_big_endian Put 16-bit big endian */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_read_disk_information(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG allocation_length; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_GET_CONFIGURATION, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Initialize the length of the disk information field. */ + _ux_utility_short_put_big_endian(usbx_device_class_storage_disk_information, (USBX_DEVICE_CLASS_STORAGE_DISK_INFORMATION_LENGTH - 2)); + + /* Clean the disk status. */ + usbx_device_class_storage_disk_information[2] &= (UCHAR)~3; + + /* Update the disk status. */ + usbx_device_class_storage_disk_information[2] = (UCHAR)(usbx_device_class_storage_disk_information[2] | (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_disk_status & 3)); + + /* Clean the last session state. */ + usbx_device_class_storage_disk_information[2] &= (UCHAR)~0x0c; + + /* Update the last session state. */ + usbx_device_class_storage_disk_information[2] = (UCHAR)(usbx_device_class_storage_disk_information[2] | ((storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_last_session_state << 2) & 0x0c)); + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Get the allocation length. */ + allocation_length = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_DISK_INFORMATION_ALLOCATION_LENGTH); + + /* Can we send all the disk information ? */ + if (allocation_length > USBX_DEVICE_CLASS_STORAGE_DISK_INFORMATION_LENGTH) + + /* Yes, so send only the disk information profile. */ + allocation_length = USBX_DEVICE_CLASS_STORAGE_DISK_INFORMATION_LENGTH; + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + usbx_device_class_storage_disk_information, + allocation_length); + + /* Send a data payload with the read_capacity response buffer. */ + _ux_device_stack_transfer_request(transfer_request, + allocation_length, + allocation_length); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_read_dvd_structure.c b/common/usbx_device_classes/src/ux_device_class_storage_read_dvd_structure.c new file mode 100644 index 0000000..5375fd0 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_read_dvd_structure.c @@ -0,0 +1,276 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +#define USBX_DEVICE_CLASS_STORAGE_DVD_STRUCTURE_LENGTH (2048 + 4) +UCHAR usbx_device_class_storage_dvd_structure[] = { + 0x08, 0x02, 0x00, 0x00, 0x25, 0x0F, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0xAF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00, 0x00, 0x04, 0x6E, 0xA0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x78, 0x80, 0x00, 0x03, 0x54, 0x59, 0x47, 0x30, 0x32, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0xAA, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x08, 0x01, 0x11, + 0x87, 0x78, 0x80, 0x00, 0x07, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x13, 0x0D, + 0x10, 0x0E, 0x07, 0x00, 0x09, 0x97, 0x07, 0x0D, 0x0B, 0x78, 0x88, 0x00, 0x0A, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x0B, 0x06, 0x18, 0x17, 0x85, 0x78, 0x75, 0x00, 0x0C, 0xD6, 0x89, 0xA8, + 0x92, 0x01, 0x20, 0x00, 0x0D, 0x10, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x09, 0x21, 0x35, + 0x2D, 0x29, 0x1B, 0x00, 0x0F, 0x50, 0x1B, 0x2B, 0x1D, 0x97, 0xB5, 0x00, 0x10, 0x88, 0x84, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x09, 0x2D, 0x35, + 0x2D, 0x2B, 0x1A, 0x00, 0x13, 0x50, 0x1D, 0x2B, 0x1F, 0x97, 0xB5, 0x00, 0x14, 0x88, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + + }; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_read_dvd_structure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a READ_DVD_STRUCTURE command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_read_dvd_structure(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG allocation_length; +ULONG transfer_length = 0; +UCHAR *dvd_structure_pointer; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_OTHER, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Get the allocation length. */ + allocation_length = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_DISK_INFORMATION_ALLOCATION_LENGTH); + + /* Can we send all the disk information ? */ + if (allocation_length > USBX_DEVICE_CLASS_STORAGE_DVD_STRUCTURE_LENGTH) + + /* Yes, so send only the disk information profile. */ + allocation_length = USBX_DEVICE_CLASS_STORAGE_DVD_STRUCTURE_LENGTH; + + /* Set dvd structure pointer. */ + dvd_structure_pointer = usbx_device_class_storage_dvd_structure; + + /* Send back everything in chunks. */ + while (allocation_length) + { + + /* Check length. Is it more than one packet ? */ + if (allocation_length > UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE) + + /* Compute the transfer length based on the maximum allowed. */ + transfer_length = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE; + + else + + /* Compute the transfer length based on what is left to transfer. */ + transfer_length = allocation_length; + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + dvd_structure_pointer, + transfer_length); + + /* Send a data payload with the read_capacity response buffer. */ + status = _ux_device_stack_transfer_request(transfer_request, + transfer_length, + transfer_length); + + /* Check transfer completion status. */ + if (status != UX_SUCCESS) + return(status); + + /* Advance pointer to dvd structure. */ + dvd_structure_pointer += transfer_length; + + /* Subtract what is left to send out. */ + allocation_length -= transfer_length; + } + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_read_format_capacity.c b/common/usbx_device_classes/src/ux_device_class_storage_read_format_capacity.c new file mode 100644 index 0000000..3be024e --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_read_format_capacity.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_read_format_capacity PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a READ_FORMAT_CAPACITY command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_long_put_big_endian Put 32-bit big endian */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_read_format_capacity(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR read_format_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LENGTH]; + + UX_PARAMETER_NOT_USED(cbwcb); + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_READ_FORMAT_CAPACITY, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Ensure it is cleaned. */ + _ux_utility_memory_set(read_format_capacity_buffer, 0, UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LENGTH); + + /* Insert the size of the response block. */ + _ux_utility_long_put_big_endian(&read_format_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_SIZE], 8); + + /* Insert the last LBA address in the response. */ + _ux_utility_long_put_big_endian(&read_format_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LAST_LBA], + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_last_lba); + + /* Insert the block length in the response. This is in 3 bytes. */ + _ux_utility_long_put_big_endian(&read_format_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_BLOCK_SIZE], + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length); + + /* Insert the response code : always 2. */ + read_format_capacity_buffer[UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_DESC_CODE] = 2; + + /* Copy the CSW into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + read_format_capacity_buffer, UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LENGTH); + + /* Send a data payload with the read_capacity response buffer. */ + _ux_device_stack_transfer_request(transfer_request, + UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LENGTH, + UX_SLAVE_CLASS_STORAGE_READ_FORMAT_CAPACITY_RESPONSE_LENGTH); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_read_toc.c b/common/usbx_device_classes/src/ux_device_class_storage_read_toc.c new file mode 100644 index 0000000..515adfe --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_read_toc.c @@ -0,0 +1,188 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_read_toc PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a READ_TOC SCSI command. This is only for */ +/* supporting CD-ROM emulation and is hardwired to what Windows wants. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_utility_memory_set Set memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_read_toc(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG allocation_length; +ULONG toc_length; +UCHAR toc_buffer[20]; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_READ_TOC, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Reset this buffer. */ + _ux_utility_memory_set(toc_buffer,0,20); + + /* Get the allocation length. */ + allocation_length = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_TOC_ALLOCATION_LENGTH); + + /* Insert the fist and last tack number. */ + toc_buffer[2] = 0x01; + toc_buffer[3] = 0x01; + + /* Set TOC length by default. */ + toc_length = 20; + + /* Insert the ADR and control values. */ + toc_buffer[5] = 0x14; + + /* Insert the TOC tack number. */ + toc_buffer[6] = 0x01; + + /* Check if the request is for the TOC or time stamp. */ + switch (*(cbwcb + UX_SLAVE_CLASS_STORAGE_READ_TOC_FORMAT)) + { + + case 0x02 : + + /* Set the toc buffer length. */ + toc_length = 20; + + /* Insert the TOC buffer length. */ + toc_buffer[1] = 0x12; + + /* Insert some time values. */ + toc_buffer[10] = 0x02; + toc_buffer[13] = 0x17; + toc_buffer[14] = 0xAA; + toc_buffer[18] = 0x04; + toc_buffer[19] = 0x1a; + + break; + + case 0x01 : + + /* Set the toc buffer length. */ + toc_length = 19; + + /* Insert the TOC buffer length. */ + toc_buffer[1] = 0x12; + + toc_buffer[13] = 0x17; + toc_buffer[14] = 0xAA; + toc_buffer[19] = 0xb0; + + break; + + case 0x00 : + + /* Set the toc buffer length. */ + toc_length = 20; + + /* Insert the TOC buffer length. */ + toc_buffer[1] = 0x12; + + /* Insert some time values. */ + toc_buffer[10] = 0x02; + toc_buffer[13] = 0x17; + toc_buffer[14] = 0xAA; + toc_buffer[18] = 0x04; + toc_buffer[19] = 0x1a; + + break; + } + + /* Copy the TOC Buffer into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, toc_buffer, toc_length); + + /* Check how much we can send back. */ + if (allocation_length > toc_length) + + /* We return less than demanded. */ + allocation_length = toc_length; + + /* Send a data payload with the TOC response buffer. */ + _ux_device_stack_transfer_request(transfer_request, allocation_length, allocation_length); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); + +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_report_key.c b/common/usbx_device_classes/src/ux_device_class_storage_report_key.c new file mode 100644 index 0000000..4deb505 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_report_key.c @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_report_key PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a REPORT_KEY SCSI command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_utility_memory_set Set memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_report_key(UX_SLAVE_CLASS_STORAGE *storage, + ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, + UCHAR *cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG allocation_length; +ULONG key_format; + + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_OTHER, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Get the report key. */ + key_format = (ULONG) *(cbwcb + UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT); + + /* Extract the length to be returned by the cbwcb. */ + allocation_length = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ALLOCATION_LENGTH); + + /* Ensure memory buffer cleaned. */ + _ux_utility_memory_set(transfer_request -> ux_slave_transfer_request_data_pointer, 0, 64); + + /* Filter page code. This is necessary to isolate the CD-ROM mode sense response. */ + switch (key_format) + { + + case UX_SLAVE_CLASS_STORAGE_REPORT_KEY_FORMAT_RPC : + + + /* Put the length to be returned. */ + _ux_utility_short_put_big_endian(transfer_request -> ux_slave_transfer_request_data_pointer, + UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_PAYLOAD); + + /* Put the reset field. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_RESET_FIELD) = 0x25; + + /* Put the region mask. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_REGION_MASK) = 0xFF; + + /* No region code. */ + *(transfer_request -> ux_slave_transfer_request_data_pointer + UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_RPC_SCHEME) = 0x00; + + /* Compute the payload to return. Depends on the request. */ + if (allocation_length > UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_LENGTH) + + /* Adjust the reply. */ + allocation_length = UX_SLAVE_CLASS_STORAGE_REPORT_KEY_ANSWER_LENGTH; + + /* Send a data payload with the read_capacity response buffer. */ + _ux_device_stack_transfer_request(transfer_request, allocation_length, allocation_length); + + break; + + default : + + /* Send a data payload with the read_capacity response buffer. */ + status = _ux_device_stack_transfer_request(transfer_request, 0, 0); + break; + + } + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x00; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_request_sense.c b/common/usbx_device_classes/src/ux_device_class_storage_request_sense.c new file mode 100644 index 0000000..8601187 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_request_sense.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_request_sense PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a request sense command. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_set Set memory */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_request_sense(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +UCHAR sense_buffer[UX_SLAVE_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH]; + + UX_PARAMETER_NOT_USED(cbwcb); + UX_PARAMETER_NOT_USED(endpoint_out); + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_in -> ux_slave_endpoint_transfer_request; + + /* Ensure it is cleaned. */ + _ux_utility_memory_set(sense_buffer, 0, UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH); + + /* Initialize the response buffer with the error code. */ + sense_buffer[UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ERROR_CODE] = + UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ERROR_CODE_VALUE; + + /* Initialize the response buffer with the sense key. */ + sense_buffer[UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_SENSE_KEY] = + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key; + + /* Initialize the response buffer with the code. */ + sense_buffer[UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE] = + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code; + + /* Initialize the response buffer with the code qualifier. */ + sense_buffer[UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE_QUALIFIER] = + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_REQUEST_SENSE, storage, lun, + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key, + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Initialize the response buffer with the additional length. */ + sense_buffer[UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ADD_LENGTH] = 10; + + + /* Copy the request sense response into the transfer request memory. */ + _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, + sense_buffer, UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH); + + /* Send a data payload with the sense codes. */ + _ux_device_stack_transfer_request(transfer_request, UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH, + UX_SLAVE_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH); + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_start_stop.c b/common/usbx_device_classes/src/ux_device_class_storage_start_stop.c new file mode 100644 index 0000000..2bd9f12 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_start_stop.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_start_stop PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts or stops the media. This command will not do */ +/* anything here, just the CSW is returned with a SUCCESS code. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_start_stop(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + + UX_PARAMETER_NOT_USED(cbwcb); + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_START_STOP, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* We return a CSW with success. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_synchronize_cache.c b/common/usbx_device_classes/src/ux_device_class_storage_synchronize_cache.c new file mode 100644 index 0000000..bff1623 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_synchronize_cache.c @@ -0,0 +1,174 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_synchronize_cache PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a SYNCHRONIZE_CACHE command in 32 or 16 */ +/* bits. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to the CBWCB */ +/* scsi_command SCSI command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_storage_media_status) Get media status */ +/* (ux_slave_class_storage_media_flush) Flush media */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_utility_long_get_big_endian Get 32-bit big endian */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_synchronize_cache(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR *cbwcb, UCHAR scsi_command) +{ + +UINT status; +ULONG lba; +USHORT number_blocks; +ULONG media_status; +UCHAR flags; + + + UX_PARAMETER_NOT_USED(endpoint_out); + UX_PARAMETER_NOT_USED(scsi_command); + + /* Is there not an implementation? */ + if (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_flush == UX_NULL) + { + + /* This means the application is not using a cache. */ + + /* Return a CSW with success. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return success. */ + return(UX_SUCCESS); + } + + flags = *(cbwcb + UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_FLAGS); + + /* Get the LBA and number of blocks from the CBWCB in 16 bits. */ + lba = _ux_utility_long_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_LBA); + number_blocks = (USHORT)_ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_NUMBER_OF_BLOCKS); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_SYNCHRONIZE_CACHE, storage, lun, lba, number_blocks, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the status of the device. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_status(storage, + lun, storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, &media_status); + + /* Update the request sense. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (UCHAR) (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = (UCHAR) ((media_status >> 8 ) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = (UCHAR) ((media_status >> 16 ) & 0xff); + + /* If there is a problem, return a failed command. */ + if (status != UX_SUCCESS) + { + + /* We have a problem, media status error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* We are done here. */ + return(UX_ERROR); + } + + /* If the immediate bit is set, we return a CSW before flush. */ + else if ((flags & UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_FLAGS_IMMED) != 0) + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Send the flush command to the local media. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_flush(storage, lun, number_blocks, lba, &media_status); + + /* Update the request sense. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (UCHAR) (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = (UCHAR) ((media_status >> 8 ) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = (UCHAR) ((media_status >> 16 ) & 0xff); + + /* If the immediate bit is set, we are already done, no matter what local operation status is. */ + if ((flags & UX_SLAVE_CLASS_STORAGE_SYNCHRONIZE_CACHE_FLAGS_IMMED) != 0) + return(status); + + /* If there is a problem, return a failed command. */ + if (status != UX_SUCCESS) + { + + /* We have a problem, request error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Return an error. */ + return(UX_ERROR); + } + else + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); +} diff --git a/common/usbx_device_classes/src/ux_device_class_storage_test_ready.c b/common/usbx_device_classes/src/ux_device_class_storage_test_ready.c new file mode 100644 index 0000000..8444133 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_test_ready.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_test_ready PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function tests if the SCSI slave device is ready. */ +/* The status function of the storage devices is called. If there is */ +/* an error, the request sense parameters are set for the host to */ +/* investigate. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_storage_media_status) Get media status */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_test_ready(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + +UINT status; +ULONG media_status; + + UX_PARAMETER_NOT_USED(cbwcb); + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_TEST_READY, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the status of the device. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_status(storage, lun, + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, &media_status); + + /* Set the sense/code/qualifier codes for the REQUEST_SENSE command. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = ((media_status >> 8) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = ((media_status >> 16) & 0xff); + + /* Check the status for error. */ + if (status != UX_SUCCESS) + { + + /* We return a CSW with error. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + } + else + { + + /* We return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_thread.c b/common/usbx_device_classes/src/ux_device_class_storage_thread.c new file mode 100644 index 0000000..790fec3 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_thread.c @@ -0,0 +1,417 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the thread of the storage class. */ +/* */ +/* INPUT */ +/* */ +/* class Address of storage class */ +/* container */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_format Storage class format */ +/* _ux_device_class_storage_inquiry Storage class inquiry */ +/* _ux_device_class_storage_mode_select Mode select */ +/* _ux_device_class_storage_mode_sense Mode sense */ +/* _ux_device_class_storage_prevent_allow_media_removal */ +/* Prevent media removal */ +/* _ux_device_class_storage_read Read */ +/* _ux_device_class_storage_read_capacity */ +/* Read capacity */ +/* _ux_device_class_storage_read_format_capacity */ +/* Read format capacity */ +/* _ux_device_class_storage_request_sense */ +/* Sense request */ +/* _ux_device_class_storage_start_stop Start/Stop */ +/* _ux_device_class_storage_synchronize_cache */ +/* Synchronize cache */ +/* _ux_device_class_storage_test_ready Ready test */ +/* _ux_device_class_storage_verify Verify */ +/* _ux_device_class_storage_write Write */ +/* _ux_device_stack_endpoint_stall Endpoint stall */ +/* _ux_device_stack_interface_delete Interface delete */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_thread_suspend Suspend thread */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_device_class_storage_thread(ULONG storage_class) +{ + +UX_SLAVE_CLASS *class; +UX_SLAVE_CLASS_STORAGE *storage; +UX_SLAVE_TRANSFER *transfer_request; +UX_SLAVE_DEVICE *device; +UX_SLAVE_INTERFACE *interface; +UX_SLAVE_ENDPOINT *endpoint_in; +UX_SLAVE_ENDPOINT *endpoint_out; +UINT status; +ULONG length; +ULONG cbwcb_length; +ULONG lun; +UCHAR *scsi_command; + + + /* This thread runs forever but can be suspended or resumed. */ + while(1) + { + + /* Cast properly the storage instance. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_SLAVE_CLASS, storage_class) + + /* Get the storage instance from this class container. */ + storage = (UX_SLAVE_CLASS_STORAGE *) class -> ux_slave_class_instance; + + /* Get the pointer to the device. */ + device = &_ux_system_slave -> ux_system_slave_device; + + /* This is the first time we are activated. We need the interface to the class. */ + interface = storage -> ux_slave_class_storage_interface; + + /* Locate the endpoints. */ + endpoint_in = interface -> ux_slave_interface_first_endpoint; + + /* Check the endpoint direction, if IN we have the correct endpoint. */ + if ((endpoint_in -> ux_slave_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) != UX_ENDPOINT_IN) + { + + /* Wrong direction, we found the OUT endpoint first. */ + endpoint_out = endpoint_in; + + /* So the next endpoint has to be the IN endpoint. */ + endpoint_in = endpoint_out -> ux_slave_endpoint_next_endpoint; + } + else + { + + /* We found the endpoint IN first, so next endpoint is OUT. */ + endpoint_out = endpoint_in -> ux_slave_endpoint_next_endpoint; + } + + /* All SCSI commands are on the endpoint OUT, from the host. */ + transfer_request = &endpoint_out -> ux_slave_endpoint_transfer_request; + + /* As long as the device is in the CONFIGURED state. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* We assume the worst situation. */ + status = UX_ERROR; + + /* Check state, they must be both RESET. */ + if (endpoint_out -> ux_slave_endpoint_state == UX_ENDPOINT_RESET && + !storage -> ux_slave_class_storage_phase_error) + { + + /* Send the request to the device controller. */ + status = _ux_device_stack_transfer_request(transfer_request, 64, 64); + + } + + /* Check the status. Our status is UX_ERROR if one of the endpoint was STALLED. We must wait for the host + to clear the mess. */ + if (status == UX_SUCCESS) + { + + /* Obtain the length of the transaction. */ + length = transfer_request -> ux_slave_transfer_request_actual_length; + + /* Obtain the buffer address containing the SCSI command. */ + scsi_command = transfer_request -> ux_slave_transfer_request_data_pointer; + + /* Obtain the lun from the CBW. */ + lun = (ULONG) *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_LUN); + + /* We have to memorize the SCSI command tag for the CSW phase. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_scsi_tag = _ux_utility_long_get(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_TAG); + + /* Ensure the LUN number is within our declared values and check the command + content and format. First we make sure we have a complete CBW. */ + if ((lun < storage -> ux_slave_class_storage_number_lun) && (length == UX_SLAVE_CLASS_STORAGE_CBW_LENGTH)) + { + + /* The length of the CBW is correct, analyze the header. */ + if (_ux_utility_long_get(scsi_command) == UX_SLAVE_CLASS_STORAGE_CBW_SIGNATURE_MASK) + { + + /* Get the length of the CBWCB. */ + cbwcb_length = (ULONG) *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB_LENGTH); + + /* Check the length of the CBWCB to ensure there is at least a command. */ + if (cbwcb_length != 0) + { + + /* Analyze the command stored in the CBWCB. */ + switch (*(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB)) + { + + case UX_SLAVE_CLASS_STORAGE_SCSI_TEST_READY: + + _ux_device_class_storage_test_ready(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_REQUEST_SENSE: + + _ux_device_class_storage_request_sense(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_FORMAT: + + _ux_device_class_storage_format(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_INQUIRY: + + _ux_device_class_storage_inquiry(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_START_STOP: + + _ux_device_class_storage_start_stop(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_PREVENT_ALLOW_MEDIA_REMOVAL: + + _ux_device_class_storage_prevent_allow_media_removal(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_READ_FORMAT_CAPACITY: + + _ux_device_class_storage_read_format_capacity(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_READ_CAPACITY: + + _ux_device_class_storage_read_capacity(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_VERIFY: + + _ux_device_class_storage_verify(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SELECT: + + _ux_device_class_storage_mode_select(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT: + case UX_SLAVE_CLASS_STORAGE_SCSI_MODE_SENSE: + + _ux_device_class_storage_mode_sense(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_READ32: + + _ux_device_class_storage_read(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB, + UX_SLAVE_CLASS_STORAGE_SCSI_READ32); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_READ16: + + _ux_device_class_storage_read(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB, + UX_SLAVE_CLASS_STORAGE_SCSI_READ16); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32: + + _ux_device_class_storage_write(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB, + UX_SLAVE_CLASS_STORAGE_SCSI_WRITE32); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16: + + _ux_device_class_storage_write(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB, + UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_SYNCHRONIZE_CACHE: + + _ux_device_class_storage_synchronize_cache(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB, *(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB)); + break; + +#ifdef UX_SLAVE_CLASS_STORAGE_INCLUDE_MMC + case UX_SLAVE_CLASS_STORAGE_SCSI_GET_STATUS_NOTIFICATION: + + _ux_device_class_storage_get_status_notification(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_GET_CONFIGURATION: + + _ux_device_class_storage_get_configuration(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DISK_INFORMATION: + + _ux_device_class_storage_read_disk_information(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_REPORT_KEY: + + _ux_device_class_storage_report_key(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_GET_PERFORMANCE: + + _ux_device_class_storage_get_performance(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_READ_DVD_STRUCTURE: + + _ux_device_class_storage_read_dvd_structure(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + break; + + case UX_SLAVE_CLASS_STORAGE_SCSI_READ_TOC: + + status = _ux_device_class_storage_read_toc(storage, lun, endpoint_in, endpoint_out, scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_CB); + + /* Special treatment of TOC command. If error, default to Stall endpoint. */ + if (status == UX_SUCCESS) + break; + /* fall through */ +#endif + + default: + + /* The command is unknown or unsupported, so we stall the endpoint. */ + + if (_ux_utility_long_get(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_DATA_LENGTH) > 0 && + ((*(scsi_command + UX_SLAVE_CLASS_STORAGE_CBW_FLAGS) & 0x80) == 0)) + + /* Data-Out from host to device, stall OUT. */ + _ux_device_stack_endpoint_stall(endpoint_out); + else + + /* Data-In from device to host, stall IN. */ + _ux_device_stack_endpoint_stall(endpoint_in); + + /* Initialize the request sense keys. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = UX_SLAVE_CLASS_STORAGE_SENSE_KEY_ILLEGAL_REQUEST; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = UX_SLAVE_CLASS_STORAGE_ASC_KEY_INVALID_COMMAND; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0; + + /* This is the tricky part of the SCSI state machine. We must send the CSW BUT need to wait + for the endpoint_in to be reset by the host. */ + while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED) + { + + /* Check the endpoint state. */ + if (endpoint_in -> ux_slave_endpoint_state == UX_ENDPOINT_RESET) + { + + /* Now we return a CSW with failure. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Check error code. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + break; + } + + else + + /* We must therefore wait a while. */ + _ux_utility_thread_relinquish(); + } + break; + } + } + else + + /* Phase error! */ + storage -> ux_slave_class_storage_phase_error = TX_TRUE; + } + + else + + /* Phase error! */ + storage -> ux_slave_class_storage_phase_error = TX_TRUE; + } + else + + /* Phase error! */ + storage -> ux_slave_class_storage_phase_error = TX_TRUE; + } + else + { + + if (storage -> ux_slave_class_storage_phase_error == TX_TRUE) + { + + /* We should keep the endpoints stalled. */ + _ux_device_stack_endpoint_stall(endpoint_out); + _ux_device_stack_endpoint_stall(endpoint_in); + } + + /* We must therefore wait a while. */ + _ux_utility_thread_relinquish(); + } + } + + /* We need to suspend ourselves. We will be resumed by the + device enumeration module. */ + _ux_utility_thread_suspend(&class -> ux_slave_class_thread); + } +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_uninitialize.c b/common/usbx_device_classes/src/ux_device_class_storage_uninitialize.c new file mode 100644 index 0000000..112225a --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_uninitialize.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_uninitialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deinitializes the USB storage device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to storage command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_thread_delete Delete thread */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_uninitialize(UX_SLAVE_CLASS_COMMAND *command) +{ + +UX_SLAVE_CLASS_STORAGE *storage; +UX_SLAVE_CLASS *class; + + /* Get the class container. */ + class = command -> ux_slave_class_command_class_ptr; + + /* Get the class instance in the container. */ + storage = (UX_SLAVE_CLASS_STORAGE *) class -> ux_slave_class_instance; + + /* Sanity check. */ + if (storage != UX_NULL) + { + + /* Remove STORAGE thread. */ + _ux_utility_thread_delete(&class -> ux_slave_class_thread); + + /* Remove the thread used by STORAGE. */ + _ux_utility_memory_free(class -> ux_slave_class_thread_stack); + + /* Free the resources. */ + _ux_utility_memory_free(storage); + } + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_verify.c b/common/usbx_device_classes/src/ux_device_class_storage_verify.c new file mode 100644 index 0000000..c915ec6 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_verify.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_verify PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function verifies a previous write command. */ +/* Here, the CSW is returned with a SUCCESS code. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to CBWCB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_verify(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb) +{ + + UX_PARAMETER_NOT_USED(cbwcb); + UX_PARAMETER_NOT_USED(endpoint_out); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_VERIFY, storage, lun, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* We return a CSW with success. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return success! */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_device_classes/src/ux_device_class_storage_write.c b/common/usbx_device_classes/src/ux_device_class_storage_write.c new file mode 100644 index 0000000..dbf0b49 --- /dev/null +++ b/common/usbx_device_classes/src/ux_device_class_storage_write.c @@ -0,0 +1,238 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Device Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define UX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "ux_api.h" +#include "ux_device_class_storage.h" +#include "ux_device_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_device_class_storage_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a WRITE command in 32 or 16 bits. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* endpoint_in Pointer to IN endpoint */ +/* endpoint_out Pointer to OUT endpoint */ +/* cbwcb Pointer to the CBWCB */ +/* scsi_command SCSI command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_slave_class_storage_media_status) Get media status */ +/* (ux_slave_class_storage_media_write) Write to media */ +/* _ux_device_class_storage_csw_send Send CSW */ +/* _ux_device_stack_endpoint_stall Stall endpoint */ +/* _ux_device_stack_transfer_request Transfer request */ +/* _ux_utility_long_get_big_endian Get 32-bit big endian */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Release memory */ +/* _ux_utility_long_get_big_endian Get 32-bit big endian */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* */ +/* CALLED BY */ +/* */ +/* Device Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_device_class_storage_write(UX_SLAVE_CLASS_STORAGE *storage, ULONG lun, + UX_SLAVE_ENDPOINT *endpoint_in, + UX_SLAVE_ENDPOINT *endpoint_out, UCHAR * cbwcb, UCHAR scsi_command) +{ + +UINT status; +UX_SLAVE_TRANSFER *transfer_request; +ULONG lba; +ULONG total_number_blocks; +ULONG number_blocks; +ULONG media_status; +ULONG total_length; +ULONG transfer_length; + + /* Get the LBA from the CBWCB. */ + lba = _ux_utility_long_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_WRITE_LBA); + + /* The type of commands will tell us the width of the field containing the number + of sectors to read. */ + if (scsi_command == UX_SLAVE_CLASS_STORAGE_SCSI_WRITE16) + + /* Get the number of blocks from the CBWCB in 16 bits. */ + total_number_blocks = _ux_utility_short_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_WRITE_TRANSFER_LENGTH_16); + + else + + /* Get the number of blocks from the CBWCB in 32 bits. */ + total_number_blocks = _ux_utility_long_get_big_endian(cbwcb + UX_SLAVE_CLASS_STORAGE_WRITE_TRANSFER_LENGTH_32); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_STORAGE_WRITE, storage, lun, lba, total_number_blocks, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0) + + /* Obtain the pointer to the transfer request. */ + transfer_request = &endpoint_out -> ux_slave_endpoint_transfer_request; + + /* Obtain the status of the device. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_status(storage, + lun, storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_id, &media_status); + + /* Update the request sense. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (UCHAR) (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = (UCHAR) ((media_status >> 8 ) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = (UCHAR) ((media_status >> 16 ) & 0xff); + + /* If there is a problem, return a failed command. */ + if (status != UX_SUCCESS) + { + + /* We have a problem, media status error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_out); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* We are done here. */ + return(UX_ERROR); + } + + /* Check Read Only flag. */ + if (storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_read_only_flag == UX_TRUE) + { + + /* Update the request sense. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = UX_SLAVE_CLASS_STORAGE_SENSE_KEY_DATA_PROTECT; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = UX_SLAVE_CLASS_STORAGE_REQUEST_CODE_MEDIA_PROTECTED; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0; + + /* We have a problem, cannot write to RO drive. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_out); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* We are done here. */ + return(UX_ERROR); + } + + /* Compute the total length to transfer and how much remains. */ + total_length = total_number_blocks * storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length; + + /* Default status to success. */ + status = UX_SUCCESS; + + /* It may take several transfers to send the requested data. */ + while (total_length) + { + + /* How much can we receive in this transfer? */ + if (total_length > UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE) + transfer_length = UX_SLAVE_CLASS_STORAGE_BUFFER_SIZE; + else + transfer_length = total_length; + + /* Get the data payload from the host. */ + status = _ux_device_stack_transfer_request(transfer_request, transfer_length, transfer_length); + + /* Check the status. */ + if (status != UX_SUCCESS) + { + + /* We have a problem, request error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_out); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = 0x02; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = 0x54; + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = 0x00; + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Return an error. */ + return(UX_ERROR); + } + + /* Compute the number of blocks to transfer. */ + number_blocks = transfer_length / storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_block_length; + + /* Execute the write command to the local media. */ + status = storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_media_write(storage, lun, transfer_request -> ux_slave_transfer_request_data_pointer, number_blocks, lba, &media_status); + + /* If there is a problem, return a failed command. */ + if (status != UX_SUCCESS) + { + + /* We have a problem, request error. Return a bad completion and wait for the + REQUEST_SENSE command. */ + _ux_device_stack_endpoint_stall(endpoint_out); + + /* And update the REQUEST_SENSE codes. */ + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_sense_key = (UCHAR) (media_status & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code = (UCHAR) ((media_status >> 8 ) & 0xff); + storage -> ux_slave_class_storage_lun[lun].ux_slave_class_storage_request_code_qualifier = (UCHAR) ((media_status >> 16 ) & 0xff); + + /* Now we return a CSW with failure. */ + _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_FAILED); + + /* Return an error. */ + return(UX_ERROR); + } + + /* Update the lba. */ + lba += number_blocks; + + /* Update the length to remain. */ + total_length -= transfer_length; + } + + /* Now we return a CSW with success. */ + status = _ux_device_class_storage_csw_send(storage, lun, endpoint_in, UX_SLAVE_CLASS_STORAGE_CSW_PASSED); + + /* Return completion status. */ + return(status); +} diff --git a/common/usbx_host_classes/CMakeLists.txt b/common/usbx_host_classes/CMakeLists.txt new file mode 100644 index 0000000..5be86cc --- /dev/null +++ b/common/usbx_host_classes/CMakeLists.txt @@ -0,0 +1,267 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_interrupt_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_setup.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_transmission_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_asix_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_alternate_setting_locate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_control_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_control_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_control_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_device_controls_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_device_type_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_streaming_sampling_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_streaming_sampling_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_streaming_terminal_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_audio_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_capabilities_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_acm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_interrupt_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_mac_address_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_transmission_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_transmit_queue_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_cdc_ecm_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_gser_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_client_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_client_search.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_descriptor_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_field_decompress.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_global_item_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_idle_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_idle_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_instance_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_interrupt_endpoint_search.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_item_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_key_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_keyboard_thread.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_local_item_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_main_item_parse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_buttons_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_position_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_mouse_wheel_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_periodic_report_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_periodic_report_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_remote_control_usage_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_callback_register.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_compress.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_decompress.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_id_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_item_analyse.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_report_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_resources_free.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hid_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_change_detect.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_feature.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_hub_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_interrupt_endpoint_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_connection_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_enable_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_over_current_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_reset_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_change_suspend_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_ports_power.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_hub_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_device_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_device_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_notification.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_num_objects_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_copy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_delete.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_handles_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_info_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_move.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_send.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_object_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_request_cancel.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_session_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_session_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_storage_ids_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_storage_info_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_thumb_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_pima_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_name_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_soft_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_printer_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_command.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_setup.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_transfer_request_completed.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_prolific_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_cbw_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_device_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_device_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_device_support_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_driver_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_max_lun_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_capacity_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_characteristics_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_format_capacity_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_mount.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_protection_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_recovery_sense_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_media_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_partition_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_request_sense.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_sense_code_translate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_start_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_thread_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport_bo.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport_cb.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_transport_cbi.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_storage_unit_ready_test.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_reception_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_reception_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_reception_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_swar_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_activate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_alternate_setting_locate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_channel_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_configure.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_value_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_control_value_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_deactivate.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_descriptor_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_endpoints_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_format_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_frame_data_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_frame_interval_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_frame_parameters_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_input_format_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_input_terminal_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_ioctl.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_max_payload_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_start.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_stop.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_buffer_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_buffers_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_callback_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_request.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_request_callback.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_host_class_video_transfer_request_completed.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/common/usbx_host_classes/inc/ux_host_class_asix.h b/common/usbx_host_classes/inc/ux_host_class_asix.h new file mode 100644 index 0000000..71d1083 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_asix.h @@ -0,0 +1,413 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** ASIX Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_asix.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX ASIX class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_ASIX_H +#define UX_HOST_CLASS_ASIX_H + + +#include "nx_api.h" +#include "ux_network_driver.h" + +/* Define to check if NetX preserved header size is compatible with ASIX. */ +/* #define UX_HOST_CLASS_ASIX_HEADER_CHECK_ENABLE */ + + +/* Define ASIX Class constants. Insert here the PID/VID of vendors and products using the Asix chipset. */ +#ifndef UX_HOST_CLASS_ASIX_VENDOR_ID +#define UX_HOST_CLASS_ASIX_VENDOR_ID 0X2001 +#define UX_HOST_CLASS_ASIX_PRODUCT_ID 0X3C05 +#endif + +#define UX_HOST_CLASS_ASIX_VENDOR_FUJIEI_ID 0X0B95 +#define UX_HOST_CLASS_ASIX_PRODUCT_FUJIEI_ID 0X772B + +#define UX_HOST_CLASS_ASIX_SPEED_SELECTED_100MPBS 0x100 +#define UX_HOST_CLASS_ASIX_SPEED_SELECTED_10MPBS 0x10 +#define UX_HOST_CLASS_ASIX_LINK_STATE_DOWN 0 +#define UX_HOST_CLASS_ASIX_LINK_STATE_UP 1 +#define UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_UP 2 +#define UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_DOWN 3 +#define UX_HOST_CLASS_ASIX_BASE_IP_ADDRESS 0xC0A80001 +#define UX_HOST_CLASS_ASIX_BASE_IP_MASK 0xFFFFFF00 +#define UX_HOST_CLASS_ASIX_MAX_MTU 1518 +#define UX_HOST_CLASS_ASIX_ETHERNET_IP 0x0800 +#define UX_HOST_CLASS_ASIX_ETHERNET_ARP 0x0806 +#define UX_HOST_CLASS_ASIX_ETHERNET_RARP 0x8035 +#define UX_HOST_CLASS_ASIX_ETHERNET_PACKET_SIZE 1536 +#define UX_HOST_CLASS_ASIX_NX_ALIGN_PADDING 2 +#ifndef UX_HOST_CLASS_ASIX_NX_PKPOOL_ENTRIES +#define UX_HOST_CLASS_ASIX_NX_PKPOOL_ENTRIES 16 +#endif + +#define UX_HOST_CLASS_ASIX_NX_PACKET_SIZE sizeof(NX_PACKET) + +#define UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE_ASSERT UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG(UX_HOST_CLASS_ASIX_ETHERNET_PACKET_SIZE, UX_HOST_CLASS_ASIX_NX_ALIGN_PADDING), UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE_calc_ovf) +#define UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE (UX_HOST_CLASS_ASIX_ETHERNET_PACKET_SIZE + UX_HOST_CLASS_ASIX_NX_ALIGN_PADDING) + +#define UX_HOST_CLASS_ASIX_NX_BUFF_SIZE_ASSERT \ + UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE, \ + UX_HOST_CLASS_ASIX_NX_PACKET_SIZE), \ + UX_HOST_CLASS_ASIX_NX_BUFF_SIZE_calc_ovf) +#define UX_HOST_CLASS_ASIX_NX_BUFF_SIZE (UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE + UX_HOST_CLASS_ASIX_NX_PACKET_SIZE) + +#define UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT \ + UX_HOST_CLASS_ASIX_NX_BUFF_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG( \ + UX_HOST_CLASS_ASIX_NX_PKPOOL_ENTRIES, \ + UX_HOST_CLASS_ASIX_NX_BUFF_SIZE), \ + UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE_calc1_ovf) \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_HOST_CLASS_ASIX_NX_PKPOOL_ENTRIES * \ + UX_HOST_CLASS_ASIX_NX_BUFF_SIZE, \ + 32), UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE_calc2_ovf) +#define UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE (UX_HOST_CLASS_ASIX_NX_PKPOOL_ENTRIES * UX_HOST_CLASS_ASIX_NX_BUFF_SIZE + 32) + +#define UX_HOST_CLASS_ASIX_ETHERNET_SIZE 14 + +#define UX_HOST_CLASS_ASIX_DEVICE_INIT_DELAY (1 * UX_PERIODIC_RATE) +#define UX_HOST_CLASS_ASIX_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_HOST_CLASS_ASIX_SETUP_BUFFER_SIZE 16 + +/* Define NetX errors inside the Asix class. */ +#define UX_HOST_CLASS_ASIX_NX_SUCCESS 0x00 +#define UX_HOST_CLASS_ASIX_NX_NO_PACKET 0x01 +#define UX_HOST_CLASS_ASIX_NX_UNDERFLOW 0x02 +#define UX_HOST_CLASS_ASIX_NX_OVERFLOW 0x03 +#define UX_HOST_CLASS_ASIX_NX_NO_MAPPING 0x04 +#define UX_HOST_CLASS_ASIX_NX_DELETED 0x05 +#define UX_HOST_CLASS_ASIX_NX_POOL_ERROR 0x06 +#define UX_HOST_CLASS_ASIX_NX_PTR_ERROR 0x07 +#define UX_HOST_CLASS_ASIX_NX_WAIT_ERROR 0x08 +#define UX_HOST_CLASS_ASIX_NX_SIZE_ERROR 0x09 +#define UX_HOST_CLASS_ASIX_NX_OPTION_ERROR 0x0a +#define UX_HOST_CLASS_ASIX_NX_DELETE_ERROR 0x10 +#define UX_HOST_CLASS_ASIX_NX_CALLER_ERROR 0x11 +#define UX_HOST_CLASS_ASIX_NX_INVALID_PACKET 0x12 +#define UX_HOST_CLASS_ASIX_NX_INVALID_SOCKET 0x13 +#define UX_HOST_CLASS_ASIX_NX_NOT_ENABLED 0x14 +#define UX_HOST_CLASS_ASIX_NX_ALREADY_ENABLED 0x15 +#define UX_HOST_CLASS_ASIX_NX_ENTRY_NOT_FOUND 0x16 +#define UX_HOST_CLASS_ASIX_NX_NO_MORE_ENTRIES 0x17 +#define UX_HOST_CLASS_ASIX_NX_ARP_TIMER_ERROR 0x18 +#define UX_HOST_CLASS_ASIX_NX_RESERVED_CODE0 0x19 +#define UX_HOST_CLASS_ASIX_NX_WAIT_ABORTED 0x1A +#define UX_HOST_CLASS_ASIX_NX_IP_INTERNAL_ERROR 0x20 +#define UX_HOST_CLASS_ASIX_NX_IP_ADDRESS_ERROR 0x21 +#define UX_HOST_CLASS_ASIX_NX_ALREADY_BOUND 0x22 +#define UX_HOST_CLASS_ASIX_NX_PORT_UNAVAILABLE 0x23 +#define UX_HOST_CLASS_ASIX_NX_NOT_BOUND 0x24 +#define UX_HOST_CLASS_ASIX_NX_RESERVED_CODE1 0x25 +#define UX_HOST_CLASS_ASIX_NX_SOCKET_UNBOUND 0x26 +#define UX_HOST_CLASS_ASIX_NX_NOT_CREATED 0x27 +#define UX_HOST_CLASS_ASIX_NX_SOCKETS_BOUND 0x28 +#define UX_HOST_CLASS_ASIX_NX_NO_RESPONSE 0x29 +#define UX_HOST_CLASS_ASIX_NX_POOL_DELETED 0x30 +#define UX_HOST_CLASS_ASIX_NX_ALREADY_RELEASED 0x31 +#define UX_HOST_CLASS_ASIX_NX_RESERVED_CODE2 0x32 +#define UX_HOST_CLASS_ASIX_NX_MAX_LISTEN 0x33 +#define UX_HOST_CLASS_ASIX_NX_DUPLICATE_LISTEN 0x34 +#define UX_HOST_CLASS_ASIX_NX_NOT_CLOSED 0x35 +#define UX_HOST_CLASS_ASIX_NX_NOT_LISTEN_STATE 0x36 +#define UX_HOST_CLASS_ASIX_NX_IN_PROGRESS 0x37 +#define UX_HOST_CLASS_ASIX_NX_NOT_CONNECTED 0x38 +#define UX_HOST_CLASS_ASIX_NX_WINDOW_OVERFLOW 0x39 +#define UX_HOST_CLASS_ASIX_NX_ALREADY_SUSPENDED 0x40 +#define UX_HOST_CLASS_ASIX_NX_DISCONNECT_FAILED 0x41 +#define UX_HOST_CLASS_ASIX_NX_STILL_BOUND 0x42 +#define UX_HOST_CLASS_ASIX_NX_NOT_SUCCESSFUL 0x43 +#define UX_HOST_CLASS_ASIX_NX_UNHANDLED_COMMAND 0x44 +#define UX_HOST_CLASS_ASIX_NX_NO_FREE_PORTS 0x45 +#define UX_HOST_CLASS_ASIX_NX_INVALID_PORT 0x46 +#define UX_HOST_CLASS_ASIX_NX_INVALID_RELISTEN 0x47 +#define UX_HOST_CLASS_ASIX_NX_CONNECTION_PENDING 0x48 +#define UX_HOST_CLASS_ASIX_NX_TX_QUEUE_DEPTH 0x49 +#define UX_HOST_CLASS_ASIX_NX_NOT_IMPLEMENTED 0x80 + + + +/* Define ASIX command request values. */ + +#define UX_HOST_CLASS_ASIX_REQ_RX_TX_SRAM_REG_READ 0x02 +#define UX_HOST_CLASS_ASIX_REQ_RX_TX_SRAM_REG_WRITE 0x03 +#define UX_HOST_CLASS_ASIX_REQ_OWN_SMI 0x06 +#define UX_HOST_CLASS_ASIX_REQ_READ_PHY_REG 0x07 +#define UX_HOST_CLASS_ASIX_REQ_WRITE_PHY_REG 0x08 +#define UX_HOST_CLASS_ASIX_REQ_WHO_OWNS_SMI 0x09 +#define UX_HOST_CLASS_ASIX_REQ_RELEASE_SMI 0x0a +#define UX_HOST_CLASS_ASIX_REQ_READ_SROM 0x0b +#define UX_HOST_CLASS_ASIX_REQ_WRITE_SROM 0x0c +#define UX_HOST_CLASS_ASIX_REQ_WRITE_SROM_EN 0x0d +#define UX_HOST_CLASS_ASIX_REQ_WRITE_SROM_DIS 0x0e +#define UX_HOST_CLASS_ASIX_REQ_READ_RX_CTL 0x0f +#define UX_HOST_CLASS_ASIX_REQ_WRITE_RX_CTL 0x10 +#define UX_HOST_CLASS_ASIX_REQ_READ_IPG012 0x11 +#define UX_HOST_CLASS_ASIX_REQ_WRITE_IPG012 0x12 +#define UX_HOST_CLASS_ASIX_REQ_READ_NODE_ID 0x13 +#define UX_HOST_CLASS_ASIX_REQ_WRITE_NODE_ID 0x14 +#define UX_HOST_CLASS_ASIX_REQ_WRITE_MULTICAST_FILTER 0x16 +#define UX_HOST_CLASS_ASIX_REQ_TEST_REGISTER 0x17 +#define UX_HOST_CLASS_ASIX_REQ_READ_PHY_ID 0x19 +#define UX_HOST_CLASS_ASIX_REQ_READ_MEDIUM_STATUS 0x1a +#define UX_HOST_CLASS_ASIX_REQ_WRITE_MEDIUM_MODE 0x1b +#define UX_HOST_CLASS_ASIX_REQ_READ_MONITOR_MODE_STATUS 0x1c +#define UX_HOST_CLASS_ASIX_REQ_WRITE_MONITOR_MODE_STATUS 0x1d +#define UX_HOST_CLASS_ASIX_REQ_READ_GPIO_STATUS 0x1e +#define UX_HOST_CLASS_ASIX_REQ_WRITE_GPIO_STATUS 0x1f +#define UX_HOST_CLASS_ASIX_REQ_SW_RESET 0x20 +#define UX_HOST_CLASS_ASIX_REQ_READ_SW_PHY_SELECT_STATUS 0x21 +#define UX_HOST_CLASS_ASIX_REQ_WRITE_SW_PHY_SELECT_STATUS 0x22 + +/* Define ASIX Interrupt Packet format. */ + +#define UX_HOST_CLASS_ASIX_INTERRUPT_SIGNATURE_VALUE 0xA1 +#define UX_HOST_CLASS_ASIX_INTERRUPT_SIGNATURE_OFFSET 0x00 +#define UX_HOST_CLASS_ASIX_INTERRUPT_STATE_OFFSET 0x02 +#define UX_HOST_CLASS_ASIX_INTERRUPT_STATE_PPLS 0x01 +#define UX_HOST_CLASS_ASIX_INTERRUPT_STATE_SPLS 0x02 +#define UX_HOST_CLASS_ASIX_INTERRUPT_STATE_FLE 0x04 +#define UX_HOST_CLASS_ASIX_INTERRUPT_STATE_MDINT 0x08 +#define UX_HOST_CLASS_ASIX_INTERRUPT_PHY_REG_VALUE_OFFSET 0x05 + +/* Define ASIX Class PHY ID Packet format. */ + +#define UX_HOST_CLASS_ASIX_PHY_ID_SECONDARY 0x00 +#define UX_HOST_CLASS_ASIX_PHY_ID_PRIMARY 0x01 +#define UX_HOST_CLASS_ASIX_PHY_ID_MASK 0x01f +#define UX_HOST_CLASS_ASIX_PHY_TYPE_SHIFT 0x05 +#define UX_HOST_CLASS_ASIX_PHY_TYPE_MASK 0x07 + +/* Define ASIX Class GPIO Register. */ + +#define UX_HOST_CLASS_ASIX_GPIO_GPO0EN 0x01 +#define UX_HOST_CLASS_ASIX_GPIO_GPO_0 0x02 +#define UX_HOST_CLASS_ASIX_GPIO_GPO1EN 0x04 +#define UX_HOST_CLASS_ASIX_GPIO_GPO_1 0x08 +#define UX_HOST_CLASS_ASIX_GPIO_GPO2EN 0x10 +#define UX_HOST_CLASS_ASIX_GPIO_GPO_2 0x20 +#define UX_HOST_CLASS_ASIX_GPIO_RSE 0x80 + +/* Define ASIX Class Software reset Register. */ + +#define UX_HOST_CLASS_ASIX_SW_RESET_RR 0x01 +#define UX_HOST_CLASS_ASIX_SW_RESET_RT 0x02 +#define UX_HOST_CLASS_ASIX_SW_RESET_PRTE 0x04 +#define UX_HOST_CLASS_ASIX_SW_RESET_PRL 0x08 +#define UX_HOST_CLASS_ASIX_SW_RESET_BZ 0x10 +#define UX_HOST_CLASS_ASIX_SW_RESET_IPRL 0x20 +#define UX_HOST_CLASS_ASIX_SW_RESET_IPPD 0x40 + +/* Define ASIX Class Receive Control Register. */ + +#define UX_HOST_CLASS_ASIX_RXCR_PRO 0x0001 +#define UX_HOST_CLASS_ASIX_RXCR_AMALL 0x0002 +#define UX_HOST_CLASS_ASIX_RXCR_SEP 0x0004 +#define UX_HOST_CLASS_ASIX_RXCR_AB 0x0008 +#define UX_HOST_CLASS_ASIX_RXCR_AM 0x0010 +#define UX_HOST_CLASS_ASIX_RXCR_AP 0x0020 +#define UX_HOST_CLASS_ASIX_RXCR_SO 0x0080 + +#define UX_HOST_CLASS_ASIX_RXCR_MFB_2048 0x0000 +#define UX_HOST_CLASS_ASIX_RXCR_MFB_4096 0x0100 +#define UX_HOST_CLASS_ASIX_RXCR_MFB_8192 0x0200 +#define UX_HOST_CLASS_ASIX_RXCR_MFB_16384 0x0300 + +/* Define ASIX Class packet equivalences. */ + +#define UX_HOST_CLASS_ASIX_PACKET_SIZE 128 +#define UX_HOST_CLASS_ASIX_NODE_ID_LENGTH 6 + +/* Define ASIX PHY registers description. */ + +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR 0x00 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMSR 0x01 +#define UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1 0x02 +#define UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR2 0x03 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR 0x04 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANLPAR 0x05 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANER 0x06 + +/* Define ASIX PHY BMCR registers description. */ + +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_RESET 0x8000 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_LOOPBACK_ENABLED 0x4000 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_SPEED_100MBS 0x2000 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_SPEED_10MBS 0x0000 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_AUTO_NEGOTIATION 0x1000 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_POWER_DOWN 0x0800 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_ISOLATE 0x0400 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_RESTART_NEG 0x0200 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_DUPLEX_MODE 0x0100 +#define UX_HOST_CLASS_ASIX_PHY_REG_BMCR_COLLISION_TEST 0x0080 + + +/* Define ASIX PHY PHYIDR1 register description. */ + +#define UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_MDL_REV_SHIFT 0x00 +#define UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_MDL_REV_MASK 0x0f +#define UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_VNDR_REV_SHIFT 0x04 +#define UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_VNDR_REV_MASK 0x2f + + +/* Define ASIX PHY ANAR register description. */ + +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_DEFAULT_SELECTOR 0x0001 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_10_HD 0x0020 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_10_FD 0x0040 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_TX_HD 0x0080 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_TX_FD 0x0100 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_T4 0x0200 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_PAUSE 0x0400 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_RF 0x1000 +#define UX_HOST_CLASS_ASIX_PHY_REG_ANAR_ACK 0x2000 + +/* Define ASIX MEDIUM register description. */ + +#define UX_HOST_CLASS_ASIX_MEDIUM_FD 0x0002 +#define UX_HOST_CLASS_ASIX_MEDIUM_BIT2 0x0004 +#define UX_HOST_CLASS_ASIX_MEDIUM_BIT3 0x0000 +#define UX_HOST_CLASS_ASIX_MEDIUM_RFC_ENABLED 0x0010 +#define UX_HOST_CLASS_ASIX_MEDIUM_TFC_ENABLED 0x0020 + +#define UX_HOST_CLASS_ASIX_MEDIUM_RE_ENABLED 0x0100 +#define UX_HOST_CLASS_ASIX_MEDIUM_PS 0x0200 +#define UX_HOST_CLASS_ASIX_MEDIUM_SBP 0x0800 +#define UX_HOST_CLASS_ASIX_MEDIUM_SM 0x1000 + +/* Define ASIX IPG default values register description. */ + +#define UX_HOST_CLASS_ASIX_PPG0_IPG1 0x0C15 +#define UX_HOST_CLASS_ASIX_PPG2 0x000E + +/* Define ASIX Reception States. */ + +#define UX_HOST_CLASS_ASIX_RECEPTION_STATE_STOPPED 0 +#define UX_HOST_CLASS_ASIX_RECEPTION_STATE_STARTED 1 +#define UX_HOST_CLASS_ASIX_RECEPTION_STATE_IN_TRANSFER 2 + + +/* Define ASIX Class instance structure. */ + +typedef struct UX_HOST_CLASS_ASIX_STRUCT +{ + struct UX_HOST_CLASS_ASIX_STRUCT + *ux_host_class_asix_next_instance; + UX_HOST_CLASS *ux_host_class_asix_class; + UX_DEVICE *ux_host_class_asix_device; + UX_ENDPOINT *ux_host_class_asix_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_asix_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_asix_interrupt_endpoint; + UX_INTERFACE *ux_host_class_asix_interface; + UINT ux_host_class_asix_instance_status; + UINT ux_host_class_asix_state; + TX_SEMAPHORE ux_host_class_asix_semaphore; + TX_SEMAPHORE ux_host_class_asix_interrupt_notification_semaphore; + TX_THREAD ux_host_class_asix_thread; + UCHAR *ux_host_class_asix_thread_stack; + ULONG ux_host_class_asix_notification_count; + ULONG ux_host_class_asix_primary_phy_id; + ULONG ux_host_class_asix_primary_phy_type; + ULONG ux_host_class_asix_secondary_phy_id; + ULONG ux_host_class_asix_secondary_phy_type; + ULONG ux_host_class_asix_model_revision_number; + ULONG ux_host_class_asix_vendor_model_number; + ULONG ux_host_class_asix_speed_selected; + ULONG ux_host_class_asix_device_state; + ULONG ux_host_class_asix_link_state; + NX_PACKET *ux_host_class_asix_xmit_queue; + NX_PACKET *ux_host_class_asix_receive_queue; + NX_PACKET_POOL ux_host_class_asix_packet_pool; + UCHAR *ux_host_class_asix_pool_memory; + UCHAR ux_host_class_asix_node_id[UX_HOST_CLASS_ASIX_NODE_ID_LENGTH]; + VOID (*ux_host_class_asix_device_status_change_callback)(struct UX_HOST_CLASS_ASIX_STRUCT *asix, + ULONG device_state); + VOID *ux_host_class_asix_network_handle; + +} UX_HOST_CLASS_ASIX; + + +/* Define ASIX reception structure. */ + +typedef struct UX_HOST_CLASS_ASIX_RECEPTION_STRUCT +{ + + ULONG ux_host_class_asix_reception_state; + ULONG ux_host_class_asix_reception_block_size; + UCHAR *ux_host_class_asix_reception_data_buffer; + ULONG ux_host_class_asix_reception_data_buffer_size; + UCHAR *ux_host_class_asix_reception_data_head; + UCHAR *ux_host_class_asix_reception_data_tail; + VOID (*ux_host_class_asix_reception_callback)(struct UX_HOST_CLASS_ASIX_STRUCT *asix, + UINT status, + UCHAR *reception_buffer, + ULONG reception_size); + +} UX_HOST_CLASS_ASIX_RECEPTION; + +/* Define Asix Class function prototypes. */ + +UINT _ux_host_class_asix_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_asix_configure(UX_HOST_CLASS_ASIX *asix); +UINT _ux_host_class_asix_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_asix_endpoints_get(UX_HOST_CLASS_ASIX *asix); +UINT _ux_host_class_asix_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_asix_read (UX_HOST_CLASS_ASIX *asix, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_asix_write(VOID *asix_class, NX_PACKET *packet); +VOID _ux_host_class_asix_interrupt_notification(UX_TRANSFER *transfer_request); +VOID _ux_host_class_asix_reception_callback (UX_TRANSFER *transfer_request); +VOID _ux_host_class_asix_thread(ULONG parameter); +VOID _ux_host_class_asix_transmission_callback (UX_TRANSFER *transfer_request); +UINT _ux_host_class_asix_setup(UX_HOST_CLASS_ASIX *asix); + +/* Define Asix Class API prototypes. */ + +#define ux_host_class_asix_entry _ux_host_class_asix_entry +#define ux_host_class_asix_read _ux_host_class_asix_read +#define ux_host_class_asix_write _ux_host_class_asix_write + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_audio.h b/common/usbx_host_classes/inc/ux_host_class_audio.h new file mode 100644 index 0000000..c976e2d --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_audio.h @@ -0,0 +1,445 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_audio.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX audio class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_AUDIO_H +#define UX_HOST_CLASS_AUDIO_H + + +/* Define Audio Class main constants. */ + +#define UX_HOST_CLASS_AUDIO_CLASS_TRANSFER_TIMEOUT 30 +#define UX_HOST_CLASS_AUDIO_CLASS 1 +#define UX_HOST_CLASS_AUDIO_SUBCLASS_UNDEFINED 0 +#define UX_HOST_CLASS_AUDIO_SUBCLASS_CONTROL 1 +#define UX_HOST_CLASS_AUDIO_SUBCLASS_STREAMING 2 +#define UX_HOST_CLASS_AUDIO_SUBCLASS_MIDI_STREAMING 3 +#define UX_HOST_CLASS_AUDIO_PROTOCOL_UNDEFINED 0 + + +/* Define Audio Class main descriptor types. */ + +#define UX_HOST_CLASS_AUDIO_CS_UNDEFINED 0x20 +#define UX_HOST_CLASS_AUDIO_CS_DEVICE 0x21 +#define UX_HOST_CLASS_AUDIO_CS_CONFIGURATION 0x22 +#define UX_HOST_CLASS_AUDIO_CS_STRING 0x23 +#define UX_HOST_CLASS_AUDIO_CS_INTERFACE 0x24 +#define UX_HOST_CLASS_AUDIO_CS_ENDPOINT 0x25 + + +/* Define Audio Class specific AC interface descriptor subclasses. */ + +#define UX_HOST_CLASS_AUDIO_CS_AC_UNDEFINED 0x00 +#define UX_HOST_CLASS_AUDIO_CS_HEADER 0x01 +#define UX_HOST_CLASS_AUDIO_CS_INPUT_TERMINAL 0x02 +#define UX_HOST_CLASS_AUDIO_CS_OUTPUT_TERMINAL 0x03 +#define UX_HOST_CLASS_AUDIO_CS_MIXER_UNIT 0x04 +#define UX_HOST_CLASS_AUDIO_CS_SELECTOR_UNIT 0x05 +#define UX_HOST_CLASS_AUDIO_CS_FEATURE_UNIT 0x06 +#define UX_HOST_CLASS_AUDIO_CS_PROCESSING_UNIT 0x07 +#define UX_HOST_CLASS_AUDIO_CS_EXTENSION_UNIT 0x08 + + +/* Define Audio Class specific AS interface descriptor subclasses. */ + +#define UX_HOST_CLASS_AUDIO_CS_AS_UNDEFINED 0x00 +#define UX_HOST_CLASS_AUDIO_CS_AS_GENERAL 0x01 +#define UX_HOST_CLASS_AUDIO_CS_FORMAT_TYPE 0x02 +#define UX_HOST_CLASS_AUDIO_CS_FORMAT_SPECIFIC 0x03 + + +/* Define Audio Class specific processing unit process types. */ + +#define UX_HOST_CLASS_AUDIO_PROCESS_UNDEFINED 0x00 +#define UX_HOST_CLASS_AUDIO_UP_DOWN_MIX_PROCESS 0x01 +#define UX_HOST_CLASS_AUDIO_DOLBY_PROLOGIC_PROCESS 0x02 +#define UX_HOST_CLASS_AUDIO_3D_STEREO_EXTENDED_PROCESS 0x03 +#define UX_HOST_CLASS_AUDIO_REVERBERATION_PROCESS 0x04 +#define UX_HOST_CLASS_AUDIO_CHORUS_PROCESS 0x05 +#define UX_HOST_CLASS_AUDIO_DYN_RANGE_COMP_PROCESS 0x06 + + +/* Define Audio Class specific endpoint descriptor subtypes. */ + +#define UX_HOST_CLASS_AUDIO_DESCRIPTOR_UNDEFINED 0x00 +#define UX_HOST_CLASS_AUDIO_EP_GENERAL 0x01 + + +/* Define Audio Class specific request codes. */ + +#define UX_HOST_CLASS_AUDIO_REQUEST_CODE_UNDEFINED 0x00 +#define UX_HOST_CLASS_AUDIO_SET_CUR 0x01 +#define UX_HOST_CLASS_AUDIO_GET_CUR 0x81 +#define UX_HOST_CLASS_AUDIO_SET_MIN 0x02 +#define UX_HOST_CLASS_AUDIO_GET_MIN 0x82 +#define UX_HOST_CLASS_AUDIO_SET_MAX 0x03 +#define UX_HOST_CLASS_AUDIO_GET_MAX 0x83 +#define UX_HOST_CLASS_AUDIO_SET_RES 0x04 +#define UX_HOST_CLASS_AUDIO_GET_RES 0x84 +#define UX_HOST_CLASS_AUDIO_SET_MEM 0x05 +#define UX_HOST_CLASS_AUDIO_GET_MEM 0x85 +#define UX_HOST_CLASS_AUDIO_GET_STAT 0xFF + + +/* Define Audio Class specific terminal control selectors. */ + +#define UX_HOST_CLASS_AUDIO_TE_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_AUDIO_COPY_PROTECT_CONTROL 0x01 + + +/* Define Audio Class specific feature unit control selectors. */ + +#define UX_HOST_CLASS_AUDIO_FU_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_AUDIO_MUTE_CONTROL 0x01 +#define UX_HOST_CLASS_AUDIO_VOLUME_CONTROL 0x02 +#define UX_HOST_CLASS_AUDIO_BASS_CONTROL 0x03 +#define UX_HOST_CLASS_AUDIO_MID_CONTROL 0x04 +#define UX_HOST_CLASS_AUDIO_TREBLE_CONTROL 0x05 +#define UX_HOST_CLASS_AUDIO_GRAPHIC_EQUALIZER_CONTROL 0x06 +#define UX_HOST_CLASS_AUDIO_AUTOMATIC_GAIN_CONTROL 0x07 +#define UX_HOST_CLASS_AUDIO_DELAY_CONTROL 0x08 +#define UX_HOST_CLASS_AUDIO_BASS_BOOST_CONTROL 0x09 +#define UX_HOST_CLASS_AUDIO_LOUNDNESS_CONTROL 0x0A + + +/* Define Audio Class input terminal types. */ + +#define UX_HOST_CLASS_AUDIO_INPUT 0x0200 +#define UX_HOST_CLASS_AUDIO_MICROPHONE 0x0201 +#define UX_HOST_CLASS_AUDIO_DESKTOP_MICROPHONE 0x0202 +#define UX_HOST_CLASS_AUDIO_PERSONAL_MICROPHONE 0x0203 +#define UX_HOST_CLASS_AUDIO_OMNI_DIRECTIONAL_MICROPHONE 0x0204 +#define UX_HOST_CLASS_AUDIO_MICROPHONE_ARRAY 0x0205 +#define UX_HOST_CLASS_AUDIO_PROCESSING_MICROPHONE_ARRAY 0x0206 + + +/* Define Audio Class output terminal types. */ + +#define UX_HOST_CLASS_AUDIO_OUTPUT 0x0300 +#define UX_HOST_CLASS_AUDIO_SPEAKER 0x0301 +#define UX_HOST_CLASS_AUDIO_HEADPHONES 0x0302 +#define UX_HOST_CLASS_AUDIO_HEAD_MOUNTED_DISPLAY 0x0303 +#define UX_HOST_CLASS_AUDIO_DESKTOP_SPEAKER 0x0304 +#define UX_HOST_CLASS_AUDIO_ROOM_SPEAKER 0x0305 +#define UX_HOST_CLASS_AUDIO_COMMUNICATION_SPEAKER 0x0306 +#define UX_HOST_CLASS_AUDIO_LOW_FREQUENCY_SPEAKER 0x0307 + + +/* Define Audio Class bidirectional terminal types. */ + +#define UX_HOST_CLASS_AUDIO_BIDIRECTIONAL_UNDEFINED 0x0400 +#define UX_HOST_CLASS_AUDIO_HANDSET 0x0401 +#define UX_HOST_CLASS_AUDIO_HEADSET 0x0402 +#define UX_HOST_CLASS_AUDIO_SPEAKERPHONE 0x0403 +#define UX_HOST_CLASS_AUDIO_ECHO_SUPRESS_SPEAKERPHONE 0x0404 +#define UX_HOST_CLASS_AUDIO_ECHO_CANCEL_SPEAKERPHONE 0x0405 + + +/* Define Audio Class telephony terminal types. */ + +#define UX_HOST_CLASS_AUDIO_TELEPHONTY_UNDEFINED 0x0400 +#define UX_HOST_CLASS_AUDIO_PHONE_LINE 0x0401 +#define UX_HOST_CLASS_AUDIO_TELEPHONE 0x0402 +#define UX_HOST_CLASS_AUDIO_DOWN_LINE_PHONE 0x0403 + + +/* Define Audio Class encoding format types. */ + +#define UX_HOST_CLASS_AUDIO_FORMAT_TYPE_UNDEFINED 0 +#define UX_HOST_CLASS_AUDIO_FORMAT_TYPE_I 1 +#define UX_HOST_CLASS_AUDIO_FORMAT_TYPE_II 2 +#define UX_HOST_CLASS_AUDIO_FORMAT_TYPE_III 3 + +#define UX_HOST_CLASS_AUDIO_FORMAT_PCM 1 +#define UX_HOST_CLASS_AUDIO_FORMAT_PCM8 2 +#define UX_HOST_CLASS_AUDIO_FORMAT_IEEE_FLOAT 3 +#define UX_HOST_CLASS_AUDIO_FORMAT_ALAW 4 +#define UX_HOST_CLASS_AUDIO_FORMAT_MULAW 5 + +#define UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_ENTRIES 8 +#define UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH 8 + +#define UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR_ENTRIES 10 +#define UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR_LENGTH 12 + +#define UX_HOST_CLASS_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR_ENTRIES 8 +#define UX_HOST_CLASS_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR_LENGTH 9 + +#define UX_HOST_CLASS_AUDIO_FEATURE_UNIT_DESCRIPTOR_ENTRIES 7 +#define UX_HOST_CLASS_AUDIO_FEATURE_UNIT_DESCRIPTOR_LENGTH 7 + +#define UX_HOST_CLASS_AUDIO_STREAMING_INTERFACE_DESCRIPTOR_ENTRIES 6 +#define UX_HOST_CLASS_AUDIO_STREAMING_INTERFACE_DESCRIPTOR_LENGTH 6 + +#define UX_HOST_CLASS_AUDIO_STREAMING_ENDPOINT_DESCRIPTOR_ENTRIES 6 +#define UX_HOST_CLASS_AUDIO_STREAMING_ENDPOINT_DESCRIPTOR_LENGTH 6 + + +/* Define Audio Class specific interface descriptor. */ + +#define UX_HOST_CLASS_AUDIO_MAX_CHANNEL 8 +#define UX_HOST_CLASS_AUDIO_NAME_LENGTH 64 + +typedef struct UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bFormatType; + ULONG bNrChannels; + ULONG bSubframeSize; + ULONG bBitResolution; + ULONG bSamFreqType; +} UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR; + + +/* Define Audio Class specific input terminal interface descriptor. */ + +typedef struct UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG bNrChannels; + ULONG wChannelConfig; + ULONG iChannelNames; + ULONG iTerminal; +} UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR; + + +/* Define Audio Class specific output terminal interface descriptor. */ + +typedef struct UX_HOST_CLASS_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG bSourceID; + ULONG iTerminal; +} UX_HOST_CLASS_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR; + + +/* Define Audio Class specific feature unit descriptor. */ + +typedef struct UX_HOST_CLASS_AUDIO_FEATURE_UNIT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bUnitID; + ULONG bSourceID; + ULONG bControlSize; + ULONG bmaControls; +} UX_HOST_CLASS_AUDIO_FEATURE_UNIT_DESCRIPTOR; + + +/* Define Audio Class streaming interface descriptor. */ + +typedef struct UX_HOST_CLASS_AUDIO_STREAMING_INTERFACE_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bTerminalLink; + ULONG bDelay; + ULONG wFormatTag; +} UX_HOST_CLASS_AUDIO_STREAMING_INTERFACE_DESCRIPTOR; + + +/* Define Audio Class specific streaming endpoint descriptor. */ + +typedef struct UX_HOST_CLASS_AUDIO_STREAMING_ENDPOINT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bmAttributes; + ULONG bLockDelayUnits; + ULONG wLockDelay; +} UX_HOST_CLASS_AUDIO_STREAMING_ENDPOINT_DESCRIPTOR; + + +/* Define Audio Class instance structure. */ + +typedef struct UX_HOST_CLASS_AUDIO_STRUCT +{ + + struct UX_HOST_CLASS_AUDIO_STRUCT + *ux_host_class_audio_next_instance; + UX_HOST_CLASS *ux_host_class_audio_class; + UX_DEVICE *ux_host_class_audio_device; + UX_INTERFACE *ux_host_class_audio_streaming_interface; + ULONG ux_host_class_audio_control_interface_number; + UX_ENDPOINT *ux_host_class_audio_isochronous_endpoint; + struct UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST_STRUCT + *ux_host_class_audio_head_transfer_request; + struct UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST_STRUCT + *ux_host_class_audio_tail_transfer_request; + UINT ux_host_class_audio_state; + ULONG ux_host_class_audio_terminal_link; + ULONG ux_host_class_audio_type; + UCHAR * ux_host_class_audio_configuration_descriptor; + ULONG ux_host_class_audio_configuration_descriptor_length; + ULONG ux_host_class_audio_feature_unit_id; + UINT ux_host_class_audio_channels; + ULONG ux_host_class_audio_channel_control[UX_HOST_CLASS_AUDIO_MAX_CHANNEL]; + UCHAR ux_host_class_audio_name[UX_HOST_CLASS_AUDIO_NAME_LENGTH]; + TX_SEMAPHORE ux_host_class_audio_semaphore; +} UX_HOST_CLASS_AUDIO; + + +/* Define Audio Class isochronous USB transfer request structure. */ + +typedef struct UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST_STRUCT +{ + + ULONG ux_host_class_audio_transfer_request_status; + UCHAR * ux_host_class_audio_transfer_request_data_pointer; + ULONG ux_host_class_audio_transfer_request_requested_length; + ULONG ux_host_class_audio_transfer_request_actual_length; + VOID (*ux_host_class_audio_transfer_request_completion_function) (struct UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST_STRUCT *); + TX_SEMAPHORE ux_host_class_audio_transfer_request_semaphore; + VOID *ux_host_class_audio_transfer_request_class_instance; + UINT ux_host_class_audio_transfer_request_completion_code; + struct UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST_STRUCT + *ux_host_class_audio_transfer_request_next_audio_transfer_request; + UX_TRANSFER ux_host_class_audio_transfer_request; +} UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST; + + +/* Define Audio Class channel/value control structures. */ + +typedef struct UX_HOST_CLASS_AUDIO_CONTROL_STRUCT +{ + + ULONG ux_host_class_audio_control; + ULONG ux_host_class_audio_control_channel; + ULONG ux_host_class_audio_control_min; + ULONG ux_host_class_audio_control_max; + ULONG ux_host_class_audio_control_res; + ULONG ux_host_class_audio_control_cur; +} UX_HOST_CLASS_AUDIO_CONTROL; + + +typedef struct UX_HOST_CLASS_AUDIO_CHANNEL_STRUCT +{ + + ULONG ux_host_class_audio_channel_control; + ULONG ux_host_class_audio_channel; +} UX_HOST_CLASS_AUDIO_CHANNEL; + + +/* Define Audio Class sampling selection structure. */ + +typedef struct UX_HOST_CLASS_AUDIO_SAMPLING_STRUCT +{ + + ULONG ux_host_class_audio_sampling_channels; + ULONG ux_host_class_audio_sampling_frequency; + ULONG ux_host_class_audio_sampling_resolution; +} UX_HOST_CLASS_AUDIO_SAMPLING; + + +/* Define Audio Class sampling characteristics structure. */ + +typedef struct UX_HOST_CLASS_AUDIO_SAMPLING_CHARACTERISTICS_STRUCT +{ + + ULONG ux_host_class_audio_sampling_characteristics_channels; + ULONG ux_host_class_audio_sampling_characteristics_frequency_low; + ULONG ux_host_class_audio_sampling_characteristics_frequency_high; + ULONG ux_host_class_audio_sampling_characteristics_resolution; +} UX_HOST_CLASS_AUDIO_SAMPLING_CHARACTERISTICS; + + +/* Define Audio Class function prototypes. */ + +UINT _ux_host_class_audio_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_audio_alternate_setting_locate(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling, UINT *alternate_setting); +UINT _ux_host_class_audio_configure(UX_HOST_CLASS_AUDIO *audio); +UINT _ux_host_class_audio_control_get(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_CONTROL *audio_control); +UINT _ux_host_class_audio_control_value_get(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_CONTROL *audio_control); +UINT _ux_host_class_audio_control_value_set(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_CONTROL *audio_control); +UINT _ux_host_class_audio_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_audio_descriptor_get(UX_HOST_CLASS_AUDIO *audio); +UINT _ux_host_class_audio_device_controls_list_get(UX_HOST_CLASS_AUDIO *audio); +UINT _ux_host_class_audio_device_type_get(UX_HOST_CLASS_AUDIO *audio); +UINT _ux_host_class_audio_endpoints_get(UX_HOST_CLASS_AUDIO *audio); +UINT _ux_host_class_audio_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_audio_read(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *audio_transfer_request); +UINT _ux_host_class_audio_streaming_sampling_get(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING_CHARACTERISTICS *audio_sampling); +UINT _ux_host_class_audio_streaming_sampling_set(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling); +UINT _ux_host_class_audio_streaming_terminal_get(UX_HOST_CLASS_AUDIO *audio); +UINT _ux_host_class_audio_transfer_request(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *audio_transfer_request); +VOID _ux_host_class_audio_transfer_request_completed(UX_TRANSFER *transfer_request); +UINT _ux_host_class_audio_write(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *audio_transfer_request); + +/* Define Asix Class API prototypes. */ + +#define ux_host_class_audio_entry _ux_host_class_audio_entry +#define ux_host_class_audio_control_get _ux_host_class_audio_control_get +#define ux_host_class_audio_control_value_get _ux_host_class_audio_control_value_get +#define ux_host_class_audio_control_value_set _ux_host_class_audio_control_value_set +#define ux_host_class_audio_read _ux_host_class_audio_read +#define ux_host_class_audio_streaming_sampling_get _ux_host_class_audio_streaming_sampling_get +#define ux_host_class_audio_streaming_sampling_set _ux_host_class_audio_streaming_sampling_set +#define ux_host_class_audio_streaming_terminal_get _ux_host_class_audio_streaming_terminal_get +#define ux_host_class_audio_write _ux_host_class_audio_write + +#endif + + diff --git a/common/usbx_host_classes/inc/ux_host_class_cdc_acm.h b/common/usbx_host_classes/inc/ux_host_class_cdc_acm.h new file mode 100644 index 0000000..62ff9f9 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_cdc_acm.h @@ -0,0 +1,315 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_cdc_acm.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX CDC ACM class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_CDC_ACM_H +#define UX_HOST_CLASS_CDC_ACM_H + +/* Define CDC ACM Class constants. */ + +#define UX_HOST_CLASS_CDC_ACM_DEVICE_INIT_DELAY 1000 +#define UX_HOST_CLASS_CDC_ACM_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_HOST_CLASS_CDC_DATA_CLASS 0x0A +#define UX_HOST_CLASS_CDC_CONTROL_CLASS 0x02 +#define UX_HOST_CLASS_CDC_ACM_SUBCLASS 0X02 +#define UX_HOST_CLASS_CDC_DLC_SUBCLASS 0X01 +#define UX_HOST_CLASS_CDC_ACM_CS_INTERFACE 0x24 + +/* Define CDC ACM Class descriptor subtypes in functional descriptors. */ +#define UX_HOST_CLASS_CDC_ACM_HEADER_DESCRIPTOR 0X00 +#define UX_HOST_CLASS_CDC_ACM_CALL_MANAGEMENT_DESCRIPTOR 0X01 +#define UX_HOST_CLASS_CDC_ACM_ABSTRACT_CONTROL_MGT_DESCRIPTOR 0X02 +#define UX_HOST_CLASS_CDC_ACM_DIRECT_LINE_MGT_DESCRIPTOR 0X03 +#define UX_HOST_CLASS_CDC_ACM_TELEPHONE_RINGER_DESCRIPTOR 0X04 +#define UX_HOST_CLASS_CDC_ACM_REPORT_CAPABILITY_DESCRIPTOR 0X05 +#define UX_HOST_CLASS_CDC_ACM_UNION_DESCRIPTOR 0X06 +#define UX_HOST_CLASS_CDC_ACM_COUNTRY_SELECTION_DESCRIPTOR 0X07 +#define UX_HOST_CLASS_CDC_ACM_TELEPHONE_OPERATIONAL_DESCRIPTOR 0X08 +#define UX_HOST_CLASS_CDC_ACM_USB_TERMINAL_DESCRIPTOR 0X09 + +/* Define CDC ACM Class call management descriptors. */ +#define UX_HOST_CLASS_CDC_ACM_CALL_MANAGEMENT_CAPABILITIES 0x03 +#define UX_HOST_CLASS_CDC_ACM_CALL_MANAGEMENT_DCM 0x01 +#define UX_HOST_CLASS_CDC_ACM_CALL_MANAGEMENT_DCI 0x02 + +/* Define CDC ACM command request values. */ + +#define UX_HOST_CLASS_CDC_ACM_REQ_SEND_ENCAPSULATED_COMMAND 0x00 +#define UX_HOST_CLASS_CDC_ACM_REQ_GET_ENCAPSULATED_COMMAND 0x01 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_COMM_FEATURE 0x02 +#define UX_HOST_CLASS_CDC_ACM_REQ_GET_COMM_FEATURE 0x03 +#define UX_HOST_CLASS_CDC_ACM_REQ_CLEAR_COMM_FEATURE 0x04 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_AUX_LINE_STATE 0x10 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_HOOK_STATE 0x11 +#define UX_HOST_CLASS_CDC_ACM_REQ_PULSE_SETUP 0x12 +#define UX_HOST_CLASS_CDC_ACM_REQ_SEND_PULSE 0x13 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_PUSLE_TIME 0x14 +#define UX_HOST_CLASS_CDC_ACM_REQ_RING_AUX_JACK 0x15 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_CODING 0x20 +#define UX_HOST_CLASS_CDC_ACM_REQ_GET_LINE_CODING 0x21 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_STATE 0x22 +#define UX_HOST_CLASS_CDC_ACM_REQ_SEND_BREAK 0x23 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_RINGER_PARMS 0x30 +#define UX_HOST_CLASS_CDC_ACM_REQ_GET_RINGER_PARMS 0x31 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_OPERATION_PARMS 0x32 +#define UX_HOST_CLASS_CDC_ACM_REQ_GET_OPERATION_PARMS 0x33 +#define UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_PARMS 0x34 +#define UX_HOST_CLASS_CDC_ACM_REQ_GET_LINE_PARMS 0x35 + +/* Define CDC ACM line output control values. */ + +#define UX_HOST_CLASS_CDC_ACM_CTRL_DTR 0x01 +#define UX_HOST_CLASS_CDC_ACM_CTRL_RTS 0x02 + +/* Define CDC ACM line input control values. */ + +#define UX_HOST_CLASS_CDC_ACM_CTRL_DCD 0x01 +#define UX_HOST_CLASS_CDC_ACM_CTRL_DSR 0x02 +#define UX_HOST_CLASS_CDC_ACM_CTRL_BRK 0x04 +#define UX_HOST_CLASS_CDC_ACM_CTRL_RI 0x08 + +#define UX_HOST_CLASS_CDC_ACM_CTRL_FRAMING 0x10 +#define UX_HOST_CLASS_CDC_ACM_CTRL_PARITY 0x20 +#define UX_HOST_CLASS_CDC_ACM_CTRL_OVERRUN 0x40 + +/* Define CDC ACM Class packet equivalences. */ + +#define UX_HOST_CLASS_CDC_ACM_PACKET_SIZE 128 + +/* Define CDC ACM default values. */ + +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_RATE 9600 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_DATA_BIT 8 + +/* Define CDC ACM line coding definitions. */ + +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_0 0 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_15 1 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_2 2 + +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY_NONE 0 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY_ODD 1 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY_EVEN 2 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY_MARK 3 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY_SPACE 4 + +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_LENGTH 7 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_RATE 0 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT 4 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY 5 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_DATA_BIT 6 + +/* Define CDC ACM line state definitions. */ + +#define UX_HOST_CLASS_CDC_ACM_LINE_STATE_STOP_BIT_0 0 +#define UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_15 1 + +/* Define CDC ACM IOCTL Functions. */ + +#define UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING 0 +#define UX_HOST_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING 1 +#define UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE 2 +#define UX_HOST_CLASS_CDC_ACM_IOCTL_SEND_BREAK 3 +#define UX_HOST_CLASS_CDC_ACM_IOCTL_ABORT_IN_PIPE 5 +#define UX_HOST_CLASS_CDC_ACM_IOCTL_ABORT_OUT_PIPE 6 +#define UX_HOST_CLASS_CDC_ACM_IOCTL_NOTIFICATION_CALLBACK 7 +#define UX_HOST_CLASS_CDC_ACM_IOCTL_GET_DEVICE_STATUS 8 + +/* Define CDC ACM Reception States. */ + +#define UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STOPPED 0 +#define UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STARTED 1 +#define UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_IN_TRANSFER 2 + +/* Define supported notification types. */ + +#define UX_HOST_CLASS_CDC_ACM_NOTIFICATION_NETWORK_CONNECTION 0x00 +#define UX_HOST_CLASS_CDC_ACM_NOTIFICATION_RESPONSE_AVAILABLE 0x01 +#define UX_HOST_CLASS_CDC_ACM_NOTIFICATION_SERIAL_STATE 0x20 +#define UX_HOST_CLASS_CDC_ACM_NOTIFICATION_CALL_STATE_CHANGE 0x28 +#define UX_HOST_CLASS_CDC_ACM_NOTIFICATION_LINE_STATE_CHANGE 0x29 +#define UX_HOST_CLASS_CDC_ACM_NOTIFICATION_SPEED_CHANGE 0x2A + +/* Define notification packet format. */ + +#define UX_HOST_CLASS_CDC_ACM_NPF_REQUEST_TYPE 0x00 +#define UX_HOST_CLASS_CDC_ACM_NPF_NOTIFICATION_TYPE 0x01 +#define UX_HOST_CLASS_CDC_ACM_NPF_VALUE 0x02 +#define UX_HOST_CLASS_CDC_ACM_NPF_INDEX 0x04 +#define UX_HOST_CLASS_CDC_ACM_NPF_LENGTH 0x06 + +/* Define CDC ACM Class instance structure. */ + +typedef struct UX_HOST_CLASS_CDC_ACM_STRUCT +{ + struct UX_HOST_CLASS_CDC_ACM_STRUCT + *ux_host_class_cdc_acm_next_instance; + UX_HOST_CLASS *ux_host_class_cdc_acm_class; + UX_DEVICE *ux_host_class_cdc_acm_device; + UX_ENDPOINT *ux_host_class_cdc_acm_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_cdc_acm_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_cdc_acm_interrupt_endpoint; + UX_INTERFACE *ux_host_class_cdc_acm_interface; + UINT ux_host_class_cdc_acm_instance_status; + UINT ux_host_class_cdc_acm_state; + TX_SEMAPHORE ux_host_class_cdc_acm_semaphore; + ULONG ux_host_class_cdc_acm_notification_count; + UCHAR ux_host_class_cdc_acm_capabilities; + ULONG ux_host_class_cdc_acm_device_state; + struct UX_HOST_CLASS_CDC_ACM_RECEPTION_STRUCT + *ux_host_class_cdc_acm_reception; + + VOID (*ux_host_class_cdc_acm_device_status_change_callback)(struct UX_HOST_CLASS_CDC_ACM_STRUCT *cdc_acm, + ULONG notification_type, ULONG notification_value); +} UX_HOST_CLASS_CDC_ACM; + +/* Define CDC DLC Class instance structure. */ + + +typedef struct UX_HOST_CLASS_CDC_DLC_STRUCT +{ + struct UX_HOST_CLASS_CDC_DLC_STRUCT + *ux_host_class_cdc_dlc_next_instance; + UX_HOST_CLASS *ux_host_class_cdc_dlc_class; + UX_DEVICE *ux_host_class_cdc_dlc_device; + UX_ENDPOINT *ux_host_class_cdc_dlc_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_cdc_dlc_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_cdc_dlc_interrupt_endpoint; + UX_INTERFACE *ux_host_class_cdc_dlc_interface; + UINT ux_host_class_cdc_dlc_instance_status; + UINT ux_host_class_cdc_dlc_state; + TX_SEMAPHORE ux_host_class_cdc_dlc_semaphore; + ULONG ux_host_class_cdc_dlc_notification_count; + UCHAR ux_host_class_cdc_dlc_capabilities; + struct UX_HOST_CLASS_CDC_DLC_RECEPTION_STRUCT + *ux_host_class_cdc_dlc_reception; + +} UX_HOST_CLASS_CDC_DLC; + +/* Define CDC ACM reception structure. */ + +typedef struct UX_HOST_CLASS_CDC_ACM_RECEPTION_STRUCT +{ + + ULONG ux_host_class_cdc_acm_reception_state; + ULONG ux_host_class_cdc_acm_reception_block_size; + UCHAR *ux_host_class_cdc_acm_reception_data_buffer; + ULONG ux_host_class_cdc_acm_reception_data_buffer_size; + UCHAR *ux_host_class_cdc_acm_reception_data_head; + UCHAR *ux_host_class_cdc_acm_reception_data_tail; + VOID (*ux_host_class_cdc_acm_reception_callback)(struct UX_HOST_CLASS_CDC_ACM_STRUCT *cdc_acm, + UINT status, + UCHAR *reception_buffer, + ULONG reception_size); + +} UX_HOST_CLASS_CDC_ACM_RECEPTION; + +/* Define CDC ACM Line Coding IOCTL structure. */ + +typedef struct UX_HOST_CLASS_CDC_ACM_LINE_CODING_STRUCT +{ + + ULONG ux_host_class_cdc_acm_line_coding_dter; + ULONG ux_host_class_cdc_acm_line_coding_stop_bit; + ULONG ux_host_class_cdc_acm_line_coding_parity; + ULONG ux_host_class_cdc_acm_line_coding_data_bits; + +} UX_HOST_CLASS_CDC_ACM_LINE_CODING; + +/* Define CDC ACM Line State IOCTL structure. */ + +typedef struct UX_HOST_CLASS_CDC_ACM_LINE_STATE_STRUCT +{ + + ULONG ux_host_class_cdc_acm_line_state_rts; + ULONG ux_host_class_cdc_acm_line_state_dtr; + +} UX_HOST_CLASS_CDC_ACM_LINE_STATE; + +/* Define CDC ACM Line break IOCTL structure. */ + +typedef struct UX_HOST_CLASS_CDC_ACM_LINE_BREAK_STRUCT +{ + + ULONG ux_host_class_cdc_acm_line_break; + +} UX_HOST_CLASS_CDC_ACM_LINE_BREAK; + +/* Define CDC ACM Class function prototypes. */ + +UINT _ux_host_class_cdc_acm_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_cdc_acm_configure(UX_HOST_CLASS_CDC_ACM *cdc_acm); +UINT _ux_host_class_cdc_acm_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_cdc_acm_endpoints_get(UX_HOST_CLASS_CDC_ACM *cdc_acm); +UINT _ux_host_class_cdc_acm_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_cdc_acm_read (UX_HOST_CLASS_CDC_ACM *cdc_acm, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_cdc_acm_write(UX_HOST_CLASS_CDC_ACM *cdc_acm, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_cdc_acm_ioctl(UX_HOST_CLASS_CDC_ACM *cdc_acm, ULONG request, + VOID *parameter); +UINT _ux_host_class_cdc_acm_command(UX_HOST_CLASS_CDC_ACM *cdc_acm, ULONG command, + ULONG value, UCHAR *data_buffer, ULONG data_length); +VOID _ux_host_class_cdc_acm_transfer_request_completed(UX_TRANSFER *transfer_request); +UINT _ux_host_class_cdc_acm_capabilities_get(UX_HOST_CLASS_CDC_ACM *cdc_acm); +UINT _ux_host_class_cdc_acm_reception_stop (UX_HOST_CLASS_CDC_ACM *cdc_acm, + UX_HOST_CLASS_CDC_ACM_RECEPTION *cdc_acm_reception); +UINT _ux_host_class_cdc_acm_reception_start (UX_HOST_CLASS_CDC_ACM *cdc_acm, + UX_HOST_CLASS_CDC_ACM_RECEPTION *cdc_acm_reception); + +VOID _ux_host_class_cdc_acm_reception_callback (UX_TRANSFER *transfer_request); + + +/* Define CDC ACM Class API prototypes. */ + +#define ux_host_class_cdc_acm_entry _ux_host_class_cdc_acm_entry +#define ux_host_class_cdc_acm_read _ux_host_class_cdc_acm_read +#define ux_host_class_cdc_acm_write _ux_host_class_cdc_acm_write +#define ux_host_class_cdc_acm_ioctl _ux_host_class_cdc_acm_ioctl +#define ux_host_class_cdc_acm_reception_start _ux_host_class_cdc_acm_reception_start +#define ux_host_class_cdc_acm_reception_stop _ux_host_class_cdc_acm_reception_stop + + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_cdc_ecm.h b/common/usbx_host_classes/inc/ux_host_class_cdc_ecm.h new file mode 100644 index 0000000..a3f15d1 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_cdc_ecm.h @@ -0,0 +1,289 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_cdc_ecm.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX CDC_ECM class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_CDC_ECM_H +#define UX_HOST_CLASS_CDC_ECM_H + +/* Include the NetX API. */ +#include "nx_api.h" +#include "ux_network_driver.h" + +/* Define CDC_ECM Class constants. Insert here the PID/VID of vendors and products using the CDC ECM chipset. + It is a better mechanism to put this value in the ux_user.h file. */ +#ifndef UX_HOST_CLASS_CDC_ECM_VENDOR_ID +#define UX_HOST_CLASS_CDC_ECM_VENDOR_ID 0x0770 +#define UX_HOST_CLASS_CDC_ECM_PRODUCT_ID 0x1042 +#endif + +/* Define CDC_ECM Class/subclass/protocol constants. */ +#define UX_HOST_CLASS_CDC_CONTROL_CLASS 0x02 +#define UX_HOST_CLASS_CDC_DATA_CLASS 0x0A +#define UX_HOST_CLASS_CDC_ECM_CONTROL_SUBCLASS 0x06 +#define UX_HOST_CLASS_CDC_ECM_CONTROL_PROTOCOL 0x00 +#define UX_HOST_CLASS_CDC_ECM_CS_INTERFACE 0x24 +#define UX_HOST_CLASS_CDC_ECM_FUNCTIONAL_DESCRIPTOR 0x0F +#define UX_HOST_CLASS_CDC_ECM_MAC_ADDRESS_STRING_LENGTH 32 +#define UX_HOST_CLASS_CDC_ECM_MAC_ADDRESS_ASCII_LENGTH 8 + +#define UX_HOST_CLASS_CDC_ECM_SPEED_SELECTED_100MPBS 0x100 +#define UX_HOST_CLASS_CDC_ECM_SPEED_SELECTED_10MPBS 0x10 +#define UX_HOST_CLASS_CDC_ECM_LINK_STATE_DOWN 0 +#define UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP 1 +#define UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_UP 2 +#define UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_DOWN 3 +#define UX_HOST_CLASS_CDC_ECM_BASE_IP_ADDRESS 0xC0A80001 +#define UX_HOST_CLASS_CDC_ECM_BASE_IP_MASK 0xFFFFFF00 +#define UX_HOST_CLASS_CDC_ECM_MAX_MTU 1518 +#define UX_HOST_CLASS_CDC_ECM_ETHERNET_IP 0x0800 +#define UX_HOST_CLASS_CDC_ECM_ETHERNET_ARP 0x0806 +#define UX_HOST_CLASS_CDC_ECM_ETHERNET_RARP 0x8035 +#define UX_HOST_CLASS_CDC_ECM_ETHERNET_PACKET_SIZE 1536 +#define UX_HOST_CLASS_CDC_ECM_NX_ALIGN_PADDING 2 +#ifndef UX_HOST_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES +#define UX_HOST_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES 16 +#endif + +#define UX_HOST_CLASS_CDC_ECM_NX_PACKET_SIZE sizeof(NX_PACKET) + +#define UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE_ASSERT UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG(UX_HOST_CLASS_CDC_ECM_ETHERNET_PACKET_SIZE, UX_HOST_CLASS_CDC_ECM_NX_ALIGN_PADDING), UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE_calc_ovf) +#define UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE (UX_HOST_CLASS_CDC_ECM_ETHERNET_PACKET_SIZE + UX_HOST_CLASS_CDC_ECM_NX_ALIGN_PADDING) + +#define UX_HOST_CLASS_CDC_ECM_NX_BUFF_SIZE_ASSERT \ + UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE, \ + UX_HOST_CLASS_CDC_ECM_NX_PACKET_SIZE), \ + UX_HOST_CLASS_CDC_ECM_NX_BUFF_SIZE_calc_ovf) +#define UX_HOST_CLASS_CDC_ECM_NX_BUFF_SIZE (UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE + UX_HOST_CLASS_CDC_ECM_NX_PACKET_SIZE) + +#define UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT \ + UX_HOST_CLASS_CDC_ECM_NX_BUFF_SIZE_ASSERT \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG( \ + UX_HOST_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES, \ + UX_HOST_CLASS_CDC_ECM_NX_BUFF_SIZE), \ + UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_calc1_ovf) \ + UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_ADD_ULONG( \ + UX_HOST_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES * \ + UX_HOST_CLASS_CDC_ECM_NX_BUFF_SIZE, \ + 32), UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_calc2_ovf) +#define UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE (UX_HOST_CLASS_CDC_ECM_NX_PKPOOL_ENTRIES * UX_HOST_CLASS_CDC_ECM_NX_BUFF_SIZE + 32) + +#define UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE 14 + +#define UX_HOST_CLASS_CDC_ECM_DEVICE_INIT_DELAY (1 * UX_PERIODIC_RATE) +#define UX_HOST_CLASS_CDC_ECM_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_HOST_CLASS_CDC_ECM_SETUP_BUFFER_SIZE 16 + +/* Define NetX errors inside the CDC ECM class. */ +#define UX_HOST_CLASS_CDC_ECM_NX_SUCCESS 0x00 +#define UX_HOST_CLASS_CDC_ECM_NX_NO_PACKET 0x01 +#define UX_HOST_CLASS_CDC_ECM_NX_UNDERFLOW 0x02 +#define UX_HOST_CLASS_CDC_ECM_NX_OVERFLOW 0x03 +#define UX_HOST_CLASS_CDC_ECM_NX_NO_MAPPING 0x04 +#define UX_HOST_CLASS_CDC_ECM_NX_DELETED 0x05 +#define UX_HOST_CLASS_CDC_ECM_NX_POOL_ERROR 0x06 +#define UX_HOST_CLASS_CDC_ECM_NX_PTR_ERROR 0x07 +#define UX_HOST_CLASS_CDC_ECM_NX_WAIT_ERROR 0x08 +#define UX_HOST_CLASS_CDC_ECM_NX_SIZE_ERROR 0x09 +#define UX_HOST_CLASS_CDC_ECM_NX_OPTION_ERROR 0x0a +#define UX_HOST_CLASS_CDC_ECM_NX_DELETE_ERROR 0x10 +#define UX_HOST_CLASS_CDC_ECM_NX_CALLER_ERROR 0x11 +#define UX_HOST_CLASS_CDC_ECM_NX_INVALID_PACKET 0x12 +#define UX_HOST_CLASS_CDC_ECM_NX_INVALID_SOCKET 0x13 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_ENABLED 0x14 +#define UX_HOST_CLASS_CDC_ECM_NX_ALREADY_ENABLED 0x15 +#define UX_HOST_CLASS_CDC_ECM_NX_ENTRY_NOT_FOUND 0x16 +#define UX_HOST_CLASS_CDC_ECM_NX_NO_MORE_ENTRIES 0x17 +#define UX_HOST_CLASS_CDC_ECM_NX_ARP_TIMER_ERROR 0x18 +#define UX_HOST_CLASS_CDC_ECM_NX_RESERVED_CODE0 0x19 +#define UX_HOST_CLASS_CDC_ECM_NX_WAIT_ABORTED 0x1A +#define UX_HOST_CLASS_CDC_ECM_NX_IP_INTERNAL_ERROR 0x20 +#define UX_HOST_CLASS_CDC_ECM_NX_IP_ADDRESS_ERROR 0x21 +#define UX_HOST_CLASS_CDC_ECM_NX_ALREADY_BOUND 0x22 +#define UX_HOST_CLASS_CDC_ECM_NX_PORT_UNAVAILABLE 0x23 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_BOUND 0x24 +#define UX_HOST_CLASS_CDC_ECM_NX_RESERVED_CODE1 0x25 +#define UX_HOST_CLASS_CDC_ECM_NX_SOCKET_UNBOUND 0x26 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_CREATED 0x27 +#define UX_HOST_CLASS_CDC_ECM_NX_SOCKETS_BOUND 0x28 +#define UX_HOST_CLASS_CDC_ECM_NX_NO_RESPONSE 0x29 +#define UX_HOST_CLASS_CDC_ECM_NX_POOL_DELETED 0x30 +#define UX_HOST_CLASS_CDC_ECM_NX_ALREADY_RELEASED 0x31 +#define UX_HOST_CLASS_CDC_ECM_NX_RESERVED_CODE2 0x32 +#define UX_HOST_CLASS_CDC_ECM_NX_MAX_LISTEN 0x33 +#define UX_HOST_CLASS_CDC_ECM_NX_DUPLICATE_LISTEN 0x34 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_CLOSED 0x35 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_LISTEN_STATE 0x36 +#define UX_HOST_CLASS_CDC_ECM_NX_IN_PROGRESS 0x37 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_CONNECTED 0x38 +#define UX_HOST_CLASS_CDC_ECM_NX_WINDOW_OVERFLOW 0x39 +#define UX_HOST_CLASS_CDC_ECM_NX_ALREADY_SUSPENDED 0x40 +#define UX_HOST_CLASS_CDC_ECM_NX_DISCONNECT_FAILED 0x41 +#define UX_HOST_CLASS_CDC_ECM_NX_STILL_BOUND 0x42 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_SUCCESSFUL 0x43 +#define UX_HOST_CLASS_CDC_ECM_NX_UNHANDLED_COMMAND 0x44 +#define UX_HOST_CLASS_CDC_ECM_NX_NO_FREE_PORTS 0x45 +#define UX_HOST_CLASS_CDC_ECM_NX_INVALID_PORT 0x46 +#define UX_HOST_CLASS_CDC_ECM_NX_INVALID_RELISTEN 0x47 +#define UX_HOST_CLASS_CDC_ECM_NX_CONNECTION_PENDING 0x48 +#define UX_HOST_CLASS_CDC_ECM_NX_TX_QUEUE_DEPTH 0x49 +#define UX_HOST_CLASS_CDC_ECM_NX_NOT_IMPLEMENTED 0x80 + +/* Define CDC_ECM Class packet equivalences. */ + +#define UX_HOST_CLASS_CDC_ECM_PACKET_SIZE 128 +#define UX_HOST_CLASS_CDC_ECM_NODE_ID_LENGTH 6 + +/* Define supported notification types. */ + +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_NETWORK_CONNECTION 0x00 +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_RESPONSE_AVAILABLE 0x01 +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_SERIAL_STATE 0x20 +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_CALL_STATE_CHANGE 0x28 +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_LINE_STATE_CHANGE 0x29 +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_SPEED_CHANGE 0x2A + +/* Define notification packet format. */ + +#define UX_HOST_CLASS_CDC_ECM_NPF_REQUEST_TYPE 0x00 +#define UX_HOST_CLASS_CDC_ECM_NPF_NOTIFICATION_TYPE 0x01 +#define UX_HOST_CLASS_CDC_ECM_NPF_VALUE 0x02 +#define UX_HOST_CLASS_CDC_ECM_NPF_INDEX 0x04 +#define UX_HOST_CLASS_CDC_ECM_NPF_LENGTH 0x06 + +/* Define supported notification values. */ + +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_NETWORK_LINK_DOWN 0x00 +#define UX_HOST_CLASS_CDC_ECM_NOTIFICATION_NETWORK_LINK_UP 0x01 + +/* Define packet allocation timeout in milliseconds. */ + +#ifndef UX_HOST_CLASS_CDC_ECM_PACKET_POOL_WAIT +#define UX_HOST_CLASS_CDC_ECM_PACKET_POOL_WAIT 1000 +#endif + +/* Define CDC_ECM Class instance structure. */ + +typedef struct UX_HOST_CLASS_CDC_ECM_STRUCT +{ + struct UX_HOST_CLASS_CDC_ECM_STRUCT + *ux_host_class_cdc_ecm_next_instance; + UX_HOST_CLASS *ux_host_class_cdc_ecm_class; + UX_DEVICE *ux_host_class_cdc_ecm_device; + UX_ENDPOINT *ux_host_class_cdc_ecm_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_cdc_ecm_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_cdc_ecm_interrupt_endpoint; + UX_INTERFACE *ux_host_class_cdc_ecm_interface_data; + UX_INTERFACE *ux_host_class_cdc_ecm_interface_control; + UCHAR ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process; + UCHAR ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish; + TX_SEMAPHORE ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore; + UCHAR ux_host_class_cdc_ecm_bulk_out_transfer_check_and_arm_in_process; + UCHAR ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish; + TX_SEMAPHORE ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish_semaphore; + UINT ux_host_class_cdc_ecm_instance_status; + UINT ux_host_class_cdc_ecm_state; + TX_SEMAPHORE ux_host_class_cdc_ecm_interrupt_notification_semaphore; + TX_THREAD ux_host_class_cdc_ecm_thread; + UCHAR *ux_host_class_cdc_ecm_thread_stack; + ULONG ux_host_class_cdc_ecm_notification_count; + ULONG ux_host_class_cdc_ecm_primary_phy_id; + ULONG ux_host_class_cdc_ecm_primary_phy_type; + ULONG ux_host_class_cdc_ecm_secondary_phy_id; + ULONG ux_host_class_cdc_ecm_secondary_phy_type; + ULONG ux_host_class_cdc_ecm_model_revision_number; + ULONG ux_host_class_cdc_ecm_vendor_model_number; + ULONG ux_host_class_cdc_ecm_speed_selected; + ULONG ux_host_class_cdc_ecm_device_state; + ULONG ux_host_class_cdc_ecm_link_state; + NX_PACKET *ux_host_class_cdc_ecm_xmit_queue_head; + NX_PACKET *ux_host_class_cdc_ecm_xmit_queue_tail; + NX_PACKET_POOL ux_host_class_cdc_ecm_packet_pool; + UCHAR *ux_host_class_cdc_ecm_pool_memory; + UCHAR ux_host_class_cdc_ecm_node_id[UX_HOST_CLASS_CDC_ECM_NODE_ID_LENGTH]; + VOID (*ux_host_class_cdc_ecm_device_status_change_callback)(struct UX_HOST_CLASS_CDC_ECM_STRUCT *cdc_ecm, + ULONG device_state); + VOID *ux_host_class_cdc_ecm_network_handle; + +} UX_HOST_CLASS_CDC_ECM; + + +/* Define ECM Interface Functional descriptor. */ + +#define UX_HOST_CLASS_CDC_ECM_INTERFACE_DESCRIPTOR_ENTRIES 8 +#define UX_HOST_CLASS_CDC_ECM_INTERFACE_DESCRIPTOR_LENGTH 13 + +typedef struct UX_HOST_CLASS_ECM_INTERFACE_DESCRIPTOR_STRUCT +{ + ULONG bFunctionLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG iMACAddress; + ULONG bmEthernetStatistics; + ULONG wMaxSegmentSize; + ULONG wNumberMCFilters; + ULONG bNumberPowerFilters; +} UX_HOST_CLASS_ECM_INTERFACE_DESCRIPTOR; + +/* Define CDC ECM Class function prototypes. */ + +UINT _ux_host_class_cdc_ecm_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_cdc_ecm_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_cdc_ecm_endpoints_get(UX_HOST_CLASS_CDC_ECM *cdc_ecm); +UINT _ux_host_class_cdc_ecm_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_cdc_ecm_write(VOID *cdc_ecm_class, NX_PACKET *packet); +VOID _ux_host_class_cdc_ecm_interrupt_notification(UX_TRANSFER *transfer_request); +VOID _ux_host_class_cdc_ecm_thread(ULONG parameter); +VOID _ux_host_class_cdc_ecm_transmission_callback(UX_TRANSFER *transfer_request); +VOID _ux_host_class_cdc_ecm_transmit_queue_clean(UX_HOST_CLASS_CDC_ECM *cdc_ecm_control); +UINT _ux_host_class_cdc_ecm_mac_address_get(UX_HOST_CLASS_CDC_ECM *cdc_ecm); + +/* Define CDC ECM Class API prototypes. */ + +#define ux_host_class_cdc_ecm_entry _ux_host_class_cdc_ecm_entry +#define ux_host_class_cdc_ecm_write _ux_host_class_cdc_ecm_write + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_gser.h b/common/usbx_host_classes/inc/ux_host_class_gser.h new file mode 100644 index 0000000..a9d76b6 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_gser.h @@ -0,0 +1,261 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_gser.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX Generic Serial Class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_GSER_H +#define UX_HOST_CLASS_GSER_H + + +/* Define Sierra Wireless AR Class constants. */ + +#define UX_HOST_CLASS_GSER_CLASS_TRANSFER_TIMEOUT 300000 +#ifndef UX_HOST_CLASS_GSER_VENDOR_ID +#define UX_HOST_CLASS_GSER_VENDOR_ID 0X05C6 +#define UX_HOST_CLASS_GSER_PRODUCT_ID 0X9002 +#endif + +/* Define serial interfaces equivalences. */ +#define UX_HOST_CLASS_GSER_INTERFACE_NUMBER 3 + +/* Define packet equivalences. */ +#define UX_HOST_CLASS_GSER_PACKET_SIZE 128 + +/* Define Generic AR IOCTL functions. */ + +#define UX_HOST_CLASS_GSER_REQ_SEND_ENCAPSULATED_COMMAND 0x00 +#define UX_HOST_CLASS_GSER_REQ_GET_ENCAPSULATED_COMMAND 0x01 +#define UX_HOST_CLASS_GSER_REQ_SET_COMM_FEATURE 0x02 +#define UX_HOST_CLASS_GSER_REQ_GET_COMM_FEATURE 0x03 +#define UX_HOST_CLASS_GSER_REQ_CLEAR_COMM_FEATURE 0x04 +#define UX_HOST_CLASS_GSER_REQ_SET_AUX_LINE_STATE 0x10 +#define UX_HOST_CLASS_GSER_REQ_SET_HOOK_STATE 0x11 +#define UX_HOST_CLASS_GSER_REQ_PULSE_SETUP 0x12 +#define UX_HOST_CLASS_GSER_REQ_SEND_PULSE 0x13 +#define UX_HOST_CLASS_GSER_REQ_SET_PUSLE_TIME 0x14 +#define UX_HOST_CLASS_GSER_REQ_RING_AUX_JACK 0x15 +#define UX_HOST_CLASS_GSER_REQ_SET_LINE_CODING 0x20 +#define UX_HOST_CLASS_GSER_REQ_GET_LINE_CODING 0x21 +#define UX_HOST_CLASS_GSER_REQ_SET_LINE_STATE 0x22 +#define UX_HOST_CLASS_GSER_REQ_SEND_BREAK 0x23 +#define UX_HOST_CLASS_GSER_REQ_SET_RINGER_PARMS 0x30 +#define UX_HOST_CLASS_GSER_REQ_GET_RINGER_PARMS 0x31 +#define UX_HOST_CLASS_GSER_REQ_SET_OPERATION_PARMS 0x32 +#define UX_HOST_CLASS_GSER_REQ_GET_OPERATION_PARMS 0x33 +#define UX_HOST_CLASS_GSER_REQ_SET_LINE_PARMS 0x34 +#define UX_HOST_CLASS_GSER_REQ_GET_LINE_PARMS 0x35 + +/* Define CDC ACM line output control values. */ + +#define UX_HOST_CLASS_GSER_CTRL_DTR 0x01 +#define UX_HOST_CLASS_GSER_CTRL_RTS 0x02 + +/* Define CDC ACM line input control values. */ + +#define UX_HOST_CLASS_GSER_CTRL_DCD 0x01 +#define UX_HOST_CLASS_GSER_CTRL_DSR 0x02 +#define UX_HOST_CLASS_GSER_CTRL_BRK 0x04 +#define UX_HOST_CLASS_GSER_CTRL_RI 0x08 + +#define UX_HOST_CLASS_GSER_CTRL_FRAMING 0x10 +#define UX_HOST_CLASS_GSER_CTRL_PARITY 0x20 +#define UX_HOST_CLASS_GSER_CTRL_OVERRUN 0x40 + +/* Define CDC ACM default values. */ + +#define UX_HOST_CLASS_GSER_LINE_CODING_DEFAULT_RATE 9600 +#define UX_HOST_CLASS_GSER_LINE_CODING_DEFAULT_DATA_BIT 8 + +/* Define CDC ACM line coding definitions. */ + +#define UX_HOST_CLASS_GSER_LINE_CODING_STOP_BIT_0 0 +#define UX_HOST_CLASS_GSER_LINE_CODING_STOP_BIT_15 1 +#define UX_HOST_CLASS_GSER_LINE_CODING_STOP_BIT_2 2 + +#define UX_HOST_CLASS_GSER_LINE_CODING_PARITY_NONE 0 +#define UX_HOST_CLASS_GSER_LINE_CODING_PARITY_ODD 1 +#define UX_HOST_CLASS_GSER_LINE_CODING_PARITY_EVEN 2 +#define UX_HOST_CLASS_GSER_LINE_CODING_PARITY_MARK 3 +#define UX_HOST_CLASS_GSER_LINE_CODING_PARITY_SPACE 4 + +#define UX_HOST_CLASS_GSER_LINE_CODING_LENGTH 7 +#define UX_HOST_CLASS_GSER_LINE_CODING_RATE 0 +#define UX_HOST_CLASS_GSER_LINE_CODING_STOP_BIT 4 +#define UX_HOST_CLASS_GSER_LINE_CODING_PARITY 5 +#define UX_HOST_CLASS_GSER_LINE_CODING_DATA_BIT 6 + +/* Define CDC ACM line state definitions. */ + +#define UX_HOST_CLASS_GSER_LINE_STATE_STOP_BIT_0 0 +#define UX_HOST_CLASS_GSER_LINE_CODING_STOP_BIT_15 1 + +/* Define CDC ACM IOCTL Functions. */ + +#define UX_HOST_CLASS_GSER_IOCTL_SET_LINE_CODING 0 +#define UX_HOST_CLASS_GSER_IOCTL_GET_LINE_CODING 1 +#define UX_HOST_CLASS_GSER_IOCTL_SET_LINE_STATE 2 +#define UX_HOST_CLASS_GSER_IOCTL_SEND_BREAK 3 +#define UX_HOST_CLASS_GSER_IOCTL_ABORT_IN_PIPE 5 +#define UX_HOST_CLASS_GSER_IOCTL_ABORT_OUT_PIPE 6 +#define UX_HOST_CLASS_GSER_IOCTL_NOTIFICATION_CALLBACK 7 +#define UX_HOST_CLASS_GSER_IOCTL_GET_DEVICE_STATUS 8 + +/* Define Reception States. */ + +#define UX_HOST_CLASS_GSER_RECEPTION_STATE_STOPPED 0 +#define UX_HOST_CLASS_GSER_RECEPTION_STATE_STARTED 1 +#define UX_HOST_CLASS_GSER_RECEPTION_STATE_IN_TRANSFER 2 + +/* Define the interface structures. */ + +typedef struct UX_HOST_CLASS_GSER_INTERFACE_STRUCT +{ + + UX_INTERFACE *ux_host_class_gser_interface; + UX_ENDPOINT *ux_host_class_gser_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_gser_bulk_in_endpoint; + TX_SEMAPHORE ux_host_class_gser_semaphore; + struct UX_HOST_CLASS_GSER_RECEPTION_STRUCT *ux_host_class_gser_reception; + ULONG ux_host_class_gser_notification_count; +} UX_HOST_CLASS_GSER_INTERFACE; + +/* Define Generic Serial Class instance structure. */ + +typedef struct UX_HOST_CLASS_GSER_STRUCT +{ + + struct UX_HOST_CLASS_GSER_STRUCT *ux_host_class_gser_next_instance; + UX_HOST_CLASS *ux_host_class_gser_class; + UX_DEVICE *ux_host_class_gser_device; + UINT ux_host_class_gser_state; + struct UX_HOST_CLASS_GSER_INTERFACE_STRUCT ux_host_class_gser_interface_array[UX_HOST_CLASS_GSER_INTERFACE_NUMBER]; + ULONG ux_host_class_gser_device_state; + VOID (*ux_host_class_gser_device_status_change_callback)(struct UX_HOST_CLASS_GSER_STRUCT *gser, + ULONG notification_type, ULONG notification_value); +} UX_HOST_CLASS_GSER; + +/* Define generic serial class reception structure. */ + + +typedef struct UX_HOST_CLASS_GSER_RECEPTION_STRUCT +{ + + ULONG ux_host_class_gser_reception_interface_index; + ULONG ux_host_class_gser_reception_state; + ULONG ux_host_class_gser_reception_block_size; + UCHAR *ux_host_class_gser_reception_data_buffer; + ULONG ux_host_class_gser_reception_data_buffer_size; + UCHAR *ux_host_class_gser_reception_data_head; + UCHAR *ux_host_class_gser_reception_data_tail; + VOID (*ux_host_class_gser_reception_callback)(struct UX_HOST_CLASS_GSER_STRUCT *gser, + UINT status, + UCHAR *reception_buffer, + ULONG reception_size); + +} UX_HOST_CLASS_GSER_RECEPTION; + + +/* Define GSER Line Coding IOCTL structure. */ + +typedef struct UX_HOST_CLASS_GSER_LINE_CODING_STRUCT +{ + + ULONG ux_host_class_gser_line_coding_dter; + ULONG ux_host_class_gser_line_coding_stop_bit; + ULONG ux_host_class_gser_line_coding_parity; + ULONG ux_host_class_gser_line_coding_data_bits; + +} UX_HOST_CLASS_GSER_LINE_CODING; + +/* Define GSER Line State IOCTL structure. */ + +typedef struct UX_HOST_CLASS_GSER_LINE_STATE_STRUCT +{ + + ULONG ux_host_class_gser_line_state_rts; + ULONG ux_host_class_gser_line_state_dtr; + +} UX_HOST_CLASS_GSER_LINE_STATE; + +/* Define GSER Line break IOCTL structure. */ + +typedef struct UX_HOST_CLASS_GSER_LINE_BREAK_STRUCT +{ + + ULONG ux_host_class_gser_line_break; + +} UX_HOST_CLASS_GSER_LINE_BREAK; + + +/* Define GSER Class function prototypes. */ + +UINT _ux_host_class_gser_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_gser_configure(UX_HOST_CLASS_GSER *gser); +UINT _ux_host_class_gser_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_gser_endpoints_get(UX_HOST_CLASS_GSER *gser); +UINT _ux_host_class_gser_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_gser_read (UX_HOST_CLASS_GSER *gser, ULONG interface_index,UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_gser_write(UX_HOST_CLASS_GSER *gser, ULONG interface_index,UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_gser_command(UX_HOST_CLASS_GSER *gser, ULONG interface_index, ULONG command, + ULONG value, UCHAR *data_buffer, ULONG data_length); +UINT _ux_host_class_gser_ioctl(UX_HOST_CLASS_GSER *gser, ULONG interface_index, ULONG ioctl_function, + VOID *parameter); +VOID _ux_host_class_gser_reception_callback (UX_TRANSFER *transfer_request); +UINT _ux_host_class_gser_reception_stop (UX_HOST_CLASS_GSER *gser, + UX_HOST_CLASS_GSER_RECEPTION *gser_reception); +UINT _ux_host_class_gser_reception_start (UX_HOST_CLASS_GSER *gser, + UX_HOST_CLASS_GSER_RECEPTION *gser_reception); + +/* Define GSER Class API prototypes. */ + +#define ux_host_class_gser_entry _ux_host_class_gser_entry +#define ux_host_class_gser_read _ux_host_class_gser_read +#define ux_host_class_gser_write _ux_host_class_gser_write +#define ux_host_class_gser_ioctl _ux_host_class_gser_ioctl +#define ux_host_class_gser_reception_start _ux_host_class_gser_reception_start +#define ux_host_class_gser_reception_stop _ux_host_class_gser_reception_stop + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_hid.h b/common/usbx_host_classes/inc/ux_host_class_hid.h new file mode 100644 index 0000000..acce24e --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_hid.h @@ -0,0 +1,1030 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_hid.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX HID class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_HID_H +#define UX_HOST_CLASS_HID_H + + +/* Define HID Class constants. */ + +#define UX_HOST_CLASS_HID_CLASS 3 +#define UX_HOST_CLASS_HID_FIELDS 16 +#define UX_HOST_CLASS_HID_MAX_COLLECTION 4 +#define UX_HOST_CLASS_HID_MAX_REPORT 8 +#define UX_HOST_CLASS_HID_REPORT_SIZE 32 +#define UX_HOST_CLASS_HID_DESCRIPTOR 0x21 +#define UX_HOST_CLASS_HID_ITEM_LENGTH_MASK 3 +#define UX_HOST_CLASS_HID_ITEM_TAG_MASK 0xf0 +#define UX_HOST_CLASS_HID_ITEM_TAG_SHORT 1 +#define UX_HOST_CLASS_HID_ITEM_TAG_LONG 0xf0 +#define UX_HOST_CLASS_HID_MAX_CLIENTS 8 +#define UX_HOST_CLASS_HID_MAX_CLIENT_NAME_LENGTH 63 /* Exclude string null-terminator */ + +#ifndef UX_HOST_CLASS_HID_DECOMPRESSION_BUFFER +#define UX_HOST_CLASS_HID_DECOMPRESSION_BUFFER 4096 +#endif +#define UX_HOST_CLASS_HID_REPORT_DECOMPRESSED 0 +#define UX_HOST_CLASS_HID_REPORT_RAW 1 +#define UX_HOST_CLASS_HID_REPORT_INDIVIDUAL_USAGE 2 +#define UX_HOST_CLASS_HID_INTERRUPT_ENDPOINT_READY 1 +#define UX_HOST_CLASS_HID_INTERRUPT_ENDPOINT_ACTIVE 2 + + +/* Define HID Class item types. */ + +#define UX_HOST_CLASS_HID_TYPE_MAIN 0x0 +#define UX_HOST_CLASS_HID_TYPE_GLOBAL 0x1 +#define UX_HOST_CLASS_HID_TYPE_LOCAL 0x2 +#define UX_HOST_CLASS_HID_TYPE_RESERVED 0x3 + + +/* Define HID Class main tags. */ + +#define UX_HOST_CLASS_HID_MAIN_TAG_INPUT 0x8 +#define UX_HOST_CLASS_HID_MAIN_TAG_OUTPUT 0x9 +#define UX_HOST_CLASS_HID_MAIN_TAG_FEATURE 0xb +#define UX_HOST_CLASS_HID_MAIN_TAG_COLLECTION 0xa +#define UX_HOST_CLASS_HID_MAIN_TAG_END_COLLECTION 0xc + + +/* Define HID Class global tags. */ + +#define UX_HOST_CLASS_HID_GLOBAL_TAG_USAGE_PAGE 0x0 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_LOGICAL_MINIMUM 0x1 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_LOGICAL_MAXIMUM 0x2 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_PHYSICAL_MINIMUM 0x3 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_PHYSICAL_MAXIMUM 0x4 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_UNIT_EXPONENT 0x5 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_UNIT 0x6 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_REPORT_SIZE 0x7 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_REPORT_ID 0x8 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_REPORT_COUNT 0x9 +#define UX_HOST_CLASS_HID_GLOBAL_TAG_PUSH 0xa +#define UX_HOST_CLASS_HID_GLOBAL_TAG_POP 0xb + + +/* Define HID Class local tags. */ + +#define UX_HOST_CLASS_HID_LOCAL_TAG_USAGE 0x0 +#define UX_HOST_CLASS_HID_LOCAL_TAG_USAGE_MINIMUM 0x1 +#define UX_HOST_CLASS_HID_LOCAL_TAG_USAGE_MAXIMUM 0x2 +#define UX_HOST_CLASS_HID_LOCAL_TAG_DESIGNATOR_INDEX 0x3 +#define UX_HOST_CLASS_HID_LOCAL_TAG_DESIGNATOR_MINIMUM 0x4 +#define UX_HOST_CLASS_HID_LOCAL_TAG_DESIGNATOR_MAXIMUM 0x5 +#define UX_HOST_CLASS_HID_LOCAL_TAG_STRING_INDEX 0x7 +#define UX_HOST_CLASS_HID_LOCAL_TAG_STRING_MINIMUM 0x8 +#define UX_HOST_CLASS_HID_LOCAL_TAG_STRING_MAXIMUM 0x9 +#define UX_HOST_CLASS_HID_LOCAL_TAG_DELIMITER 0xa + + +/* Define HID Class collection item types. */ + +#define UX_HOST_CLASS_HID_COLLECTION_PHYSICAL 0 +#define UX_HOST_CLASS_HID_COLLECTION_APPLICATION 1 +#define UX_HOST_CLASS_HID_COLLECTION_LOGICAL 2 + + +/* Define HID Class delimiter set. */ + +#define UX_HOST_CLASS_HID_DELIMITER_OPEN 1 +#define UX_HOST_CLASS_HID_DELIMITER_CLOSE 0 + + +/* Define HID Class item bit masks. */ + +#define UX_HOST_CLASS_HID_ITEM_CONSTANT 0x0001 +#define UX_HOST_CLASS_HID_ITEM_VARIABLE 0x0002 +#define UX_HOST_CLASS_HID_ITEM_RELATIVE 0x0004 +#define UX_HOST_CLASS_HID_ITEM_WRAP 0x0008 +#define UX_HOST_CLASS_HID_ITEM_NON_LINEAR 0x0010 +#define UX_HOST_CLASS_HID_ITEM_NO_PREFERRED_STATE 0x0020 +#define UX_HOST_CLASS_HID_ITEM_NULL_STATE 0x0040 +#define UX_HOST_CLASS_HID_ITEM_VOLATILE 0x0080 +#define UX_HOST_CLASS_HID_ITEM_BUFFERED_BYTES 0x0100 + + +/* Define HID Class commands. */ + +#define UX_HOST_CLASS_HID_GET_REPORT 0x01 +#define UX_HOST_CLASS_HID_GET_IDLE 0x02 +#define UX_HOST_CLASS_HID_GET_PROTOCOL 0x03 +#define UX_HOST_CLASS_HID_SET_REPORT 0x09 +#define UX_HOST_CLASS_HID_SET_IDLE 0x0A +#define UX_HOST_CLASS_HID_SET_PROTOCOL 0x0B + + +/* Define HID Class descriptors. */ + +#define UX_HOST_CLASS_HID_DESCRIPTOR 0x21 +#define UX_HOST_CLASS_HID_REPORT_DESCRIPTOR 0x22 +#define UX_HOST_CLASS_HID_PHYSICAL_DESCRIPTOR 0x23 + + +#define UX_HID_DESCRIPTOR_ENTRIES 7 +#define UX_HID_DESCRIPTOR_LENGTH 9 + + +/* Define HID Class page constants. */ + +#define UX_HOST_CLASS_HID_PAGE_GENERIC_DESKTOP_CONTROLS 0x01 +#define UX_HOST_CLASS_HID_PAGE_SIMULATION_CONTROLS 0x02 +#define UX_HOST_CLASS_HID_PAGE_VR_CONTROLS 0x03 +#define UX_HOST_CLASS_HID_PAGE_SPORT_CONTROLS 0x04 +#define UX_HOST_CLASS_HID_PAGE_GAME_CONTROLS 0x05 +#define UX_HOST_CLASS_HID_PAGE_GENERIC_DEVICE_CONTROLS 0x06 +#define UX_HOST_CLASS_HID_PAGE_KEYBOARD_KEYPAD 0x07 +#define UX_HOST_CLASS_HID_PAGE_LEDS 0x08 +#define UX_HOST_CLASS_HID_PAGE_BUTTON 0x09 +#define UX_HOST_CLASS_HID_PAGE_ORDINAL 0x0A +#define UX_HOST_CLASS_HID_PAGE_TELEPHONY 0x0B +#define UX_HOST_CLASS_HID_PAGE_CONSUMER 0x0C +#define UX_HOST_CLASS_HID_PAGE_DIGITIZER 0x0D +#define UX_HOST_CLASS_HID_PAGE_PHYSICAL_INTERFACE_DEVICE 0x0F +#define UX_HOST_CLASS_HID_PAGE_UNICODE 0x10 +#define UX_HOST_CLASS_HID_PAGE_ALPHANUMERIC_DISPLAY 0x14 +#define UX_HOST_CLASS_HID_PAGE_MEDICAL_INSTRUMENTS 0x40 +#define UX_HOST_CLASS_HID_PAGE_BAR_CODE_SCANNER 0x8C +#define UX_HOST_CLASS_HID_PAGE_SCALE_PAGE 0x8D +#define UX_HOST_CLASS_HID_PAGE_MAGNETIC_STRIPE_READING 0x8E +#define UX_HOST_CLASS_HID_PAGE_CAMERA_CONTROL_PAGE 0x90 + + +/* Define HID Class generic desktop page constants. */ + +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_UNDEFINED 0x00 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_POINTER 0x01 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_MOUSE 0x02 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_RESERVED 0x03 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_JOYSTICK 0x04 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_GAME PAD 0x05 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_KEYBOARD 0x06 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_KEYPAD 0x07 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_MULTI_AXIS_CONTROLLER 0x08 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_X 0x30 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_Y 0x31 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_Z 0x32 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_RX 0x33 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_RY 0x34 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_RZ 0x35 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SLIDER 0x36 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_DIAL 0x37 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_WHEEL 0x38 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_HAT_SWITCH 0x39 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_COUNTED_BUFFER 0x3A +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_BYTE_COUNT 0x3B +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_MOTION_WAKEUP 0x3C +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_START 0x3D +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SELECT 0x3E +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_VX 0x40 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_VY 0x41 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_VZ 0x42 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_VBRX 0x43 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_VBRY 0x44 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_VBRZ 0x45 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_VNO 0x46 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_FEATURE_NOTIFICATION 0x47 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_CONTROL 0x80 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_POWER_DOWN 0x81 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_SLEEP 0x82 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_WAKE_UP 0x83 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_CONTEXT_MENU 0x84 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MAIN_MENU 0x85 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_APP_MENU 0x86 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MENU_HELP 0x87 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MENU_EXIT 0x88 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MENU_SELECT 0x89 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MENU_RIGHT 0x8A +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MENU_LEFT 0x8B +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MENU_UP 0x8C +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_MENU_DOWN 0x8D +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_COLD_RESTART 0x8E +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_WARM_RESTART 0x8F +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_D_PAD_UP 0x90 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_D_PAD_DOWN 0x91 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_D_PAD_RIGHT 0x92 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_D_PAD_LEFT 0x93 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DOCK 0xA0 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_UNDOCK 0xA1 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_SETUP 0xA2 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_BREAK 0xA3 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DEBUGGER_BREAK 0xA4 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_APPLICAION_BREAK 0xA5 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_APPLICATION_DEBUGGER_BREAK 0xA6 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_SPEAKER_MUTE 0xA7 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_HIBERNATE 0xA8 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_INVERT 0xB0 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_INTERNAL 0xB1 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_EXTERNAL 0xB2 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_BOTH 0xB3 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_DUAL 0xB4 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_TOGGLE 0xB5 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_SWAP 0xB6 +#define UX_HOST_CLASS_HID_GENERIC_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE 0xB7 + + +/* Define HID Class game control page constants. */ + +#define UX_HOST_CLASS_HID_GAME_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_HID_GAME_CONTROL_3D_GAME_CONTROLLER 0x01 +#define UX_HOST_CLASS_HID_GAME_CONTROL_PINBALL_DEVICE 0x02 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_DEVICE 0x03 +#define UX_HOST_CLASS_HID_GAME_CONTROL_POINT_OF_VIEW 0x20 +#define UX_HOST_CLASS_HID_GAME_CONTROL_TURN_RIGHT_LEFT 0x21 +#define UX_HOST_CLASS_HID_GAME_CONTROL_PITCH_FORWARD_BACKWARD 0x22 +#define UX_HOST_CLASS_HID_GAME_CONTROL_ROLL_RIGHT_LEFT 0x23 +#define UX_HOST_CLASS_HID_GAME_CONTROL_MOVE_RIGHT_LEFT 0x24 +#define UX_HOST_CLASS_HID_GAME_CONTROL_MOVE_FORWARD_BACKWARD 0x25 +#define UX_HOST_CLASS_HID_GAME_CONTROL_MOVE_UP_DOWN 0x26 +#define UX_HOST_CLASS_HID_GAME_CONTROL_LEAN_RIGHT_LEFT 0x27 +#define UX_HOST_CLASS_HID_GAME_CONTROL_LEAN_FORWARD_BACKWARD 0x28 +#define UX_HOST_CLASS_HID_GAME_CONTROL_HEIGHT_OF_POV 0x29 +#define UX_HOST_CLASS_HID_GAME_CONTROL_FLIPPER 0x2A +#define UX_HOST_CLASS_HID_GAME_CONTROL_SECONDARY_FLIPPER 0x2B +#define UX_HOST_CLASS_HID_GAME_CONTROL_BUMP 0x2C +#define UX_HOST_CLASS_HID_GAME_CONTROL_NEW_GAME 0x2D +#define UX_HOST_CLASS_HID_GAME_CONTROL_SHOOT_BALL 0x2E +#define UX_HOST_CLASS_HID_GAME_CONTROL_PLAYER 0x2F +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_BOLT 0x30 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_CLIP 0x31 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_SELECTOR 0x32 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_SINGLE_SHOT 0x33 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_BURST 0x34 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_AUTOMATIC 0x35 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GUN_SAFETY 0x36 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GAMEAD_FIRE_JUMP 0x37 +#define UX_HOST_CLASS_HID_GAME_CONTROL_GAMEPAD_TRIGGER 0x39 + + +/* Define HID Class LED page constants. */ + +#define UX_HOST_CLASS_HID_LED_UNDEFINED 0x00 +#define UX_HOST_CLASS_HID_LED_NUM_LOCK 0x01 +#define UX_HOST_CLASS_HID_LED_CAPS_LOCK 0x02 +#define UX_HOST_CLASS_HID_LED_SCROLL_LOCK 0x03 +#define UX_HOST_CLASS_HID_LED_COMPOSE 0x04 +#define UX_HOST_CLASS_HID_LED_KANA 0x05 +#define UX_HOST_CLASS_HID_LED_POWER 0x06 +#define UX_HOST_CLASS_HID_LED_SHIFT 0x07 +#define UX_HOST_CLASS_HID_LED_DO_NOT_DISTURB 0x08 +#define UX_HOST_CLASS_HID_LED_MUTE 0x09 +#define UX_HOST_CLASS_HID_LED_TONE_ENABLE 0x0A +#define UX_HOST_CLASS_HID_LED_HIGH_CUT_FILTER 0x0B +#define UX_HOST_CLASS_HID_LED_LOW_CUT_FILTER 0x0C +#define UX_HOST_CLASS_HID_LED_EQUALIZER_ENABLE 0x0D +#define UX_HOST_CLASS_HID_LED_SOUND_FIELD_ON 0x0E +#define UX_HOST_CLASS_HID_LED_SURROUND_ON 0x0F +#define UX_HOST_CLASS_HID_LED_REPEAT 0x10 +#define UX_HOST_CLASS_HID_LED_STEREO 0x11 +#define UX_HOST_CLASS_HID_LED_SAMPLING_RATE_DETECT 0x12 +#define UX_HOST_CLASS_HID_LED_SPINNING 0x13 +#define UX_HOST_CLASS_HID_LED_CAV 0x14 +#define UX_HOST_CLASS_HID_LED_CLV 0x15 +#define UX_HOST_CLASS_HID_LED_RECORDING_FORMAT_DETECT 0x16 +#define UX_HOST_CLASS_HID_LED_OFF_HOOK 0x17 +#define UX_HOST_CLASS_HID_LED_RING 0x18 +#define UX_HOST_CLASS_HID_LED_MESSAGE_WAITING 0x19 +#define UX_HOST_CLASS_HID_LED_DATA_MODE 0x1A +#define UX_HOST_CLASS_HID_LED_BATTERY_OPERATION 0x1B +#define UX_HOST_CLASS_HID_LED_BATTERY_OK 0x1C +#define UX_HOST_CLASS_HID_LED_BATTERY_LOW 0x1D +#define UX_HOST_CLASS_HID_LED_SPEAKER 0x1E +#define UX_HOST_CLASS_HID_LED_HEAD_SET 0x1F +#define UX_HOST_CLASS_HID_LED_HOLD 0x20 +#define UX_HOST_CLASS_HID_LED_MICROPHONE 0x21 +#define UX_HOST_CLASS_HID_LED_COVERAGE 0x22 +#define UX_HOST_CLASS_HID_LED_NIGHT_MODE 0x23 +#define UX_HOST_CLASS_HID_LED_SEND_CALLS 0x24 +#define UX_HOST_CLASS_HID_LED_CALL_PICKUP 0x25 +#define UX_HOST_CLASS_HID_LED_CONFERENCE 0x26 +#define UX_HOST_CLASS_HID_LED_STAND_BY 0x27 +#define UX_HOST_CLASS_HID_LED_CAMERA_ON 0x28 +#define UX_HOST_CLASS_HID_LED_CAMERA_OFF 0x29 +#define UX_HOST_CLASS_HID_LED_ON_LINE 0x2A +#define UX_HOST_CLASS_HID_LED_OFF_LINE 0x2B +#define UX_HOST_CLASS_HID_LED_BUSY 0x2C +#define UX_HOST_CLASS_HID_LED_READY 0x2D +#define UX_HOST_CLASS_HID_LED_PAPER_OUT 0x2E +#define UX_HOST_CLASS_HID_LED_PAPER_JAM 0x2F +#define UX_HOST_CLASS_HID_LED_REMOTE 0x30 +#define UX_HOST_CLASS_HID_LED_FORWARD 0x31 +#define UX_HOST_CLASS_HID_LED_REVERSE 0x32 +#define UX_HOST_CLASS_HID_LED_STOP 0x33 +#define UX_HOST_CLASS_HID_LED_REWIND 0x34 +#define UX_HOST_CLASS_HID_LED_FAST_FORWARD 0x35 +#define UX_HOST_CLASS_HID_LED_PLAY 0x36 +#define UX_HOST_CLASS_HID_LED_PAUSE 0x37 +#define UX_HOST_CLASS_HID_LED_RECORD 0x38 +#define UX_HOST_CLASS_HID_LED_ERROR 0x39 +#define UX_HOST_CLASS_HID_LED_USAGE_SELECTED_INDICATOR 0x3A +#define UX_HOST_CLASS_HID_LED_USAGE_IN_USE_INDICATOR 0x3B +#define UX_HOST_CLASS_HID_LED_USAGE MULTI_MODE_INDICATOR 0x3C +#define UX_HOST_CLASS_HID_LED_INDICATOR_ON 0x3D +#define UX_HOST_CLASS_HID_LED_INDICATOR_FLASH 0x3E +#define UX_HOST_CLASS_HID_LED_INDICATOR_SLOW_BLINK 0x3F +#define UX_HOST_CLASS_HID_LED_INDICATOR_FAST_BLINK 0x40 +#define UX_HOST_CLASS_HID_LED_INDICATOR_OFF 0x41 +#define UX_HOST_CLASS_HID_LED_FLASH_ON_TIME 0x42 +#define UX_HOST_CLASS_HID_LED_SLOW_BLINK_ON_TIME 0x43 +#define UX_HOST_CLASS_HID_LED_SLOW_BLINK_OFF_TIME 0x44 +#define UX_HOST_CLASS_HID_LED_FAST_BLINK_ON_TIME 0x45 +#define UX_HOST_CLASS_HID_LED_FAST_BLINK_OFF_TIME 0x46 +#define UX_HOST_CLASS_HID_LED_USAGE_INDICATOR_COLOR 0x47 +#define UX_HOST_CLASS_HID_LED_INDICATOR_RED 0x48 +#define UX_HOST_CLASS_HID_LED_INDICATOR_GREEN 0x49 +#define UX_HOST_CLASS_HID_LED_INDICATOR_AMBER 0x4A +#define UX_HOST_CLASS_HID_LED_GENERIC_INDICATOR 0x4B +#define UX_HOST_CLASS_HID_LED_SYSTEM_SUSPEND 0x4C +#define UX_HOST_CLASS_HID_LED_EXTERNAL_POWER_CONNECTED 0x4D + + +/* Define HID Class consumer page constants. */ + +#define UX_HOST_CLASS_HID_CONSUMER_UNASSIGNED 0x00 +#define UX_HOST_CLASS_HID_CONSUMER_REMOTE_CONTROL 0x01 +#define UX_HOST_CLASS_HID_CONSUMER_NUMERIC_KEY_PAD 0x02 +#define UX_HOST_CLASS_HID_CONSUMER_PROGRAMMABLE_BUTTONS 0x03 +#define UX_HOST_CLASS_HID_CONSUMER_MICROPHONE 0x04 +#define UX_HOST_CLASS_HID_CONSUMER_HEADPHONE 0x05 +#define UX_HOST_CLASS_HID_CONSUMER_GRAPHIC_EQUALIZER 0x06 +#define UX_HOST_CLASS_HID_CONSUMER_PLUS_10 0x20 +#define UX_HOST_CLASS_HID_CONSUMER_PLUS_100 0x21 +#define UX_HOST_CLASS_HID_CONSUMER_AM_PM 0x22 +#define UX_HOST_CLASS_HID_CONSUMER_POWER 0x30 +#define UX_HOST_CLASS_HID_CONSUMER_RESET 0x31 +#define UX_HOST_CLASS_HID_CONSUMER_SLEEP 0x32 +#define UX_HOST_CLASS_HID_CONSUMER_SLEEP_AFTER 0x33 +#define UX_HOST_CLASS_HID_CONSUMER_SLEEP_MODE_RTC 0x34 +#define UX_HOST_CLASS_HID_CONSUMER_ILLUMINATION 0x35 +#define UX_HOST_CLASS_HID_CONSUMER_FUNCTION_BUTTONS 0x36 +#define UX_HOST_CLASS_HID_CONSUMER_MENU 0x40 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_PICK 0x41 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_UP 0x42 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_DOWN 0x43 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_LEFT 0x44 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_RIGHT 0x45 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_ESCAPE 0x46 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_VALUE_INCREASE 0x47 +#define UX_HOST_CLASS_HID_CONSUMER_MENU_VALUE_DECREASE 0x48 +#define UX_HOST_CLASS_HID_CONSUMER_DATA_ON_SCREEN 0x60 +#define UX_HOST_CLASS_HID_CONSUMER_CLOSED_CAPTION 0x61 +#define UX_HOST_CLASS_HID_CONSUMER_CLOSED_CAPTION_SELECT 0x62 +#define UX_HOST_CLASS_HID_CONSUMER_VCR_TV 0x63 +#define UX_HOST_CLASS_HID_CONSUMER_BROADCAST_MODE 0x64 +#define UX_HOST_CLASS_HID_CONSUMER_SNAPSHOT 0x65 +#define UX_HOST_CLASS_HID_CONSUMER_STILL 0x66 +#define UX_HOST_CLASS_HID_CONSUMER_SELECTION 0x80 +#define UX_HOST_CLASS_HID_CONSUMER_ASSIGN_SELECTION 0x81 +#define UX_HOST_CLASS_HID_CONSUMER_MODE_STEP 0x82 +#define UX_HOST_CLASS_HID_CONSUMER_RECALL_LAST 0x83 +#define UX_HOST_CLASS_HID_CONSUMER_ENTER_CHANNEL 0x84 +#define UX_HOST_CLASS_HID_CONSUMER_ORDER_MOVIE 0x85 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_L 0x86 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECTION 0x87 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_COMPUTER 0x88 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_TV 0x89 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_WWW 0x8A +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_DVD 0x8B +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_TELEPHONE 0x8C +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_PROGRAM_GUIDE 0x8D +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_VIDEO_PHONE 0x8E +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_GAMES 0x8F +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_MESSAGES 0x90 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_CD 0x91 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_VCR 0x92 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_TUNER 0x93 +#define UX_HOST_CLASS_HID_CONSUMER_QUIT 0x94 +#define UX_HOST_CLASS_HID_CONSUMER_HELP 0x95 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_TAPE 0x96 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_CABLE 0x97 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_SATELLITE 0x98 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_SECURITY 0x99 +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_HOME 0x9A +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT_CALL 0x9B +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_INCREMENT 0x9C +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_DECREMENT 0x9D +#define UX_HOST_CLASS_HID_CONSUMER_MEDIA_SELECT 0x9E +#define UX_HOST_CLASS_HID_CONSUMER_VCR_PLUS 0xA0 +#define UX_HOST_CLASS_HID_CONSUMER_ONCE 0xA1 +#define UX_HOST_CLASS_HID_CONSUMER_DAILY 0xA2 +#define UX_HOST_CLASS_HID_CONSUMER_WEEKLY 0xA3 +#define UX_HOST_CLASS_HID_CONSUMER_MONTHLY 0xA4 +#define UX_HOST_CLASS_HID_CONSUMER_PLAY 0xB0 +#define UX_HOST_CLASS_HID_CONSUMER_PAUSE 0xB1 +#define UX_HOST_CLASS_HID_CONSUMER_RECORD 0xB2 +#define UX_HOST_CLASS_HID_CONSUMER_FAST_FORWARD 0xB3 +#define UX_HOST_CLASS_HID_CONSUMER_REWIND 0xB4 +#define UX_HOST_CLASS_HID_CONSUMER_SCAN_NEXT_TRACK 0xB5 +#define UX_HOST_CLASS_HID_CONSUMER_SCAN_PREVIOUS_TRACK 0xB6 +#define UX_HOST_CLASS_HID_CONSUMER_STOP 0xB7 +#define UX_HOST_CLASS_HID_CONSUMER_EJECT 0xB8 +#define UX_HOST_CLASS_HID_CONSUMER_RANDOM_PLAY 0xB9 +#define UX_HOST_CLASS_HID_CONSUMER_SELECT_DISC 0xBA +#define UX_HOST_CLASS_HID_CONSUMER_ENTER_DISC 0xBB +#define UX_HOST_CLASS_HID_CONSUMER_REPEAT 0xBC +#define UX_HOST_CLASS_HID_CONSUMER_TRACKING 0xBD +#define UX_HOST_CLASS_HID_CONSUMER_TRACK_NORMAL 0xBE +#define UX_HOST_CLASS_HID_CONSUMER_SLOW_TRACKING 0xBF +#define UX_HOST_CLASS_HID_CONSUMER_FRAME_FORWARD 0xC0 +#define UX_HOST_CLASS_HID_CONSUMER_FRAME_BACK 0xC1 +#define UX_HOST_CLASS_HID_CONSUMER_MARK 0xC2 +#define UX_HOST_CLASS_HID_CONSUMER_CLEAR_MARK 0xC3 +#define UX_HOST_CLASS_HID_CONSUMER_REPEAT_FROM_MARK 0xC4 +#define UX_HOST_CLASS_HID_CONSUMER_RETURN_TO_MARK 0xC5 +#define UX_HOST_CLASS_HID_CONSUMER_SEARCH_MARK_FORWARD 0xC6 +#define UX_HOST_CLASS_HID_CONSUMER_SEARCH_MARK_BACKWARDS 0xC7 +#define UX_HOST_CLASS_HID_CONSUMER_COUNTER_RESET 0xC8 +#define UX_HOST_CLASS_HID_CONSUMER_SHOW_COUNTER 0xC9 +#define UX_HOST_CLASS_HID_CONSUMER_TRACKING_INCREMENT 0xCA +#define UX_HOST_CLASS_HID_CONSUMER_TRACKING_DECREMENT 0xCB +#define UX_HOST_CLASS_HID_CONSUMER_STOP_EJECT 0xCC +#define UX_HOST_CLASS_HID_CONSUMER_PLAY_PAUSE 0xCD +#define UX_HOST_CLASS_HID_CONSUMER_PLAY_SKIP 0xCE +#define UX_HOST_CLASS_HID_CONSUMER_VOLUME 0xE0 +#define UX_HOST_CLASS_HID_CONSUMER_BALANCE 0xE1 +#define UX_HOST_CLASS_HID_CONSUMER_MUTE 0xE2 +#define UX_HOST_CLASS_HID_CONSUMER_BASS 0xE3 +#define UX_HOST_CLASS_HID_CONSUMER_TREBLE 0xE4 +#define UX_HOST_CLASS_HID_CONSUMER_BASS_BOOST 0xE5 +#define UX_HOST_CLASS_HID_CONSUMER_SURROUND_MODE 0xE6 +#define UX_HOST_CLASS_HID_CONSUMER_LOUDNESS 0xE7 +#define UX_HOST_CLASS_HID_CONSUMER_MPX 0xE8 +#define UX_HOST_CLASS_HID_CONSUMER_VOLUME_INCREMENT 0xE9 +#define UX_HOST_CLASS_HID_CONSUMER_VOLUME_DECREMENT 0xEA +#define UX_HOST_CLASS_HID_CONSUMER_SPEED_SELECT 0xF0 +#define UX_HOST_CLASS_HID_CONSUMER_PLAYBACK_SPEED 0xF1 +#define UX_HOST_CLASS_HID_CONSUMER_STANDARD_PLAY 0xF2 +#define UX_HOST_CLASS_HID_CONSUMER_LONG_PLAY 0xF3 +#define UX_HOST_CLASS_HID_CONSUMER_EXTENDED_PLAY 0xF4 +#define UX_HOST_CLASS_HID_CONSUMER_SLOW 0xF5 +#define UX_HOST_CLASS_HID_CONSUMER_FAN_ENABLE 0xF6 +#define UX_HOST_CLASS_HID_CONSUMER_FAN_SPEED 0x100 +#define UX_HOST_CLASS_HID_CONSUMER_LIGHT_ENABLE 0x101 +#define UX_HOST_CLASS_HID_CONSUMER_LIGHT_ILLUMINATION_LEVEL 0x102 +#define UX_HOST_CLASS_HID_CONSUMER_CLIMATE_CONTROL_ENABLE 0x103 +#define UX_HOST_CLASS_HID_CONSUMER_ROOM_TEMPERATURE 0x104 +#define UX_HOST_CLASS_HID_CONSUMER_SECURITY_ENABLE 0x105 +#define UX_HOST_CLASS_HID_CONSUMER_FIRE_ALARM 0x106 +#define UX_HOST_CLASS_HID_CONSUMER_POLICE_ALARM 0x107 +#define UX_HOST_CLASS_HID_CONSUMER_PROXIMITY 0x108 +#define UX_HOST_CLASS_HID_CONSUMER_MOTION 0x109 +#define UX_HOST_CLASS_HID_CONSUMER_DURESS_ALARM 0x10A +#define UX_HOST_CLASS_HID_CONSUMER_HOLDUP_ALARM 0x10B +#define UX_HOST_CLASS_HID_CONSUMER_MEDICAL_ALARM 0x10C +#define UX_HOST_CLASS_HID_CONSUMER_BALANCE_RIGHT 0x10D +#define UX_HOST_CLASS_HID_CONSUMER_BALANCE_LEFT 0x150 +#define UX_HOST_CLASS_HID_CONSUMER_BASS_INCREMENT 0x151 +#define UX_HOST_CLASS_HID_CONSUMER_BASS_DECREMENT 0x152 +#define UX_HOST_CLASS_HID_CONSUMER_TREBLE_INCREMENT 0x153 +#define UX_HOST_CLASS_HID_CONSUMER_TREBLE_DECREMENT 0x154 +#define UX_HOST_CLASS_HID_CONSUMER_SPEAKER_SYSTEM 0x155 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_LEFT 0x160 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_RIGHT 0x161 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_CENTER 0x162 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_FRONT 0x163 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_CENTER_FRONT 0x164 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_SIDE 0x165 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_SURROUND 0x166 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_LOW_FREQUENCY 0x167 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_TOP 0x168 +#define UX_HOST_CLASS_HID_CONSUMER_CHANNEL_UNKNOWN 0x169 +#define UX_HOST_CLASS_HID_CONSUMER_SUB_CHANNEL 0x16A +#define UX_HOST_CLASS_HID_CONSUMER_SUB_CHANNEL_INCREMENT 0x170 +#define UX_HOST_CLASS_HID_CONSUMER_SUB_CHANNEL_DECREMENT 0x171 +#define UX_HOST_CLASS_HID_CONSUMER_ALTERNATE_AUDIO_INCREMENT 0x172 +#define UX_HOST_CLASS_HID_CONSUMER_ALTERNATE_AUDIO_DECREMENT 0x173 +#define UX_HOST_CLASS_HID_CONSUMER_APPLICATION_LAUNCH_BUTTONS 0x174 +#define UX_HOST_CLASS_HID_CONSUMER_AL_LAUNCH_BUTTON_CONFIGURATION 0x180 +#define UX_HOST_CLASS_HID_CONSUMER_AL_PROGRAMMABLE_BUTTON 0x181 +#define UX_HOST_CLASS_HID_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION 0x182 +#define UX_HOST_CLASS_HID_CONSUMER_AL_WORD_PROCESSOR 0x183 +#define UX_HOST_CLASS_HID_CONSUMER_AL_TEXT_EDITOR 0x184 +#define UX_HOST_CLASS_HID_CONSUMER_AL_SPREADSHEET 0x185 +#define UX_HOST_CLASS_HID_CONSUMER_AL_GRAPHICS_EDITOR 0x186 +#define UX_HOST_CLASS_HID_CONSUMER_AL_PRESENTATION_APP 0x187 +#define UX_HOST_CLASS_HID_CONSUMER_AL_DATABASE_APP 0x188 +#define UX_HOST_CLASS_HID_CONSUMER_AL_EMAIL_READER 0x189 +#define UX_HOST_CLASS_HID_CONSUMER_AL_NEWSREADER 0x18A +#define UX_HOST_CLASS_HID_CONSUMER_AL_VOICEMAIL 0x18B +#define UX_HOST_CLASS_HID_CONSUMER_AL_CONTACTS_ADDRESS_BOOK 0x18C +#define UX_HOST_CLASS_HID_CONSUMER_AL_CALENDAR_SCHEDULE 0x18D +#define UX_HOST_CLASS_HID_CONSUMER_AL_TASK_PROJECT_MANAGER 0x18E +#define UX_HOST_CLASS_HID_CONSUMER_AL_LOG_JOURNAL_TIMECARD 0x18F +#define UX_HOST_CLASS_HID_CONSUMER_AL_CHECKBOOK_FINANCE 0x190 +#define UX_HOST_CLASS_HID_CONSUMER_AL_CALCULATOR 0x191 +#define UX_HOST_CLASS_HID_CONSUMER_AL_A_V_CAPTURE_PLAYBACK 0x192 +#define UX_HOST_CLASS_HID_CONSUMER_AL_LOCAL_MACHINE_BROWSER 0x193 +#define UX_HOST_CLASS_HID_CONSUMER_AL_LAN_WAN_BROWSER 0x194 +#define UX_HOST_CLASS_HID_CONSUMER_AL_INTERNET_BROWSER 0x195 +#define UX_HOST_CLASS_HID_CONSUMER_AL_REMOTE_NETWORKING 0x196 +#define UX_HOST_CLASS_HID_CONSUMER_AL_NETWORK_CONFERENCE 0x197 +#define UX_HOST_CLASS_HID_CONSUMER_AL_NETWORK_CHAT 0x198 +#define UX_HOST_CLASS_HID_CONSUMER_AL_TELEPHONY_DIALER 0x199 +#define UX_HOST_CLASS_HID_CONSUMER_AL_LOGON 0x19A +#define UX_HOST_CLASS_HID_CONSUMER_AL_LOGOFF 0x19B +#define UX_HOST_CLASS_HID_CONSUMER_AL_LOGON_LOGOFF 0x19C +#define UX_HOST_CLASS_HID_CONSUMER_AL_SCREENSAVER 0x19D +#define UX_HOST_CLASS_HID_CONSUMER_AL_CONTROL_PANEL 0x19E +#define UX_HOST_CLASS_HID_CONSUMER_AL_COMMAND_LINE_PROCESSOR 0x19F +#define UX_HOST_CLASS_HID_CONSUMER_AL_PROCESS_MANAGER 0x1A0 +#define UX_HOST_CLASS_HID_CONSUMER_AL_SELECT_APPLICATION 0x1A1 +#define UX_HOST_CLASS_HID_CONSUMER_AL_NEXT_APPLICATION 0x1A2 +#define UX_HOST_CLASS_HID_CONSUMER_AL_PREVIOUS_APPLICATION 0x1A3 +#define UX_HOST_CLASS_HID_CONSUMER_AL_PREEMPTIVE_HALT_APPLICATION 0x1A4 +#define UX_HOST_CLASS_HID_CONSUMER_AL_INTEGRATED_HELP_CENTER 0x1A5 +#define UX_HOST_CLASS_HID_CONSUMER_AL_DOCUMENTS 0x1A6 +#define UX_HOST_CLASS_HID_CONSUMER_AL_THESAURUS 0x1A7 +#define UX_HOST_CLASS_HID_CONSUMER_AL_DICTIONARY 0x1A8 +#define UX_HOST_CLASS_HID_CONSUMER_AL_DESKTOP 0x1A9 +#define UX_HOST_CLASS_HID_CONSUMER_AL_SPELL_CHECK 0x1AA +#define UX_HOST_CLASS_HID_CONSUMER_AL_GRAMMAR_CHECK 0x1AB +#define UX_HOST_CLASS_HID_CONSUMER_AL_WIRELESS_STATUS 0x1AC +#define UX_HOST_CLASS_HID_CONSUMER_AL_KEYBOARD_LAYOUT 0x1AD +#define UX_HOST_CLASS_HID_CONSUMER_AL_VIRUS_PROTECTION 0x1AE +#define UX_HOST_CLASS_HID_CONSUMER_AL_ENCRYPTION 0x1AF +#define UX_HOST_CLASS_HID_CONSUMER_AL_SCREEN_SAVER 0x1B0 +#define UX_HOST_CLASS_HID_CONSUMER_AL_ALARMS 0x1B1 +#define UX_HOST_CLASS_HID_CONSUMER_AL_CLOCK 0x1B2 +#define UX_HOST_CLASS_HID_CONSUMER_AL_FILE_BROWSER 0x1B3 +#define UX_HOST_CLASS_HID_CONSUMER_AL_POWER_STATUS 0x1B4 +#define UX_HOST_CLASS_HID_CONSUMER_AC_NEW 0x1B5 +#define UX_HOST_CLASS_HID_CONSUMER_AC_OPEN 0x201 +#define UX_HOST_CLASS_HID_CONSUMER_AC_CLOSE 0x202 +#define UX_HOST_CLASS_HID_CONSUMER_AC_EXIT 0x203 +#define UX_HOST_CLASS_HID_CONSUMER_AC_MAXIMIZE 0x204 +#define UX_HOST_CLASS_HID_CONSUMER_AC_MINIMIZE 0x205 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SAVE 0x206 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PRINT 0x207 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PROPERTIES 0x208 +#define UX_HOST_CLASS_HID_CONSUMER_AC_UNDO 0x209 +#define UX_HOST_CLASS_HID_CONSUMER_AC_COPY 0x21A +#define UX_HOST_CLASS_HID_CONSUMER_AC_CUT 0x21B +#define UX_HOST_CLASS_HID_CONSUMER_AC_PASTE 0x21C +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_ALL 0x21D +#define UX_HOST_CLASS_HID_CONSUMER_AC_FIND 0x21E +#define UX_HOST_CLASS_HID_CONSUMER_AC_FIND_AND_REPLACE 0x21F +#define UX_HOST_CLASS_HID_CONSUMER_AC_SEARCH 0x220 +#define UX_HOST_CLASS_HID_CONSUMER_AC_GO_TO 0x221 +#define UX_HOST_CLASS_HID_CONSUMER_AC_HOME 0x222 +#define UX_HOST_CLASS_HID_CONSUMER_AC_BACK 0x223 +#define UX_HOST_CLASS_HID_CONSUMER_AC_FORWARD 0x224 +#define UX_HOST_CLASS_HID_CONSUMER_AC_STOP 0x225 +#define UX_HOST_CLASS_HID_CONSUMER_AC_REFRESH 0x226 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PREVIOUS_LINK 0x227 +#define UX_HOST_CLASS_HID_CONSUMER_AC_NEXT_LINK 0x228 +#define UX_HOST_CLASS_HID_CONSUMER_AC_BOOKMARKS 0x229 +#define UX_HOST_CLASS_HID_CONSUMER_AC_HISTORY 0x22A +#define UX_HOST_CLASS_HID_CONSUMER_AC_SUBSCRIPTIONS 0x22B +#define UX_HOST_CLASS_HID_CONSUMER_AC_ZOOM_IN 0x22C +#define UX_HOST_CLASS_HID_CONSUMER_AC_ZOOM_OUT 0x22D +#define UX_HOST_CLASS_HID_CONSUMER_AC_ZOOM 0x22E +#define UX_HOST_CLASS_HID_CONSUMER_AC_FULL_SCREEN_VIEW 0x22F +#define UX_HOST_CLASS_HID_CONSUMER_AC_NORMAL_VIEW 0x230 +#define UX_HOST_CLASS_HID_CONSUMER_AC_VIEW_TOGGLE 0x231 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SCROLL_UP 0x232 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SCROLL_DOWN 0x233 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SCROLL 0x234 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PAN_LEFT 0x235 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PAN_RIGHT 0x236 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PAN 0x237 +#define UX_HOST_CLASS_HID_CONSUMER_AC_NEW_WINDOW 0x238 +#define UX_HOST_CLASS_HID_CONSUMER_AC_TILE_HORIZONTALLY 0x239 +#define UX_HOST_CLASS_HID_CONSUMER_AC_TILE_VERTICALLY 0x23A +#define UX_HOST_CLASS_HID_CONSUMER_AC_FORMAT 0x23B +#define UX_HOST_CLASS_HID_CONSUMER_AC_EDIT 0x23C +#define UX_HOST_CLASS_HID_CONSUMER_AC_BOLD 0x23D +#define UX_HOST_CLASS_HID_CONSUMER_AC_ITALICS 0x23E +#define UX_HOST_CLASS_HID_CONSUMER_AC_UNDERLINE 0x23F +#define UX_HOST_CLASS_HID_CONSUMER_AC_STRIKETHROUGH 0x240 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SUBSCRIPT 0x241 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SUPERSCRIPT 0x242 +#define UX_HOST_CLASS_HID_CONSUMER_AC_ALL_CAPS 0x243 +#define UX_HOST_CLASS_HID_CONSUMER_AC_ROTATE 0x244 +#define UX_HOST_CLASS_HID_CONSUMER_AC_RESIZE 0x245 +#define UX_HOST_CLASS_HID_CONSUMER_AC_FLIP_HORIZONTAL 0x246 +#define UX_HOST_CLASS_HID_CONSUMER_AC_FLIP_VERTICAL 0x247 +#define UX_HOST_CLASS_HID_CONSUMER_AC_MIRROR_HORIZONTAL 0x248 +#define UX_HOST_CLASS_HID_CONSUMER_AC_MIRROR_VERTICAL 0x249 +#define UX_HOST_CLASS_HID_CONSUMER_AC_FONT_SELECT 0x24A +#define UX_HOST_CLASS_HID_CONSUMER_AC_FONT_COLOR 0x24B +#define UX_HOST_CLASS_HID_CONSUMER_AC_FONT_SIZE 0x24C +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_LEFT 0x24D +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_CENTER_H 0x24E +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_RIGHT 0x24F +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_BLOCK_H 0x250 +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_TOP 0x251 +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_CENTER_V 0x252 +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_BOTTOM 0x253 +#define UX_HOST_CLASS_HID_CONSUMER_AC_JUSTIFY_BLOCK_V 0x254 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INDENT_DECREASE 0x255 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INDENT_INCREASE 0x256 +#define UX_HOST_CLASS_HID_CONSUMER_AC_NUMBERED_LIST 0x257 +#define UX_HOST_CLASS_HID_CONSUMER_AC_RESTART_NUMBERING 0x258 +#define UX_HOST_CLASS_HID_CONSUMER_AC_BULLETED_LIST 0x259 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PROMOTE 0x25A +#define UX_HOST_CLASS_HID_CONSUMER_AC_DEMOTE 0x25B +#define UX_HOST_CLASS_HID_CONSUMER_AC_YES 0x25C +#define UX_HOST_CLASS_HID_CONSUMER_AC_NO 0x25D +#define UX_HOST_CLASS_HID_CONSUMER_AC_CANCEL 0x25E +#define UX_HOST_CLASS_HID_CONSUMER_AC_CATALOG 0x25F +#define UX_HOST_CLASS_HID_CONSUMER_AC_BUY_CHECKOUT 0x260 +#define UX_HOST_CLASS_HID_CONSUMER_AC_ADD_TO_CART 0x261 +#define UX_HOST_CLASS_HID_CONSUMER_AC_EXPAND 0x262 +#define UX_HOST_CLASS_HID_CONSUMER_AC_EXPAND_ALL 0x263 +#define UX_HOST_CLASS_HID_CONSUMER_AC_COLLAPSE 0x264 +#define UX_HOST_CLASS_HID_CONSUMER_AC_COLLAPSE_ALL 0x265 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PRINT_PREVIEW 0x266 +#define UX_HOST_CLASS_HID_CONSUMER_AC_PASTE_SPECIAL 0x267 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INSERT_MODE 0x268 +#define UX_HOST_CLASS_HID_CONSUMER_AC_DELETE 0x269 +#define UX_HOST_CLASS_HID_CONSUMER_AC_LOCK 0x26A +#define UX_HOST_CLASS_HID_CONSUMER_AC_UNLOCK 0x26B +#define UX_HOST_CLASS_HID_CONSUMER_AC_PROTECT 0x26C +#define UX_HOST_CLASS_HID_CONSUMER_AC_UNPROTECT 0x26D +#define UX_HOST_CLASS_HID_CONSUMER_AC_ATTACH_COMMENT 0x26E +#define UX_HOST_CLASS_HID_CONSUMER_AC_DELETE_COMMENT 0x26F +#define UX_HOST_CLASS_HID_CONSUMER_AC_VIEW_COMMENT 0x270 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_WORD 0x271 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_SENTENCE 0x272 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_PARAGRAPH 0x273 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_COLUMN 0x274 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_ROW 0x275 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_TABLE 0x276 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_OBJECT 0x277 +#define UX_HOST_CLASS_HID_CONSUMER_AC_REDO_REPEAT 0x278 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SORT 0x279 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SORT_ASCENDING 0x27A +#define UX_HOST_CLASS_HID_CONSUMER_AC_SORT_DESCENDING 0x27B +#define UX_HOST_CLASS_HID_CONSUMER_AC_FILTER 0x27C +#define UX_HOST_CLASS_HID_CONSUMER_AC_SET_CLOCK 0x27D +#define UX_HOST_CLASS_HID_CONSUMER_AC_VIEW_CLOCK 0x27E +#define UX_HOST_CLASS_HID_CONSUMER_AC_SELECT_TIME_ZONE 0x27F +#define UX_HOST_CLASS_HID_CONSUMER_AC_EDIT_TIME_ZONES 0x280 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SET_ALARM 0x281 +#define UX_HOST_CLASS_HID_CONSUMER_AC_CLEAR_ALARM 0x282 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SNOOZE_ALARM 0x283 +#define UX_HOST_CLASS_HID_CONSUMER_AC_RESET_ALARM 0x284 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SYNCHRONIZE 0x285 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SEND_RECEIVE 0x286 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SEND_TO 0x287 +#define UX_HOST_CLASS_HID_CONSUMER_AC_REPLY 0x288 +#define UX_HOST_CLASS_HID_CONSUMER_AC_REPLY_ALL 0x289 +#define UX_HOST_CLASS_HID_CONSUMER_AC_FORWARD_MSG 0x28A +#define UX_HOST_CLASS_HID_CONSUMER_AC_SEND 0x28B +#define UX_HOST_CLASS_HID_CONSUMER_AC_ATTACH_FILE 0x28C +#define UX_HOST_CLASS_HID_CONSUMER_AC_UPLOAD 0x28D +#define UX_HOST_CLASS_HID_CONSUMER_AC_DOWNLOAD 0x28E +#define UX_HOST_CLASS_HID_CONSUMER_AC_SET_BORDERS 0x28F +#define UX_HOST_CLASS_HID_CONSUMER_AC_INSERT_ROW 0x290 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INSERT_COLUMN 0x291 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INSERT_FILE 0x292 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INSERT_PICTURE 0x293 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INSERT_OBJECT 0x294 +#define UX_HOST_CLASS_HID_CONSUMER_AC_INSERT_SYMBOL 0x295 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SAVE_CLOSE 0x296 +#define UX_HOST_CLASS_HID_CONSUMER_AC_RENAME 0x297 +#define UX_HOST_CLASS_HID_CONSUMER_AC_MERGE 0x298 +#define UX_HOST_CLASS_HID_CONSUMER_AC_SPLIT 0x299 +#define UX_HOST_CLASS_HID_CONSUMER_AC_DISRIBUTE_HORIZONTALLY 0x29A +#define UX_HOST_CLASS_HID_CONSUMER_AC_DISTRIBUTE_VERTICALLY 0x29B + +/* Define HID Report Types. */ + +#define UX_HOST_CLASS_HID_REPORT_TYPE_INPUT 0x1 +#define UX_HOST_CLASS_HID_REPORT_TYPE_OUTPUT 0x2 +#define UX_HOST_CLASS_HID_REPORT_TYPE_FEATURE 0x3 + +/* Define HID Class report callback structure. */ + +#ifndef UX_HOST_CLASS_HID_USAGES +#define UX_HOST_CLASS_HID_USAGES 1024 +#endif + +#define UX_HOST_CLASS_HID_MAX_GLOBAL 4 +#define UX_HOST_CLASS_HID_MAX_COLLECTION 4 + +/* Define HID Class descriptor. */ + +typedef struct UX_HID_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bcdHID; + ULONG bCountryCode; + ULONG bNumDescriptor; + ULONG bReportDescriptorType; + ULONG wItemLength; +} UX_HID_DESCRIPTOR; + + +typedef struct UX_HOST_CLASS_HID_REPORT_CALLBACK_STRUCT +{ + + struct UX_HOST_CLASS_HID_CLIENT_STRUCT + *ux_host_class_hid_report_callback_client; + ULONG ux_host_class_hid_report_callback_id; + ULONG ux_host_class_hid_report_callback_status; + ULONG ux_host_class_hid_report_callback_flags; + ULONG ux_host_class_hid_report_callback_value; + ULONG ux_host_class_hid_report_callback_usage; + ULONG ux_host_class_hid_report_callback_length; + ULONG ux_host_class_hid_report_callback_actual_length; + VOID *ux_host_class_hid_report_callback_buffer; + VOID (*ux_host_class_hid_report_callback_function) (struct UX_HOST_CLASS_HID_REPORT_CALLBACK_STRUCT *); +} UX_HOST_CLASS_HID_REPORT_CALLBACK; + + + +typedef struct UX_HOST_CLASS_HID_REPORT_GET_ID_STRUCT +{ + + ULONG ux_host_class_hid_report_get_id; + ULONG ux_host_class_hid_report_get_type; + struct UX_HOST_CLASS_HID_REPORT_STRUCT + *ux_host_class_hid_report_get_report; +} UX_HOST_CLASS_HID_REPORT_GET_ID; + + +/* Define HID Class local item structure. */ + +typedef struct UX_HOST_CLASS_HID_LOCAL_ITEM_STRUCT +{ + + ULONG ux_host_class_hid_local_item_usages[UX_HOST_CLASS_HID_USAGES]; + ULONG ux_host_class_hid_local_item_number_usage; + ULONG ux_host_class_hid_local_item_usage_min; + ULONG ux_host_class_hid_local_item_usage_max; + ULONG ux_host_class_hid_local_item_delimiter_level; + ULONG ux_host_class_hid_local_item_delimiter_branch; +} UX_HOST_CLASS_HID_LOCAL_ITEM; + + +/* Define HID Class global item structure. */ + +typedef struct UX_HOST_CLASS_HID_GLOBAL_ITEM_STRUCT +{ + + ULONG ux_host_class_hid_global_item_usage_page; + SLONG ux_host_class_hid_global_item_logical_min; + SLONG ux_host_class_hid_global_item_logical_max; + SLONG ux_host_class_hid_global_item_physical_min; + SLONG ux_host_class_hid_global_item_physical_max; + ULONG ux_host_class_hid_global_item_unit_expo; + ULONG ux_host_class_hid_global_item_unit; + ULONG ux_host_class_hid_global_item_report_size; + ULONG ux_host_class_hid_global_item_report_id; + ULONG ux_host_class_hid_global_item_report_count; +} UX_HOST_CLASS_HID_GLOBAL_ITEM; + + +/* Define HID Class field structure. */ + +typedef struct UX_HOST_CLASS_HID_FIELD_STRUCT +{ + + ULONG ux_host_class_hid_field_physical; + ULONG ux_host_class_hid_field_logical; + ULONG ux_host_class_hid_field_application; + ULONG ux_host_class_hid_field_usage_page; + ULONG ux_host_class_hid_field_usage_min; + ULONG ux_host_class_hid_field_usage_max; + SLONG ux_host_class_hid_field_logical_min; + SLONG ux_host_class_hid_field_logical_max; + SLONG ux_host_class_hid_field_physical_min; + SLONG ux_host_class_hid_field_physical_max; + ULONG ux_host_class_hid_field_unit; + ULONG ux_host_class_hid_field_unit_expo; + ULONG ux_host_class_hid_field_report_type; + ULONG ux_host_class_hid_field_report_id; + ULONG ux_host_class_hid_field_report_offset; + ULONG ux_host_class_hid_field_report_size; + ULONG ux_host_class_hid_field_report_count; + ULONG ux_host_class_hid_field_value; + ULONG *ux_host_class_hid_field_usages; + ULONG ux_host_class_hid_field_number_usage; + ULONG *ux_host_class_hid_field_values; + ULONG ux_host_class_hid_field_number_values; + struct UX_HOST_CLASS_HID_REPORT + *ux_host_class_hid_field_report; + struct UX_HOST_CLASS_HID_FIELD_STRUCT + *ux_host_class_hid_field_next_field; +} UX_HOST_CLASS_HID_FIELD; + + +/* Define HID Class report structure. */ + +typedef struct UX_HOST_CLASS_HID_REPORT_STRUCT +{ + + ULONG ux_host_class_hid_report_id; + ULONG ux_host_class_hid_report_type; + struct UX_HOST_CLASS_HID_FIELD_STRUCT + *ux_host_class_hid_report_field; + ULONG ux_host_class_hid_report_number_item; + ULONG ux_host_class_hid_report_byte_length; + ULONG ux_host_class_hid_report_bit_length; + ULONG ux_host_class_hid_report_callback_flags; + VOID *ux_host_class_hid_report_callback_buffer; + ULONG ux_host_class_hid_report_callback_length; + VOID (*ux_host_class_hid_report_callback_function) (struct UX_HOST_CLASS_HID_REPORT_CALLBACK_STRUCT *); + struct UX_HOST_CLASS_HID_REPORT_STRUCT + *ux_host_class_hid_report_next_report; +} UX_HOST_CLASS_HID_REPORT; + + +/* Define HID main parser structure. */ + +typedef struct UX_HOST_CLASS_HID_PARSER_STRUCT +{ + + UX_HOST_CLASS_HID_GLOBAL_ITEM + ux_host_class_hid_parser_global; + UX_HOST_CLASS_HID_GLOBAL_ITEM + ux_host_class_hid_parser_global_pool[UX_HOST_CLASS_HID_MAX_GLOBAL]; + ULONG ux_host_class_hid_parser_number_global; + UX_HOST_CLASS_HID_LOCAL_ITEM + ux_host_class_hid_parser_local; + ULONG ux_host_class_hid_parser_application; + ULONG ux_host_class_hid_parser_collection[UX_HOST_CLASS_HID_MAX_COLLECTION]; + ULONG ux_host_class_hid_parser_number_collection; + ULONG ux_host_class_hid_parser_main_page; + ULONG ux_host_class_hid_parser_main_usage; + UX_HOST_CLASS_HID_REPORT + *ux_host_class_hid_parser_input_report; + UX_HOST_CLASS_HID_REPORT + *ux_host_class_hid_parser_output_report; + UX_HOST_CLASS_HID_REPORT + *ux_host_class_hid_parser_feature_report; +} UX_HOST_CLASS_HID_PARSER; + + +/* Define HID Class item analysis structure. */ + +typedef struct UX_HOST_CLASS_HID_ITEM_STRUCT +{ + + UCHAR ux_host_class_hid_item_report_type; + UCHAR ux_host_class_hid_item_report_tag; + USHORT ux_host_class_hid_item_report_length; + USHORT ux_host_class_hid_item_report_format; +} UX_HOST_CLASS_HID_ITEM; + + +/* Define HID Class instance structure. */ + +typedef struct UX_HOST_CLASS_HID_STRUCT +{ + + struct UX_HOST_CLASS_HID_STRUCT + *ux_host_class_hid_next_instance; + UX_HOST_CLASS *ux_host_class_hid_class; + UX_DEVICE *ux_host_class_hid_device; + UX_ENDPOINT *ux_host_class_hid_interrupt_endpoint; + UINT ux_host_class_hid_interrupt_endpoint_status; + UX_INTERFACE *ux_host_class_hid_interface; + ULONG ux_host_class_hid_state; + struct UX_HID_DESCRIPTOR_STRUCT + ux_host_class_hid_descriptor; + UX_HOST_CLASS_HID_PARSER + ux_host_class_hid_parser; + struct UX_HOST_CLASS_HID_CLIENT_STRUCT + *ux_host_class_hid_client; + TX_SEMAPHORE ux_host_class_hid_semaphore; +} UX_HOST_CLASS_HID; + + +/* Define HID Class client command format structure. */ + +typedef struct UX_HOST_CLASS_HID_CLIENT_COMMAND_STRUCT +{ + + UINT ux_host_class_hid_client_command_request; + VOID *ux_host_class_hid_client_command_container; + UX_HOST_CLASS_HID *ux_host_class_hid_client_command_instance; + ULONG ux_host_class_hid_client_command_page; + ULONG ux_host_class_hid_client_command_usage; +} UX_HOST_CLASS_HID_CLIENT_COMMAND; + + +/* Define HID Class report command structure. */ + +typedef struct UX_HOST_CLASS_HID_CLIENT_REPORT_STRUCT +{ + + UX_HOST_CLASS_HID_REPORT + *ux_host_class_hid_client_report; + ULONG *ux_host_class_hid_client_report_buffer; + ULONG ux_host_class_hid_client_report_length; + ULONG ux_host_class_hid_client_report_actual_length; + UINT ux_host_class_hid_client_report_flags; +} UX_HOST_CLASS_HID_CLIENT_REPORT; + + +/* Define HID Class client structure. */ + +typedef struct UX_HOST_CLASS_HID_CLIENT_STRUCT +{ + + ULONG ux_host_class_hid_client_status; + UCHAR ux_host_class_hid_client_name[UX_HOST_CLASS_HID_MAX_CLIENT_NAME_LENGTH + 1]; /* "+1" for string null-terminator */ + UINT (*ux_host_class_hid_client_handler) (struct UX_HOST_CLASS_HID_CLIENT_COMMAND_STRUCT *); + VOID *ux_host_class_hid_client_local_instance; +} UX_HOST_CLASS_HID_CLIENT; + +/* Define HID Class function prototypes. */ + +UINT _ux_host_class_hid_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_hid_client_register(UCHAR *hid_client_name, + UINT (*hid_client_handler)(struct UX_HOST_CLASS_HID_CLIENT_COMMAND_STRUCT *)); +UINT _ux_host_class_hid_client_search(UX_HOST_CLASS_HID *hid); +UINT _ux_host_class_hid_configure(UX_HOST_CLASS_HID *hid); +UINT _ux_host_class_hid_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_hid_descriptor_parse(UX_HOST_CLASS_HID *hid); +UINT _ux_host_class_hid_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_hid_field_decompress(UX_HOST_CLASS_HID_FIELD *hid_field, UCHAR *report_buffer, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report); +UINT _ux_host_class_hid_global_item_parse(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_ITEM *item, UCHAR *descriptor); +UINT _ux_host_class_hid_idle_get(UX_HOST_CLASS_HID *hid, USHORT *idle_time, USHORT report_id); +UINT _ux_host_class_hid_idle_set(UX_HOST_CLASS_HID *hid, USHORT idle_time, USHORT report_id); +UINT _ux_host_class_hid_instance_clean(UX_HOST_CLASS_HID *hid); +UINT _ux_host_class_hid_interrupt_endpoint_search(UX_HOST_CLASS_HID *hid); +ULONG _ux_host_class_hid_item_data_get(UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item); +UINT _ux_host_class_hid_local_item_parse(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_ITEM *item, UCHAR *descriptor); +UINT _ux_host_class_hid_main_item_parse(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_ITEM *item, UCHAR *descriptor); +UINT _ux_host_class_hid_periodic_report_start(UX_HOST_CLASS_HID *hid); +UINT _ux_host_class_hid_periodic_report_stop(UX_HOST_CLASS_HID *hid); +UINT _ux_host_class_hid_report_add(UX_HOST_CLASS_HID *hid, UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item); +UINT _ux_host_class_hid_report_callback_register(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_REPORT_CALLBACK *call_back); +UINT _ux_host_class_hid_report_compress(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report, + UCHAR *report_buffer, ULONG report_length); +UINT _ux_host_class_hid_report_decompress(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report, + UCHAR *report_buffer, ULONG report_length); +UINT _ux_host_class_hid_report_descriptor_get(UX_HOST_CLASS_HID *hid, ULONG length); +UINT _ux_host_class_hid_report_get(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report); +UINT _ux_host_class_hid_report_id_get(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_REPORT_GET_ID *report_id); +UINT _ux_host_class_hid_report_item_analyse(UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item); +UINT _ux_host_class_hid_report_set(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report); +UINT _ux_host_class_hid_resources_free(UX_HOST_CLASS_HID *hid); +VOID _ux_host_class_hid_transfer_request_completed(UX_TRANSFER *transfer_request); + +/* Define HID Class API prototypes. */ + +#define ux_host_class_hid_client_register _ux_host_class_hid_client_register +#define ux_host_class_hid_client_search _ux_host_class_hid_client_search +#define ux_host_class_hid_descriptor_parse _ux_host_class_hid_descriptor_parse +#define ux_host_class_hid_entry _ux_host_class_hid_entry +#define ux_host_class_hid_idle_get _ux_host_class_hid_idle_get +#define ux_host_class_hid_idle_set _ux_host_class_hid_idle_set +#define ux_host_class_hid_periodic_report_start _ux_host_class_hid_periodic_report_start +#define ux_host_class_hid_periodic_report_stop _ux_host_class_hid_periodic_report_stop +#define ux_host_class_hid_report_add _ux_host_class_hid_report_add +#define ux_host_class_hid_report_callback_register _ux_host_class_hid_report_callback_register +#define ux_host_class_hid_report_descriptor_get _ux_host_class_hid_report_descriptor_get +#define ux_host_class_hid_report_get _ux_host_class_hid_report_get +#define ux_host_class_hid_report_id_get _ux_host_class_hid_report_id_get +#define ux_host_class_hid_report_set _ux_host_class_hid_report_set + +#endif + diff --git a/common/usbx_host_classes/inc/ux_host_class_hid_keyboard.h b/common/usbx_host_classes/inc/ux_host_class_hid_keyboard.h new file mode 100644 index 0000000..1a6a5d2 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_hid_keyboard.h @@ -0,0 +1,219 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Keyboard Client */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_hid_keyboard.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX HID keyboard client. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_HID_KEYBOARD_H +#define UX_HOST_CLASS_HID_KEYBOARD_H + + +/* Define HID Keyboard Class constants. */ + +#define UX_HOST_CLASS_HID_KEYBOARD_BUFFER_LENGTH 128 +#define UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH 64 + +/* Each item in usage array takes 4 bytes. Check memory bytes calculation overflow here. */ +#if UX_OVERFLOW_CHECK_MULC_ULONG(UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH, 4) +#error UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH too large for memory allocation +#endif + +/* Define HID Keyboard Class LED keys. */ + +#define UX_HID_LED_KEY_CAPS_LOCK 0x39 +#define UX_HID_LED_KEY_NUM_LOCK 0x53 +#define UX_HID_LED_KEY_SCROLL_LOCK 0x47 + + +/* Define HID Keyboard Class Modifier Keys. */ + +#define UX_HID_MODIFIER_KEY_LEFT_CONTROL 0xe0 +#define UX_HID_MODIFIER_KEY_LEFT_SHIFT 0xe1 +#define UX_HID_MODIFIER_KEY_LEFT_ALT 0xe2 +#define UX_HID_MODIFIER_KEY_LEFT_GUI 0xe3 +#define UX_HID_MODIFIER_KEY_RIGHT_CONTROL 0xe4 +#define UX_HID_MODIFIER_KEY_RIGHT_SHIFT 0xe5 +#define UX_HID_MODIFIER_KEY_RIGHT_ALT 0xe6 +#define UX_HID_MODIFIER_KEY_RIGHT_GUI 0xe7 + + +/* Define HID Keyboard States. */ + +#define UX_HID_KEYBOARD_STATE_NUM_LOCK 0x0001 +#define UX_HID_KEYBOARD_STATE_CAPS_LOCK 0x0002 +#define UX_HID_KEYBOARD_STATE_SCROLL_LOCK 0x0004 +#define UX_HID_KEYBOARD_STATE_MASK_LOCK 0x0007 + +#define UX_HID_KEYBOARD_STATE_LEFT_SHIFT 0x0100 +#define UX_HID_KEYBOARD_STATE_RIGHT_SHIFT 0x0200 +#define UX_HID_KEYBOARD_STATE_SHIFT 0x0300 + +#define UX_HID_KEYBOARD_STATE_LEFT_ALT 0x0400 +#define UX_HID_KEYBOARD_STATE_RIGHT_ALT 0x0800 +#define UX_HID_KEYBOARD_STATE_ALT 0x0a00 + +#define UX_HID_KEYBOARD_STATE_LEFT_CTRL 0x1000 +#define UX_HID_KEYBOARD_STATE_RIGHT_CTRL 0x2000 +#define UX_HID_KEYBOARD_STATE_CTRL 0x3000 + +#define UX_HID_KEYBOARD_STATE_LEFT_GUI 0x4000 +#define UX_HID_KEYBOARD_STATE_RIGHT_GUI 0x8000 +#define UX_HID_KEYBOARD_STATE_GUI 0xa000 + +#define UX_HID_KEYBOARD_STATE_KEY_UP 0x10000 +#define UX_HID_KEYBOARD_STATE_FUNCTION 0x20000 + +/* Define HID keyboard generic equivalences. */ + +#define UX_HID_KEYBOARD_NO_KEY 0 +#define UX_HID_KEYBOARD_PHANTOM_STATE 0x01 +#define UX_HID_KEYBOARD_KEY_LETTER_A 0x04 +#define UX_HID_KEYBOARD_KEY_LETTER_Z 0x1D +#define UX_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE 0x54 +#define UX_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE 0x67 +#define UX_HID_KEYBOARD_KEYS_UPPER_RANGE 115 + +/* Define HID keyboard ioctl Functions. */ + +#define UX_HID_KEYBOARD_IOCTL_SET_LAYOUT 0 +#define UX_HID_KEYBOARD_IOCTL_DISABLE_KEYS_DECODE 1 +#define UX_HID_KEYBOARD_IOCTL_ENABLE_KEYS_DECODE 2 + +/* Define HID keyboard layout array. */ + +#define UX_HID_KEYBOARD_REGULAR_ARRAY_US \ + 0,0,0,0, \ + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n', \ + 'o','p','q','r','s','t','u','v','w','x','y','z', \ + '1','2','3','4','5','6','7','8','9','0', \ + 0x0d,0x1b,0x08,0x07,0x20,'-','=','[',']', \ + '\\','#',';',0x27,'`',',','.','/',0xf0, \ + 0xbb,0xbc,0xbd,0xbe,0xbf,0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6, \ + 0x00,0xf1,0x00,0xd2,0xc7,0xc9,0xd3,0xcf,0xd1,0xcd,0xcb,0xd0,0xc8,0xf2, \ + '/','*','-','+', \ + 0x0d,'1','2','3','4','5','6','7','8','9','0','.','\\',0x00,0x00,'=', \ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +#define UX_HID_KEYBOARD_SHIFT_ARRAY_US \ + 0,0,0,0, \ + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N', \ + 'O','P','Q','R','S','T','U','V','W','X','Y','Z', \ + '!','@','#','$','%','^','&','*','(',')', \ + 0x0d,0x1b,0x08,0x07,0x20,'_','+','{','}', \ + '|','~',':','"','~','<','>','?',0xf0, \ + 0xbb,0xbc,0xbd,0xbe,0xbf,0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6, \ + 0x00,0xf1,0x00,0xd2,0xc7,0xc9,0xd3,0xcf,0xd1,0xcd,0xcb,0xd0,0xc8,0xf2, \ + '/','*','-','+', \ + 0x0d,'1','2','3','4','5','6','7','8','9','0','.','\\',0x00,0x00,'=', \ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +#define UX_HID_KEYBOARD_NUMLOCK_ON_ARRAY \ + '/','*','-','+', \ + 0x0d, \ + '1','2','3','4','5','6','7','8','9','0', \ + '.','\\',0x00,0x00,'=', + +#define UX_HID_KEYBOARD_NUMLOCK_OFF_ARRAY \ + '/','*','-','+', \ + 0x0d, \ + 0xcf,0xd0,0xd1,0xcb,'5',0xcd,0xc7,0xc8,0xc9,0xd2, \ + 0xd3,'\\',0x00,0x00,'=', + +/* Define HID Keyboard layout (key mapping) structure. */ + +typedef struct UX_HOST_CLASS_HID_KEYBOARD_LAYOUT_STRUCT +{ + + UCHAR *ux_host_class_hid_keyboard_layout_regular_array; + UCHAR *ux_host_class_hid_keyboard_layout_shift_array; + UCHAR *ux_host_class_hid_keyboard_layout_numlock_on_array; + UCHAR *ux_host_class_hid_keyboard_layout_numlock_off_array; + ULONG ux_host_class_hid_keyboard_layout_keys_upper_range; + ULONG ux_host_class_hid_keyboard_layout_letters_lower_range; + ULONG ux_host_class_hid_keyboard_layout_letters_upper_range; + ULONG ux_host_class_hid_keyboard_layout_keypad_lower_range; + ULONG ux_host_class_hid_keyboard_layout_keypad_upper_range; +} UX_HOST_CLASS_HID_KEYBOARD_LAYOUT; + +/* Define HID Keyboard Class structure. */ + +typedef struct UX_HOST_CLASS_HID_KEYBOARD_STRUCT +{ + + ULONG ux_host_class_hid_keyboard_state; + UCHAR *ux_host_class_hid_keyboard_key_state; + ULONG ux_host_class_hid_keyboard_key_count; + UX_HOST_CLASS_HID *ux_host_class_hid_keyboard_hid; + USHORT ux_host_class_hid_keyboard_id; + TX_THREAD ux_host_class_hid_keyboard_thread; + TX_SEMAPHORE ux_host_class_hid_keyboard_semaphore; + ULONG ux_host_class_hid_keyboard_alternate_key_state; + ULONG ux_host_class_hid_keyboard_led_mask; + VOID *ux_host_class_hid_keyboard_thread_stack; + ULONG *ux_host_class_hid_keyboard_usage_array; + ULONG *ux_host_class_hid_keyboard_usage_array_head; + ULONG *ux_host_class_hid_keyboard_usage_array_tail; + UX_HOST_CLASS_HID_KEYBOARD_LAYOUT *ux_host_class_hid_keyboard_layout; + ULONG ux_host_class_hid_keyboard_keys_decode_disable; +} UX_HOST_CLASS_HID_KEYBOARD; + +/* Define HID Keyboard Class function prototypes. */ + +VOID _ux_host_class_hid_keyboard_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK *callback); +UINT _ux_host_class_hid_keyboard_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +UINT _ux_host_class_hid_keyboard_deactivate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +UINT _ux_host_class_hid_keyboard_entry(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +VOID _ux_host_class_hid_keyboard_thread(ULONG thread_entry); +UINT _ux_host_class_hid_keyboard_key_get(UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance, + ULONG *keyboard_key, ULONG *keyboard_state); +UINT _ux_host_class_hid_keyboard_ioctl(UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance, + ULONG ioctl_function, VOID *parameter); + +/* Define HID Keyboard Class API prototypes. */ + +#define ux_host_class_hid_keyboard_entry _ux_host_class_hid_keyboard_entry +#define ux_host_class_hid_keyboard_key_get _ux_host_class_hid_keyboard_key_get +#define ux_host_class_hid_keyboard_ioctl _ux_host_class_hid_keyboard_ioctl + +#endif + diff --git a/common/usbx_host_classes/inc/ux_host_class_hid_mouse.h b/common/usbx_host_classes/inc/ux_host_class_hid_mouse.h new file mode 100644 index 0000000..363611e --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_hid_mouse.h @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_hid_mouse.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX HID mouse class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_HID_MOUSE_H +#define UX_HOST_CLASS_HID_MOUSE_H + + +/* Define HID Mouse Class constants. */ + +#define UX_HOST_CLASS_HID_MOUSE_BUFFER_LENGTH 128 +#define UX_HOST_CLASS_HID_MOUSE_USAGE_ARRAY_LENGTH 64 + +#define UX_HOST_CLASS_HID_MOUSE_BUTTON_1 0x00090001 +#define UX_HOST_CLASS_HID_MOUSE_BUTTON_2 0x00090002 +#define UX_HOST_CLASS_HID_MOUSE_BUTTON_3 0x00090003 + +#define UX_HOST_CLASS_HID_MOUSE_AXIS_X 0x00010030 +#define UX_HOST_CLASS_HID_MOUSE_AXIS_Y 0x00010031 + + +#define UX_HOST_CLASS_HID_MOUSE_BUTTON_1_PRESSED 0x01 +#define UX_HOST_CLASS_HID_MOUSE_BUTTON_2_PRESSED 0x02 +#define UX_HOST_CLASS_HID_MOUSE_BUTTON_3_PRESSED 0x04 + +#define UX_HOST_CLASS_HID_MOUSE_WHEEL 0x00010038 + +/* Define HID Mouse Class structure. */ + +typedef struct UX_HOST_CLASS_HID_MOUSE_STRUCT +{ + + ULONG ux_host_class_hid_mouse_state; + UX_HOST_CLASS_HID *ux_host_class_hid_mouse_hid; + USHORT ux_host_class_hid_mouse_id; + SLONG ux_host_class_hid_mouse_x_position; + SLONG ux_host_class_hid_mouse_y_position; + ULONG ux_host_class_hid_mouse_buttons; + SLONG ux_host_class_hid_mouse_wheel; + + +} UX_HOST_CLASS_HID_MOUSE; + +/* Define HID Mouse Class function prototypes. */ + +UINT _ux_host_class_hid_mouse_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +VOID _ux_host_class_hid_mouse_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK *callback); +UINT _ux_host_class_hid_mouse_deactivate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +UINT _ux_host_class_hid_mouse_entry(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +UINT _ux_host_class_hid_mouse_buttons_get(UX_HOST_CLASS_HID_MOUSE *mouse_instance, + ULONG *mouse_buttons); +UINT _ux_host_class_hid_mouse_position_get(UX_HOST_CLASS_HID_MOUSE *mouse_instance, + SLONG *mouse_x_position, + SLONG *mouse_y_position); +UINT _ux_host_class_hid_mouse_wheel_get(UX_HOST_CLASS_HID_MOUSE *mouse_instance, + SLONG *mouse_wheel_movement); + +/* Define HID Mouse Class API prototypes. */ + +#define ux_host_class_hid_mouse_entry _ux_host_class_hid_mouse_entry +#define ux_host_class_hid_mouse_buttons_get _ux_host_class_hid_mouse_buttons_get +#define ux_host_class_hid_mouse_position_get _ux_host_class_hid_mouse_position_get +#define ux_host_class_hid_mouse_wheel_get _ux_host_class_hid_mouse_wheel_get + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_hid_remote_control.h b/common/usbx_host_classes/inc/ux_host_class_hid_remote_control.h new file mode 100644 index 0000000..11d006f --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_hid_remote_control.h @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Remote Control Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_hid_remote_control.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX HID remote control class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_HID_REMOTE_CONTROL_H +#define UX_HOST_CLASS_HID_REMOTE_CONTROL_H + + +/* Define HID Remote Control Class constants. */ + +#define UX_HOST_CLASS_HID_REMOTE_CONTROL_BUFFER_LENGTH 128 +#define UX_HOST_CLASS_HID_REMOTE_CONTROL_USAGE_ARRAY_LENGTH 64 + +/* Each item in usage array takes 4 bytes. Check memory bytes calculation overflow here. */ +#if UX_OVERFLOW_CHECK_MULC_ULONG(UX_HOST_CLASS_HID_REMOTE_CONTROL_USAGE_ARRAY_LENGTH, 4) +#error UX_HOST_CLASS_HID_REMOTE_CONTROL_USAGE_ARRAY_LENGTH is too large for memory allocation, please check +#endif + +/* Define HID Remote Control Class structure. */ + +typedef struct UX_HOST_CLASS_HID_REMOTE_CONTROL_STRUCT +{ + + ULONG ux_host_class_hid_remote_control_state; + UX_HOST_CLASS_HID *ux_host_class_hid_remote_control_hid; + ULONG *ux_host_class_hid_remote_control_usage_array; + ULONG *ux_host_class_hid_remote_control_usage_array_head; + ULONG *ux_host_class_hid_remote_control_usage_array_tail; +} UX_HOST_CLASS_HID_REMOTE_CONTROL; + +/* Define HID Remote Control Class function prototypes. */ + +VOID _ux_host_class_hid_remote_control_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK *callback); +UINT _ux_host_class_hid_remote_control_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +UINT _ux_host_class_hid_remote_control_deactivate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +UINT _ux_host_class_hid_remote_control_entry(UX_HOST_CLASS_HID_CLIENT_COMMAND *command); +UINT _ux_host_class_hid_remote_control_usage_get(UX_HOST_CLASS_HID_REMOTE_CONTROL *remote_control_instance, ULONG *usage, ULONG *value); + +/* Define HID Keyboard Class API prototypes. */ + +#define ux_host_class_hid_remote_control_entry _ux_host_class_hid_remote_control_entry +#define ux_host_class_hid_remote_control_usage_get _ux_host_class_hid_remote_control_usage_get + +#endif + diff --git a/common/usbx_host_classes/inc/ux_host_class_hub.h b/common/usbx_host_classes/inc/ux_host_class_hub.h new file mode 100644 index 0000000..47a77d5 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_hub.h @@ -0,0 +1,198 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_hub.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX HUB class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_HUB_H +#define UX_HOST_CLASS_HUB_H + + +/* Define HUB Class constants. */ + +#define UX_HOST_CLASS_HUB_CLASS 9 +#define UX_HOST_CLASS_HUB_PROTOCOL_FS 0 +#define UX_HOST_CLASS_HUB_PROTOCOL_SINGLE_TT 1 +#define UX_HOST_CLASS_HUB_PROTOCOL_MULTIPLE_TT 2 + + +/* Define HUB Class descriptor field constants. */ + +#define UX_HOST_CLASS_HUB_GANG_POWER_SWITCHING 0x00 +#define UX_HOST_CLASS_HUB_INDIVIDUAL_POWER_SWITCHING 0x01 +#define UX_HOST_CLASS_HUB_NO_POWER_SWITCHING 0x02 + +#define UX_HOST_CLASS_HUB_COMPOUND_DEVICE 0x04 + +#define UX_HOST_CLASS_HUB_GLOBAL_OVERCURRENT 0x00 +#define UX_HOST_CLASS_HUB_INDIVIDUAL_OVERCURRENT 0x08 +#define UX_HOST_CLASS_HUB_NO_OVERCURRENT 0x10 + + +/* Define HUB Class command constants. */ + +#define UX_HOST_CLASS_HUB_GET_STATUS 0x00 +#define UX_HOST_CLASS_HUB_CLEAR_FEATURE 0x01 +#define UX_HOST_CLASS_HUB_GET_STATE 0x02 +#define UX_HOST_CLASS_HUB_SET_FEATURE 0x03 +#define UX_HOST_CLASS_HUB_GET_DESCRIPTOR 0x06 +#define UX_HOST_CLASS_HUB_SET_DESCRIPTOR 0x07 + + +/* Define HUB Class set_feature command constants. */ + +#define UX_HOST_CLASS_HUB_PORT_CONNECTION 0x00 +#define UX_HOST_CLASS_HUB_PORT_ENABLE 0x01 +#define UX_HOST_CLASS_HUB_PORT_SUSPEND 0x02 +#define UX_HOST_CLASS_HUB_PORT_OVER_CURRENT 0x03 +#define UX_HOST_CLASS_HUB_PORT_RESET 0x04 +#define UX_HOST_CLASS_HUB_PORT_POWER 0x08 +#define UX_HOST_CLASS_HUB_PORT_LOW_SPEED 0x09 +#define UX_HOST_CLASS_HUB_C_PORT_CONNECTION 0x10 +#define UX_HOST_CLASS_HUB_C_PORT_ENABLE 0x11 +#define UX_HOST_CLASS_HUB_C_PORT_SUSPEND 0x12 +#define UX_HOST_CLASS_HUB_C_PORT_OVER_CURRENT 0x13 +#define UX_HOST_CLASS_HUB_C_PORT_RESET 0x14 + + +/* Define HUB Class port status constants. */ + +#define UX_HOST_CLASS_HUB_PORT_STATUS_CONNECTION 0x0001 +#define UX_HOST_CLASS_HUB_PORT_STATUS_ENABLE 0x0002 +#define UX_HOST_CLASS_HUB_PORT_STATUS_SUSPEND 0x0004 +#define UX_HOST_CLASS_HUB_PORT_STATUS_OVER_CURRENT 0x0008 +#define UX_HOST_CLASS_HUB_PORT_STATUS_RESET 0x0010 +#define UX_HOST_CLASS_HUB_PORT_STATUS_POWER 0x0100 +#define UX_HOST_CLASS_HUB_PORT_STATUS_LOW_SPEED 0x0200 +#define UX_HOST_CLASS_HUB_PORT_STATUS_HIGH_SPEED 0x0400 + + +/* Define HUB Class port change constants. */ + +#define UX_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION 0x00001 +#define UX_HOST_CLASS_HUB_PORT_CHANGE_ENABLE 0x00002 +#define UX_HOST_CLASS_HUB_PORT_CHANGE_SUSPEND 0x00004 +#define UX_HOST_CLASS_HUB_PORT_CHANGE_OVER_CURRENT 0x00008 +#define UX_HOST_CLASS_HUB_PORT_CHANGE_RESET 0x00010 + + +/* Define HUB Class other constants. */ + +#define UX_HOST_CLASS_HUB_ENABLE_RETRY_COUNT 3 +#define UX_HOST_CLASS_HUB_ENABLE_RETRY_DELAY 100 +#define UX_HOST_CLASS_HUB_ENUMERATION_RETRY 3 +#define UX_HOST_CLASS_HUB_ENUMERATION_DEBOUNCE_DELAY 100 +#define UX_HOST_CLASS_HUB_ENUMERATION_RESET_RECOVERY_DELAY 10 +#define UX_HOST_CLASS_HUB_ENUMERATION_RETRY_DELAY 300 + + +/* Define HUB Descriptor. */ +#define UX_HUB_DESCRIPTOR_ENTRIES 8 +#define UX_HUB_DESCRIPTOR_LENGTH 9 + +/* Define HUB Class structure. */ + +#define UX_MAX_HUB_PORTS 15 + +typedef struct UX_HUB_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bNbPorts; + ULONG wHubCharacteristics; + ULONG bPwrOn2PwrGood; + ULONG bHubContrCurrent; + ULONG bDeviceRemovable; + ULONG bPortPwrCtrlMask; +} UX_HUB_DESCRIPTOR; + + +/* Define HUB Class instance structure. */ + +typedef struct UX_HOST_CLASS_HUB_STRUCT +{ + + struct UX_HOST_CLASS_HUB_STRUCT + *ux_host_class_hub_next_instance; + UX_HOST_CLASS *ux_host_class_hub_class; + UX_DEVICE *ux_host_class_hub_device; + UX_ENDPOINT *ux_host_class_hub_interrupt_endpoint; + UX_INTERFACE *ux_host_class_hub_interface; + UINT ux_host_class_hub_instance_status; + UINT ux_host_class_hub_state; + UINT ux_host_class_hub_enumeration_retry_count; + UINT ux_host_class_hub_change_semaphore; + struct UX_HUB_DESCRIPTOR_STRUCT + ux_host_class_hub_descriptor; + UINT ux_host_class_hub_port_state; + UINT ux_host_class_hub_port_power; + +} UX_HOST_CLASS_HUB; + + +/* Define HUB Class function prototypes. */ + +UINT _ux_host_class_hub_activate(UX_HOST_CLASS_COMMAND *command); +VOID _ux_host_class_hub_change_detect(VOID); +UINT _ux_host_class_hub_change_process(UX_HOST_CLASS_HUB *hub); +UINT _ux_host_class_hub_configure(UX_HOST_CLASS_HUB *hub); +UINT _ux_host_class_hub_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_hub_descriptor_get(UX_HOST_CLASS_HUB *hub); +UINT _ux_host_class_hub_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_hub_feature(UX_HOST_CLASS_HUB *hub, UINT port, UINT command, UINT function); +UINT _ux_host_class_hub_hub_change_process(UX_HOST_CLASS_HUB *hub); +UINT _ux_host_class_hub_interrupt_endpoint_start(UX_HOST_CLASS_HUB *hub); +VOID _ux_host_class_hub_port_change_connection_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status); +VOID _ux_host_class_hub_port_change_enable_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status); +VOID _ux_host_class_hub_port_change_over_current_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status); +UINT _ux_host_class_hub_port_change_process(UX_HOST_CLASS_HUB *hub, UINT port); +VOID _ux_host_class_hub_port_change_reset_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status); +VOID _ux_host_class_hub_port_change_suspend_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status); +UINT _ux_host_class_hub_port_reset(UX_HOST_CLASS_HUB *hub, UINT port); +UINT _ux_host_class_hub_ports_power(UX_HOST_CLASS_HUB *hub); +UINT _ux_host_class_hub_status_get(UX_HOST_CLASS_HUB *hub, UINT port, USHORT *port_status, USHORT *port_change); +VOID _ux_host_class_hub_transfer_request_completed(UX_TRANSFER *transfer_request); + +#endif + diff --git a/common/usbx_host_classes/inc/ux_host_class_pima.h b/common/usbx_host_classes/inc/ux_host_class_pima.h new file mode 100644 index 0000000..009415c --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_pima.h @@ -0,0 +1,570 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_cdc_acm.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX PIMA class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_PIMA_H +#define UX_HOST_CLASS_PIMA_H + +/* Define PIMA Class constants. */ + +#define UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_HOST_CLASS_PIMA_CLASS 0x06 +#define UX_HOST_CLASS_PIMA_SUBCLASS 0X01 +#define UX_HOST_CLASS_PIMA_PROTOCOL 0X01 +#define UX_HOST_CLASS_PIMA_CS_INTERFACE 0x24 +#define UX_HOST_CLASS_PIMA_MAGIC_NUMBER 0x50494D41 +#define UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH 256 +#define UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH 256 +#define UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH 64 +#define UX_HOST_CLASS_PIMA_MAX_STORAGE_IDS 64 +#define UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH (UX_HOST_CLASS_PIMA_MAX_STORAGE_IDS * sizeof(ULONG)) +#define UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH_ASSERT UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(sizeof(ULONG), UX_HOST_CLASS_PIMA_MAX_STORAGE_IDS), UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH_calc_ovf) +#define UX_HOST_CLASS_PIMA_MAX_PAYLOAD 1024 +#define UX_HOST_CLASS_PIMA_ZLP_NONE 0 +#define UX_HOST_CLASS_PIMA_ZLP_IN 1 +#define UX_HOST_CLASS_PIMA_ZLP_OUT 2 + +/* Define PIMA data phases. */ + +#define UX_HOST_CLASS_PIMA_DATA_PHASE_NONE 0 +#define UX_HOST_CLASS_PIMA_DATA_PHASE_IN 1 +#define UX_HOST_CLASS_PIMA_DATA_PHASE_OUT 2 + +/* Define PIMA session states. */ + +#define UX_HOST_CLASS_PIMA_SESSION_STATE_CLOSED 0 +#define UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED 1 + +/* Define PIMA object and thumb states. */ + +#define UX_HOST_CLASS_PIMA_OBJECT_STATE_CLOSED 0 +#define UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED 1 + +/* Define PIMA object and thumb transfer status. */ + +#define UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_INACTIVE 0 +#define UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE 1 +#define UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED 2 +#define UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED 3 + +/* Define PIMA Cancel Request equivalences. */ + +#define UX_HOST_CLASS_PIMA_REQUEST_CANCEL_COMMAND 0x64 +#define UX_HOST_CLASS_PIMA_REQUEST_CANCEL_DATA_LENGTH 0x06 +#define UX_HOST_CLASS_PIMA_REQUEST_CANCEL_CODE 0x04001 +#define UX_HOST_CLASS_PIMA_REQUEST_CANCEL_OFFSET_CODE 0x00 +#define UX_HOST_CLASS_PIMA_REQUEST_CANCEL_OFFSET_TRANSACTION_ID 0x02 + +/* Define PIMA Reset Request equivalences. */ + +#define UX_HOST_CLASS_PIMA_REQUEST_RESET_DEVICE 0x66 + +/* Define PIMA Status Request equivalences. */ + +#define UX_HOST_CLASS_PIMA_REQUEST_STATUS_COMMAND 0x67 +#define UX_HOST_CLASS_PIMA_REQUEST_STATUS_DATA_LENGTH 0x40 +#define UX_HOST_CLASS_PIMA_REQUEST_STATUS_OFFSET_LENGTH 0x00 +#define UX_HOST_CLASS_PIMA_REQUEST_STATUS_OFFSET_CODE 0x02 +#define UX_HOST_CLASS_PIMA_REQUEST_STATUS_COMMAND_COUNTER 16 +#define UX_HOST_CLASS_PIMA_REQUEST_STATUS_COMMAND_DELAY 1000 + +/* Define PIMA command container type. */ + +#define UX_HOST_CLASS_PIMA_CT_UNDEFINED 0x00 +#define UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK 0x01 +#define UX_HOST_CLASS_PIMA_CT_DATA_BLOCK 0x02 +#define UX_HOST_CLASS_PIMA_CT_RESPONSE_BLOCK 0x03 +#define UX_HOST_CLASS_PIMA_CT_EVENT_BLOCK 0x04 + +/* Define PIMA Extended Event Data Request payload Format. */ + +#define UX_HOST_CLASS_PIMA_EEDR_EVENT_CODE 0x00 +#define UX_HOST_CLASS_PIMA_EEDR_TRANSACTION_ID 0x02 +#define UX_HOST_CLASS_PIMA_EEDR_NUMBER_PARAMETERS 0x06 +#define UX_HOST_CLASS_PIMA_EEDR_SIZE_PARAMETER 0x08 + +/* Define PIMA Device Status Data Format. */ + +#define UX_HOST_CLASS_PIMA_DSD_LENGTH 0x00 +#define UX_HOST_CLASS_PIMA_DSD_CODE 0x02 +#define UX_HOST_CLASS_PIMA_DSD_PARAMETER 0x04 + +/* Define PIMA Command Header Format. */ + +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH 0x00 +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE 0x04 +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE 0x06 +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID 0x08 +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1 0x0C +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_2 0x10 +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_3 0x14 +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_4 0x18 +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_5 0x1C + +#define UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE 0x0C +#define UX_HOST_CLASS_PIMA_CONTAINER_SIZE 0x40 +#define UX_HOST_CLASS_PIMA_ALL_HEADER_SIZE 0x20 + +/* Define PIMA Data Header Format. */ + +#define UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH 0x00 +#define UX_HOST_CLASS_PIMA_DATA_HEADER_TYPE 0x04 +#define UX_HOST_CLASS_PIMA_DATA_HEADER_CODE 0x06 +#define UX_HOST_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID 0x08 +#define UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE 0x0C + + +/* Define PIMA Response Header Format. */ + +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_LENGTH 0x00 +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_TYPE 0x04 +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_CODE 0x06 +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_TRANSACTION_ID 0x08 +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_1 0x0C +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_2 0x10 +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_3 0x14 +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_4 0x18 +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_5 0x1C + +#define UX_HOST_CLASS_PIMA_RESPONSE_HEADER_SIZE 0x20 + +/* Define PIMA Asynchronous Event Interrupt Data Format. */ + +#define UX_HOST_CLASS_PIMA_AEI_DATA_LENGTH 0x00 +#define UX_HOST_CLASS_PIMA_AEI_TYPE 0x04 +#define UX_HOST_CLASS_PIMA_AEI_EVENT_CODE 0x06 +#define UX_HOST_CLASS_PIMA_AEI_TRANSACTION_ID 0x08 +#define UX_HOST_CLASS_PIMA_AEI_PARAMETER_1 0x0C +#define UX_HOST_CLASS_PIMA_AEI_PARAMETER_2 0x10 +#define UX_HOST_CLASS_PIMA_AEI_PARAMETER_3 0x14 +#define UX_HOST_CLASS_PIMA_AEI_MAX_LENGTH 0x18 + +/* Define PIMA Operation Commands. */ + +#define UX_HOST_CLASS_PIMA_OC_UNDEFINED 0x1000 +#define UX_HOST_CLASS_PIMA_OC_GET_DEVICE_INFO 0x1001 +#define UX_HOST_CLASS_PIMA_OC_OPEN_SESSION 0x1002 +#define UX_HOST_CLASS_PIMA_OC_CLOSE_SESSION 0x1003 +#define UX_HOST_CLASS_PIMA_OC_GET_STORAGE_IDS 0x1004 +#define UX_HOST_CLASS_PIMA_OC_GET_STORAGE_INFO 0x1005 +#define UX_HOST_CLASS_PIMA_OC_GET_NUM_OBJECTS 0x1006 +#define UX_HOST_CLASS_PIMA_OC_GET_OBJECT_HANDLES 0x1007 +#define UX_HOST_CLASS_PIMA_OC_GET_OBJECT_INFO 0x1008 +#define UX_HOST_CLASS_PIMA_OC_GET_OBJECT 0x1009 +#define UX_HOST_CLASS_PIMA_OC_GET_THUMB 0x100A +#define UX_HOST_CLASS_PIMA_OC_DELETE_OBJECT 0x100B +#define UX_HOST_CLASS_PIMA_OC_SEND_OBJECT_INFO 0x100C +#define UX_HOST_CLASS_PIMA_OC_SEND_OBJECT 0x100D +#define UX_HOST_CLASS_PIMA_OC_INITIATE_CAPTURE 0x100E +#define UX_HOST_CLASS_PIMA_OC_FORMAT_STORE 0x100F +#define UX_HOST_CLASS_PIMA_OC_RESET_DEVICE 0x1010 +#define UX_HOST_CLASS_PIMA_OC_SELF_TEST 0x1011 +#define UX_HOST_CLASS_PIMA_OC_SET_OBJECT_PROTECTION 0x1012 +#define UX_HOST_CLASS_PIMA_OC_POWER_DOWN 0x1013 +#define UX_HOST_CLASS_PIMA_OC_GET_DEVICE_PROP_DESC 0x1014 +#define UX_HOST_CLASS_PIMA_OC_GET_DEVICE_PROP_VALUE 0x1015 +#define UX_HOST_CLASS_PIMA_OC_SET_DEVICE_PROP_VALUE 0x1016 +#define UX_HOST_CLASS_PIMA_OC_RESET_DEVICE_PROP_VALUE 0x1017 +#define UX_HOST_CLASS_PIMA_OC_TERMINATE_OPEN_CAPTURE 0x1018 +#define UX_HOST_CLASS_PIMA_OC_MOVE_OBJECT 0x1019 +#define UX_HOST_CLASS_PIMA_OC_COPY_OBJECT 0x101A +#define UX_HOST_CLASS_PIMA_OC_GET_PARTIAL_OBJECT 0x101B +#define UX_HOST_CLASS_PIMA_OC_INITIATE_OPEN_CAPTURE 0x101C + +/* Define PIMA Response Codes. */ + +#define UX_HOST_CLASS_PIMA_RC_UNDEFINED 0x2000 +#define UX_HOST_CLASS_PIMA_RC_OK 0x2001 +#define UX_HOST_CLASS_PIMA_RC_GENERAL_ERROR 0x2002 +#define UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN 0x2003 +#define UX_HOST_CLASS_PIMA_RC_INVALID_TRANSACTION_ID 0x2004 +#define UX_HOST_CLASS_PIMA_RC_OPERATION_NOT_SUPPORTED 0x2005 +#define UX_HOST_CLASS_PIMA_RC_PARAMETER_NOT_SUPPORTED 0x2006 +#define UX_HOST_CLASS_PIMA_RC_INCOMPLETE_TRANSFER 0x2007 +#define UX_HOST_CLASS_PIMA_RC_INVALID_STORAGE_ID 0x2008 +#define UX_HOST_CLASS_PIMA_RC_INVALID_OBJECT_HANDLE 0x2009 +#define UX_HOST_CLASS_PIMA_RC_DEVICE_PROP_NOT_SUPPORTED 0x200A +#define UX_HOST_CLASS_PIMA_RC_INVALID_OBJECT_FORMAT_CODE 0x200B +#define UX_HOST_CLASS_PIMA_RC_STORE_FULL 0x200C +#define UX_HOST_CLASS_PIMA_RC_OBJECT_WRITE_PROTECTED 0x200D +#define UX_HOST_CLASS_PIMA_RC_STORE_READ_ONLY 0x200E +#define UX_HOST_CLASS_PIMA_RC_ACCESS_DENIED 0x200F +#define UX_HOST_CLASS_PIMA_RC_NO_THUMBNAIL_PRESENT 0x2010 +#define UX_HOST_CLASS_PIMA_RC_SELF_TEST_FAILED 0x2011 +#define UX_HOST_CLASS_PIMA_RC_PARTIAL_DELETION 0x2012 +#define UX_HOST_CLASS_PIMA_RC_STORE_NOT_AVAILABLE 0x2013 +#define UX_HOST_CLASS_PIMA_RC_FORMAT_UNSUPPORTED 0x2014 +#define UX_HOST_CLASS_PIMA_RC_NO_VALID_OBJECT_INFO 0x2015 +#define UX_HOST_CLASS_PIMA_RC_INVALID_CODE_FORMAT 0x2016 +#define UX_HOST_CLASS_PIMA_RC_UNKNOWN_VENDOR_CODE 0x2017 +#define UX_HOST_CLASS_PIMA_RC_CAPTURE_ALREADY_TERMINATED 0x2018 +#define UX_HOST_CLASS_PIMA_RC_DEVICE_BUSY 0x2019 +#define UX_HOST_CLASS_PIMA_RC_INVALID_PARENT_OBJECT 0x201A +#define UX_HOST_CLASS_PIMA_RC_INVALID_DEVICE_PROP_FORMAT 0x201B +#define UX_HOST_CLASS_PIMA_RC_INVALID_DEVICE_PROP_VALUE 0x201C +#define UX_HOST_CLASS_PIMA_RC_INVALID_PARAMETER 0x201D +#define UX_HOST_CLASS_PIMA_RC_SESSION_ALREADY_OPENED 0x201E +#define UX_HOST_CLASS_PIMA_RC_TRANSACTION_CANCELED 0x201F +#define UX_HOST_CLASS_PIMA_RC_DESTINATION_UNSUPPORTED 0x2020 +#define UX_HOST_CLASS_PIMA_RC_OBJECT_ALREADY_OPENED 0x2021 +#define UX_HOST_CLASS_PIMA_RC_OBJECT_ALREADY_CLOSED 0x2022 +#define UX_HOST_CLASS_PIMA_RC_OBJECT_NOT_OPENED 0x2023 + +/* Define PIMA Event Codes. */ + +#define UX_HOST_CLASS_PIMA_EC_UNDEFINED 0x4000 +#define UX_HOST_CLASS_PIMA_EC_CANCEL_TRANSACTION 0x4001 +#define UX_HOST_CLASS_PIMA_EC_OBJECT_ADDED 0x4002 +#define UX_HOST_CLASS_PIMA_EC_OBJECT_REMOVED 0x4003 +#define UX_HOST_CLASS_PIMA_EC_STORE_ADDED 0x4004 +#define UX_HOST_CLASS_PIMA_EC_STORE_REMOVED 0x4005 +#define UX_HOST_CLASS_PIMA_EC_DEVICE_PROP_CHANGED 0x4006 +#define UX_HOST_CLASS_PIMA_EC_OBJECT_INFO_CHANGED 0x4007 +#define UX_HOST_CLASS_PIMA_EC_DEVICE_INFO_CHANGED 0x4008 +#define UX_HOST_CLASS_PIMA_EC_REQUEST_OBJECT_TRANSFER 0x4009 +#define UX_HOST_CLASS_PIMA_EC_STORE_FULL 0x400A +#define UX_HOST_CLASS_PIMA_EC_DEVICE_RESET 0x400B +#define UX_HOST_CLASS_PIMA_EC_STORAGE_INFO_CHANGED 0x400C +#define UX_HOST_CLASS_PIMA_EC_CAPTURE_COMPLETE 0x400D +#define UX_HOST_CLASS_PIMA_EC_UNREPORTED_STATUS 0x400E + +/* Define PIMA Object Format Codes. */ + +#define UX_HOST_CLASS_PIMA_OFC_UNDEFINED 0x3000 +#define UX_HOST_CLASS_PIMA_OFC_ASSOCIATION 0x3001 +#define UX_HOST_CLASS_PIMA_OFC_SCRIPT 0x3002 +#define UX_HOST_CLASS_PIMA_OFC_EXECUTABLE 0x3003 +#define UX_HOST_CLASS_PIMA_OFC_TEXT 0x3004 +#define UX_HOST_CLASS_PIMA_OFC_HTML 0x3005 +#define UX_HOST_CLASS_PIMA_OFC_DPOF 0x3006 +#define UX_HOST_CLASS_PIMA_OFC_AIFF 0x3007 +#define UX_HOST_CLASS_PIMA_OFC_WAV 0x3008 +#define UX_HOST_CLASS_PIMA_OFC_MP3 0x3009 +#define UX_HOST_CLASS_PIMA_OFC_AVI 0x300A +#define UX_HOST_CLASS_PIMA_OFC_MPEG 0x300B +#define UX_HOST_CLASS_PIMA_OFC_ASF 0x300C +#define UX_HOST_CLASS_PIMA_OFC_QT 0x300D +#define UX_HOST_CLASS_PIMA_OFC_EXIF_JPEG 0x3801 +#define UX_HOST_CLASS_PIMA_OFC_TIFF_EP 0x3802 +#define UX_HOST_CLASS_PIMA_OFC_FLASHPIX 0x3803 +#define UX_HOST_CLASS_PIMA_OFC_BMP 0x3804 +#define UX_HOST_CLASS_PIMA_OFC_CIFF 0x3805 +#define UX_HOST_CLASS_PIMA_OFC_GIF 0x3807 +#define UX_HOST_CLASS_PIMA_OFC_JFIF 0x3808 +#define UX_HOST_CLASS_PIMA_OFC_PCD 0x3809 +#define UX_HOST_CLASS_PIMA_OFC_PICT 0x380A +#define UX_HOST_CLASS_PIMA_OFC_PNG 0x380B +#define UX_HOST_CLASS_PIMA_OFC_TIFF 0x380D +#define UX_HOST_CLASS_PIMA_OFC_TIFF_IT 0x380E +#define UX_HOST_CLASS_PIMA_OFC_JP2 0x380F +#define UX_HOST_CLASS_PIMA_OFC_JPX 0x3810 + +/* Define PIMA Object Protection Status Values. */ + +#define UX_HOST_CLASS_PIMA_OPS_NO_PROTECTION 0x0000 +#define UX_HOST_CLASS_PIMA_OPS_READ_ONLY 0x0001 + +/* Define PIMA Storage Types Codes. */ + +#define UX_HOST_CLASS_PIMA_STC_UNDEFINED 0x0000 +#define UX_HOST_CLASS_PIMA_STC_FIXED_ROM 0x0001 +#define UX_HOST_CLASS_PIMA_STC_REMOVABLE_ROM 0x0002 +#define UX_HOST_CLASS_PIMA_STC_FIXED_RAM 0x0003 +#define UX_HOST_CLASS_PIMA_STC_REMOVABLE_RAM 0x0004 + +/* Define PIMA File System Types Codes. */ + +#define UX_HOST_CLASS_PIMA_FSTC_UNDEFINED 0x0000 +#define UX_HOST_CLASS_PIMA_FSTC_GENERIC_FLAT 0x0001 +#define UX_HOST_CLASS_PIMA_FSTC_GENERIC_HIERARCHICAL 0x0002 +#define UX_HOST_CLASS_PIMA_FSTC_DCF 0x0003 + +/* Define PIMA event info structure. */ + +typedef struct UX_HOST_CLASS_PIMA_EVENT_STRUCT +{ + struct UX_HOST_CLASS_PIMA_SESSION_STRUCT + *ux_host_class_pima_event_session; + struct UX_HOST_CLASS_PIMA_STRUCT + *ux_host_class_pima_event_pima_instance; + ULONG ux_host_class_pima_event_code; + ULONG ux_host_class_pima_event_session_id; + ULONG ux_host_class_pima_event_transaction_id; + ULONG ux_host_class_pima_event_parameter_1; + ULONG ux_host_class_pima_event_parameter_2; + ULONG ux_host_class_pima_event_parameter_3; + +} UX_HOST_CLASS_PIMA_EVENT; + +/* Define PIMA structure. */ + +typedef struct UX_HOST_CLASS_PIMA_STRUCT +{ + + struct UX_HOST_CLASS_PIMA_STRUCT + *ux_host_class_pima_next_instance; + UX_HOST_CLASS *ux_host_class_pima_class; + UX_DEVICE *ux_host_class_pima_device; + UX_INTERFACE *ux_host_class_pima_interface; + UX_ENDPOINT *ux_host_class_pima_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_pima_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_pima_interrupt_endpoint; + UINT ux_host_class_pima_state; + ULONG ux_host_class_pima_transaction_id; + ULONG ux_host_class_pima_operation_code; + ULONG ux_host_class_pima_event_code; + ULONG ux_host_class_pima_event_transaction_id; + ULONG ux_host_class_pima_event_session; + ULONG ux_host_class_pima_event_parameter_1; + ULONG ux_host_class_pima_event_parameter_2; + ULONG ux_host_class_pima_event_parameter_3; + UCHAR *ux_host_class_pima_event_buffer; + UCHAR *ux_host_class_pima_event_buffer_current_offset; + ULONG ux_host_class_pima_event_buffer_current_length; + ULONG ux_host_class_pima_event_buffer_expected_length; + struct UX_HOST_CLASS_PIMA_SESSION_STRUCT + *ux_host_class_pima_session; + UCHAR *ux_host_class_pima_container; + TX_SEMAPHORE ux_host_class_pima_semaphore; + VOID *ux_host_class_pima_application; + ULONG ux_host_class_pima_zlp_flag; + +} UX_HOST_CLASS_PIMA; + +/* Define PIMA Session structure. */ + +typedef struct UX_HOST_CLASS_PIMA_SESSION_STRUCT +{ + + ULONG ux_host_class_pima_session_magic; + ALIGN_TYPE ux_host_class_pima_session_id; + ULONG ux_host_class_pima_session_state; + struct UX_HOST_CLASS_PIMA_STRUCT + *ux_host_class_pima_session_pima_instance; + ULONG ux_host_class_pima_session_nb_storage_ids; + ULONG ux_host_class_pima_session_nb_objects; + VOID (*ux_host_class_pima_session_event_callback)(struct UX_HOST_CLASS_PIMA_EVENT_STRUCT *pima_event); + +} UX_HOST_CLASS_PIMA_SESSION; + + +/* Define PIMA command structure. */ + +typedef struct UX_HOST_CLASS_PIMA_COMMAND_STRUCT +{ + + ULONG ux_host_class_pima_command_nb_parameters; + ULONG ux_host_class_pima_command_operation_code; + ULONG ux_host_class_pima_command_parameter_1; + ULONG ux_host_class_pima_command_parameter_2; + ULONG ux_host_class_pima_command_parameter_3; + ULONG ux_host_class_pima_command_parameter_4; + ULONG ux_host_class_pima_command_parameter_5; + +} UX_HOST_CLASS_PIMA_COMMAND; + +/* Define PIMA object info structure. */ + +typedef struct UX_HOST_CLASS_PIMA_OBJECT_STRUCT +{ + + ULONG ux_host_class_pima_object_storage_id; + ULONG ux_host_class_pima_object_format; + ULONG ux_host_class_pima_object_protection_satus; + ULONG ux_host_class_pima_object_compressed_size; + ULONG ux_host_class_pima_object_thumb_format; + ULONG ux_host_class_pima_object_thumb_compressed_size; + ULONG ux_host_class_pima_object_thumb_pix_width; + ULONG ux_host_class_pima_object_thumb_pix_height; + ULONG ux_host_class_pima_object_image_pix_width; + ULONG ux_host_class_pima_object_image_pix_height; + ULONG ux_host_class_pima_object_image_bit_depth; + ULONG ux_host_class_pima_object_parent_object; + ULONG ux_host_class_pima_object_association_type; + ULONG ux_host_class_pima_object_association_desc; + ULONG ux_host_class_pima_object_sequence_number; + UCHAR ux_host_class_pima_object_filename[UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH]; + UCHAR ux_host_class_pima_object_capture_date[UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_host_class_pima_object_modification_date[UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_host_class_pima_object_keywords[UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH]; + ULONG ux_host_class_pima_object_state; + ULONG ux_host_class_pima_object_offset; + ULONG ux_host_class_pima_object_transfer_status; + ULONG ux_host_class_pima_object_handle_id; + ULONG ux_host_class_pima_object_length; + UCHAR *ux_host_class_pima_object_buffer; + +} UX_HOST_CLASS_PIMA_OBJECT; + +/* Define PIMA Object decompaction structure. */ + +#define UX_HOST_CLASS_PIMA_OBJECT_MAX_LENGTH 512 +#define UX_HOST_CLASS_PIMA_OBJECT_VARIABLE_OFFSET 52 +#define UX_HOST_CLASS_PIMA_OBJECT_ENTRIES 15 + +/* Define PIMA device info structure. */ + +typedef struct UX_HOST_CLASS_PIMA_DEVICE_STRUCT +{ + + ULONG ux_host_class_pima_device_standard_version; + ULONG ux_host_class_pima_device_vendor_extension_id; + ULONG ux_host_class_pima_device_vendor_extension_version; + UCHAR ux_host_class_pima_device_vendor_extension_desc[UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH]; + ULONG ux_host_class_pima_device_functional_mode; + UCHAR ux_host_class_pima_device_operations_supported[UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_events_supported[UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_properties_supported[UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_capture_formats[UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_image_formats[UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_manufacturer[UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_model[UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_version[UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH]; + UCHAR ux_host_class_pima_device_serial_number[UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH]; + +} UX_HOST_CLASS_PIMA_DEVICE; + +/* Define PIMA Device decompaction structure. */ + +#define UX_HOST_CLASS_PIMA_DEVICE_MAX_LENGTH 512 +#define UX_HOST_CLASS_PIMA_DEVICE_STANDARD_VERSION 0 +#define UX_HOST_CLASS_PIMA_DEVICE_VENDOR_EXTENSION_ID 2 +#define UX_HOST_CLASS_PIMA_DEVICE_VENDOR_EXTENSION_VERSION 6 +#define UX_HOST_CLASS_PIMA_DEVICE_VENDOR_EXTENSION_DESC 8 + +/* Define PIMA storage info structure. */ + +typedef struct UX_HOST_CLASS_PIMA_STORAGE_STRUCT +{ + + ULONG ux_host_class_pima_storage_type; + ULONG ux_host_class_pima_storage_file_system_type; + ULONG ux_host_class_pima_storage_access_capability; + ULONG ux_host_class_pima_storage_max_capacity_low; + ULONG ux_host_class_pima_storage_max_capacity_high; + ULONG ux_host_class_pima_storage_free_space_bytes_low; + ULONG ux_host_class_pima_storage_free_space_bytes_high; + ULONG ux_host_class_pima_storage_free_space_images; + UCHAR ux_host_class_pima_storage_description[UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH]; + UCHAR ux_host_class_pima_storage_volume_label[UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH]; + +} UX_HOST_CLASS_PIMA_STORAGE; + +/* Define PIMA storage decompaction structure. */ + +#define UX_HOST_CLASS_PIMA_STORAGE_MAX_LENGTH 512 +#define UX_HOST_CLASS_PIMA_STORAGE_VARIABLE_OFFSET 26 +#define UX_HOST_CLASS_PIMA_STORAGE_ENTRIES 8 + +/* Define Pima Class function prototypes. */ + +UINT _ux_host_class_pima_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_pima_configure(UX_HOST_CLASS_PIMA *cdc_acm); +UINT _ux_host_class_pima_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_pima_endpoints_get(UX_HOST_CLASS_PIMA *cdc_acm); +UINT _ux_host_class_pima_entry(UX_HOST_CLASS_COMMAND *command); +VOID _ux_host_class_pima_notification(UX_TRANSFER *transfer_request); +UINT _ux_host_class_pima_command(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_COMMAND *command, + ULONG direction, UCHAR *data_buffer, ULONG data_length, + ULONG max_payload_length); +UINT _ux_host_class_pima_device_reset(UX_HOST_CLASS_PIMA *pima); +UINT _ux_host_class_pima_num_objects_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG storage_id, + ULONG object_format_code); +UINT _ux_host_class_pima_object_copy(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG object_handle, + ULONG storage_id, ULONG parent_object_handle); +UINT _ux_host_class_pima_object_delete(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG object_handle); +UINT _ux_host_class_pima_object_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object, + UCHAR *object_buffer, ULONG object_buffer_length, ULONG *object_actual_length); +UINT _ux_host_class_pima_object_handles_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG *object_handles_array, ULONG object_handles_length, ULONG storage_id, ULONG object_format_code, ULONG object_handle_association); +UINT _ux_host_class_pima_object_info_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object); +UINT _ux_host_class_pima_object_info_send(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG storage_id, ULONG parent_object_id, + UX_HOST_CLASS_PIMA_OBJECT *object); +UINT _ux_host_class_pima_object_move(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG object_handle, ULONG storage_id, ULONG parent_object_handle); +UINT _ux_host_class_pima_object_send(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, UX_HOST_CLASS_PIMA_OBJECT *object, + UCHAR *object_buffer, ULONG object_buffer_length); +UINT _ux_host_class_pima_read(UX_HOST_CLASS_PIMA *pima, UCHAR *data_pointer, ULONG data_length, ULONG max_payload_length); +UINT _ux_host_class_pima_session_close(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session); +UINT _ux_host_class_pima_session_open(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session); +UINT _ux_host_class_pima_storage_ids_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG *storage_ids_array, ULONG storage_id_length); +UINT _ux_host_class_pima_storage_info_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG storage_id, UX_HOST_CLASS_PIMA_STORAGE *storage); +UINT _ux_host_class_pima_thumb_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object, + UCHAR *thumb_buffer, ULONG thumb_buffer_length, ULONG *thumb_actual_length); +UINT _ux_host_class_pima_write(UX_HOST_CLASS_PIMA *pima, UCHAR *data_pointer, ULONG data_length, ULONG operation_code, ULONG max_payload_length); +UINT _ux_host_class_pima_request_cancel(UX_HOST_CLASS_PIMA *pima); +UINT _ux_host_class_pima_object_transfer_abort(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object); +UINT _ux_host_class_pima_object_close(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object); +UINT _ux_host_class_pima_object_open(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object); +UINT _ux_host_class_pima_device_info_get(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_DEVICE *pima_device); + +/* Define Device PIMA Class API prototypes. */ + +#define ux_host_class_pima_entry _ux_host_class_pima_entry +#define ux_host_class_pima_device_info_get _ux_host_class_pima_device_info_get +#define ux_host_class_pima_object_info_send _ux_host_class_pima_object_info_send +#define ux_host_class_pima_object_info_get _ux_host_class_pima_object_info_get +#define ux_host_class_pima_object_open _ux_host_class_pima_object_open +#define ux_host_class_pima_object_get _ux_host_class_pima_object_get +#define ux_host_class_pima_thumb_get _ux_host_class_pima_thumb_get +#define ux_host_class_pima_object_send _ux_host_class_pima_object_send +#define ux_host_class_pima_object_delete _ux_host_class_pima_object_delete +#define ux_host_class_pima_object_transfer_abort _ux_host_class_pima_object_transfer_abort +#define ux_host_class_pima_object_close _ux_host_class_pima_object_close +#define ux_host_class_pima_num_objects_get _ux_host_class_pima_num_objects_get +#define ux_host_class_pima_session_open _ux_host_class_pima_session_open +#define ux_host_class_pima_session_close _ux_host_class_pima_session_close +#define ux_host_class_pima_storage_ids_get _ux_host_class_pima_storage_ids_get +#define ux_host_class_pima_storage_info_get _ux_host_class_pima_storage_info_get +#define ux_host_class_pima_object_handles_get _ux_host_class_pima_object_handles_get +#define ux_host_class_pima_num_objects_get _ux_host_class_pima_num_objects_get + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_printer.h b/common/usbx_host_classes/inc/ux_host_class_printer.h new file mode 100644 index 0000000..7cdf4b7 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_printer.h @@ -0,0 +1,117 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_printer.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX printer class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_PRINTER_H +#define UX_HOST_CLASS_PRINTER_H + + +/* Define Printer Class constants. */ + +#define UX_HOST_CLASS_PRINTER_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_HOST_CLASS_PRINTER_CLASS 7 +#define UX_HOST_CLASS_PRINTER_SUBCLASS 1 +#define UX_HOST_CLASS_PRINTER_PROTOCOL_BI_DIRECTIONAL 2 +#define UX_HOST_CLASS_PRINTER_GET_STATUS 1 +#define UX_HOST_CLASS_PRINTER_SOFT_RESET 2 +#define UX_HOST_CLASS_PRINTER_STATUS_LENGTH 4 +#define UX_HOST_CLASS_PRINTER_DESCRIPTOR_LENGTH 1024 +#define UX_HOST_CLASS_PRINTER_GET_DEVICE_ID 0 +#define UX_HOST_CLASS_PRINTER_NAME_LENGTH 64 + + +/* Define Printer Class 1284 descriptor tag constants. */ + +#define UX_HOST_CLASS_PRINTER_TAG_DESCRIPTION "DESCRIPTION:" +#define UX_HOST_CLASS_PRINTER_TAG_DES "DES:" + + +/* Define Printer Class string constants. */ + +#define UX_HOST_CLASS_PRINTER_GENERIC_NAME "USB PRINTER" + +/* Define Printer Class structure. */ + +typedef struct UX_HOST_CLASS_PRINTER_STRUCT +{ + + struct UX_HOST_CLASS_PRINTER_STRUCT + *ux_host_class_printer_next_instance; + UX_HOST_CLASS *ux_host_class_printer_class; + UX_DEVICE *ux_host_class_printer_device; + UX_INTERFACE *ux_host_class_printer_interface; + UX_ENDPOINT *ux_host_class_printer_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_printer_bulk_in_endpoint; + UINT ux_host_class_printer_state; + UCHAR ux_host_class_printer_name[UX_HOST_CLASS_PRINTER_NAME_LENGTH]; + TX_SEMAPHORE ux_host_class_printer_semaphore; +} UX_HOST_CLASS_PRINTER; + + +/* Define Printer Class function prototypes. */ + +UINT _ux_host_class_printer_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_printer_configure(UX_HOST_CLASS_PRINTER *printer); +UINT _ux_host_class_printer_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_printer_endpoints_get(UX_HOST_CLASS_PRINTER *printer); +UINT _ux_host_class_printer_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_printer_name_get(UX_HOST_CLASS_PRINTER *printer); +UINT _ux_host_class_printer_read (UX_HOST_CLASS_PRINTER *printer, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_printer_soft_reset(UX_HOST_CLASS_PRINTER *printer); +UINT _ux_host_class_printer_status_get(UX_HOST_CLASS_PRINTER *printer, ULONG *printer_status); +UINT _ux_host_class_printer_write(UX_HOST_CLASS_PRINTER *printer, UCHAR * data_pointer, + ULONG requested_length, ULONG *actual_length); + +/* Define Printer Class API prototypes. */ + +#define ux_host_class_printer_activate _ux_host_class_printer_activate +#define ux_host_class_printer_name_get _ux_host_class_printer_name_get +#define ux_host_class_printer_read _ux_host_class_printer_read +#define ux_host_class_printer_soft_reset _ux_host_class_printer_soft_reset +#define ux_host_class_printer_status_get _ux_host_class_printer_status_get +#define ux_host_class_printer_write _ux_host_class_printer_write + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_prolific.h b/common/usbx_host_classes/inc/ux_host_class_prolific.h new file mode 100644 index 0000000..79b4918 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_prolific.h @@ -0,0 +1,293 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PROLIFIC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_prolific.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX PROLIFIC class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_PROLIFIC_H +#define UX_HOST_CLASS_PROLIFIC_H + +/* Define PROLIFIC Class constants. */ + +#define UX_HOST_CLASS_PROLIFIC_DEVICE_INIT_DELAY (1 * UX_PERIODIC_RATE) +#define UX_HOST_CLASS_PROLIFIC_CLASS_TRANSFER_TIMEOUT 300000 +#define UX_HOST_CLASS_PROLIFIC_SETUP_BUFFER_SIZE 16 +#define UX_HOST_CLASS_PROLIFIC_DEVICE_PRESENT 1 +#define UX_HOST_CLASS_PROLIFIC_DEVICE_NOT_PRESENT 0 +#define UX_HOST_CLASS_PROLIFIC_DEVICE_STATE_OFFSET 8 +#define UX_HOST_CLASS_PROLIFIC_DEVICE_STATE_MASK 0x7F +#define UX_HOST_CLASS_PROLIFIC_DEVICE_TYPE_0 0 +#define UX_HOST_CLASS_PROLIFIC_DEVICE_TYPE_1 1 +#define UX_HOST_CLASS_PROLIFIC_DEVICE_TYPE_HX 2 +#define UX_HOST_CLASS_PROLIFIC_VENDOR_READ_REQUEST 1 +#define UX_HOST_CLASS_PROLIFIC_VENDOR_WRITE_REQUEST 1 + +/* Define PROLIFIC Class descriptor subtypes in functional descriptors. */ +#define UX_HOST_CLASS_PROLIFIC_HEADER_DESCRIPTOR 0X00 +#define UX_HOST_CLASS_PROLIFIC_CALL_MANAGEMENT_DESCRIPTOR 0X01 +#define UX_HOST_CLASS_PROLIFIC_ABSTRACT_CONTROL_MGT_DESCRIPTOR 0X02 +#define UX_HOST_CLASS_PROLIFIC_DIRECT_LINE_MGT_DESCRIPTOR 0X03 +#define UX_HOST_CLASS_PROLIFIC_TELEPHONE_RINGER_DESCRIPTOR 0X04 +#define UX_HOST_CLASS_PROLIFIC_REPORT_CAPABILITY_DESCRIPTOR 0X05 +#define UX_HOST_CLASS_PROLIFIC_UNION_DESCRIPTOR 0X06 +#define UX_HOST_CLASS_PROLIFIC_COUNTRY_SELECTION_DESCRIPTOR 0X07 +#define UX_HOST_CLASS_PROLIFIC_TELEPHONE_OPERATIONAL_DESCRIPTOR 0X08 +#define UX_HOST_CLASS_PROLIFIC_USB_TERMINAL_DESCRIPTOR 0X09 + +/* Define PROLIFIC Class call management descriptors. */ +#define UX_HOST_CLASS_PROLIFIC_CALL_MANAGEMENT_CAPABILITIES 0x03 +#define UX_HOST_CLASS_PROLIFIC_CALL_MANAGEMENT_DCM 0x01 +#define UX_HOST_CLASS_PROLIFIC_CALL_MANAGEMENT_DCI 0x02 + +/* Define PROLIFIC command request values. */ + +#define UX_HOST_CLASS_PROLIFIC_REQ_SEND_ENCAPSULATED_COMMAND 0x00 +#define UX_HOST_CLASS_PROLIFIC_REQ_GET_ENCAPSULATED_COMMAND 0x01 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_COMM_FEATURE 0x02 +#define UX_HOST_CLASS_PROLIFIC_REQ_GET_COMM_FEATURE 0x03 +#define UX_HOST_CLASS_PROLIFIC_REQ_CLEAR_COMM_FEATURE 0x04 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_AUX_LINE_STATE 0x10 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_HOOK_STATE 0x11 +#define UX_HOST_CLASS_PROLIFIC_REQ_PULSE_SETUP 0x12 +#define UX_HOST_CLASS_PROLIFIC_REQ_SEND_PULSE 0x13 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_PUSLE_TIME 0x14 +#define UX_HOST_CLASS_PROLIFIC_REQ_RING_AUX_JACK 0x15 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_LINE_CODING 0x20 +#define UX_HOST_CLASS_PROLIFIC_REQ_GET_LINE_CODING 0x21 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_LINE_STATE 0x22 +#define UX_HOST_CLASS_PROLIFIC_REQ_SEND_BREAK 0x23 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_RINGER_PARMS 0x30 +#define UX_HOST_CLASS_PROLIFIC_REQ_GET_RINGER_PARMS 0x31 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_OPERATION_PARMS 0x32 +#define UX_HOST_CLASS_PROLIFIC_REQ_GET_OPERATION_PARMS 0x33 +#define UX_HOST_CLASS_PROLIFIC_REQ_SET_LINE_PARMS 0x34 +#define UX_HOST_CLASS_PROLIFIC_REQ_GET_LINE_PARMS 0x35 + +/* Define PROLIFIC line output control values. */ + +#define UX_HOST_CLASS_PROLIFIC_CTRL_DTR 0x01 +#define UX_HOST_CLASS_PROLIFIC_CTRL_RTS 0x02 + +/* Define PROLIFIC line input control values. */ + +#define UX_HOST_CLASS_PROLIFIC_CTRL_DCD 0x01 +#define UX_HOST_CLASS_PROLIFIC_CTRL_DSR 0x02 +#define UX_HOST_CLASS_PROLIFIC_CTRL_BRK 0x04 +#define UX_HOST_CLASS_PROLIFIC_CTRL_RI 0x08 + +#define UX_HOST_CLASS_PROLIFIC_CTRL_FRAMING 0x10 +#define UX_HOST_CLASS_PROLIFIC_CTRL_PARITY 0x20 +#define UX_HOST_CLASS_PROLIFIC_CTRL_OVERRUN 0x40 + +#define UX_HOST_CLASS_PROLIFIC_COMMAND_EEPROM_READ 0x8484 +#define UX_HOST_CLASS_PROLIFIC_COMMAND_EEPROM_WRITE 0x0404 +#define UX_HOST_CLASS_PROLIFIC_COMMAND_EEPROM_ADDRESS 0x8383 +#define UX_HOST_CLASS_PROLIFIC_COMMAND_REG_CONFIGURE 0x0002 +#define UX_HOST_CLASS_PROLIFIC_COMMAND_PIPE1_RESET 0x0008 +#define UX_HOST_CLASS_PROLIFIC_COMMAND_PIPE2_RESET 0x0009 + +/* Define PROLIFIC Class packet equivalences. */ + +#define UX_HOST_CLASS_PROLIFIC_PACKET_SIZE 128 + +/* Define PROLIFIC default values. */ + +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_DEFAULT_RATE 19200 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_DEFAULT_DATA_BIT 8 + +/* Define PROLIFIC line coding definitions. */ + +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT_0 0 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT_15 1 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT_2 2 + +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY_NONE 0 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY_ODD 1 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY_EVEN 2 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY_MARK 3 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY_SPACE 4 + +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_LENGTH 7 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_RATE 0 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT 4 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY 5 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_DATA_BIT 6 + +/* Define PROLIFIC line state definitions. */ + +#define UX_HOST_CLASS_PROLIFIC_LINE_STATE_STOP_BIT_0 0 +#define UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT_15 1 + +/* Define PROLIFIC IOCTL Functions. */ + +#define UX_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_CODING 0 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_GET_LINE_CODING 1 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_STATE 2 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_SEND_BREAK 3 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_PURGE 4 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_ABORT_IN_PIPE 5 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_ABORT_OUT_PIPE 6 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_REPORT_DEVICE_STATUS_CHANGE 7 +#define UX_HOST_CLASS_PROLIFIC_IOCTL_GET_DEVICE_STATUS 8 + +/* Define PROLIFIC Reception States. */ + +#define UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STOPPED 0 +#define UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STARTED 1 +#define UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_IN_TRANSFER 2 + + +/* Define PROLIFIC Class instance structure. */ + +typedef struct UX_HOST_CLASS_PROLIFIC_STRUCT +{ + struct UX_HOST_CLASS_PROLIFIC_STRUCT + *ux_host_class_prolific_next_instance; + UX_HOST_CLASS *ux_host_class_prolific_class; + UX_DEVICE *ux_host_class_prolific_device; + UX_ENDPOINT *ux_host_class_prolific_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_prolific_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_prolific_interrupt_endpoint; + UX_INTERFACE *ux_host_class_prolific_interface; + UINT ux_host_class_prolific_instance_status; + UINT ux_host_class_prolific_state; + TX_SEMAPHORE ux_host_class_prolific_semaphore; + ULONG ux_host_class_prolific_notification_count; + ULONG ux_host_class_prolific_device_state; + VOID (*ux_host_class_prolific_device_status_change_callback)(struct UX_HOST_CLASS_PROLIFIC_STRUCT *prolific, + ULONG device_state); + + ULONG ux_host_class_prolific_version; + UCHAR ux_host_class_prolific_device_type; + struct UX_HOST_CLASS_PROLIFIC_RECEPTION_STRUCT + *ux_host_class_prolific_reception; + +} UX_HOST_CLASS_PROLIFIC; + + +/* Define PROLIFIC reception structure. */ + +typedef struct UX_HOST_CLASS_PROLIFIC_RECEPTION_STRUCT +{ + + ULONG ux_host_class_prolific_reception_state; + ULONG ux_host_class_prolific_reception_block_size; + UCHAR *ux_host_class_prolific_reception_data_buffer; + ULONG ux_host_class_prolific_reception_data_buffer_size; + UCHAR *ux_host_class_prolific_reception_data_head; + UCHAR *ux_host_class_prolific_reception_data_tail; + VOID (*ux_host_class_prolific_reception_callback)(struct UX_HOST_CLASS_PROLIFIC_STRUCT *prolific, + UINT status, + UCHAR *reception_buffer, + ULONG reception_size); + +} UX_HOST_CLASS_PROLIFIC_RECEPTION; + +/* Define PROLIFIC Line Coding IOCTL structure. */ + +typedef struct UX_HOST_CLASS_PROLIFIC_LINE_CODING_STRUCT +{ + + ULONG ux_host_class_prolific_line_coding_dter; + ULONG ux_host_class_prolific_line_coding_stop_bit; + ULONG ux_host_class_prolific_line_coding_parity; + ULONG ux_host_class_prolific_line_coding_data_bits; + +} UX_HOST_CLASS_PROLIFIC_LINE_CODING; + +/* Define PROLIFIC Line State IOCTL structure. */ + +typedef struct UX_HOST_CLASS_PROLIFIC_LINE_STATE_STRUCT +{ + + ULONG ux_host_class_prolific_line_state_rts; + ULONG ux_host_class_prolific_line_state_dtr; + +} UX_HOST_CLASS_PROLIFIC_LINE_STATE; + +/* Define PROLIFIC Line break IOCTL structure. */ + +typedef struct UX_HOST_CLASS_PROLIFIC_LINE_BREAK_STRUCT +{ + + ULONG ux_host_class_prolific_line_break; + +} UX_HOST_CLASS_PROLIFIC_LINE_BREAK; + + +/* Define Prolific Class function prototypes. */ + +UINT _ux_host_class_prolific_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_prolific_configure(UX_HOST_CLASS_PROLIFIC *prolific); +UINT _ux_host_class_prolific_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_prolific_endpoints_get(UX_HOST_CLASS_PROLIFIC *prolific); +UINT _ux_host_class_prolific_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_prolific_read (UX_HOST_CLASS_PROLIFIC *prolific, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_prolific_write(UX_HOST_CLASS_PROLIFIC *prolific, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_prolific_ioctl(UX_HOST_CLASS_PROLIFIC *prolific, ULONG request, + VOID *parameter); +UINT _ux_host_class_prolific_command(UX_HOST_CLASS_PROLIFIC *prolific, ULONG command, + ULONG value, UCHAR *data_buffer, ULONG data_length); +VOID _ux_host_class_prolific_transfer_request_completed(UX_TRANSFER *transfer_request); +UINT _ux_host_class_prolific_reception_stop (UX_HOST_CLASS_PROLIFIC *prolific, + UX_HOST_CLASS_PROLIFIC_RECEPTION *prolific_reception); +UINT _ux_host_class_prolific_reception_start (UX_HOST_CLASS_PROLIFIC *prolific, + UX_HOST_CLASS_PROLIFIC_RECEPTION *prolific_reception); + +VOID _ux_host_class_prolific_reception_callback (UX_TRANSFER *transfer_request); +UINT _ux_host_class_prolific_setup(UX_HOST_CLASS_PROLIFIC *prolific); + +/* Define Prolific Class API prototypes. */ + +#define ux_host_class_prolific_entry _ux_host_class_prolific_entry +#define ux_host_class_prolific_read _ux_host_class_prolific_read +#define ux_host_class_prolific_write _ux_host_class_prolific_write +#define ux_host_class_prolific_ioctl _ux_host_class_prolific_ioctl +#define ux_host_class_prolific_command _ux_host_class_prolific_command +#define ux_host_class_prolific_reception_stop _ux_host_class_prolific_reception_stop +#define ux_host_class_prolific_reception_start _ux_host_class_prolific_reception_start +#define ux_host_class_prolific_setup _ux_host_class_prolific_setup + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_storage.h b/common/usbx_host_classes/inc/ux_host_class_storage.h new file mode 100644 index 0000000..afe000d --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_storage.h @@ -0,0 +1,451 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_storage.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX storage class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_STORAGE_H +#define UX_HOST_CLASS_STORAGE_H + + +/* Include the FileX API. */ +#include "fx_api.h" + +/* Define User configurable Storage Class constants. */ + +#ifndef UX_MAX_HOST_LUN +#define UX_MAX_HOST_LUN 1 +#endif + +#ifndef UX_HOST_CLASS_STORAGE_MAX_MEDIA +#define UX_HOST_CLASS_STORAGE_MAX_MEDIA 1 +#endif + +#ifndef UX_HOST_CLASS_STORAGE_MEMORY_BUFFER_SIZE +#define UX_HOST_CLASS_STORAGE_MEMORY_BUFFER_SIZE (1024) +#endif + +#ifndef UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE +#define UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE (1024) +#endif + +#ifndef UX_HOST_CLASS_STORAGE_THREAD_STACK_SIZE +#define UX_HOST_CLASS_STORAGE_THREAD_STACK_SIZE UX_THREAD_STACK_SIZE +#endif + +/* Define Storage Class constants. */ + +#define UX_HOST_CLASS_STORAGE_DEVICE_INIT_DELAY (200) +#define UX_HOST_CLASS_STORAGE_THREAD_SLEEP_TIME (2000) +#define UX_HOST_CLASS_STORAGE_INSTANCE_SHUTDOWN_TIMER (10) +#define UX_HOST_CLASS_STORAGE_THREAD_PRIORITY_CLASS 20 +#define UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT 10000 +#define UX_HOST_CLASS_STORAGE_CBI_STATUS_TIMEOUT 3000 +#define UX_HOST_CLASS_STORAGE_CLASS 8 +#define UX_HOST_CLASS_STORAGE_SUBCLASS_RBC 1 +#define UX_HOST_CLASS_STORAGE_SUBCLASS_SFF8020 2 +#define UX_HOST_CLASS_STORAGE_SUBCLASS_UFI 4 +#define UX_HOST_CLASS_STORAGE_SUBCLASS_SFF8070 5 +#define UX_HOST_CLASS_STORAGE_SUBCLASS_SCSI 6 + +#define UX_HOST_CLASS_STORAGE_CBW_SIZE 64 + +#define UX_HOST_CLASS_STORAGE_PROTOCOL_CBI 0 +#define UX_HOST_CLASS_STORAGE_PROTOCOL_CB 1 +#define UX_HOST_CLASS_STORAGE_PROTOCOL_BO 0x50 + +#define UX_HOST_CLASS_STORAGE_DATA_OUT 0 +#define UX_HOST_CLASS_STORAGE_DATA_IN 0x80 + +#define UX_HOST_CLASS_STORAGE_CSW_PASSED 0 +#define UX_HOST_CLASS_STORAGE_CSW_FAILED 1 +#define UX_HOST_CLASS_STORAGE_CSW_PHASE_ERROR 2 + +#define UX_HOST_CLASS_STORAGE_CBW_SIGNATURE_MASK 0x43425355 +#define UX_HOST_CLASS_STORAGE_CBW_TAG_MASK 0x55534243 + +#define UX_HOST_CLASS_STORAGE_MEDIA_NAME "usb disk" + +#define UX_HOST_CLASS_STORAGE_MEDIA_REMOVABLE 0x80 +#define UX_HOST_CLASS_STORAGE_MEDIA_UNKNOWN 0 +#define UX_HOST_CLASS_STORAGE_MEDIA_KNOWN 1 + +#define UX_HOST_CLASS_STORAGE_MEDIA_FAT_DISK 0 +#define UX_HOST_CLASS_STORAGE_MEDIA_CDROM 5 +#define UX_HOST_CLASS_STORAGE_MEDIA_OPTICAL_DISK 7 +#define UX_HOST_CLASS_STORAGE_MEDIA_IOMEGA_CLICK 0x55 + +#define UX_HOST_CLASS_STORAGE_RESET 0xff +#define UX_HOST_CLASS_STORAGE_GET_MAX_LUN 0xfe + +#define UX_HOST_CLASS_STORAGE_TRANSPORT_ERROR 1 +#define UX_HOST_CLASS_STORAGE_COMMAND_ERROR 2 +#define UX_HOST_CLASS_STORAGE_SENSE_ERROR 3 + +#define UX_HOST_CLASS_STORAGE_SECTOR_SIZE_FAT 512 +#define UX_HOST_CLASS_STORAGE_SECTOR_SIZE_OTHER 2048 + +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RETRY 10 + +#define UX_HOST_CLASS_STORAGE_START_MEDIA 1 +#define UX_HOST_CLASS_STORAGE_STOP_MEDIA 0 + + +#define UX_HOST_CLASS_STORAGE_MEDIA_UNMOUNTED 0 +#define UX_HOST_CLASS_STORAGE_MEDIA_MOUNTED 1 + +/* Define Storage Class SCSI command constants. */ + +#define UX_HOST_CLASS_STORAGE_SCSI_TEST_READY 0x00 +#define UX_HOST_CLASS_STORAGE_SCSI_REQUEST_SENSE 0x03 +#define UX_HOST_CLASS_STORAGE_SCSI_FORMAT 0x04 +#define UX_HOST_CLASS_STORAGE_SCSI_INQUIRY 0x12 +#define UX_HOST_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT 0x1a +#define UX_HOST_CLASS_STORAGE_SCSI_START_STOP 0x1b +#define UX_HOST_CLASS_STORAGE_SCSI_READ_FORMAT_CAPACITY 0x23 +#define UX_HOST_CLASS_STORAGE_SCSI_READ_CAPACITY 0x25 +#define UX_HOST_CLASS_STORAGE_SCSI_READ16 0x28 +#define UX_HOST_CLASS_STORAGE_SCSI_WRITE16 0x2a +#define UX_HOST_CLASS_STORAGE_SCSI_VERIFY 0x2f +#define UX_HOST_CLASS_STORAGE_SCSI_MODE_SELECT 0x55 +#define UX_HOST_CLASS_STORAGE_SCSI_MODE_SENSE 0x5a +#define UX_HOST_CLASS_STORAGE_SCSI_READ32 0xa8 +#define UX_HOST_CLASS_STORAGE_SCSI_WRITE32 0xaa + + +/* Define Storage Class SCSI command block wrapper constants. */ + +#define UX_HOST_CLASS_STORAGE_CBW_SIGNATURE 0 +#define UX_HOST_CLASS_STORAGE_CBW_TAG 4 +#define UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH 8 +#define UX_HOST_CLASS_STORAGE_CBW_FLAGS 12 +#define UX_HOST_CLASS_STORAGE_CBW_LUN 13 +#define UX_HOST_CLASS_STORAGE_CBW_CB_LENGTH 14 +#define UX_HOST_CLASS_STORAGE_CBW_CB 15 + + +/* Define Storage Class SCSI response status wrapper constants. */ + +#define UX_HOST_CLASS_STORAGE_CSW_SIGNATURE 0 +#define UX_HOST_CLASS_STORAGE_CSW_TAG 4 +#define UX_HOST_CLASS_STORAGE_CSW_DATA_RESIDUE 8 +#define UX_HOST_CLASS_STORAGE_CSW_STATUS 12 +#define UX_HOST_CLASS_STORAGE_CSW_LENGTH 13 + + +/* Define Storage Class SCSI inquiry command constants. */ + +#define UX_HOST_CLASS_STORAGE_INQUIRY_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_INQUIRY_LUN 1 +#define UX_HOST_CLASS_STORAGE_INQUIRY_PAGE_CODE 2 +#define UX_HOST_CLASS_STORAGE_INQUIRY_ALLOCATION_LENGTH 4 +#define UX_HOST_CLASS_STORAGE_INQUIRY_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_INQUIRY_COMMAND_LENGTH_SBC 06 + + +/* Define Storage Class SCSI inquiry response constants. */ + +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_PERIPHERAL_TYPE 0 +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_REMOVABLE_MEDIA 1 +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_DATA_FORMAT 3 +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_ADDITIONAL_LENGTH 4 +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_VENDOR_INFORMATION 8 +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_PRODUCT_ID 16 +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_PRODUCT_REVISION 32 +#define UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH 36 + + +/* Define Storage Class SCSI start/stop command constants. */ + +#define UX_HOST_CLASS_STORAGE_START_STOP_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_START_STOP_LBUFLAGS 1 +#define UX_HOST_CLASS_STORAGE_START_STOP_START_BIT 4 +#define UX_HOST_CLASS_STORAGE_START_STOP_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_START_STOP_COMMAND_LENGTH_SBC 12 + + +/* Define Storage Class SCSI mode sense command constants. */ + +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_LUN 1 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_PC_PAGE_CODE 2 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_PARAMETER_LIST_LENGTH 7 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_SBC 12 + +/* Define Storage Class SCSI mode sense command constants. */ + +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_MODE_DATA_LENGTH 0 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_MEDIUM_TYPE_CODE 2 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_ATTRIBUTES_SHORT 2 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_ATTRIBUTES 3 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_ATTRIBUTES_WP 0x80 + +/* Define Storage Class SCSI request sense command constants. */ + +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_LUN 1 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_ALLOCATION_LENGTH 4 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_COMMAND_LENGTH_SBC 12 + + +/* Define Storage Class request sense response constants. */ + +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ERROR_CODE 0 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_SENSE_KEY 2 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_INFORMATION 3 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_ADD_LENGTH 7 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE 12 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE_QUALIFIER 13 +#define UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH 18 + + +/* Define Storage Class read format command constants. */ + +#define UX_HOST_CLASS_STORAGE_READ_FORMAT_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_READ_FORMAT_LUN 1 +#define UX_HOST_CLASS_STORAGE_READ_FORMAT_LBA 2 +#define UX_HOST_CLASS_STORAGE_READ_FORMAT_PARAMETER_LIST_LENGTH 7 +#define UX_HOST_CLASS_STORAGE_READ_FORMAT_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_READ_FORMAT_COMMAND_LENGTH_SBC 10 +#define UX_HOST_CLASS_STORAGE_READ_FORMAT_RESPONSE_LENGTH 0xFC + +/* Define Storage Class read capacity command constants. */ + +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_LUN 1 +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_LBA 2 +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_COMMAND_LENGTH_SBC 10 +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH 8 + +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_DATA_LBA 0 +#define UX_HOST_CLASS_STORAGE_READ_CAPACITY_DATA_SECTOR_SIZE 4 + + +/* Define Storage Class test unit read command constants. */ + +#define UX_HOST_CLASS_STORAGE_TEST_READY_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_TEST_READY_LUN 1 +#define UX_HOST_CLASS_STORAGE_TEST_READY_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_TEST_READY_COMMAND_LENGTH_SBC 6 + +/* Define Storage Class SCSI read command constants. */ + +#define UX_HOST_CLASS_STORAGE_READ_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_READ_LUN 1 +#define UX_HOST_CLASS_STORAGE_READ_LBA 2 +#define UX_HOST_CLASS_STORAGE_READ_TRANSFER_LENGTH 7 +#define UX_HOST_CLASS_STORAGE_READ_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_READ_COMMAND_LENGTH_SBC 10 + + +/* Define Storage Class SCSI write command constants. */ + +#define UX_HOST_CLASS_STORAGE_WRITE_OPERATION 0 +#define UX_HOST_CLASS_STORAGE_WRITE_LUN 1 +#define UX_HOST_CLASS_STORAGE_WRITE_LBA 2 +#define UX_HOST_CLASS_STORAGE_WRITE_TRANSFER_LENGTH 7 +#define UX_HOST_CLASS_STORAGE_WRITE_COMMAND_LENGTH_UFI 12 +#define UX_HOST_CLASS_STORAGE_WRITE_COMMAND_LENGTH_SBC 10 + + +/* Define Storage Class SCSI sense key definition constants. */ + +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_NO_SENSE 0x0 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_RECOVERED_ERROR 0x1 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_NOT_READY 0x2 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_MEDIUM_ERROR 0x3 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_HARDWARE_ERROR 0x4 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_ILLEGAL_REQUEST 0x5 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_UNIT_ATTENTION 0x6 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_DATA_PROTECT 0x7 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_BLANK_CHECK 0x8 +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_ABORTED_COMMAND 0x0b +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_VOLUME_OVERFLOW 0x0d +#define UX_HOST_CLASS_STORAGE_SENSE_KEY_MISCOMPARE 0x0e + +/* Define Mode Sense page codes. */ +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RWER_PAGE 0x01 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_FD_PAGE 0x05 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RBAC_PAGE 0x1B +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_TP_PAGE 0x1C +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE 0x3F + +/* Define Mode Sense page codes response length . */ +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_HEADER_PAGE_LENGTH 0x08 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RWER_PAGE_LENGTH 0x0c +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_FD_PAGE_LENGTH 0x20 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_RBAC_PAGE_LENGTH 0x0c +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_TP_PAGE_LENGTH 0x08 +#define UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE_LENGTH 0xC0 + +/* Define Storage Class useful error sense key/code constant. */ + +#define UX_HOST_CLASS_STORAGE_ERROR_MEDIA_NOT_READ 0x023A00 + + +/* Define Storage Class MS-DOS partition entry constants. */ + +#define UX_HOST_CLASS_STORAGE_PARTITION_SIGNATURE 0xaa55 +#define UX_HOST_CLASS_STORAGE_PARTITION_TABLE_START 446 + +#define UX_HOST_CLASS_STORAGE_PARTITION_BOOT_FLAG 0 +#define UX_HOST_CLASS_STORAGE_PARTITION_START_HEAD 1 +#define UX_HOST_CLASS_STORAGE_PARTITION_START_SECTOR 2 +#define UX_HOST_CLASS_STORAGE_PARTITION_START_TRACK 3 +#define UX_HOST_CLASS_STORAGE_PARTITION_TYPE 4 +#define UX_HOST_CLASS_STORAGE_PARTITION_END_HEAD 5 +#define UX_HOST_CLASS_STORAGE_PARTITION_END_SECTOR 6 +#define UX_HOST_CLASS_STORAGE_PARTITION_END_TRACK 7 +#define UX_HOST_CLASS_STORAGE_PARTITION_SECTORS_BEFORE 8 +#define UX_HOST_CLASS_STORAGE_PARTITION_NUMBER_SECTORS 12 +#define UX_HOST_CLASS_STORAGE_PARTITION_TABLE_SIZE 16 + +#define UX_HOST_CLASS_STORAGE_PARTITION_FAT_12 1 +#define UX_HOST_CLASS_STORAGE_PARTITION_FAT_16 4 +#define UX_HOST_CLASS_STORAGE_PARTITION_EXTENDED 5 +#define UX_HOST_CLASS_STORAGE_PARTITION_FAT_16L 6 +#define UX_HOST_CLASS_STORAGE_PARTITION_FAT_32_1 0x0b +#define UX_HOST_CLASS_STORAGE_PARTITION_FAT_32_2 0x0c +#define UX_HOST_CLASS_STORAGE_PARTITION_FAT_16_LBA_MAPPED 0x0e +#define UX_HOST_CLASS_STORAGE_PARTITION_EXTENDED_LBA_MAPPED 0x0f + +/* Define Storage Class instance structure. */ + +#define UX_HOST_CLASS_STORAGE_CBW_LENGTH 31 +#define UX_HOST_CLASS_STORAGE_CSW_LENGTH 13 +#define UX_HOST_CLASS_STORAGE_CBW_LENGTH_ALIGNED 64 +#define UX_HOST_CLASS_STORAGE_CSW_LENGTH_ALIGNED 64 + + +typedef struct UX_HOST_CLASS_STORAGE_STRUCT +{ + + struct UX_HOST_CLASS_STORAGE_STRUCT + *ux_host_class_storage_next_instance; + UX_HOST_CLASS *ux_host_class_storage_class; + UX_DEVICE *ux_host_class_storage_device; + UX_INTERFACE *ux_host_class_storage_interface; + UX_ENDPOINT *ux_host_class_storage_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_storage_bulk_in_endpoint; + UX_ENDPOINT *ux_host_class_storage_interrupt_endpoint; + UCHAR ux_host_class_storage_cbw[UX_HOST_CLASS_STORAGE_CBW_LENGTH_ALIGNED]; + UCHAR ux_host_class_storage_saved_cbw[UX_HOST_CLASS_STORAGE_CBW_LENGTH_ALIGNED]; + UCHAR ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_LENGTH_ALIGNED]; + UINT ux_host_class_storage_state; + UINT ux_host_class_storage_media_type; + UINT ux_host_class_storage_lun_removable_media_flags[UX_MAX_HOST_LUN]; + UINT ux_host_class_storage_write_protected_media; + UINT ux_host_class_storage_max_lun; + UINT ux_host_class_storage_lun; + UINT ux_host_class_storage_lun_types[UX_MAX_HOST_LUN]; + ULONG ux_host_class_storage_sector_size; + ULONG ux_host_class_storage_data_phase_length; + UINT (*ux_host_class_storage_transport) (struct UX_HOST_CLASS_STORAGE_STRUCT *storage, UCHAR * data_pointer); + ULONG ux_host_class_storage_sense_code; + UCHAR *ux_host_class_storage_memory; + TX_SEMAPHORE ux_host_class_storage_semaphore; +} UX_HOST_CLASS_STORAGE; + + +/* Define Host Storage Class Media structure. */ + +typedef struct UX_HOST_CLASS_STORAGE_MEDIA_STRUCT +{ + + FX_MEDIA ux_host_class_storage_media; + ULONG ux_host_class_storage_media_status; + ULONG ux_host_class_storage_media_lun; + ULONG ux_host_class_storage_media_partition_start; + ULONG ux_host_class_storage_media_sector_size; + VOID *ux_host_class_storage_media_memory; + +} UX_HOST_CLASS_STORAGE_MEDIA; + + +/* Define Storage Class function prototypes. */ + +UINT _ux_host_class_storage_activate(UX_HOST_CLASS_COMMAND *command); +VOID _ux_host_class_storage_cbw_initialize(UX_HOST_CLASS_STORAGE *storage, UINT direction, + ULONG data_transfer_length, UINT command_length); +UINT _ux_host_class_storage_configure(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_storage_device_initialize(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_device_reset(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_device_support_check(UX_HOST_CLASS_STORAGE *storage); +VOID _ux_host_class_storage_driver_entry(FX_MEDIA *media); +UINT _ux_host_class_storage_endpoints_get(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_storage_max_lun_get(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_media_capacity_get(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_media_characteristics_get(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_media_format_capacity_get(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_media_mount(UX_HOST_CLASS_STORAGE *storage, ULONG sector); +UINT _ux_host_class_storage_media_open(UX_HOST_CLASS_STORAGE *storage, ULONG hidden_sectors); +UINT _ux_host_class_storage_media_protection_check(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_media_read(UX_HOST_CLASS_STORAGE *storage, ULONG sector_start, + ULONG sector_count, UCHAR *data_pointer); +UINT _ux_host_class_storage_media_recovery_sense_get(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_media_write(UX_HOST_CLASS_STORAGE *storage, ULONG sector_start, + ULONG sector_count, UCHAR *data_pointer); +UINT _ux_host_class_storage_partition_read(UX_HOST_CLASS_STORAGE *storage, UCHAR *sector_memory, ULONG sector); +UINT _ux_host_class_storage_request_sense(UX_HOST_CLASS_STORAGE *storage); +UINT _ux_host_class_storage_sense_code_translate(UX_HOST_CLASS_STORAGE *storage, UINT status); +UINT _ux_host_class_storage_start_stop(UX_HOST_CLASS_STORAGE *storage, + ULONG start_stop_signal); +VOID _ux_host_class_storage_thread_entry(ULONG class_address); +UINT _ux_host_class_storage_transport(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer); +UINT _ux_host_class_storage_transport_bo(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer); +UINT _ux_host_class_storage_transport_cb(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer); +UINT _ux_host_class_storage_transport_cbi(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer); +UINT _ux_host_class_storage_unit_ready_test(UX_HOST_CLASS_STORAGE *storage); + +/* Define Storage Class API prototypes. */ + +#define ux_host_class_storage_entry _ux_host_class_storage_entry +#define ux_host_class_storage_media_read _ux_host_class_storage_media_read +#define ux_host_class_storage_media_write _ux_host_class_storage_media_write + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_swar.h b/common/usbx_host_classes/inc/ux_host_class_swar.h new file mode 100644 index 0000000..2eecbd2 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_swar.h @@ -0,0 +1,144 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_swar.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX Sierra Wireless AR Class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_SWAR_H +#define UX_HOST_CLASS_SWAR_H + + +/* Define Sierra Wireless AR Class constants. */ + +#define UX_HOST_CLASS_SWAR_CLASS_TRANSFER_TIMEOUT 300000 +#ifndef UX_HOST_CLASS_SWAR_VENDOR_ID +#define UX_HOST_CLASS_SWAR_VENDOR_ID 0X1199 +#define UX_HOST_CLASS_SWAR_PRODUCT_ID 0X68A3 +#endif + +/* Define Sierra Wireless AR Class packet equivalences. */ +#define UX_HOST_CLASS_SWAR_PACKET_SIZE 128 + +/* Define Sierra Wireless AR Class data interface. */ +#define UX_HOST_CLASS_SWAR_DATA_INTERFACE 3 + +/* Define Sierra Wireless AR IOCTL functions. */ +#define UX_HOST_CLASS_SWAR_IOCTL_ABORT_IN_PIPE 1 +#define UX_HOST_CLASS_SWAR_IOCTL_ABORT_OUT_PIPE 2 + +/* Define CDC ACM Reception States. */ + +#define UX_HOST_CLASS_SWAR_RECEPTION_STATE_STOPPED 0 +#define UX_HOST_CLASS_SWAR_RECEPTION_STATE_STARTED 1 +#define UX_HOST_CLASS_SWAR_RECEPTION_STATE_IN_TRANSFER 2 + + +/* Define Sierra Wireless Airprime Class instance structure. */ + +typedef struct UX_HOST_CLASS_SWAR_STRUCT +{ + + struct UX_HOST_CLASS_SWAR_STRUCT + *ux_host_class_swar_next_instance; + UX_HOST_CLASS *ux_host_class_swar_class; + UX_DEVICE *ux_host_class_swar_device; + UX_INTERFACE *ux_host_class_swar_interface; + UX_ENDPOINT *ux_host_class_swar_bulk_out_endpoint; + UX_ENDPOINT *ux_host_class_swar_bulk_in_endpoint; + UINT ux_host_class_swar_state; + TX_SEMAPHORE ux_host_class_swar_semaphore; + + struct UX_HOST_CLASS_SWAR_RECEPTION_STRUCT + *ux_host_class_swar_reception; + ULONG ux_host_class_swar_notification_count; +} UX_HOST_CLASS_SWAR; + +/* Define Sierra Wireless reception structure. */ + +typedef struct UX_HOST_CLASS_SWAR_RECEPTION_STRUCT +{ + + ULONG ux_host_class_swar_reception_state; + ULONG ux_host_class_swar_reception_block_size; + UCHAR *ux_host_class_swar_reception_data_buffer; + ULONG ux_host_class_swar_reception_data_buffer_size; + UCHAR *ux_host_class_swar_reception_data_head; + UCHAR *ux_host_class_swar_reception_data_tail; + VOID (*ux_host_class_swar_reception_callback)(struct UX_HOST_CLASS_SWAR_STRUCT *swar, + UINT status, + UCHAR *reception_buffer, + ULONG reception_size); + +} UX_HOST_CLASS_SWAR_RECEPTION; + +/* Define Sierra Wireless Airprime Classfunction prototypes. */ + +UINT _ux_host_class_swar_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_swar_configure(UX_HOST_CLASS_SWAR *swar); +UINT _ux_host_class_swar_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_swar_endpoints_get(UX_HOST_CLASS_SWAR *swar); +UINT _ux_host_class_swar_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_swar_read (UX_HOST_CLASS_SWAR *swar, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_swar_write(UX_HOST_CLASS_SWAR *swar, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_swar_ioctl(UX_HOST_CLASS_SWAR *swar, ULONG ioctl_function, + VOID *parameter); +VOID _ux_host_class_swar_reception_callback (UX_TRANSFER *transfer_request); +UINT _ux_host_class_swar_reception_stop (UX_HOST_CLASS_SWAR *swar, + UX_HOST_CLASS_SWAR_RECEPTION *swar_reception); +UINT _ux_host_class_swar_reception_start (UX_HOST_CLASS_SWAR *swar, + UX_HOST_CLASS_SWAR_RECEPTION *swar_reception); + +/* Define SWAR Class API prototypes. */ + +#define ux_host_class_swar_entry _ux_host_class_swar_entry +#define ux_host_class_swar_read _ux_host_class_swar_read +#define ux_host_class_swar_write _ux_host_class_swar_write +#define ux_host_class_swar_ioctl _ux_host_class_swar_ioctl +#define ux_host_class_swar_command _ux_host_class_swar_command +#define ux_host_class_swar_reception_stop _ux_host_class_swar_reception_stop +#define ux_host_class_swar_reception_start _ux_host_class_swar_reception_start +#define ux_host_class_swar_setup _ux_host_class_swar_setup + +#endif diff --git a/common/usbx_host_classes/inc/ux_host_class_video.h b/common/usbx_host_classes/inc/ux_host_class_video.h new file mode 100644 index 0000000..7308c87 --- /dev/null +++ b/common/usbx_host_classes/inc/ux_host_class_video.h @@ -0,0 +1,635 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_host_class_video.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX video class. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HOST_CLASS_VIDEO_H +#define UX_HOST_CLASS_VIDEO_H + +/* Define external static data. */ +extern UCHAR _ux_system_class_video_interface_descriptor_structure[]; +extern UCHAR _ux_system_class_video_input_terminal_descriptor_structure[]; +extern UCHAR _ux_system_class_video_input_header_descriptor_structure[]; +extern UCHAR _ux_system_class_video_processing_unit_descriptor_structure[]; +extern UCHAR _ux_system_class_video_streaming_interface_descriptor_structure[]; +extern UCHAR _ux_system_class_video_streaming_endpoint_descriptor_structure[]; +extern UCHAR _ux_system_class_video_frame_descriptor_structure[]; + +extern UCHAR _ux_system_host_class_video_name[]; + +/* Define Video Class IOCTL constants. */ + +#define UX_HOST_CLASS_VIDEO_IOCTL_GET_INPUT_TERMINAL 0x01 +#define UX_HOST_CLASS_VIDEO_IOCTL_GET_FORMAT_NUMBER 0x02 +#define UX_HOST_CLASS_VIDEO_IOCTL_GET_FORMAT_DATA 0x03 +#define UX_HOST_CLASS_VIDEO_IOCTL_GET_FRAME_NUMBER 0x04 +#define UX_HOST_CLASS_VIDEO_IOCTL_GET_FRAME_DATA 0x05 +#define UX_HOST_CLASS_VIDEO_IOCTL_CHANNEL_START 0x06 +#define UX_HOST_CLASS_VIDEO_IOCTL_CHANNEL_STOP 0x07 +#define UX_HOST_CLASS_VIDEO_IOCTL_GET_FRAME_INTERVAL 0x08 + +#define UX_HOST_CLASS_VIDEO_IOCTL_ABORT_IN_PIPE 0x80 + +/* Define Video Class main constants. */ + +#define UX_HOST_CLASS_VIDEO_CLASS_TRANSFER_TIMEOUT 30 +#define UX_HOST_CLASS_VIDEO_CLASS 0x0e +#define UX_HOST_CLASS_VIDEO_SUBCLASS_UNDEFINED 0 +#define UX_HOST_CLASS_VIDEO_SUBCLASS_CONTROL 1 +#define UX_HOST_CLASS_VIDEO_SUBCLASS_STREAMING 2 + + +/* Define Video Class main descriptor types. */ + +#define UX_HOST_CLASS_VIDEO_CS_UNDEFINED 0x20 +#define UX_HOST_CLASS_VIDEO_CS_DEVICE 0x21 +#define UX_HOST_CLASS_VIDEO_CS_CONFIGURATION 0x22 +#define UX_HOST_CLASS_VIDEO_CS_STRING 0x23 +#define UX_HOST_CLASS_VIDEO_CS_INTERFACE 0x24 +#define UX_HOST_CLASS_VIDEO_CS_ENDPOINT 0x25 + +/* Define Video Class specific VC . */ + +#define UX_HOST_CLASS_VIDEO_VC_DESCRIPTOR_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_VC_HEADER 0x01 +#define UX_HOST_CLASS_VIDEO_VC_INPUT_TERMINAL 0x02 +#define UX_HOST_CLASS_VIDEO_VC_OUTPUT_TERMINAL 0x03 +#define UX_HOST_CLASS_VIDEO_VC_SELECTOR_UNIT 0x04 +#define UX_HOST_CLASS_VIDEO_VC_PROCESSING_UNIT 0x05 +#define UX_HOST_CLASS_VIDEO_VC_EXTENSION_UNIT 0x06 + +/* Define Video Class specific VS . */ + +#define UX_HOST_CLASS_VIDEO_VS_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_VS_INPUT_HEADER 0x01 +#define UX_HOST_CLASS_VIDEO_VS_OUTPUT_HEADER 0x02 +#define UX_HOST_CLASS_VIDEO_VS_STILL_IMAGE_FRAME 0x03 +#define UX_HOST_CLASS_VIDEO_VS_FORMAT_UNCOMPRESSED 0x04 +#define UX_HOST_CLASS_VIDEO_VS_FRAME_UNCOMPRESSED 0x05 +#define UX_HOST_CLASS_VIDEO_VS_FORMAT_MJPEG 0x06 +#define UX_HOST_CLASS_VIDEO_VS_FRAME_MJPEG 0x07 +#define UX_HOST_CLASS_VIDEO_VS_FORMAT_MPEG2TS 0x0A +#define UX_HOST_CLASS_VIDEO_VS_FORMAT_DV 0x0C +#define UX_HOST_CLASS_VIDEO_VS_COLORFORMAT 0x0D +#define UX_HOST_CLASS_VIDEO_VS_FORMAT_FRAME_BASED 0x10 +#define UX_HOST_CLASS_VIDEO_VS_FRAME_FRAME_BASED 0x11 +#define UX_HOST_CLASS_VIDEO_VS_FORMAT_STREAM_BASED 0x12 + + +/* Define Video Class specific Control Selectors. */ + +#define UX_HOST_CLASS_VIDEO_CT_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_CT_SCANNING_MODE_CONTROL 0x01 +#define UX_HOST_CLASS_VIDEO_CT_AE_MODE_CONTROL 0x02 +#define UX_HOST_CLASS_VIDEO_CT_AE_PRIORITY_CONTROL 0x03 +#define UX_HOST_CLASS_VIDEO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 +#define UX_HOST_CLASS_VIDEO_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 +#define UX_HOST_CLASS_VIDEO_CT_FOCUS_ABSOLUTE_CONTROL 0x06 +#define UX_HOST_CLASS_VIDEO_CT_FOCUS_RELATIVE_CONTROL 0x07 +#define UX_HOST_CLASS_VIDEO_CT_FOCUS_AUTO_CONTROL 0x08 +#define UX_HOST_CLASS_VIDEO_CT_IRIS_ABSOLUTE_CONTROL 0x09 +#define UX_HOST_CLASS_VIDEO_CT_IRIS_RELATIVE_CONTROL 0x0A +#define UX_HOST_CLASS_VIDEO_CT_ZOOM_ABSOLUTE_CONTROL 0x0B +#define UX_HOST_CLASS_VIDEO_CT_ZOOM_RELATIVE_CONTROL 0x0C +#define UX_HOST_CLASS_VIDEO_CT_PANTILT_ABSOLUTE_CONTROL 0x0D +#define UX_HOST_CLASS_VIDEO_CT_PANTILT_RELATIVE_CONTROL 0x0E +#define UX_HOST_CLASS_VIDEO_CT_ROLL_ABSOLUTE_CONTROL 0x0F +#define UX_HOST_CLASS_VIDEO_CT_ROLL_RELATIVE_CONTROL 0x10 +#define UX_HOST_CLASS_VIDEO_CT_PRIVACY_CONTROL 0x11 + +#define UX_HOST_CLASS_VIDEO_PU_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 +#define UX_HOST_CLASS_VIDEO_PU_BRIGHTNESS_CONTROL 0x02 +#define UX_HOST_CLASS_VIDEO_PU_CONTRAST_CONTROL 0x03 +#define UX_HOST_CLASS_VIDEO_PU_GAIN_CONTROL 0x04 +#define UX_HOST_CLASS_VIDEO_PU_POWER_LINE_FREQUENCY_CONTROL 0x05 +#define UX_HOST_CLASS_VIDEO_PU_HUE_CONTROL 0x06 +#define UX_HOST_CLASS_VIDEO_PU_SATURATION_CONTROL 0x07 +#define UX_HOST_CLASS_VIDEO_PU_SHARPNESS_CONTROL 0x08 +#define UX_HOST_CLASS_VIDEO_PU_GAMMA_CONTROL 0x09 +#define UX_HOST_CLASS_VIDEO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0A +#define UX_HOST_CLASS_VIDEO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0B +#define UX_HOST_CLASS_VIDEO_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0C +#define UX_HOST_CLASS_VIDEO_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0D +#define UX_HOST_CLASS_VIDEO_PU_DIGITAL_MULTIPLIER_CONTROL 0x0E +#define UX_HOST_CLASS_VIDEO_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0F +#define UX_HOST_CLASS_VIDEO_PU_HUE_AUTO_CONTROL 0x10 +#define UX_HOST_CLASS_VIDEO_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 +#define UX_HOST_CLASS_VIDEO_PU_ANALOG_LOCK_STATUS_CONTROL 0x12 + + +#define UX_HOST_CLASS_VIDEO_VS_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_VS_PROBE_CONTROL 0x01 +#define UX_HOST_CLASS_VIDEO_VS_COMMIT_CONTROL 0x02 +#define UX_HOST_CLASS_VIDEO_VS_STILL_PROBE_CONTROL 0x03 +#define UX_HOST_CLASS_VIDEO_VS_STILL_COMMIT_CONTROL 0x04 +#define UX_HOST_CLASS_VIDEO_VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 +#define UX_HOST_CLASS_VIDEO_VS_STREAM_ERROR_CODE_CONTROL 0x06 +#define UX_HOST_CLASS_VIDEO_VS_GENERATE_KEY_FRAME_CONTROL 0x07 +#define UX_HOST_CLASS_VIDEO_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 +#define UX_HOST_CLASS_VIDEO_VS_SYNCH_DELAY_CONTROL 0x09 + + +/* Define USB Video Class terminal types. */ + +#define UX_HOST_CLASS_VIDEO_TT_VENDOR_SPECIFIC 0x0100 +#define UX_HOST_CLASS_VIDEO_TT_STREAMING 0x0101 + +#define UX_HOST_CLASS_VIDEO_ITT_VENDOR_SPECIFIC 0x0200 +#define UX_HOST_CLASS_VIDEO_ITT_CAMERA 0x0201 +#define UX_HOST_CLASS_VIDEO_ITT_MEDIA_TRANSPORT_INPUT 0x0202 + +#define UX_HOST_CLASS_VIDEO_OTT_VENDOR_SPECIFIC 0x0300 +#define UX_HOST_CLASS_VIDEO_OTT_CAMERA 0x0301 +#define UX_HOST_CLASS_VIDEO_OTT_MEDIA_TRANSPORT_INPUT 0x0302 + + +/* Define USB Video Class Request Error Code Control. */ + +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_NO_ERROR 0x00 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_NOT_READY 0x01 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_WRONG_STATE 0x02 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_POWER 0x03 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_OUT_OF_RANGE 0x04 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_INVALID_INPUT 0x05 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_INVALID_CONTROL 0x06 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_INVALID_REQUEST 0x07 +#define UX_HOST_CLASS_VIDEO_REQUEST_ERROR_CODE_UNKNOWN 0xFF + + +/* Define Video Class encoding format types. */ + +#define UX_HOST_CLASS_VIDEO_INTERFACE_DESCRIPTOR_ENTRIES 8 +#define UX_HOST_CLASS_VIDEO_INTERFACE_DESCRIPTOR_LENGTH 8 + +#define UX_HOST_CLASS_VIDEO_INPUT_TERMINAL_DESCRIPTOR_ENTRIES 7 +#define UX_HOST_CLASS_VIDEO_INPUT_TERMINAL_DESCRIPTOR_LENGTH 8 + +#define UX_HOST_CLASS_VIDEO_INPUT_HEADER_DESCRIPTOR_ENTRIES 12 +#define UX_HOST_CLASS_VIDEO_INPUT_HEADER_DESCRIPTOR_LENGTH 13 + +#define UX_HOST_CLASS_VIDEO_PROCESSING_UNIT_DESCRIPTOR_ENTRIES 8 +#define UX_HOST_CLASS_VIDEO_PROCESSING_UNIT_DESCRIPTOR_LENGTH 9 + +#define UX_HOST_CLASS_VIDEO_STREAMING_INTERFACE_DESCRIPTOR_ENTRIES 6 +#define UX_HOST_CLASS_VIDEO_STREAMING_INTERFACE_DESCRIPTOR_LENGTH 6 + +#define UX_HOST_CLASS_VIDEO_STREAMING_ENDPOINT_DESCRIPTOR_ENTRIES 6 +#define UX_HOST_CLASS_VIDEO_STREAMING_ENDPOINT_DESCRIPTOR_LENGTH 6 + +#define UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR_ENTRIES 12 +#define UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR_LENGTH 30 + + +/* Define Video Class specific interface descriptor. */ + +#define UX_HOST_CLASS_VIDEO_MAX_CHANNEL 8 +#define UX_HOST_CLASS_VIDEO_NAME_LENGTH 64 + +/* Define Video Class specific request codes. */ + +#define UX_HOST_CLASS_VIDEO_REQUEST_CODE_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_SET_CUR 0x01 +#define UX_HOST_CLASS_VIDEO_GET_CUR 0x81 +#define UX_HOST_CLASS_VIDEO_GET_MIN 0x82 +#define UX_HOST_CLASS_VIDEO_GET_MAX 0x83 +#define UX_HOST_CLASS_VIDEO_GET_RES 0x84 +#define UX_HOST_CLASS_VIDEO_GET_INFO 0x86 +#define UX_HOST_CLASS_VIDEO_GET_DEF 0x87 + +/* Define Video Class error codes. */ + +#define UX_HOST_CLASS_VIDEO_WRONG_TYPE 0x90 +#define UX_HOST_CLASS_VIDEO_WRONG_INTERFACE 0x91 +#define UX_HOST_CLASS_VIDEO_PARAMETER_ERROR 0x92 + +/* Define Video Class Terminal Control Selectors. */ +#define UX_HOST_CLASS_VIDEO_TCS_VC_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_TCS_VC_VIDEO_POWER_MODE_CONTROL 0x01 +#define UX_HOST_CLASS_VIDEO_TCS_VC_REQUEST_ERROR_CODE_CONTROL 0x02 + +#define UX_HOST_CLASS_VIDEO_TCS_TE_CONTROL_UNDEFINED 0x00 + +#define UX_HOST_CLASS_VIDEO_TCS_SU_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_TCS_SU_INPUT_SELECT_CONTROL 0x01 + +#define UX_HOST_CLASS_VIDEO_TCS_CT_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_TCS_CT_SCANNING_MODE_CONTROL 0x01 +#define UX_HOST_CLASS_VIDEO_TCS_CT_AE_MODE_CONTROL 0x02 +#define UX_HOST_CLASS_VIDEO_TCS_CT_AE_PRIORITY_CONTROL 0x03 +#define UX_HOST_CLASS_VIDEO_TCS_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 +#define UX_HOST_CLASS_VIDEO_TCS_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 +#define UX_HOST_CLASS_VIDEO_TCS_CT_FOCUS_ABSOLUTE_CONTROL 0x06 +#define UX_HOST_CLASS_VIDEO_TCS_CT_FOCUS_RELATIVE_CONTROL 0x07 +#define UX_HOST_CLASS_VIDEO_TCS_CT_FOCUS_AUTO_CONTROL 0x08 +#define UX_HOST_CLASS_VIDEO_TCS_CT_IRIS_ABSOLUTE_CONTROL 0x09 +#define UX_HOST_CLASS_VIDEO_TCS_CT_IRIS_RELATIVE_CONTROL 0x0A +#define UX_HOST_CLASS_VIDEO_TCS_CT_ZOOM_ABSOLUTE_CONTROL 0x0B +#define UX_HOST_CLASS_VIDEO_TCS_CT_ZOOM_RELATIVE_CONTROL 0x0C +#define UX_HOST_CLASS_VIDEO_TCS_CT_PANTILT_ABSOLUTE_CONTROL 0x0D +#define UX_HOST_CLASS_VIDEO_TCS_CT_PANTILT_RELATIVE_CONTROL 0x0E +#define UX_HOST_CLASS_VIDEO_TCS_CT_ROLL_ABSOLUTE_CONTROL 0x0F +#define UX_HOST_CLASS_VIDEO_TCS_CT_ROLL_RELATIVE_CONTROL 0x10 +#define UX_HOST_CLASS_VIDEO_TCS_CT_PRIVACY_CONTROL 0x11 + +#define UX_HOST_CLASS_VIDEO_TCS_PU_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_TCS_PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 +#define UX_HOST_CLASS_VIDEO_TCS_PU_BRIGHTNESS_CONTROL 0x02 +#define UX_HOST_CLASS_VIDEO_TCS_PU_CONTRAST_CONTROL 0x03 +#define UX_HOST_CLASS_VIDEO_TCS_PU_GAIN_CONTROL 0x04 +#define UX_HOST_CLASS_VIDEO_TCS_PU_POWER_LINE_FREQUENCY_CONTROL 0x05 +#define UX_HOST_CLASS_VIDEO_TCS_PU_HUE_CONTROL 0x06 +#define UX_HOST_CLASS_VIDEO_TCS_PU_SATURATION_CONTROL 0x07 +#define UX_HOST_CLASS_VIDEO_TCS_PU_SHARPNESS_CONTROL 0x08 +#define UX_HOST_CLASS_VIDEO_TCS_PU_GAMMA_CONTROL 0x09 +#define UX_HOST_CLASS_VIDEO_TCS_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0A +#define UX_HOST_CLASS_VIDEO_TCS_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0B +#define UX_HOST_CLASS_VIDEO_TCS_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0C + +#define UX_HOST_CLASS_VIDEO_TCS_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0D +#define UX_HOST_CLASS_VIDEO_TCS_PU_DIGITAL_MULTIPLIER_CONTROL 0x0E +#define UX_HOST_CLASS_VIDEO_TCS_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0F +#define UX_HOST_CLASS_VIDEO_TCS_PU_HUE_AUTO_CONTROL 0x10 +#define UX_HOST_CLASS_VIDEO_TCS_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 +#define UX_HOST_CLASS_VIDEO_TCS_PU_ANALOG_LOCK_STATUS_CONTROL 0x12 + +#define UX_HOST_CLASS_VIDEO_TCS_XU_CONTROL_UNDEFINED 0x00 + +#define UX_HOST_CLASS_VIDEO_TCS_VS_CONTROL_UNDEFINED 0x00 +#define UX_HOST_CLASS_VIDEO_TCS_VS_PROBE_CONTROL 0x01 +#define UX_HOST_CLASS_VIDEO_TCS_VS_COMMIT_CONTROL 0x02 +#define UX_HOST_CLASS_VIDEO_TCS_VS_STILL_PROBE_CONTROL 0x03 +#define UX_HOST_CLASS_VIDEO_TCS_VS_STILL_COMMIT_CONTROL 0x04 +#define UX_HOST_CLASS_VIDEO_TCS_VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 +#define UX_HOST_CLASS_VIDEO_TCS_VS_STREAM_ERROR_CODE_CONTROL 0x06 +#define UX_HOST_CLASS_VIDEO_TCS_VS_GENERATE_KEY_FRAME_CONTROL 0x07 +#define UX_HOST_CLASS_VIDEO_TCS_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 +#define UX_HOST_CLASS_VIDEO_TCS_VS_SYNCH_DELAY_CONTROL 0x09 + +/* Define Video Class Probe and Commit Controls */ + +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH 34 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_HINT 0 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FORMAT_INDEX 2 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FRAME_INDEX 3 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FRAME_INTERVAL 4 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_KEY_FRAME_RATE 8 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_PFRAME_RAE 10 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_COMP_QUALITY 12 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_COMP_WINDOW_SIZE 14 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_DELAY 16 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_MAX_VIDEO_FRAME_SIZE 18 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_MAX_PAYLOAD_TRANSFER_SIZE 22 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_CLOCK_FREQUENCY 26 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FRAMING_INFO 30 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_PREFERED_VERSION 31 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_MIN_VERSION 32 +#define UX_HOST_CLASS_VIDEO_PROBE_COMMIT_MAX_VERSION 33 + +#ifndef UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT +#define UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT 8 +#endif + +typedef struct UX_HOST_CLASS_VIDEO_INTERFACE_HEADER_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bcdUVC; + ULONG wTotalLength; + ULONG dwClockFrequency; + ULONG bInCollection; + ULONG baInterfaceNr; +} UX_HOST_CLASS_VIDEO_INTERFACE_HEADER_DESCRIPTOR; + +/* Define Video Class specific input header interface descriptor. */ + +typedef struct UX_HOST_CLASS_VIDEO_INPUT_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG iTerminal; +} UX_HOST_CLASS_VIDEO_INPUT_TERMINAL_DESCRIPTOR; + +/* Define Video Class specific input header interface descriptor. */ + +typedef struct UX_HOST_CLASS_VIDEO_INPUT_HEADER_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubType; + ULONG bNumFormats; + ULONG wTotalLength; + ULONG bEndpointAddress; + ULONG bmInfo; + ULONG bTerminalLink; + ULONG bStillCaptureMethod; + ULONG bTriggerSupport; + ULONG bTriggerUsage; + ULONG bControlSize; + ULONG bmaControls; +} UX_HOST_CLASS_VIDEO_INPUT_HEADER_DESCRIPTOR; + +/* Define Video Class Selector descriptor. */ + +typedef struct UX_HOST_CLASS_VIDEO_SELECTOR_UNIT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bUnitID; + ULONG bNrInPins; + ULONG baSourceID; +} UX_HOST_CLASS_VIDEO_SELECTOR_UNIT_DESCRIPTOR; + +/* Define Video Class Camera Terminal descriptor. */ + +typedef struct UX_HOST_CLASS_VIDEO_CAMERA_TERMINAL_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bTerminalID; + ULONG wTerminalType; + ULONG bAssocTerminal; + ULONG iTerminal; + ULONG wObjectiveFocalLengthMin; + ULONG wObjectiveFocalLengthMax; + ULONG wOcularFocalLength; + ULONG bControlSize; + ULONG bmControls; +} UX_HOST_CLASS_VIDEO_CAMERA_TERMINAL_DESCRIPTOR; + +/* Define Video Class Frame descriptor. */ + +typedef struct UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bFrameIndex; + ULONG bmCapabilities; + ULONG wWidth; + ULONG wHeight; + ULONG dwMinBitRate; + ULONG dwMaxBitRate; + ULONG dwMaxVideoFrameBufferSize; + ULONG dwDefaultFrameInterval; + ULONG bFrameIntervalType; +} UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR; + + + +/* Define Video Class Processing Unit descriptor. */ + +typedef struct UX_HOST_CLASS_VIDEO_PROCESSING_UNIT_DESCRIPTOR_STRUCT +{ + + ULONG bLength; + ULONG bDescriptorType; + ULONG bDescriptorSubtype; + ULONG bUnitID; + ULONG bSourceID; + ULONG wMaxMultiplier; + ULONG bControlSize; + ULONG bmControls; + ULONG iProcessing; + ULONG bmVideoStandards; +} UX_HOST_CLASS_VIDEO_PROCESSING_UNIT_DESCRIPTOR; + + +/* Define Video Class instance structure. */ + +typedef struct UX_HOST_CLASS_VIDEO_STRUCT +{ + + struct UX_HOST_CLASS_VIDEO_STRUCT + *ux_host_class_video_next_instance; + UX_HOST_CLASS *ux_host_class_video_class; + UX_DEVICE *ux_host_class_video_device; + UX_INTERFACE *ux_host_class_video_streaming_interface; + ULONG ux_host_class_video_control_interface_number; + UX_ENDPOINT *ux_host_class_video_isochronous_endpoint; + UINT ux_host_class_video_state; + ULONG ux_host_class_video_feature_unit_id; + ULONG ux_host_class_video_terminal_id; + ULONG ux_host_class_video_terminal_type; + UCHAR *ux_host_class_video_configuration_descriptor; + ULONG ux_host_class_video_configuration_descriptor_length; + UCHAR ux_host_class_video_name[UX_HOST_CLASS_VIDEO_NAME_LENGTH]; + ULONG ux_host_class_video_number_formats; + ULONG ux_host_class_video_length_formats; + UCHAR *ux_host_class_video_format_address; + UCHAR *ux_host_class_video_current_format_address; + ULONG ux_host_class_video_current_format; + ULONG ux_host_class_video_number_frames; + ULONG ux_host_class_video_current_frame; + UCHAR *ux_host_class_video_current_frame_address; + ULONG ux_host_class_video_current_frame_interval; + ULONG ux_host_class_video_current_max_payload_size; + UX_TRANSFER ux_host_class_video_transfer_requests[UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT]; + ULONG ux_host_class_video_transfer_request_start_index; + ULONG ux_host_class_video_transfer_request_end_index; + TX_SEMAPHORE ux_host_class_video_semaphore; + VOID (*ux_host_class_video_transfer_completion_function)(UX_TRANSFER*); + +} UX_HOST_CLASS_VIDEO; + + +/* Define Video Class isochronous USB transfer request structure. */ + +typedef struct UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_STRUCT +{ + + ULONG ux_host_class_video_transfer_request_status; + UCHAR * ux_host_class_video_transfer_request_data_pointer; + ULONG ux_host_class_video_transfer_request_requested_length; + ULONG ux_host_class_video_transfer_request_actual_length; + VOID (*ux_host_class_video_transfer_request_completion_function) (struct UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_STRUCT *); + TX_SEMAPHORE ux_host_class_video_transfer_request_semaphore; + VOID *ux_host_class_video_transfer_request_class_instance; + UINT ux_host_class_video_transfer_request_completion_code; + struct UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_STRUCT + *ux_host_class_video_transfer_request_next_video_transfer_request; + UX_TRANSFER ux_host_class_video_transfer_request; +} UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST; + + +/* Define Video Class channel/value control structures. */ + +typedef struct UX_HOST_CLASS_VIDEO_CONTROL_STRUCT +{ + + ULONG ux_host_class_video_control; + LONG ux_host_class_video_control_min; + LONG ux_host_class_video_control_max; + LONG ux_host_class_video_control_res; + LONG ux_host_class_video_control_cur; +} UX_HOST_CLASS_VIDEO_CONTROL; + +/* Define Video Class input terminal structures. */ + +typedef struct UX_HOST_CLASS_VIDEO_PARAMETER_INPUT_TERMINAL_STRUCT +{ + + ULONG ux_host_class_video_parameter_input_terminal_id; + ULONG ux_host_class_video_parameter_input_terminal_type; + +} UX_HOST_CLASS_VIDEO_PARAMETER_INPUT_TERMINAL; + +/* Define Video Class format number structure. */ + +typedef struct UX_HOST_CLASS_VIDEO_PARAMETER_NUMBER_FORMATS_STRUCT +{ + + ULONG ux_host_class_video_parameter_number_formats; + +} UX_HOST_CLASS_VIDEO_PARAMETER_NUMBER_FORMATS; + +/* Define Video Class format data structure. */ + +typedef struct UX_HOST_CLASS_VIDEO_PARAMETER_FORMAT_DATA_STRUCT +{ + + ULONG ux_host_class_video_parameter_format_requested; + ULONG ux_host_class_video_parameter_format_subtype; + ULONG ux_host_class_video_parameter_number_frame_descriptors; + +} UX_HOST_CLASS_VIDEO_PARAMETER_FORMAT_DATA; + +typedef struct UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_DATA_STRUCT +{ + + ULONG ux_host_class_video_parameter_frame_requested; + ULONG ux_host_class_video_parameter_frame_subtype; + ULONG ux_host_class_video_parameter_frame_width; + ULONG ux_host_class_video_parameter_frame_height; + ULONG ux_host_class_video_parameter_default_frame_interval; + ULONG ux_host_class_video_parameter_frame_interval_type; + +} UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_DATA; + + +typedef struct UX_HOST_CLASS_VIDEO_PARAMETER_CHANNEL_STRUCT +{ + + ULONG ux_host_class_video_parameter_channel_bandwidth_selection; + ULONG ux_host_class_video_parameter_format_requested; + ULONG ux_host_class_video_parameter_frame_requested; + ULONG ux_host_class_video_parameter_frame_interval_requested; + +} UX_HOST_CLASS_VIDEO_PARAMETER_CHANNEL; + +typedef struct UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_INTERVAL_STRUCT +{ + + ULONG ux_host_class_video_parameter_frame_requested; + ULONG *ux_host_class_video_parameter_frame_interval_buffer; + ULONG ux_host_class_video_parameter_frame_interval_buffer_length; + ULONG ux_host_class_video_parameter_frame_interval_buffer_length_written; + +} UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_INTERVAL; + +/* Define Video Class function prototypes. */ + +UINT _ux_host_class_video_activate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_video_configure(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_control_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_CONTROL *video_control); +UINT _ux_host_class_video_control_value_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_CONTROL *video_control); +UINT _ux_host_class_video_control_value_set(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_CONTROL *video_control); +UINT _ux_host_class_video_deactivate(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_video_descriptor_get(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_endpoints_get(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_entry(UX_HOST_CLASS_COMMAND *command); +UINT _ux_host_class_video_read(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST *video_transfer_request); +UINT _ux_host_class_video_transfer_request(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST *video_transfer_request); +VOID _ux_host_class_video_transfer_request_completed(UX_TRANSFER *transfer_request); +VOID _ux_host_class_video_transfer_request_callback(UX_TRANSFER *transfer_request); +UINT _ux_host_class_video_control_list_get(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_input_format_get(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_input_terminal_get(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_alternate_setting_locate(UX_HOST_CLASS_VIDEO *video, UINT max_payload_size, UINT *alternate_setting); +UINT _ux_host_class_video_ioctl(UX_HOST_CLASS_VIDEO *video, ULONG ioctl_function, VOID *parameter); +UINT _ux_host_class_video_format_data_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_FORMAT_DATA *format_parameter); +UINT _ux_host_class_video_frame_data_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_DATA *frame_parameter); +UINT _ux_host_class_video_frame_interval_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_INTERVAL *interval_parameter); +UINT _ux_host_class_video_channel_start(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_CHANNEL *video_parameter); +UINT _ux_host_class_video_stop(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_start(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_frame_parameters_set(UX_HOST_CLASS_VIDEO *video, ULONG frame_format, ULONG width, ULONG height, ULONG frame_interval); +ULONG _ux_host_class_video_max_payload_get(UX_HOST_CLASS_VIDEO *video); +UINT _ux_host_class_video_transfer_buffer_add(UX_HOST_CLASS_VIDEO *video, UCHAR* buffer); +UINT _ux_host_class_video_transfer_buffers_add(UX_HOST_CLASS_VIDEO *video, UCHAR** buffers, ULONG num_buffers); +VOID _ux_host_class_video_transfer_callback_set(UX_HOST_CLASS_VIDEO *video, VOID (*callback_function)(UX_TRANSFER*)); + + +/* Define Video Class API prototypes. */ + +#define ux_host_class_video_entry _ux_host_class_video_entry +#define ux_host_class_video_control_get _ux_host_class_video_control_get +#define ux_host_class_video_control_value_get _ux_host_class_video_control_value_get +#define ux_host_class_video_control_value_set _ux_host_class_video_control_value_set +#define ux_host_class_video_read _ux_host_class_video_read +#define ux_host_class_video_ioctl _ux_host_class_video_ioctl +#define ux_host_class_video_start _ux_host_class_video_start +#define ux_host_class_video_stop _ux_host_class_video_stop +#define ux_host_class_video_frame_parameters_set _ux_host_class_video_frame_parameters_set +#define ux_host_class_video_max_payload_get _ux_host_class_video_max_payload_get +#define ux_host_class_video_transfer_buffer_add _ux_host_class_video_transfer_buffer_add +#define ux_host_class_video_transfer_buffers_add _ux_host_class_video_transfer_buffers_add +#define ux_host_class_video_transfer_callback_set _ux_host_class_video_transfer_callback_set + +#endif + + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_activate.c b/common/usbx_host_classes/src/ux_host_class_asix_activate.c new file mode 100644 index 0000000..d5b2778 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_activate.c @@ -0,0 +1,281 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + +UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the asix instance, configure the device. */ +/* WARNING !!!! The NX_PHYSICAL_HEADER should be set to 20 in nx_api.h */ +/* */ +/* INPUT */ +/* */ +/* command Asix class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_asix_configure Configure asix class */ +/* _ux_host_class_asix_endpoints_get Get endpoints of asix */ +/* _ux_host_class_asix_setup Set up asix */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_delete Delete thread */ +/* nx_packet_pool_create Create NetX packet pool */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_asix_entry Entry of asix class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_activate(UX_HOST_CLASS_COMMAND *command) +{ +#if NX_PHYSICAL_HEADER < 20 + + UX_PARAMETER_NOT_USED(command); + + /* Error trap - function not supported due to NX lib settings. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + return(UX_FUNCTION_NOT_SUPPORTED); +#else +UX_DEVICE *device; +UX_HOST_CLASS_ASIX *asix; +UINT status; +UX_TRANSFER *transfer_request; + + /* We need to make sure that the value of the NX_PHYSICAL_HEADER is at least 20. + This should be changed in the nx_user.h file */ + + + /* The asix class is always activated by the device descriptor. */ + device = (UX_DEVICE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + asix = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, sizeof(UX_HOST_CLASS_ASIX)); + if (asix == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + asix -> ux_host_class_asix_class = command -> ux_host_class_command_class_ptr; + + /* Store the device container into the asix class instance. */ + asix -> ux_host_class_asix_device = device; + + /* Store the instance in the device container, this is for the USBX stack + when it needs to invoke the class for deactivation. */ + device -> ux_device_class_instance = (VOID *) asix; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(asix -> ux_host_class_asix_class, (VOID *) asix); + + /* Configure the asix class. */ + status = _ux_host_class_asix_configure(asix); + + /* Get the asix endpoint(s). We need to search for Bulk Out and Bulk In endpoints + and the interrupt endpoint. */ + if (status == UX_SUCCESS) + status = _ux_host_class_asix_endpoints_get(asix); + + /* Create the semaphore to protect 2 threads from accessing the same asix instance. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&asix -> ux_host_class_asix_semaphore, "ux_host_class_asix_semaphore", 1); + if (status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Create the semaphore to wake up the Asix thread. */ + if(status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&asix -> ux_host_class_asix_interrupt_notification_semaphore, "ux_host_class_asix_interrupt_notification_semaphore", 0); + if (status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Allocate a Thread stack. */ + if (status == UX_SUCCESS) + { + asix -> ux_host_class_asix_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check the completion status. */ + if (asix -> ux_host_class_asix_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Create the asix class thread. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_thread_create(&asix -> ux_host_class_asix_thread, + "ux_asix_thread", _ux_host_class_asix_thread, + (ULONG) asix, + asix -> ux_host_class_asix_thread_stack, + UX_THREAD_STACK_SIZE, + UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, + TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Check the completion status. */ + if (status != UX_SUCCESS) + status = UX_THREAD_ERROR; + } + + UX_THREAD_EXTENSION_PTR_SET(&(asix -> ux_host_class_asix_thread), asix) + + /* The asix chip needs to be setup properly. */ + if (status == UX_SUCCESS) + status = _ux_host_class_asix_setup(asix); + + /* Go on if there is no error. */ + if (status == UX_SUCCESS) + { + + /* The ethernet link is down by default. */ + asix -> ux_host_class_asix_link_state = UX_HOST_CLASS_ASIX_LINK_STATE_DOWN; + + /* Start the interrupt pipe now. */ + transfer_request = &asix -> ux_host_class_asix_interrupt_endpoint -> ux_endpoint_transfer_request; + status = _ux_host_stack_transfer_request(transfer_request); + } + + /* Allocate some packet pool for reception. */ + if (status == UX_SUCCESS) + { + + /* UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE overflow has been checked by + * UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT outside of function. + */ + asix -> ux_host_class_asix_pool_memory = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE); + if (asix -> ux_host_class_asix_pool_memory == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Create a packet pool. */ + if (status == UX_SUCCESS) + { + status = nx_packet_pool_create(&asix -> ux_host_class_asix_packet_pool, "Asix Packet Pool", + UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE, asix -> ux_host_class_asix_pool_memory, UX_HOST_CLASS_ASIX_NX_ETHERNET_POOL_ALLOCSIZE); + + /* Check for pool creation error. */ + if (status) + status = UX_ERROR; + } + + /* Do final things if success. */ + if (status == UX_SUCCESS) + { + + /* Mark the asix instance as live now. */ + asix -> ux_host_class_asix_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, asix -> ux_host_class_asix_class, (VOID *) asix); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_ASIX_ACTIVATE, asix, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, asix, 0, 0, 0) + + /* Return completion status. */ + return(UX_SUCCESS); + } + + /* There was a problem, so free the resources. */ + + /* Last resource, asix -> ux_host_class_asix_packet_pool is not created or created error, + no need to free. */ + + /* Free asix -> ux_host_class_asix_pool_memory. */ + if (asix -> ux_host_class_asix_pool_memory) + _ux_utility_memory_free(asix -> ux_host_class_asix_pool_memory); + + /* Free asix -> ux_host_class_asix_thread. */ + if (asix -> ux_host_class_asix_thread.tx_thread_id) + _ux_utility_thread_delete(&asix -> ux_host_class_asix_thread); + + /* Free asix -> ux_host_class_asix_thread_stack. */ + if (asix -> ux_host_class_asix_thread_stack) + _ux_utility_memory_free(asix -> ux_host_class_asix_thread_stack); + + /* Free asix -> ux_host_class_asix_interrupt_notification_semaphore. */ + if (asix -> ux_host_class_asix_interrupt_notification_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&asix -> ux_host_class_asix_interrupt_notification_semaphore); + + /* Free asix -> ux_host_class_asix_semaphore. */ + if (asix -> ux_host_class_asix_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&asix -> ux_host_class_asix_semaphore); + + /* Destroy class instance. */ + _ux_host_stack_class_instance_destroy(asix -> ux_host_class_asix_class, (VOID *) asix); + + /* Detach instance. */ + device -> ux_device_class_instance = UX_NULL; + + /* Free instance memory. */ + _ux_utility_memory_free(asix); + + /* Return completion status. */ + return(status); +#endif +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_configure.c b/common/usbx_host_classes/src/ux_host_class_asix_configure.c new file mode 100644 index 0000000..7e6f2c6 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_configure.c @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* asix. Once the asix is configured, its interface will be */ +/* activated. The bulk endpoints (1 IN, 1 OUT ) and the optional */ +/* interrupt endpoint are enumerated. */ +/* */ +/* INPUT */ +/* */ +/* asix Pointer to asix class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_asix_activate Asix class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_configure(UX_HOST_CLASS_ASIX *asix) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (asix -> ux_host_class_asix_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A asix normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(asix -> ux_host_class_asix_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, asix -> ux_host_class_asix_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the asix power source and check the parent power source for + incompatible connections. */ + if (asix -> ux_host_class_asix_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = asix -> ux_host_class_asix_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root asix and we don't have to worry + if the parent is not the root asix, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, asix, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the asix default alternate setting for the asix interface is + active and the interrupt endpoint is now enabled. We have to memorize the first interface since + the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &asix -> ux_host_class_asix_interface); + + /* Check status for error. */ + if (status == UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + asix -> ux_host_class_asix_interface -> ux_interface_class_instance = (VOID *) asix; + + /* Store the class container in the interface. The device has the correct class, duplicate it to the + interface. */ + asix -> ux_host_class_asix_interface -> ux_interface_class = asix -> ux_host_class_asix_device -> ux_device_class ; + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_deactivate.c b/common/usbx_host_classes/src/ux_host_class_asix_deactivate.c new file mode 100644 index 0000000..349e785 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_deactivate.c @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the asix has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* and interrupt pipes will be destroyed and the instance */ +/* removed. */ +/* */ +/* INPUT */ +/* */ +/* command Asix class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_delete Delete thread */ +/* _ux_network_driver_deactivate Deactivate NetX USB interface */ +/* nx_packet_transmit_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_asix_entry Entry of asix class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_ASIX *asix; +UX_TRANSFER *transfer_request; +NX_PACKET *current_packet; +NX_PACKET *next_packet; +UINT status; + + /* Get the instance for this class. */ + asix = (UX_HOST_CLASS_ASIX *) command -> ux_host_class_command_instance; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&asix -> ux_host_class_asix_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* The asix is being shut down. */ + asix -> ux_host_class_asix_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + + /* Check if link was up. */ + if (asix -> ux_host_class_asix_link_state == UX_HOST_CLASS_ASIX_LINK_STATE_UP) + { + /* We need to free the packets that will not be sent. */ + current_packet = asix -> ux_host_class_asix_xmit_queue; + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + /* Parse all these packets that were scheduled. */ + while (current_packet != UX_NULL) + { + + /* Free the packet that was just sent. First do some housekeeping. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_ASIX_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_HOST_CLASS_ASIX_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + + /* Next packet becomes the current one. */ + current_packet = next_packet; + + /* Next packet now. */ + if (current_packet != UX_NULL) + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + } + + } + + /* Now the link is down. */ + asix -> ux_host_class_asix_link_state = UX_HOST_CLASS_ASIX_LINK_STATE_DOWN; + + /* Deregister this interface to the NetX USB interface broker. */ + status = _ux_network_driver_deactivate((VOID *) asix, asix -> ux_host_class_asix_network_handle); + + /* If the interrupt endpoint is defined, clean any pending transfer. */ + if (asix -> ux_host_class_asix_interrupt_endpoint != UX_NULL) + { + + /* Wait for any current transfer to be out of pending. */ + transfer_request = &asix -> ux_host_class_asix_interrupt_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* And abort any transfer. */ + _ux_host_stack_endpoint_transfer_abort(asix -> ux_host_class_asix_interrupt_endpoint); + + /* And free the memory used by the interrupt endpoint. */ + _ux_utility_memory_free(transfer_request -> ux_transfer_request_data_pointer); + + } + + /* First we take care of cleaning endpoint IN. */ + transfer_request = &asix -> ux_host_class_asix_bulk_in_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(asix -> ux_host_class_asix_bulk_in_endpoint); + + /* Then endpoint OUT. */ + transfer_request = &asix -> ux_host_class_asix_bulk_out_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk Out pipe. We normally don't need that anymore. */ + _ux_host_stack_endpoint_transfer_abort(asix -> ux_host_class_asix_bulk_out_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(asix -> ux_host_class_asix_class, (VOID *) asix); + + /* Destroy the semaphores. */ + _ux_utility_semaphore_delete(&asix -> ux_host_class_asix_semaphore); + _ux_utility_semaphore_delete(&asix -> ux_host_class_asix_interrupt_notification_semaphore); + + /* Destroy the link monitoring thread. */ + _ux_utility_thread_delete(&asix -> ux_host_class_asix_thread); + + /* free its stack memory. */ + _ux_utility_memory_free(asix -> ux_host_class_asix_thread_stack); + + /* We may have allocated a packet pool. */ + if (asix -> ux_host_class_asix_pool_memory != UX_NULL) + + /* Free this pool of packets. */ + _ux_utility_memory_free(asix -> ux_host_class_asix_pool_memory); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, asix -> ux_host_class_asix_class, (VOID *) asix); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_ASIX_DEACTIVATE, asix, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(asix); + + /* Free the asix instance memory. */ + _ux_utility_memory_free(asix); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_asix_endpoints_get.c new file mode 100644 index 0000000..aef3b24 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_endpoints_get.c @@ -0,0 +1,267 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function distinguishes for either the Data or Control Class. */ +/* For the data class, we mount the bulk in and bulk out endpoints. */ +/* For the control class, we mount the optional interrupt endpoint. */ +/* */ +/* INPUT */ +/* */ +/* asix Pointer to asix class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_memory_allocate Allocate memory */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_asix_activate Activate asix class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_endpoints_get(UX_HOST_CLASS_ASIX *asix) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; +UX_TRANSFER *transfer_request; + + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < asix -> ux_host_class_asix_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(asix -> ux_host_class_asix_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_class_instance = (VOID *) asix; + + /* The transfer request has a callback function. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_completion_function = _ux_host_class_asix_transmission_callback; + + /* We have found the bulk endpoint, save it. */ + asix -> ux_host_class_asix_bulk_out_endpoint = endpoint; + break; + } + } + } + + /* The bulk out endpoint is mandatory. */ + if (asix -> ux_host_class_asix_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, asix, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < asix -> ux_host_class_asix_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(asix -> ux_host_class_asix_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_class_instance = (VOID *) asix; + + /* The transfer request has a callback function. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_completion_function = _ux_host_class_asix_reception_callback; + + /* We have found the bulk endpoint, save it. */ + asix -> ux_host_class_asix_bulk_in_endpoint = endpoint; + break; + } + } + } + + /* The bulk in endpoint is mandatory. */ + if (asix -> ux_host_class_asix_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, asix, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the Interrupt endpoint. It is mandatory. */ + for (endpoint_index = 0; endpoint_index < asix -> ux_host_class_asix_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(asix -> ux_host_class_asix_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is Interrupt and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the interrupt endpoint, save it. */ + asix -> ux_host_class_asix_interrupt_endpoint = endpoint; + + /* The endpoint is correct, Fill in the transfer request with the length requested for this endpoint. */ + transfer_request = &asix -> ux_host_class_asix_interrupt_endpoint -> ux_endpoint_transfer_request; + transfer_request -> ux_transfer_request_requested_length = asix -> ux_host_class_asix_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* The direction is always IN for the CDC interrupt endpoint. */ + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) asix; + + /* Interrupt transactions have a completion routine. */ + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_asix_interrupt_notification; + + /* Obtain a buffer for this transaction. The buffer will always be reused. */ + transfer_request -> ux_transfer_request_data_pointer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + transfer_request -> ux_transfer_request_requested_length); + + /* If the endpoint is available and we have memory, we start the interrupt endpoint. */ + if (transfer_request -> ux_transfer_request_data_pointer != UX_NULL) + { + + /* The transfer on the interrupt endpoint can be started. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check error, if endpoint interrupt IN transfer not successful, do not proceed. */ + if (status != UX_SUCCESS) + + /* Error, do not proceed. */ + return(status); + + } + + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We must return an error. */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + break; + } + } + } + + /* The interrupt endpoint is mandatory. */ + if (asix -> ux_host_class_asix_interrupt_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, asix, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + else + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_entry.c b/common/usbx_host_classes/src/ux_host_class_asix_entry.c new file mode 100644 index 0000000..aeed0ca --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_entry.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the asix class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* asix ethernet device on the bus or when the it is removed. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* command Asix class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_asix_activate Activate asix class */ +/* _ux_host_class_asix_deactivate Deactivate asix class */ +/* */ +/* CALLED BY */ +/* */ +/* Asix Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if(command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_PIDVID) + { + + /* Try all the possible PID/VID. */ + if ((command -> ux_host_class_command_pid == UX_HOST_CLASS_ASIX_PRODUCT_ID) && + (command -> ux_host_class_command_vid == UX_HOST_CLASS_ASIX_VENDOR_ID )) + return(UX_SUCCESS); + + /* Try all the possible PID/VID. */ + if ((command -> ux_host_class_command_pid == UX_HOST_CLASS_ASIX_PRODUCT_FUJIEI_ID) && + (command -> ux_host_class_command_vid == UX_HOST_CLASS_ASIX_VENDOR_FUJIEI_ID )) + return(UX_SUCCESS); + + } + + /* No match. */ + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_asix_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_asix_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_interrupt_notification.c b/common/usbx_host_classes/src/ux_host_class_asix_interrupt_notification.c new file mode 100644 index 0000000..706c5dc --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_interrupt_notification.c @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** ASIX Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_interrupt_notification PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the stack when an interrupt packet as */ +/* been received. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_host_stack_transfer_request Transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* USBX stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_asix_interrupt_notification(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_ASIX *asix; + + /* Get the class instance for this transfer request. */ + asix = (UX_HOST_CLASS_ASIX *) transfer_request -> ux_transfer_request_class_instance; + + /* Check the state of the transfer. If there is an error, we do not proceed with this notification. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + + /* We do not proceed. */ + return; + + /* Check if the class is in shutdown. */ + if (asix -> ux_host_class_asix_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) + + /* We do not proceed. */ + return; + + /* Increment the notification count. */ + asix -> ux_host_class_asix_notification_count++; + + /* Ensure the length of our interrupt pipe data is correct. */ + if (transfer_request -> ux_transfer_request_actual_length == + asix -> ux_host_class_asix_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize) + { + + /* Check if the first byte is a interrupt packet signature. */ + if (*(transfer_request -> ux_transfer_request_data_pointer + UX_HOST_CLASS_ASIX_INTERRUPT_SIGNATURE_OFFSET) == + UX_HOST_CLASS_ASIX_INTERRUPT_SIGNATURE_VALUE) + { + + /* Explore the content of the interrupt packet. We treat the link up/down flag here. */ + if (*(transfer_request -> ux_transfer_request_data_pointer + UX_HOST_CLASS_ASIX_INTERRUPT_STATE_OFFSET) & + UX_HOST_CLASS_ASIX_INTERRUPT_STATE_PPLS) + { + + /* Link is up. See if we know about that. */ + if (asix -> ux_host_class_asix_link_state != UX_HOST_CLASS_ASIX_LINK_STATE_UP && + asix -> ux_host_class_asix_link_state != UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_UP) + { + + /* Memorize the new link state. */ + asix -> ux_host_class_asix_link_state = UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_UP; + + /* We need to inform the asix thread of this change. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_interrupt_notification_semaphore); + + } + } + else + { + + /* Link is down. See if we know about that. */ + if (asix -> ux_host_class_asix_link_state != UX_HOST_CLASS_ASIX_LINK_STATE_DOWN && + asix -> ux_host_class_asix_link_state != UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_DOWN) + { + + /* Memorize the new link state. */ + asix -> ux_host_class_asix_link_state = UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_DOWN; + + /* We may have transaction pending on bulk in. Inform the class. */ + transfer_request = &asix -> ux_host_class_asix_bulk_in_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + { + + /* Set the completion error code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_NO_ANSWER; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_NO_ANSWER); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Wake up the semaphore on which the transaction is waiting. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + } + + /* We may have transaction pending on bulk out. Inform the class. */ + transfer_request = &asix -> ux_host_class_asix_bulk_out_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + { + + /* Set the completion error code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_NO_ANSWER; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_NO_ANSWER); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Wake up the semaphore on which the transaction is waiting. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + } + + + /* We need to inform the asix thread of this change. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_interrupt_notification_semaphore); + } + } + } + } + + /* Reactivate the ASIX interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_ASIX_INTERRUPT_NOTIFICATION, asix, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_read.c b/common/usbx_host_classes/src/ux_host_class_asix_read.c new file mode 100644 index 0000000..643c625 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_read.c @@ -0,0 +1,181 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the asix interface. The call is */ +/* blocking and only returns when there is either an error or when */ +/* the transfer is complete. */ +/* */ +/* INPUT */ +/* */ +/* asix Pointer to asix class */ +/* data_pointer Pointer to buffer */ +/* requested_length Requested data read */ +/* actual_length Actual data read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_read (UX_HOST_CLASS_ASIX *asix, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_ASIX_READ, asix, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (asix -> ux_host_class_asix_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, asix, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by resetting the actual length of the transfer to zero. */ + *actual_length = 0; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &asix -> ux_host_class_asix_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk in endpoint until either the transfer is + completed or until there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer_request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_ASIX_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer request in case some data was actually received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* There was an error, return to the caller */ + return(UX_TRANSFER_ERROR); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked. */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion status. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + return(transfer_request -> ux_transfer_request_completion_code); + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually received and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to receive. */ + requested_length -= transfer_request_length; + } + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_reception_callback.c b/common/usbx_host_classes/src/ux_host_class_asix_reception_callback.c new file mode 100644 index 0000000..87d2fa4 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_reception_callback.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_reception_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback from the USBX transfer functions, */ +/* it is called when a full or partial transfer has been done for a */ +/* bulk in transfer. It calls back the application. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* _ux_network_driver_packet_received Process received packet */ +/* nx_packet_transmit_release Release NetX packet */ +/* nx_packet_allocate Allocate NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_asix_reception_callback (UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_ASIX *asix; +NX_PACKET *packet; +ULONG ip_given_length; +UINT status; + + /* Get the class instance for this transfer request. */ + asix = (UX_HOST_CLASS_ASIX *) transfer_request -> ux_transfer_request_class_instance; + + /* Load the packet that is associated with this reception. */ + packet = transfer_request -> ux_transfer_request_user_specific; + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS) + { + + /* Adjust the prepend, length, append fields to take the Ether header out */ + packet -> nx_packet_prepend_ptr += 4; + packet -> nx_packet_length = transfer_request -> ux_transfer_request_actual_length - 4; + packet -> nx_packet_append_ptr = + packet->nx_packet_prepend_ptr + transfer_request -> ux_transfer_request_actual_length ; + + /* Calculate the accurate packet length from ip header */ + if((*(packet -> nx_packet_prepend_ptr + 12) == 0x08) && + (*(packet -> nx_packet_prepend_ptr + 13) == 0)) + { + ip_given_length = _ux_utility_short_get_big_endian(packet -> nx_packet_prepend_ptr + 16) + UX_HOST_CLASS_ASIX_ETHERNET_SIZE; + packet->nx_packet_length = ip_given_length ; + packet->nx_packet_append_ptr = packet->nx_packet_prepend_ptr + ip_given_length; + } + + /* Send that packet to the NetX USB broker. */ + _ux_network_driver_packet_received(asix -> ux_host_class_asix_network_handle, packet); + + } + + else + + /* Free the packet that was not successfully received. */ + nx_packet_transmit_release(packet); + + /* We can accept new reception. Get a NX Packet */ + if (nx_packet_allocate(&asix -> ux_host_class_asix_packet_pool, &packet, + NX_RECEIVE_PACKET, NX_NO_WAIT) == NX_SUCCESS) + { + + /* Adjust the prepend pointer to take into account the non 3 bit alignment of the ethernet header. */ + packet -> nx_packet_prepend_ptr += sizeof(USHORT); + + /* Set the data pointer. */ + transfer_request -> ux_transfer_request_data_pointer = packet -> nx_packet_prepend_ptr; + + /* And length. */ + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* Store the packet that owns this transaction. */ + transfer_request -> ux_transfer_request_user_specific = packet; + + /* Memorize this packet at the beginning of the queue. */ + asix -> ux_host_class_asix_receive_queue = packet; + + /* Reset the queue pointer of this packet. */ + packet -> nx_packet_queue_next = UX_NULL; + + /* Ask USB to schedule a reception. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check if the transaction was armed successfully. We do not wait for the packet to be sent here. */ + if (status != UX_SUCCESS) + { + + /* Cancel the packet. */ + asix -> ux_host_class_asix_receive_queue = UX_NULL; + + } + } + /* There is no status to be reported back to the stack. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_setup.c b/common/usbx_host_classes/src/ux_host_class_asix_setup.c new file mode 100644 index 0000000..d4c8770 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_setup.c @@ -0,0 +1,642 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_setup PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function prepares the Asix chip Phy and rx and xmit registers. */ +/* */ +/* INPUT */ +/* */ +/* asix Pointer to asix class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_asix_activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_setup(UX_HOST_CLASS_ASIX *asix) +{ + +UCHAR *setup_buffer; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +ULONG phy_register_value; +UINT status; + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &asix -> ux_host_class_asix_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the buffer. */ + setup_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_ASIX_SETUP_BUFFER_SIZE); + if (setup_buffer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Get the Ethernet Phy Address register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_READ_PHY_ID; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if ((status != UX_SUCCESS) || (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) || + (transfer_request -> ux_transfer_request_actual_length != 2)) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Extract the PHY IDs and the type. */ + asix -> ux_host_class_asix_primary_phy_id = *(setup_buffer + UX_HOST_CLASS_ASIX_PHY_ID_PRIMARY) & UX_HOST_CLASS_ASIX_PHY_ID_MASK; + asix -> ux_host_class_asix_primary_phy_type = (*(setup_buffer + UX_HOST_CLASS_ASIX_PHY_ID_PRIMARY) >> UX_HOST_CLASS_ASIX_PHY_TYPE_SHIFT) & UX_HOST_CLASS_ASIX_PHY_TYPE_MASK; + + asix -> ux_host_class_asix_secondary_phy_id = *(setup_buffer + UX_HOST_CLASS_ASIX_PHY_ID_SECONDARY) & UX_HOST_CLASS_ASIX_PHY_ID_MASK; + asix -> ux_host_class_asix_secondary_phy_type = (*(setup_buffer + UX_HOST_CLASS_ASIX_PHY_ID_SECONDARY) >> UX_HOST_CLASS_ASIX_PHY_TYPE_SHIFT) & UX_HOST_CLASS_ASIX_PHY_TYPE_MASK; + + /* Set the GPIO 2 register. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_GPIO_STATUS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_HOST_CLASS_ASIX_GPIO_RSE | UX_HOST_CLASS_ASIX_GPIO_GPO2EN | UX_HOST_CLASS_ASIX_GPIO_GPO_2); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Set the Software PHY Select register. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_SW_PHY_SELECT_STATUS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 1; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Perform a software reset of IPPD. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_SW_RESET; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_ASIX_SW_RESET_IPPD; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Perform a software reset. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_SW_RESET; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + + /* Perform a software reset of IPRL and PRL. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_SW_RESET; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_HOST_CLASS_ASIX_SW_RESET_IPRL | UX_HOST_CLASS_ASIX_SW_RESET_PRL); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Write the value of the Receive Control register. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_RX_CTL; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_RXCR_MFB_2048; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Get the Ethernet Phy Address register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_ASIX_NODE_ID_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_READ_NODE_ID; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || + transfer_request -> ux_transfer_request_actual_length != UX_HOST_CLASS_ASIX_NODE_ID_LENGTH) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + + /* Copy the node id into the Asix instance. */ + _ux_utility_memory_copy(asix -> ux_host_class_asix_node_id, setup_buffer, UX_HOST_CLASS_ASIX_NODE_ID_LENGTH); + + /* Request ownership of Serial Management Interface. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_OWN_SMI ; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + + /* Get the value of the PHYIDR1 in the PHY register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_READ_PHY_REG; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = asix -> ux_host_class_asix_primary_phy_id; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || + transfer_request -> ux_transfer_request_actual_length != 2) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Extract the Vendor model number and model revision number. First get the value + in proper 16 bit little endian. */ + phy_register_value = _ux_utility_short_get(setup_buffer); + + /* Get the model revision number. */ + asix -> ux_host_class_asix_model_revision_number = (phy_register_value >> UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_MDL_REV_SHIFT) & UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_MDL_REV_MASK; + + /* Get the vendor model number. */ + asix -> ux_host_class_asix_vendor_model_number = (phy_register_value >> UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_VNDR_REV_SHIFT) & UX_HOST_CLASS_ASIX_PHY_REG_PHYIDR1_VNDR_REV_MASK ; + + + /* Perform a software reset. External Phy Reset Pin level. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_SW_RESET; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_ASIX_SW_RESET_PRL; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Perform a software reset. Internal Phy reset Control and external Phy Reset Pin level. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_SW_RESET; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_HOST_CLASS_ASIX_SW_RESET_IPRL | UX_HOST_CLASS_ASIX_SW_RESET_PRL); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + + /* Write the value of the BMCR register in the PHY register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_PHY_REG; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = asix -> ux_host_class_asix_primary_phy_id; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_PHY_REG_BMCR; + + /* Set the value for the PHY reg. */ + phy_register_value = UX_HOST_CLASS_ASIX_PHY_REG_BMCR_RESET; + + /* Insert the value into the target buffer. */ + _ux_utility_short_put(setup_buffer, (USHORT)phy_register_value); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || + transfer_request -> ux_transfer_request_actual_length != 2) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + + /* Read the value of the BMCR register in the PHY register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_READ_PHY_REG; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = asix -> ux_host_class_asix_primary_phy_id; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_PHY_REG_BMCR; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || transfer_request -> ux_transfer_request_actual_length != 2) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Extract the speed selected by the PHY. */ + phy_register_value = _ux_utility_short_get(setup_buffer); + + /* Isolate the speed and memorize it. */ + if (phy_register_value & UX_HOST_CLASS_ASIX_PHY_REG_BMCR_SPEED_100MBS) + + /* Select 100 MBPS as our speed. */ + asix -> ux_host_class_asix_speed_selected = UX_HOST_CLASS_ASIX_SPEED_SELECTED_100MPBS; + + else + + /* Select 10 MBPS as our speed. */ + asix -> ux_host_class_asix_speed_selected = UX_HOST_CLASS_ASIX_SPEED_SELECTED_10MPBS; + + /* Set the value of the ANAR in the PHY register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_PHY_REG; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = asix -> ux_host_class_asix_primary_phy_id; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_PHY_REG_ANAR; + + /* Set the value for the PHY reg. */ + phy_register_value = (UX_HOST_CLASS_ASIX_PHY_REG_ANAR_DEFAULT_SELECTOR | + UX_HOST_CLASS_ASIX_PHY_REG_ANAR_10_HD | + UX_HOST_CLASS_ASIX_PHY_REG_ANAR_10_FD | + UX_HOST_CLASS_ASIX_PHY_REG_ANAR_TX_HD | + UX_HOST_CLASS_ASIX_PHY_REG_ANAR_TX_FD | + UX_HOST_CLASS_ASIX_PHY_REG_ANAR_PAUSE); + + /* Insert the value into the target buffer. */ + _ux_utility_short_put(setup_buffer, (USHORT)phy_register_value); + + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || + transfer_request -> ux_transfer_request_actual_length != 2) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Set the value of the BMCR in the PHY register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_PHY_REG; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = asix -> ux_host_class_asix_primary_phy_id; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_PHY_REG_BMCR; + + /* Check speed. */ + if (asix -> ux_host_class_asix_speed_selected == UX_HOST_CLASS_ASIX_SPEED_SELECTED_100MPBS) + + /* Set speed at 100MBPS. */ + phy_register_value = UX_HOST_CLASS_ASIX_PHY_REG_BMCR_SPEED_100MBS; + + /* Set the value for the PHY reg. */ + phy_register_value |= (UX_HOST_CLASS_ASIX_PHY_REG_BMCR_AUTO_NEGOTIATION | UX_HOST_CLASS_ASIX_PHY_REG_BMCR_RESTART_NEG | + UX_HOST_CLASS_ASIX_PHY_REG_BMCR_DUPLEX_MODE); + + /* Insert the value into the target buffer. */ + _ux_utility_short_put(setup_buffer, (USHORT)phy_register_value); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || + transfer_request -> ux_transfer_request_actual_length != 2) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + + /* Check speed. */ + if (asix -> ux_host_class_asix_speed_selected == UX_HOST_CLASS_ASIX_SPEED_SELECTED_100MPBS) + + /* Set speed at 100MBPS. */ + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_ASIX_MEDIUM_PS; + + /* Write the value of the Medium Mode. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_MEDIUM_MODE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value |= (UX_HOST_CLASS_ASIX_MEDIUM_FD | UX_HOST_CLASS_ASIX_MEDIUM_BIT2 | UX_HOST_CLASS_ASIX_MEDIUM_RFC_ENABLED | + UX_HOST_CLASS_ASIX_MEDIUM_TFC_ENABLED | UX_HOST_CLASS_ASIX_MEDIUM_RE_ENABLED); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + + /* Write the value of the IPG0/IPG1/IPG2. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_IPG012; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_ASIX_PPG0_IPG1; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_PPG2; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Release SMI ownership. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_RELEASE_SMI; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Set the Rx Control register value. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_RX_CTL; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_HOST_CLASS_ASIX_RXCR_AB | UX_HOST_CLASS_ASIX_RXCR_SO | UX_HOST_CLASS_ASIX_RXCR_MFB_2048); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_thread.c b/common/usbx_host_classes/src/ux_host_class_asix_thread.c new file mode 100644 index 0000000..02615fd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_thread.c @@ -0,0 +1,420 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** ASIX Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This is the Asix thread that monitors the link change flag. */ +/* */ +/* INPUT */ +/* */ +/* asix Asix instance */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_memory_set Set memory */ +/* _ux_network_driver_activate Activate NetX USB interface */ +/* _ux_network_driver_link_down Set state to link down */ +/* _ux_network_driver_link_up Set state to link up */ +/* nx_packet_allocate Allocate NetX packet */ +/* nx_packet_transmit_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* Asix class initialization */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_asix_thread(ULONG parameter) +{ + +UX_HOST_CLASS_ASIX *asix; +UCHAR *setup_buffer; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +NX_PACKET *packet; +NX_PACKET *current_packet; +NX_PACKET *next_packet; +UINT status; +ULONG physical_address_msw; +ULONG physical_address_lsw; + + /* Cast the parameter passed in the thread into the asix pointer. */ + UX_THREAD_EXTENSION_PTR_GET(asix, UX_HOST_CLASS_ASIX, parameter) + + /* Loop forever waiting for changes signaled through the semaphore. */ + while (1) + { + + /* Wait for the semaphore to be put by the asix interrupt event. */ + status = _ux_utility_semaphore_get(&asix -> ux_host_class_asix_interrupt_notification_semaphore, UX_WAIT_FOREVER); + + /* Check for successful completion. */ + if (status != UX_SUCCESS) + + return; + + /* Protect Thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&asix -> ux_host_class_asix_semaphore, UX_WAIT_FOREVER); + + /* Check for successful completion. */ + if (status != UX_SUCCESS) + + return; + + /* Check the link state. It is either pending up or down. */ + if (asix -> ux_host_class_asix_link_state == UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_UP) + { + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &asix -> ux_host_class_asix_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the buffer. */ + setup_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_ASIX_SETUP_BUFFER_SIZE); + if (setup_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + return; + } + + /* Request ownership of Serial Management Interface. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_OWN_SMI ; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + /* Return completion status. */ + return; + + } + + /* Get the value of the PHYIDR1 in the PHY register. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_READ_PHY_REG; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = asix -> ux_host_class_asix_primary_phy_id; + transfer_request -> ux_transfer_request_index = UX_HOST_CLASS_ASIX_PHY_REG_ANLPAR; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || + transfer_request -> ux_transfer_request_actual_length != 2) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + /* Return completion status. */ + return; + + } + + /* Release SMI ownership. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_RELEASE_SMI; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + /* Return completion status. */ + return; + + } + + /* Check speed. */ + if (asix -> ux_host_class_asix_speed_selected == UX_HOST_CLASS_ASIX_SPEED_SELECTED_100MPBS) + + /* Set speed at 100MBPS. */ + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_ASIX_MEDIUM_PS; + + /* Write the value of the Medium Mode. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_MEDIUM_MODE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value |= (UX_HOST_CLASS_ASIX_MEDIUM_FD | UX_HOST_CLASS_ASIX_MEDIUM_BIT2 | UX_HOST_CLASS_ASIX_MEDIUM_RFC_ENABLED | + UX_HOST_CLASS_ASIX_MEDIUM_TFC_ENABLED | UX_HOST_CLASS_ASIX_MEDIUM_RE_ENABLED); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + /* Return completion status. */ + return; + + } + + /* Set the Rx Control register value. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_RX_CTL; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_HOST_CLASS_ASIX_RXCR_AM | UX_HOST_CLASS_ASIX_RXCR_AB | + UX_HOST_CLASS_ASIX_RXCR_SO | UX_HOST_CLASS_ASIX_RXCR_MFB_2048); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + /* Return completion status. */ + return; + + } + + /* Set the multicast value. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 8; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_ASIX_REQ_WRITE_MULTICAST_FILTER; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Fill in the multicast filter. */ + _ux_utility_memory_set(setup_buffer, 0, 8); + *(setup_buffer +1) = 0x40; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS || + transfer_request -> ux_transfer_request_actual_length != 8) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + /* Return completion status. */ + return; + + } + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Setup the physical address of this IP instance. */ + physical_address_msw = (ULONG)((asix -> ux_host_class_asix_node_id[0] << 8) | (asix -> ux_host_class_asix_node_id[1])); + physical_address_lsw = (ULONG)((asix -> ux_host_class_asix_node_id[2] << 24) | (asix -> ux_host_class_asix_node_id[3] << 16) | + (asix -> ux_host_class_asix_node_id[4] << 8) | (asix -> ux_host_class_asix_node_id[5])); + + /* Register this interface to the NetX USB interface broker. */ + status = _ux_network_driver_activate((VOID *) asix, _ux_host_class_asix_write, + &asix -> ux_host_class_asix_network_handle, + physical_address_msw, + physical_address_lsw); + + /* Now the link is up. */ + asix -> ux_host_class_asix_link_state = UX_HOST_CLASS_ASIX_LINK_STATE_UP; + + /* Communicate the state with the network driver. */ + _ux_network_driver_link_up(asix -> ux_host_class_asix_network_handle); + + /* We can accept reception. Get a NX Packet */ + if (nx_packet_allocate(&asix -> ux_host_class_asix_packet_pool, &packet, + NX_RECEIVE_PACKET, NX_NO_WAIT) == NX_SUCCESS) + { + + + /* Adjust the prepend pointer to take into account the non 3 bit alignment of the ethernet header. */ + packet -> nx_packet_prepend_ptr += sizeof(USHORT); + + /* We have a packet. Link this packet to the reception transfer request on the bulk in endpoint. */ + transfer_request = &asix -> ux_host_class_asix_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Set the data pointer. */ + transfer_request -> ux_transfer_request_data_pointer = packet -> nx_packet_prepend_ptr; + + /* And length. */ + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_ASIX_NX_PAYLOAD_SIZE; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* Store the packet that owns this transaction. */ + transfer_request -> ux_transfer_request_user_specific = packet; + + /* Memorize this packet at the beginning of the queue. */ + asix -> ux_host_class_asix_receive_queue = packet; + + /* Reset the queue pointer of this packet. */ + packet -> nx_packet_queue_next = UX_NULL; + + /* Ask USB to schedule a reception. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check if the transaction was armed successfully. We do not wait for the packet to be sent here. */ + if (status != UX_SUCCESS) + { + + /* Cancel the packet. */ + asix -> ux_host_class_asix_receive_queue = UX_NULL; + + } + + } + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + return; + } + + /* Check the link state. It is either pending up or down. */ + if (asix -> ux_host_class_asix_link_state == UX_HOST_CLASS_ASIX_LINK_STATE_PENDING_DOWN) + { + + /* We need to free the packets that will not be sent. */ + current_packet = asix -> ux_host_class_asix_xmit_queue; + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + /* Parse all these packets that were scheduled. */ + while (current_packet != UX_NULL) + { + + /* Free the packet that was just sent. First do some housekeeping. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_ASIX_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_HOST_CLASS_ASIX_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + + /* Next packet becomes the current one. */ + current_packet = next_packet; + + /* Next packet now. */ + if (current_packet != UX_NULL) + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + } + + + /* Now the link is down. */ + asix -> ux_host_class_asix_link_state = UX_HOST_CLASS_ASIX_LINK_STATE_DOWN; + + /* Communicate the state with the network driver. */ + _ux_network_driver_link_down(asix -> ux_host_class_asix_network_handle); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&asix -> ux_host_class_asix_semaphore); + + return; + + } + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_transmission_callback.c b/common/usbx_host_classes/src/ux_host_class_asix_transmission_callback.c new file mode 100644 index 0000000..c1f6ce6 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_transmission_callback.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_transmission_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback from the USBX transfer functions, */ +/* it is called when a full or partial transfer has been done for a */ +/* bulk out transfer. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_short_put Put 16-bit value */ +/* nx_packet_transmit_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_asix_transmission_callback (UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_ASIX *asix; +NX_PACKET *current_packet; +NX_PACKET *next_packet; +UCHAR *packet_header; + + /* Get the class instance for this transfer request. */ + asix = (UX_HOST_CLASS_ASIX *) transfer_request -> ux_transfer_request_class_instance; + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* We do not proceed. */ + return; + + } + + /* Check if the class is in shutdown. */ + if (asix -> ux_host_class_asix_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) + + /* We do not proceed. */ + return; + + /* Check if there is another transfer for this pipe. */ + current_packet = asix -> ux_host_class_asix_xmit_queue; + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + /* Set the next packet (or a NULL value) as the head of the xmit queue. */ + asix -> ux_host_class_asix_xmit_queue = next_packet; + + /* If there is nothing else or if the link is down no need to rearm a packet. */ + if (next_packet != UX_NULL && asix -> ux_host_class_asix_link_state == UX_HOST_CLASS_ASIX_LINK_STATE_UP) + { + + /* Load the address of the current packet header of the physical header. */ + packet_header = next_packet -> nx_packet_prepend_ptr; + + /* Substract 2 USHORT to store length of the packet. */ + packet_header -= sizeof(USHORT) * 2; + + /* Store the length of the payload in the first USHORT. */ + _ux_utility_short_put(packet_header, (USHORT)(next_packet -> nx_packet_length)); + + /* Store the negative length of the payload in the first USHORT. */ + _ux_utility_short_put(packet_header+ sizeof(USHORT), (USHORT)(~next_packet -> nx_packet_length)); + + /* Prepare the values for this new transmission. */ + transfer_request -> ux_transfer_request_data_pointer = packet_header; + transfer_request -> ux_transfer_request_requested_length = next_packet -> nx_packet_length + (ULONG)sizeof(USHORT) * 2; + + /* Store the packet that owns this transaction. */ + transfer_request -> ux_transfer_request_user_specific = next_packet; + + /* Arm another transfer. */ + _ux_host_stack_transfer_request(transfer_request); + } + + /* Free the packet that was just sent. First do some housekeeping. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_ASIX_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_HOST_CLASS_ASIX_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + + /* There is no status to be reported back to the stack. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_asix_write.c b/common/usbx_host_classes/src/ux_host_class_asix_write.c new file mode 100644 index 0000000..dfe33c6 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_asix_write.c @@ -0,0 +1,195 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Asix Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_asix.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_asix_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the asix interface. The call is blocking */ +/* and only returns when there is either an error or when the transfer */ +/* is complete. */ +/* */ +/* INPUT */ +/* */ +/* asix Pointer to asix class */ +/* packet Packet to write or queue */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_short_put Put 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_asix_write(VOID *asix_class, NX_PACKET *packet) +{ + +UX_TRANSFER *transfer_request; +UINT status; +NX_PACKET *current_packet; +NX_PACKET *next_packet; +UCHAR *packet_header; +UX_HOST_CLASS_ASIX *asix; + + /* Proper class casting. */ + asix = (UX_HOST_CLASS_ASIX *) asix_class; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_ASIX_WRITE, asix, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (asix -> ux_host_class_asix_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, asix, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Load the address of the current packet header at the physical header. */ + packet_header = packet -> nx_packet_prepend_ptr; + + /* Substract 2 USHORT to store length of the packet. */ + packet_header -= sizeof(USHORT) * 2; + +#if defined(UX_HOST_CLASS_ASIX_HEADER_CHECK_ENABLE) + /* Check packet compatibility to avoid writing to unexpected area. */ + if (packet_header < packet -> nx_packet_data_start) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_MEMORY_ERROR); + return(UX_HOST_CLASS_MEMORY_ERROR); + } +#endif + + /* Store the length of the payload in the first USHORT. */ + _ux_utility_short_put(packet_header, (USHORT)(packet -> nx_packet_length)); + + /* Store the negative length of the payload in the first USHORT. */ + _ux_utility_short_put(packet_header + sizeof(USHORT), (USHORT)(~packet -> nx_packet_length)); + + /* Check the queue. See if there is something that is being sent. */ + if (asix -> ux_host_class_asix_xmit_queue == UX_NULL) + { + + /* Nothing is in the queue. We need to arm this transfer. */ + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &asix -> ux_host_class_asix_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Setup the transaction parameters. */ + transfer_request -> ux_transfer_request_data_pointer = packet_header; + transfer_request -> ux_transfer_request_requested_length = packet -> nx_packet_length + (ULONG)sizeof(USHORT) * 2; + + /* Store the packet that owns this transaction. */ + transfer_request -> ux_transfer_request_user_specific = packet; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check if the transaction was armed successfully. We do not wait for the packet to be sent here. */ + if (status == UX_SUCCESS) + { + + /* Memorize this packet at the beginning of the queue. */ + asix -> ux_host_class_asix_xmit_queue = packet; + + /* Reset the queue pointer of this packet. */ + packet -> nx_packet_queue_next = UX_NULL; + + } + + else + + /* Could not arm this transfer. */ + return(UX_ERROR); + + } + + else + + { + + /* We get here when there is something in the queue. */ + current_packet = asix -> ux_host_class_asix_xmit_queue; + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + /* Parse the current chain for the end. */ + while (next_packet != NX_NULL) + { + /* Remember the current packet. */ + current_packet = next_packet; + + /* See what the next packet in the chain is. */ + next_packet = current_packet -> nx_packet_queue_next; + } + + /* Memorize the packet to be sent. */ + current_packet -> nx_packet_queue_next = packet; + + /* The packet to be sent is the last in the chain. */ + packet -> nx_packet_queue_next = NX_NULL; + + } + + /* We are done here. */ + return(UX_SUCCESS); + +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_activate.c b/common/usbx_host_classes/src/ux_host_class_audio_activate.c new file mode 100644 index 0000000..c6bafac --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_activate.c @@ -0,0 +1,209 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates the audio class. It may be called twice by */ +/* the same device if there is a audio control interface to this */ +/* device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_audio_configure Configure the audio class */ +/* _ux_host_class_audio_descriptor_get Get audio descriptor */ +/* _ux_host_class_audio_device_controls_list_get Get controls list */ +/* _ux_host_class_audio_device_type_get Get device type */ +/* _ux_host_class_audio_streaming_terminal_get Get streaming terminal */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_utility_memory_allocate Allocate a memory block */ +/* _ux_utility_semaphore_create Create protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_AUDIO *audio; +UINT status; + + + /* The audio is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Check the subclass of the new device. If it is a Audio Control Interface, + we don't need to create an instance of this function. When we get the streaming interface, + we will search the audio control interface for the device. */ + if (interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_CONTROL) + return(UX_SUCCESS); + + /* Obtain memory for this class instance. */ + audio = (UX_HOST_CLASS_AUDIO *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_AUDIO)); + if (audio == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + audio -> ux_host_class_audio_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the audio class instance. */ + audio -> ux_host_class_audio_streaming_interface = interface; + + /* Store the device container into the audio class instance. */ + audio -> ux_host_class_audio_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) audio; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) audio; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(audio -> ux_host_class_audio_class, (VOID *) audio); + + /* Configure the audio. */ + status = _ux_host_class_audio_configure(audio); + if (status != UX_SUCCESS) + { + + /* Error, destroy the class instance. */ + _ux_host_stack_class_instance_destroy(audio -> ux_host_class_audio_class, (VOID *) audio); + + /* Return the error. */ + return(status); + } + + /* Get the audio descriptor (all the class specific stuff) and memorize them + as we will need these descriptors to change settings. */ + status = _ux_host_class_audio_descriptor_get(audio); + if (status != UX_SUCCESS) + { + + /* Error, destroy the class instance. */ + _ux_host_stack_class_instance_destroy(audio -> ux_host_class_audio_class, (VOID *) audio); + + /* Return the error. */ + return(status); + } + + /* Locate the audio device streaming terminal. */ + status = _ux_host_class_audio_streaming_terminal_get(audio); + if (status != UX_SUCCESS) + { + + /* Error, destroy the class instance. */ + _ux_host_stack_class_instance_destroy(audio -> ux_host_class_audio_class, (VOID *) audio); + + /* Return the error. */ + return(status); + } + + /* Get the audio device type. Here we only support input and output devices. */ + status = _ux_host_class_audio_device_type_get(audio); + if (status != UX_SUCCESS) + { + + /* Error, destroy the class instance. */ + _ux_host_stack_class_instance_destroy(audio -> ux_host_class_audio_class, (VOID *) audio); + + /* Return the error. */ + return(status); + } + + /* Get the audio device controls. */ + status = _ux_host_class_audio_device_controls_list_get(audio); + if (status != UX_SUCCESS) + { + + /* Error, destroy the class instance. */ + _ux_host_stack_class_instance_destroy(audio -> ux_host_class_audio_class, (VOID *) audio); + + /* Return the error. */ + return(status); + } + + /* Create the semaphore to protect multiple threads from accessing the same + audio instance. */ + status = _ux_utility_semaphore_create(&audio -> ux_host_class_audio_semaphore, "ux_hot_class_audio_semaphore", 1); + if (status != UX_SUCCESS) + return(UX_SEMAPHORE_ERROR); + + /* Mark the audio as live now. */ + audio -> ux_host_class_audio_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if ((status == UX_SUCCESS) && (_ux_system_host -> ux_system_host_change_function != UX_NULL)) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, audio -> ux_host_class_audio_class, (VOID *) audio); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_ACTIVATE, audio, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, audio, 0, 0, 0) + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_alternate_setting_locate.c b/common/usbx_host_classes/src/ux_host_class_audio_alternate_setting_locate.c new file mode 100644 index 0000000..3712513 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_alternate_setting_locate.c @@ -0,0 +1,244 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_alternate_setting_locate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the right alternate setting according to the */ +/* sampling desired. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_sampling Pointer to audio sampling */ +/* alternate_setting Pointer to located alternate */ +/* setting */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_alternate_setting_locate(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling, + UINT *alternate_setting) +{ + +UCHAR * descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR audio_interface_descriptor; +ULONG total_descriptor_length; +UINT descriptor_length; +UINT descriptor_type; +UINT descriptor_subtype; +UINT interface_found; +ULONG lower_frequency; +ULONG higher_frequency; +UINT specific_frequency_count; + + + /* Get the descriptor to the entire configuration. */ + descriptor = audio -> ux_host_class_audio_configuration_descriptor; + total_descriptor_length = audio -> ux_host_class_audio_configuration_descriptor_length; + + /* Default is Interface descriptor not yet found. */ + interface_found = UX_FALSE; + + /* Scan the descriptor for the Audio Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + /* Process relative to descriptor type. */ + switch (descriptor_type) + { + + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Audio streaming. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_AUDIO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_STREAMING)) + { + + /* Mark we have found it. */ + interface_found = UX_TRUE; + + /* And memorize the alternate setting. */ + *alternate_setting = interface_descriptor.bAlternateSetting; + } + else + { + + /* Haven't found it. */ + interface_found = UX_FALSE; + } + break; + + + case UX_HOST_CLASS_AUDIO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if ((interface_found == UX_TRUE) && (descriptor_subtype == UX_HOST_CLASS_AUDIO_CS_FORMAT_TYPE)) + { + + /* Parse the FORMAT_TYPE descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_interface_descriptor_structure, + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &audio_interface_descriptor); + + /* This descriptor must refer to a PCM audio type. */ + if (audio_interface_descriptor.bFormatType != UX_HOST_CLASS_AUDIO_FORMAT_TYPE_I) + break; + + /* The number of channels demanded by the application must match. */ + if (audio_sampling -> ux_host_class_audio_sampling_channels != audio_interface_descriptor.bNrChannels) + break; + + /* The resolution demanded by the application must match. */ + if (audio_sampling -> ux_host_class_audio_sampling_resolution != audio_interface_descriptor.bBitResolution) + break; + + /* Check the frequency demanded. The descriptor frequency is either defined + as a min and max frequency or an array of specified values. */ + if (audio_interface_descriptor.bSamFreqType == 0) + { + + /* The declaration of frequency is contiguous, so get the minimum and maximum */ + lower_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2)) << 16; + + higher_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 3) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 4)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 5)) << 16; + + /* Now compare with what is required. */ + if ((audio_sampling -> ux_host_class_audio_sampling_frequency >= lower_frequency) && + (audio_sampling -> ux_host_class_audio_sampling_frequency <= higher_frequency)) + { + + /* We have found the right alternate setting. */ + return(UX_SUCCESS); + } + } + else + { + + /* The declaration of the frequency is declared as an array of specific values. */ + for (specific_frequency_count = 0; specific_frequency_count < audio_interface_descriptor.bSamFreqType; + specific_frequency_count++) + { + + lower_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + (specific_frequency_count * 3)) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1 + (specific_frequency_count * 3))) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2 + (specific_frequency_count * 3))) << 16; + + /* Now compare with what is required. */ + if (audio_sampling -> ux_host_class_audio_sampling_frequency == lower_frequency) + { + + /* We have found the right alternate setting. */ + return(UX_SUCCESS); + } + } + } + } + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_ALTERNATE_SETTING, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We get here when either the report descriptor has a problem or we could + not find the right audio device. */ + return(UX_NO_ALTERNATE_SETTING); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_configure.c b/common/usbx_host_classes/src/ux_host_class_audio_configure.c new file mode 100644 index 0000000..c78b876 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_configure.c @@ -0,0 +1,174 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to */ +/* the device. Once the device is configured, its interface(s) will */ +/* be activated. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_configure(UX_HOST_CLASS_AUDIO *audio) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UX_DEVICE *parent_device; +ULONG interface_number; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (audio -> ux_host_class_audio_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* An audio device normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(audio -> ux_host_class_audio_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, audio -> ux_host_class_audio_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + + } + /* Check the audio power source and check the parent power source for + incompatible connections. */ + if (audio -> ux_host_class_audio_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device. */ + parent_device = audio -> ux_host_class_audio_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root audio and we don't have to worry + if the parent is not the root audio, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration */ + status = _ux_host_stack_device_configuration_select(configuration); + + /* Start with interface number 0. */ + interface_number = 0; + + /* We only need to retrieve the audio streaming interface. */ + do + { + + /* Pickup interface. */ + status = _ux_host_stack_configuration_interface_get(configuration, (UINT) interface_number, 0, &interface); + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* Check the type of interface we have found - is it streaming? */ + if (interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_STREAMING) + { + + audio -> ux_host_class_audio_streaming_interface = interface; + audio -> ux_host_class_audio_streaming_interface -> ux_interface_class_instance = (VOID *)audio; + break; + } + } + } while(status == UX_SUCCESS); + + /* After we have parsed the audio interfaces, ensure the streaming interfaces is resent. */ + if (audio -> ux_host_class_audio_streaming_interface == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_INTERFACE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We get here when we could not locate the interface(s) handle */ + return(UX_INTERFACE_HANDLE_UNKNOWN); + } + else + { + + return(UX_SUCCESS); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_control_get.c b/common/usbx_host_classes/src/ux_host_class_audio_control_get.c new file mode 100644 index 0000000..f8f625a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_control_get.c @@ -0,0 +1,212 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_control_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the static values for a single audio control */ +/* on either the master channel or a specific channel. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_control Pointer to audio control */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_get Read 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_control_get(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_CONTROL *audio_control) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * control_buffer; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &audio -> ux_host_class_audio_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the control buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return an error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer request for the GET_MIN request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_AUDIO_GET_MIN; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = audio_control -> ux_host_class_audio_control_channel | (audio_control -> ux_host_class_audio_control << 8); + transfer_request -> ux_transfer_request_index = audio -> ux_host_class_audio_control_interface_number | (audio -> ux_host_class_audio_feature_unit_id << 8); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the MIN static value for the caller. */ + audio_control -> ux_host_class_audio_control_min = _ux_utility_short_get(control_buffer); + } + else + { + + /* Free the previous control buffer. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + } + + /* Create a transfer request for the GET_MAX request. */ + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_AUDIO_GET_MAX; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the MAX static value for the caller. */ + audio_control -> ux_host_class_audio_control_max = _ux_utility_short_get(control_buffer); + } + else + { + + /* Free the previous control buffer. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + } + + /* Create a transfer request for the GET_RES request. */ + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_AUDIO_GET_RES; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the RES static value for the caller. */ + audio_control -> ux_host_class_audio_control_res = _ux_utility_short_get(control_buffer); + } + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_control_value_get.c b/common/usbx_host_classes/src/ux_host_class_audio_control_value_get.c new file mode 100644 index 0000000..1d402bc --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_control_value_get.c @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_control_value_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the dynamic values for a single audio control */ +/* on either the master channel or a specific channel. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_control Pointer to audio control */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_get Read 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_control_value_get(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_CONTROL *audio_control) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * control_buffer; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_CONTROL_VALUE_GET, audio, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &audio -> ux_host_class_audio_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the control_buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Create a transfer request for the GET_CUR_buffer request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_AUDIO_GET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = audio_control -> ux_host_class_audio_control_channel | (audio_control -> ux_host_class_audio_control << 8); + transfer_request -> ux_transfer_request_index = audio -> ux_host_class_audio_control_interface_number | (audio -> ux_host_class_audio_feature_unit_id << 8); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the CUR static value for the caller. */ + audio_control -> ux_host_class_audio_control_cur = _ux_utility_short_get(control_buffer); + } + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_control_value_set.c b/common/usbx_host_classes/src/ux_host_class_audio_control_value_set.c new file mode 100644 index 0000000..65f0b4e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_control_value_set.c @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_control_value_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function updates the dynamic values for a single audio control */ +/* on either the master channel or a specific channel. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_control Pointer to audio control */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_put Write 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_control_value_set(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_CONTROL *audio_control) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * control_buffer; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_CONTROL_VALUE_SET, audio, audio_control, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &audio -> ux_host_class_audio_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the control buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return an error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Update the control buffer with the current control value. */ + _ux_utility_short_put(control_buffer, (USHORT) audio_control -> ux_host_class_audio_control_cur); + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer request for the SET_CUR_buffer request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_AUDIO_SET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = audio_control -> ux_host_class_audio_control_channel | (audio_control -> ux_host_class_audio_control << 8); + transfer_request -> ux_transfer_request_index = audio -> ux_host_class_audio_control_interface_number | (audio -> ux_host_class_audio_feature_unit_id << 8); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_deactivate.c b/common/usbx_host_classes/src/ux_host_class_audio_deactivate.c new file mode 100644 index 0000000..0d7f192 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_deactivate.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the audio has been */ +/* removed from the bus either directly or indirectly. The iso pipes */ +/* will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort outstanding transfer */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_AUDIO *audio; +UINT status; + + /* Get the instance for this class. */ + audio= (UX_HOST_CLASS_AUDIO *) command -> ux_host_class_command_instance; + + /* The audio is being shut down. */ + audio -> ux_host_class_audio_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We need to abort transactions on the iso out pipe. */ + _ux_host_stack_endpoint_transfer_abort(audio -> ux_host_class_audio_isochronous_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(audio -> ux_host_class_audio_class, (VOID *) audio); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&audio -> ux_host_class_audio_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, audio -> ux_host_class_audio_class, (VOID *) audio); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_DEACTIVATE, audio, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(audio); + + /* Free the audio instance memory. */ + _ux_utility_memory_free(audio); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_descriptor_get.c b/common/usbx_host_classes/src/ux_host_class_audio_descriptor_get.c new file mode 100644 index 0000000..41d7332 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_descriptor_get.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_descriptor_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the entire audio configuration descriptors. */ +/* This is needed because the audio class has many class specific */ +/* descriptors which describe the alternate settings that can be used. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_descriptor_get(UX_HOST_CLASS_AUDIO *audio) +{ + +UCHAR * descriptor; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UX_CONFIGURATION configuration; +UINT status; +ULONG total_configuration_length; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &audio -> ux_host_class_audio_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_CONFIGURATION_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_CONFIGURATION_DESCRIPTOR_ITEM << 8; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH)) + { + + /* The descriptor is in a packed format, parse it locally. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration.ux_configuration_descriptor); + + /* Now we have the configuration descriptor which will tell us how many + bytes there are in the entire descriptor. */ + total_configuration_length = configuration.ux_configuration_descriptor.wTotalLength; + + /* Free the previous descriptor. */ + _ux_utility_memory_free(descriptor); + + /* Allocate enough memory to read all descriptors attached + to this configuration. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, total_configuration_length); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set the length we need to retrieve. */ + transfer_request -> ux_transfer_request_requested_length = total_configuration_length; + + /* And reprogram the descriptor buffer address. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == total_configuration_length)) + { + + /* Save the address of the entire configuration descriptor of the audio device. */ + audio -> ux_host_class_audio_configuration_descriptor = descriptor; + + /* Save the length of the entire descriptor too. */ + audio -> ux_host_class_audio_configuration_descriptor_length = total_configuration_length; + + /* We do not free the resource for the descriptor until the device is + plugged out. */ + return(UX_SUCCESS); + } + } + + /* Free all used resources. */ + _ux_utility_memory_free(descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_device_controls_list_get.c b/common/usbx_host_classes/src/ux_host_class_audio_device_controls_list_get.c new file mode 100644 index 0000000..efaf0db --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_device_controls_list_get.c @@ -0,0 +1,253 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_device_controls_list_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the controls for the audio device. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_long_get Get 32-bit word */ +/* _ux_utility_short_get Get 16-bit word */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_device_controls_list_get(UX_HOST_CLASS_AUDIO *audio) +{ + +UCHAR * descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR input_interface_descriptor; +UX_HOST_CLASS_AUDIO_FEATURE_UNIT_DESCRIPTOR feature_unit_interface_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; +ULONG descriptor_found; +ULONG control_size_bytes; +UCHAR * control_bit_map_address; +ULONG channel_number; + + + /* Get the descriptor to the entire configuration */ + descriptor = audio -> ux_host_class_audio_configuration_descriptor; + total_descriptor_length = audio -> ux_host_class_audio_configuration_descriptor_length; + + /* Default is Interface descriptor not yet found. */ + descriptor_found = UX_FALSE; + + /* Scan the descriptor for the Audio Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to descriptor type. */ + switch(descriptor_type) + { + + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Audio streaming. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_AUDIO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_CONTROL)) + + /* Mark we have found it. */ + descriptor_found = UX_TRUE; + else + + descriptor_found = UX_FALSE; + break; + + + case UX_HOST_CLASS_AUDIO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if (descriptor_found == UX_TRUE) + { + + /* Check the sub type. */ + switch (descriptor_subtype) + { + + case UX_HOST_CLASS_AUDIO_CS_INPUT_TERMINAL: + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_input_terminal_descriptor_structure, + UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR_ENTRIES, (UCHAR *) &input_interface_descriptor); + + /* Get the number of channels and check for maximum allowed. */ + if (input_interface_descriptor.bNrChannels > UX_HOST_CLASS_AUDIO_MAX_CHANNEL) + audio -> ux_host_class_audio_channels = UX_HOST_CLASS_AUDIO_MAX_CHANNEL; + else + audio -> ux_host_class_audio_channels = input_interface_descriptor.bNrChannels; + break; + + + case UX_HOST_CLASS_AUDIO_CS_FEATURE_UNIT: + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_feature_unit_descriptor_structure, + UX_HOST_CLASS_AUDIO_FEATURE_UNIT_DESCRIPTOR_ENTRIES, (UCHAR *) &feature_unit_interface_descriptor); + + /* Store the Feature Unit ID, used to set/get the audio controls. */ + audio -> ux_host_class_audio_feature_unit_id = feature_unit_interface_descriptor.bUnitID; + + /* Get the size of each control in bytes. */ + control_size_bytes = feature_unit_interface_descriptor.bControlSize; + + /* Get the address of the first control bit map. */ + control_bit_map_address = (UCHAR *) &feature_unit_interface_descriptor.bmaControls; + + /* Walk all the controls and retrieve them one by one. */ + for (channel_number = 0; channel_number < audio -> ux_host_class_audio_channels; channel_number++) + { + + /* Each control has the same size. */ + switch (control_size_bytes) + { + + case 1: + + /* Byte aligned control. */ + audio -> ux_host_class_audio_channel_control[channel_number] = (ULONG) *control_bit_map_address; + break; + + + case 2: + + /* Word aligned control. */ + audio -> ux_host_class_audio_channel_control[channel_number] = (ULONG) _ux_utility_short_get(control_bit_map_address); + break; + + + case 4: + + /* Long aligned control. */ + audio -> ux_host_class_audio_channel_control[channel_number] = _ux_utility_long_get(control_bit_map_address); + break; + + + default: + + /* Not sure what we should do here, we ignore the control. */ + break; + } + + /* Point to the next control. */ + control_bit_map_address += control_size_bytes; + } + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_AUDIO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right audio device. */ + return(UX_HOST_CLASS_AUDIO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_device_type_get.c b/common/usbx_host_classes/src/ux_host_class_audio_device_type_get.c new file mode 100644 index 0000000..36e1365 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_device_type_get.c @@ -0,0 +1,220 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_device_type_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the device type. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_device_type_get(UX_HOST_CLASS_AUDIO *audio) +{ + +UCHAR * descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR output_interface_descriptor; +UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR input_interface_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; +ULONG descriptor_found; + + + /* Get the descriptor to the entire configuration. */ + descriptor = audio -> ux_host_class_audio_configuration_descriptor; + total_descriptor_length = audio -> ux_host_class_audio_configuration_descriptor_length; + + /* Default is Interface descriptor not yet found. */ + descriptor_found = UX_FALSE; + + /* Scan the descriptor for the Audio Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to the descriptor type. */ + switch (descriptor_type) + { + + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Audio Control. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_AUDIO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_CONTROL)) + { + + /* Mark we have found it. */ + descriptor_found = UX_TRUE; + + /* Get the interface number of this descriptor and save it in the audio + instance. This will be useful to program the audio controls. */ + audio -> ux_host_class_audio_control_interface_number = interface_descriptor.bInterfaceNumber; + } + else + { + + descriptor_found = UX_FALSE; + } + break; + + + case UX_HOST_CLASS_AUDIO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if (descriptor_found == UX_TRUE) + { + + /* Check the sub type. */ + switch (descriptor_subtype) + { + + + case UX_HOST_CLASS_AUDIO_CS_INPUT_TERMINAL: + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_input_terminal_descriptor_structure, + UX_HOST_CLASS_AUDIO_INPUT_TERMINAL_DESCRIPTOR_ENTRIES, (UCHAR *) &input_interface_descriptor); + + /* Make sure we have the right terminal link. */ + if (input_interface_descriptor.bTerminalID == audio -> ux_host_class_audio_terminal_link) + { + + audio -> ux_host_class_audio_type = UX_HOST_CLASS_AUDIO_OUTPUT; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + + break; + + case UX_HOST_CLASS_AUDIO_CS_OUTPUT_TERMINAL: + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_output_terminal_descriptor_structure, + UX_HOST_CLASS_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR_ENTRIES, (UCHAR *) &output_interface_descriptor); + + /* Make sure we have the right terminal link. */ + if (output_interface_descriptor.bTerminalID == audio -> ux_host_class_audio_terminal_link) + { + + audio -> ux_host_class_audio_type = UX_HOST_CLASS_AUDIO_INPUT; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + } + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_AUDIO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right audio device. */ + return(UX_HOST_CLASS_AUDIO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_audio_endpoints_get.c new file mode 100644 index 0000000..e566d59 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_endpoints_get.c @@ -0,0 +1,118 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches for the handle of the isochronous endpoints. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_endpoints_get(UX_HOST_CLASS_AUDIO *audio) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; + + + /* Search the ISO OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < audio -> ux_host_class_audio_streaming_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(audio -> ux_host_class_audio_streaming_interface, endpoint_index, &endpoint); + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is iso and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_ISOCHRONOUS_ENDPOINT)) + { + + /* We have found the bulk endpoint, save it. */ + audio -> ux_host_class_audio_isochronous_endpoint = endpoint; + break; + } + } + } + + /* The isochronous endpoint is mandatory. If we didn't find it, return an error. */ + if (audio -> ux_host_class_audio_isochronous_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_entry.c b/common/usbx_host_classes/src/ux_host_class_audio_entry.c new file mode 100644 index 0000000..528b74d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_entry.c @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the Audio class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* audio device (speaker, microphone ...) on the bus or when the audio */ +/* device is removed. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_audio_activate Activate audio class */ +/* _ux_host_class_audio_deactivate Deactivate audio class */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if ((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + (command -> ux_host_class_command_class == UX_HOST_CLASS_AUDIO_CLASS)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_audio_activate(command); + + /* Return completion status. */ + return(status); + + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_audio_deactivate(command); + + /* Return completion status. */ + return(status); + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_read.c b/common/usbx_host_classes/src/ux_host_class_audio_read.c new file mode 100644 index 0000000..e145970 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_read.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the audio streaming interface. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_audio_transfer_request Audio transfer request */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_read(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *audio_transfer_request) +{ + +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_READ, audio, audio_transfer_request -> ux_host_class_audio_transfer_request_data_pointer, + audio_transfer_request -> ux_host_class_audio_transfer_request_requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* By default the transmission will be successful. */ + status = UX_SUCCESS; + + /* Ensure we have a selected interface that allows isoch transmission. */ + if (audio -> ux_host_class_audio_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize == 0) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_AUDIO_WRONG_INTERFACE); + + /* Return error status. */ + return(UX_HOST_CLASS_AUDIO_WRONG_INTERFACE); + } + + /* Hook the audio transfer request to the chain of transfer requests in the audio instance. */ + audio_transfer_request -> ux_host_class_audio_transfer_request_next_audio_transfer_request = audio -> ux_host_class_audio_tail_transfer_request; + audio -> ux_host_class_audio_tail_transfer_request = audio_transfer_request; + + /* Check if this is the first time we have had a transfer request, if so update the head as well. */ + if (audio -> ux_host_class_audio_head_transfer_request == UX_NULL) + audio -> ux_host_class_audio_head_transfer_request = audio_transfer_request; + + /* For audio in, we read packets at a time, so the transfer request size is the size of the + endpoint. */ + audio_transfer_request -> ux_host_class_audio_transfer_request_requested_length = + audio -> ux_host_class_audio_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + + /* Ask the stack to hook this transfer request to the iso ED. */ + status = _ux_host_class_audio_transfer_request(audio, audio_transfer_request); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_get.c b/common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_get.c new file mode 100644 index 0000000..2a9b040 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_get.c @@ -0,0 +1,395 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_streaming_sampling_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains successive sampling characteristics for the */ +/* audio streaming channel. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_sampling Pointer to audio sampling */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_utility_descriptor_parse Parse the descriptor */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_streaming_sampling_get(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING_CHARACTERISTICS *audio_sampling) +{ + +UINT status; +UCHAR * descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR audio_interface_descriptor; +ULONG total_descriptor_length; +UINT descriptor_length; +UINT descriptor_type; +UINT descriptor_subtype; +UINT interface_found; +ULONG lower_frequency; +ULONG higher_frequency; +UINT specific_frequency_count; +UINT previous_match_found; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_STREAMING_SAMPLING_GET, audio, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Reset the match flag. */ + previous_match_found = UX_FALSE; + + /* Get the descriptor to the entire configuration. */ + descriptor = audio -> ux_host_class_audio_configuration_descriptor; + total_descriptor_length = audio -> ux_host_class_audio_configuration_descriptor_length; + + /* Default is Interface descriptor not yet found. */ + interface_found = UX_FALSE; + + /* Scan the descriptor for the Audio Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to descriptor type. */ + switch(descriptor_type) + { + + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Audio streaming. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_AUDIO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_STREAMING)) + { + + /* Mark we have found it. */ + interface_found = UX_TRUE; + } + else + { + interface_found = UX_FALSE; + } + break; + + + case UX_HOST_CLASS_AUDIO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if ((interface_found == UX_TRUE) && (descriptor_subtype == UX_HOST_CLASS_AUDIO_CS_FORMAT_TYPE)) + { + + /* Parse the FORMAT_TYPE descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_interface_descriptor_structure, + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &audio_interface_descriptor); + + /* This descriptor must refer to a PCM audio type. */ + if (audio_interface_descriptor.bFormatType != UX_HOST_CLASS_AUDIO_FORMAT_TYPE_I) + break; + + /* If this is the first time we ask for a streaming interface characteristics + we return the first we find. */ + if ((audio_sampling -> ux_host_class_audio_sampling_characteristics_channels == 0) && + (audio_sampling -> ux_host_class_audio_sampling_characteristics_resolution == 0)) + { + + audio_sampling -> ux_host_class_audio_sampling_characteristics_channels = audio_interface_descriptor.bNrChannels; + audio_sampling -> ux_host_class_audio_sampling_characteristics_resolution = audio_interface_descriptor.bBitResolution; + + if (audio_interface_descriptor.bSamFreqType == 0) + { + + /* The declaration of frequency is contiguous, so get the minimum and maximum */ + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low = + (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2)) << 16; + + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_high = + (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 3) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 4)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 5)) << 16; + } + else + { + + /* The declaration of the frequency is declared as an array of specific values. + We take the first one here. */ + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low = + (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH ) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1 )) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2 )) << 16; + + /* High and low frequencies are the same here. */ + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_high = audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* We have found the first streaming characteristics. */ + return(UX_SUCCESS); + } + } + else + { + + /* This is not the second time we ask for the characteristics, we need + to find 1st where we left off and look at the next characteristics. */ + if ((audio_sampling -> ux_host_class_audio_sampling_characteristics_channels == audio_interface_descriptor.bNrChannels) && + (audio_sampling -> ux_host_class_audio_sampling_characteristics_resolution == audio_interface_descriptor.bBitResolution)) + { + + if (audio_interface_descriptor.bSamFreqType == 0) + { + + /* The declaration of frequency is contiguous, so get the minimum and maximum. */ + lower_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2)) << 16; + + higher_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 3) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 4)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 5)) << 16; + + if ((audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low == lower_frequency) && + (audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_high == higher_frequency)) + { + + /* We have a match. */ + previous_match_found = UX_TRUE; + break; + } + } + else + { + + /* The declaration of the frequency is declared as an array of specific values. */ + for (specific_frequency_count = 0; specific_frequency_count < audio_interface_descriptor.bSamFreqType; specific_frequency_count++) + { + + lower_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + (specific_frequency_count * 3)) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1 + (specific_frequency_count * 3))) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2 + (specific_frequency_count * 3))) << 16; + + /* Compare the frequency. */ + if (audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low == lower_frequency) + { + + previous_match_found = UX_TRUE; + } + else + { + + /* Now the frequency is different, if we had a match before + this becomes the next characteristics found. */ + if (previous_match_found == UX_TRUE) + { + + /* We return this characteristics. */ + audio_sampling -> ux_host_class_audio_sampling_characteristics_channels = audio_interface_descriptor.bNrChannels; + audio_sampling -> ux_host_class_audio_sampling_characteristics_resolution = audio_interface_descriptor.bBitResolution; + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low = lower_frequency; + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_high = lower_frequency; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + } + } + } + else + { + + /* We come here when the current characteristics does not match + the one given by the application. We need to check if we had found a match + before in which case, this is the one we need to return. */ + if (previous_match_found == UX_TRUE) + { + + /* We return this characteristics. */ + audio_sampling -> ux_host_class_audio_sampling_characteristics_channels = audio_interface_descriptor.bNrChannels; + audio_sampling -> ux_host_class_audio_sampling_characteristics_resolution = audio_interface_descriptor.bBitResolution; + + if (audio_interface_descriptor.bSamFreqType == 0) + { + + /* The declaration of frequency is contiguous, so get the minimum and maximum. */ + lower_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2)) << 16; + + higher_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 3) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 4)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 5)) << 16; + + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low = lower_frequency; + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_high = higher_frequency; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return successful completion. */ + return(UX_SUCCESS); + } + else + { + + lower_frequency = (ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH) | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 1)) << 8 | + ((ULONG) *(descriptor + UX_HOST_CLASS_AUDIO_INTERFACE_DESCRIPTOR_LENGTH + 2)) << 16; + + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_low = lower_frequency; + audio_sampling -> ux_host_class_audio_sampling_characteristics_frequency_high = lower_frequency; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + } + } + } + } + + /* Verify the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_NO_ALTERNATE_SETTING); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_ALTERNATE_SETTING, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We get here when either the report descriptor has a problem or we could + not find the right audio device. */ + return(UX_NO_ALTERNATE_SETTING); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_set.c b/common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_set.c new file mode 100644 index 0000000..a69a932 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_streaming_sampling_set.c @@ -0,0 +1,187 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_streaming_sampling_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function selects the right alternate setting for the audio */ +/* streaming interface based on the sampling values specified by the */ +/* user. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_sampling Pointer to audio sampling */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_audio_alternate_setting_locate */ +/* Locate alternate setting */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* _ux_host_stack_interface_setting_select Select interface */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_streaming_sampling_set(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_SAMPLING *audio_sampling) +{ + +UINT status; +UINT alternate_setting; +ULONG endpoint_index; +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UX_ENDPOINT *endpoint; +UINT streaming_interface; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_STREAMING_SAMPLING_SET, audio, audio_sampling, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS) + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* Find the correct alternate setting for the sampling desired. */ + status = _ux_host_class_audio_alternate_setting_locate(audio, audio_sampling, &alternate_setting); + + /* Did we find the alternate setting? */ + if (status != UX_SUCCESS) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + return(status); + } + + /* We found the alternate setting for the sampling values demanded, now we need + to search its container. */ + configuration = audio -> ux_host_class_audio_streaming_interface -> ux_interface_configuration; + interface = configuration -> ux_configuration_first_interface; + streaming_interface = audio -> ux_host_class_audio_streaming_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Scan all interfaces. */ + while (interface != UX_NULL) + { + + /* We search for both the right interface and alternate setting. */ + if ((interface -> ux_interface_descriptor.bInterfaceNumber == streaming_interface) && + (interface -> ux_interface_descriptor.bAlternateSetting == alternate_setting)) + { + + /* We have found the right interface/alternate setting combination + The stack will select it for us. */ + status = _ux_host_stack_interface_setting_select(interface); + + /* If the alternate setting for the streaming interface could be selected, we memorize it. */ + if (status == UX_SUCCESS) + { + + /* Memorize the interface. */ + audio -> ux_host_class_audio_streaming_interface = interface; + + /* We need to research the isoch endpoint now. */ + for (endpoint_index = 0; endpoint_index < interface -> ux_interface_descriptor.bNumEndpoints; endpoint_index++) + { + + /* Get the list of endpoints one by one. */ + status = _ux_host_stack_interface_endpoint_get(audio -> ux_host_class_audio_streaming_interface, + endpoint_index, &endpoint); + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is ISOCH, regardless of the direction. */ + if ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_ISOCHRONOUS_ENDPOINT) + { + + /* We have found the isoch endpoint, save it. */ + audio -> ux_host_class_audio_isochronous_endpoint = endpoint; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + } + } + } + + /* Move to next interface. */ + interface = interface -> ux_interface_next_interface; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_NO_ALTERNATE_SETTING); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_ALTERNATE_SETTING, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We get here if we could not get the right alternate setting. */ + return(UX_NO_ALTERNATE_SETTING); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_streaming_terminal_get.c b/common/usbx_host_classes/src/ux_host_class_audio_streaming_terminal_get.c new file mode 100644 index 0000000..3146598 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_streaming_terminal_get.c @@ -0,0 +1,191 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_streaming_terminal_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches and retrieves the streaming terminal. This */ +/* terminal is used to further search which of the input\output */ +/* terminals describes if the device is audio IN or audio OUT. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_streaming_terminal_get(UX_HOST_CLASS_AUDIO *audio) +{ + +UCHAR * descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_AUDIO_STREAMING_INTERFACE_DESCRIPTOR streaming_interface_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; +ULONG descriptor_found; + + + /* Get the descriptor to the entire configuration */ + descriptor = audio -> ux_host_class_audio_configuration_descriptor; + total_descriptor_length = audio -> ux_host_class_audio_configuration_descriptor_length; + + /* Default is Interface descriptor not yet found. */ + descriptor_found = UX_FALSE; + audio -> ux_host_class_audio_terminal_link = 0xffffffff; + + /* Scan the descriptor for the Audio Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to the descriptor type. */ + switch(descriptor_type) + { + + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Audio streaming. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_AUDIO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_AUDIO_SUBCLASS_STREAMING) && + (interface_descriptor.bInterfaceNumber == audio -> ux_host_class_audio_streaming_interface -> ux_interface_descriptor.bInterfaceNumber)) + + /* Mark we have found it. */ + descriptor_found = UX_TRUE; + else + + descriptor_found = UX_FALSE; + break; + + + case UX_HOST_CLASS_AUDIO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if (descriptor_found == UX_TRUE) + { + + /* Check the sub type. */ + switch(descriptor_subtype) + { + + + case UX_HOST_CLASS_AUDIO_CS_AS_GENERAL: + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_audio_streaming_interface_descriptor_structure, + UX_HOST_CLASS_AUDIO_STREAMING_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &streaming_interface_descriptor); + + /* Save the terminal link. */ + audio -> ux_host_class_audio_terminal_link = streaming_interface_descriptor.bTerminalLink; + return(UX_SUCCESS); + } + } + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_AUDIO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right audio device. */ + return(UX_HOST_CLASS_AUDIO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_transfer_request.c b/common/usbx_host_classes/src/ux_host_class_audio_transfer_request.c new file mode 100644 index 0000000..5c441e2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_transfer_request.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_transfer_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function submits an isochronous audio transfer request to the */ +/* USBX stack. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_transfer_request(UX_HOST_CLASS_AUDIO *audio, + UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *audio_transfer_request) +{ + +UINT status; +UX_TRANSFER *transfer_request; + + + /* The transfer request is embedded in the application transfer request. */ + transfer_request = &audio_transfer_request -> ux_host_class_audio_transfer_request; + + /* Select the direction. We do this by taking the endpoint direction. */ + transfer_request -> ux_transfer_request_type = audio -> ux_host_class_audio_isochronous_endpoint -> + ux_endpoint_descriptor.bEndpointAddress&UX_REQUEST_DIRECTION; + + /* Fill the transfer request with all the required fields. */ + transfer_request -> ux_transfer_request_endpoint = audio -> ux_host_class_audio_isochronous_endpoint; + transfer_request -> ux_transfer_request_data_pointer = audio_transfer_request -> ux_host_class_audio_transfer_request_data_pointer; + transfer_request -> ux_transfer_request_requested_length = audio_transfer_request -> ux_host_class_audio_transfer_request_requested_length; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_audio_transfer_request_completed; + transfer_request -> ux_transfer_request_class_instance = audio; + + /* We memorize the application transfer request in the local transfer request. */ + transfer_request -> ux_transfer_request_user_specific = (VOID *) audio_transfer_request; + + /* Transfer the transfer request. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_transfer_request_completed.c b/common/usbx_host_classes/src/ux_host_class_audio_transfer_request_completed.c new file mode 100644 index 0000000..b71bf7a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_transfer_request_completed.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_transfer_request_completed PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function receives a completion call back on a isoch transfer */ +/* request. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_audio_transfer_request_completion_function) */ +/* Transfer request completion */ +/* */ +/* CALLED BY */ +/* */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_audio_transfer_request_completed(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *audio_transfer_request; + + + /* Get the pointer to the audio specific transfer request, by nature of the lined transfer requests, + the corresponding transfer request has to be the head transfer request in the audio instance. */ + audio_transfer_request = (UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *) transfer_request -> ux_transfer_request_user_specific; + + /* Do a sanity check on the transfer request, if NULL something is wrong. */ + if (audio_transfer_request == UX_NULL) + return; + + /* The caller's transfer request needs to be updated. */ + audio_transfer_request -> ux_host_class_audio_transfer_request_actual_length = transfer_request -> ux_transfer_request_actual_length; + audio_transfer_request -> ux_host_class_audio_transfer_request_completion_code = transfer_request -> ux_transfer_request_completion_code; + + /* Call the completion routine. */ + audio_transfer_request -> ux_host_class_audio_transfer_request_completion_function(audio_transfer_request); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_audio_write.c b/common/usbx_host_classes/src/ux_host_class_audio_write.c new file mode 100644 index 0000000..ef197c5 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_audio_write.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Audio Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_audio.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_audio_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the audio streaming interface. */ +/* */ +/* INPUT */ +/* */ +/* audio Pointer to audio class */ +/* audio_transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_audio_transfer_request Start audio transfer request */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Audio Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_audio_write(UX_HOST_CLASS_AUDIO *audio, UX_HOST_CLASS_AUDIO_TRANSFER_REQUEST *audio_transfer_request) +{ + +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_AUDIO_WRITE, audio, audio_transfer_request -> ux_host_class_audio_transfer_request_data_pointer, + audio_transfer_request -> ux_host_class_audio_transfer_request_requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_audio_name, (VOID *) audio) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, audio, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&audio -> ux_host_class_audio_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* By default the transmission will be successful. */ + status = UX_SUCCESS; + + /* Ensure we have a selected interface that allows isoch transmission. */ + if (audio -> ux_host_class_audio_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize == 0) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_AUDIO_WRONG_INTERFACE); + + /* Return error status. */ + return(UX_HOST_CLASS_AUDIO_WRONG_INTERFACE); + } + + /* Hook the audio transfer request to the chain of transfer requests in the audio instance. + We hook the transfer request to the tail transfer request. */ + audio_transfer_request -> ux_host_class_audio_transfer_request_next_audio_transfer_request = audio -> ux_host_class_audio_tail_transfer_request; + audio -> ux_host_class_audio_tail_transfer_request = audio_transfer_request; + + /* Check if this is the first time we have a transfer request, if so update the head as well. */ + if (audio -> ux_host_class_audio_head_transfer_request == UX_NULL) + audio -> ux_host_class_audio_head_transfer_request = audio_transfer_request; + + /* Ask the stack to hook this transfer request to the iso ED. */ + status = _ux_host_class_audio_transfer_request(audio, audio_transfer_request); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&audio -> ux_host_class_audio_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_activate.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_activate.c new file mode 100644 index 0000000..add46c5 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_activate.c @@ -0,0 +1,242 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the ACM instance, configure the device ... */ +/* */ +/* INPUT */ +/* */ +/* command ACM class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_cdc_acm_configure Configure cdc_acm class */ +/* _ux_host_class_cdc_acm_endpoints_get Get endpoints of cdc_acm */ +/* _ux_host_class_cdc_acm_ioctl IOCTL function for ACM */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort transfer */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_semaphore_create Create cdc_acm semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_delay_ms Delay */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_acm_entry Entry of cdc_acm class */ +/* _ux_utility_delay_ms Delay ms */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_CDC_ACM *cdc_acm; +UX_HOST_CLASS_CDC_ACM_LINE_CODING line_coding; +UX_HOST_CLASS_CDC_ACM_LINE_STATE line_state; +UINT status; + + /* The CDC ACM class is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + cdc_acm = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, sizeof(UX_HOST_CLASS_CDC_ACM)); + + /* Instance creation fail. */ + if (cdc_acm == UX_NULL) + + /* Memory allocation fail. */ + return(UX_MEMORY_INSUFFICIENT); + + /* Create the semaphore to protect 2 threads from accessing the same acm instance. */ + status = _ux_utility_semaphore_create(&cdc_acm -> ux_host_class_cdc_acm_semaphore, "ux_host_class_cdc_acm_semaphore", 1); + if (status != UX_SUCCESS) + { + + /* Free instance memory. */ + _ux_utility_memory_free(cdc_acm); + + /* Semaphore creation error. */ + return(UX_SEMAPHORE_ERROR); + } + + /* Store the class container into this instance. */ + cdc_acm -> ux_host_class_cdc_acm_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the cdc_acm class instance. */ + cdc_acm -> ux_host_class_cdc_acm_interface = interface; + + /* Store the device container into the cdc_acm class instance. */ + cdc_acm -> ux_host_class_cdc_acm_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) cdc_acm; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm); + + /* Configure the cdc_acm. */ + status = _ux_host_class_cdc_acm_configure(cdc_acm); + + /* If we are done success, go on to get endpoints. */ + if (status == UX_SUCCESS) + + /* Get the cdc_acm endpoint(s). Depending on the interface type, we will need to search for + Bulk Out and Bulk In endpoints and the optional interrupt endpoint. */ + status = _ux_host_class_cdc_acm_endpoints_get(cdc_acm); + + /* If we are done success, go on to mount interface. */ + if (status == UX_SUCCESS) + { + /* Mark the cdc_acm as mounting now. Both interfaces need to be mounting. */ + cdc_acm -> ux_host_class_cdc_acm_state = UX_HOST_CLASS_INSTANCE_MOUNTING; + + /* If we have the Control Class, we have to configure the speed, parity ... */ + if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_CONTROL_CLASS) + { + + /* We need to wait for some device to settle. The Radicom USB Modem is an example of + these device who fail the first Set_Line_Coding command if sent too quickly. + The timing does not have to be precise so we use the thread sleep function. + The default sleep value is 1 seconds. */ + _ux_utility_delay_ms(UX_HOST_CLASS_CDC_ACM_DEVICE_INIT_DELAY); + + /* Do a GET_LINE_CODING first. */ + status = _ux_host_class_cdc_acm_ioctl(cdc_acm, UX_HOST_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING, (VOID *) &line_coding); + + /* If we are done success, go on. */ + if (status == UX_SUCCESS) + { + + /* Set the default values to the device, first line coding. */ + line_coding.ux_host_class_cdc_acm_line_coding_dter = UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_RATE; + line_coding.ux_host_class_cdc_acm_line_coding_stop_bit = UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT_0; + line_coding.ux_host_class_cdc_acm_line_coding_parity = UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY_NONE; + line_coding.ux_host_class_cdc_acm_line_coding_data_bits = UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_DATA_BIT; + status = _ux_host_class_cdc_acm_ioctl(cdc_acm, UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING, (VOID *) &line_coding); + } + + /* If we are done success, go on. */ + if (status == UX_SUCCESS) + { + + /* Set the default values to the device, line state. */ + line_state.ux_host_class_cdc_acm_line_state_rts = 1; + line_state.ux_host_class_cdc_acm_line_state_dtr = 1; + status = _ux_host_class_cdc_acm_ioctl(cdc_acm, UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE, (VOID *) &line_state); + } + + /* If we are done success, go on. */ + if (status == UX_SUCCESS) + { + + /* Get the capabilities of the device. We need to know if the commands are multiplexed over the comm + interface or the data interface. */ + status = _ux_host_class_cdc_acm_capabilities_get(cdc_acm); + } + } + + /* If we are done success, go on. */ + if (status == UX_SUCCESS) + { + /* Mark the cdc_acm as live now. Both interfaces need to be live. */ + cdc_acm -> ux_host_class_cdc_acm_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_ACTIVATE, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, cdc_acm, 0, 0, 0) + + /* We are done success. */ + return(UX_SUCCESS); + } + } + + /* On error case, it's possible data buffer allocated for interrupt endpoint and transfer started, stop and free it. */ + if (cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint && + cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer) + { + + /* The first transfer request has already been initiated. Abort it. */ + _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint); + + /* Free the memory for the data pointer. */ + _ux_utility_memory_free(cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer); + } + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&cdc_acm -> ux_host_class_cdc_acm_semaphore); + + /* Unmount instance. */ + interface -> ux_interface_class_instance = UX_NULL; + + /* Free instance. */ + _ux_utility_memory_free(cdc_acm); + + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_capabilities_get.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_capabilities_get.c new file mode 100644 index 0000000..0b614a1 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_capabilities_get.c @@ -0,0 +1,260 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_capabilities_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the entire cdc_acm configuration descriptors. */ +/* This is needed because the cdc_acm class needs to know if commands */ +/* are routed through the comm interface or the data class. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_acm_activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_capabilities_get(UX_HOST_CLASS_CDC_ACM *cdc_acm) +{ + +UCHAR *descriptor; +UCHAR *saved_descriptor; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UX_CONFIGURATION configuration; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UINT status; +ULONG total_descriptor_length; +UCHAR descriptor_length; +UCHAR descriptor_type; +UCHAR descriptor_subtype; +ULONG interface_found; + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &cdc_acm -> ux_host_class_cdc_acm_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save this descriptor address since we need to free it. */ + saved_descriptor = descriptor; + + /* Create a transfer request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_CONFIGURATION_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_CONFIGURATION_DESCRIPTOR_ITEM << 8; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH)) + { + + /* The descriptor is in a packed format, parse it locally. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration.ux_configuration_descriptor); + + /* Now we have the configuration descriptor which will tell us how many + bytes there are in the entire descriptor. */ + total_descriptor_length = configuration.ux_configuration_descriptor.wTotalLength; + + /* Free the previous descriptor. */ + _ux_utility_memory_free(descriptor); + + /* Allocate enough memory to read all descriptors attached + to this configuration. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, total_descriptor_length); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save this descriptor address since we need to free it. */ + saved_descriptor = descriptor; + + /* Set the length we need to retrieve. */ + transfer_request -> ux_transfer_request_requested_length = total_descriptor_length; + + /* And reprogram the descriptor buffer address. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == total_descriptor_length)) + { + + /* Default is Interface descriptor not yet found. */ + interface_found = UX_FALSE; + + /* Scan the descriptor for the CDC Comm interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We can free the resource now. */ + _ux_utility_memory_free(saved_descriptor); + + /* Descriptor is corrupted. */ + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to descriptor type. */ + switch (descriptor_type) + { + + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Audio streaming. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_CONTROL_CLASS) && + ((interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_CDC_ACM_SUBCLASS) || + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_CDC_DLC_SUBCLASS))) + { + + /* Mark we have found it. */ + interface_found = UX_TRUE; + + } + else + { + + /* Haven't found it. */ + interface_found = UX_FALSE; + } + break; + + + case UX_HOST_CLASS_CDC_ACM_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if ((interface_found == UX_TRUE) && (descriptor_subtype == UX_HOST_CLASS_CDC_ACM_CALL_MANAGEMENT_DESCRIPTOR)) + { + + /* Retrieve the bmCapabilities field which indicates how ACM commands are sent to the device. */ + cdc_acm -> ux_host_class_cdc_acm_capabilities = *(descriptor + UX_HOST_CLASS_CDC_ACM_CALL_MANAGEMENT_CAPABILITIES); + + + } + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We can free the resource now. */ + _ux_utility_memory_free(saved_descriptor); + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* We can free the resource now. */ + _ux_utility_memory_free(saved_descriptor); + + return(UX_SUCCESS); + } + } + + /* Free all used resources. */ + _ux_utility_memory_free(saved_descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_command.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_command.c new file mode 100644 index 0000000..b53be8f --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_command.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_command PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a command to the ACM device. The command */ +/* can be one of the following : */ +/* SET_CONTROL */ +/* SET_LINE */ +/* SEND_BREAK */ +/* */ +/* */ +/* INPUT */ +/* */ +/* acm Pointer to acm class */ +/* command command value */ +/* value value to be sent in the */ +/* command request */ +/* data_buffer buffer to be sent */ +/* data_length length of the buffer to send */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_command(UX_HOST_CLASS_CDC_ACM *cdc_acm, ULONG command, + ULONG value, UCHAR *data_buffer, ULONG data_length) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +ULONG request_direction; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &cdc_acm -> ux_host_class_cdc_acm_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Check the direction of the command. */ + switch (command) + { + + case UX_HOST_CLASS_CDC_ACM_REQ_SEND_ENCAPSULATED_COMMAND : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_COMM_FEATURE : + case UX_HOST_CLASS_CDC_ACM_REQ_CLEAR_COMM_FEATURE : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_AUX_LINE_STATE : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_HOOK_STATE : + case UX_HOST_CLASS_CDC_ACM_REQ_PULSE_SETUP : + case UX_HOST_CLASS_CDC_ACM_REQ_SEND_PULSE : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_PUSLE_TIME : + case UX_HOST_CLASS_CDC_ACM_REQ_RING_AUX_JACK : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_CODING : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_STATE : + case UX_HOST_CLASS_CDC_ACM_REQ_SEND_BREAK : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_RINGER_PARMS : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_OPERATION_PARMS : + case UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_PARMS : + + /* Direction is out */ + request_direction = UX_REQUEST_OUT; + break; + + + case UX_HOST_CLASS_CDC_ACM_REQ_GET_ENCAPSULATED_COMMAND : + case UX_HOST_CLASS_CDC_ACM_REQ_GET_COMM_FEATURE : + case UX_HOST_CLASS_CDC_ACM_REQ_GET_LINE_CODING : + case UX_HOST_CLASS_CDC_ACM_REQ_GET_RINGER_PARMS : + case UX_HOST_CLASS_CDC_ACM_REQ_GET_OPERATION_PARMS : + case UX_HOST_CLASS_CDC_ACM_REQ_GET_LINE_PARMS : + + /* Direction is in */ + request_direction = UX_REQUEST_IN; + break; + + + default : + + return(UX_ERROR); + + } + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&cdc_acm -> ux_host_class_cdc_acm_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer_request for the request. */ + transfer_request -> ux_transfer_request_data_pointer = data_buffer; + transfer_request -> ux_transfer_request_requested_length = data_length; + transfer_request -> ux_transfer_request_function = command; + transfer_request -> ux_transfer_request_type = request_direction | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = value; + transfer_request -> ux_transfer_request_index = cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_configure.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_configure.c new file mode 100644 index 0000000..81191a6 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_configure.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* cdc_acm. Once the cdc_acm is configured, its interface will be */ +/* activated. The bulk endpoints (1 IN, 1 OUT ) and the optional */ +/* interrupt endpoint are enumerated. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_acm_activate Data Pump class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_configure(UX_HOST_CLASS_CDC_ACM *cdc_acm) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (cdc_acm -> ux_host_class_cdc_acm_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A cdc_acm normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(cdc_acm -> ux_host_class_cdc_acm_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, cdc_acm -> ux_host_class_cdc_acm_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the cdc_acm power source and check the parent power source for + incompatible connections. */ + if (cdc_acm -> ux_host_class_cdc_acm_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = cdc_acm -> ux_host_class_cdc_acm_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root cdc_acm and we don't have to worry + if the parent is not the root cdc_acm, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the cdc_acm default alternate setting for the cdc_acm interface is + active and the interrupt endpoint is now enabled. We have to memorize the first interface since + the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &cdc_acm -> ux_host_class_cdc_acm_interface); + if (status != UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_class_instance = (VOID *) cdc_acm; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_deactivate.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_deactivate.c new file mode 100644 index 0000000..4ea0de0 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_deactivate.c @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the cdc_acm has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* and optional interrupt pipes will be destroyed and the instance */ +/* removed. */ +/* */ +/* INPUT */ +/* */ +/* command CDC ACM class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_acm_entry Entry of cdc_acm class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_CDC_ACM *cdc_acm; +UX_TRANSFER *transfer_request; +UINT status; + + /* Get the instance for this class. */ + cdc_acm = (UX_HOST_CLASS_CDC_ACM *) command -> ux_host_class_command_instance; + + /* The cdc_acm is being shut down. */ + cdc_acm -> ux_host_class_cdc_acm_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&cdc_acm -> ux_host_class_cdc_acm_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* If we have the Control Class, we only unmount the interrupt endpoint if it is active. */ + if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_CONTROL_CLASS) + { + + /* If the interrupt endpoint is defined, clean any pending transfer. */ + if (cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint != UX_NULL) + { + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint -> ux_endpoint_transfer_request; + + /* And abort any transfer. */ + _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint); + + /* Free data buffer for the interrupt transfer. */ + _ux_utility_memory_free(transfer_request -> ux_transfer_request_data_pointer); + } + } + else + { + + /* We come to this point when the device has been extracted and this is the data class. + So there may have been a transaction being scheduled. + We make sure the transaction has been completed by the controller driver. + When the device is extracted, the controller tries multiple times the transaction and retires it + with a DEVICE_NOT_RESPONDING error code. + + First we take care of endpoint IN. */ + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint); + + /* Then endpoint OUT. */ + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint); + + } + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&cdc_acm -> ux_host_class_cdc_acm_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm); + } + + /* Free the cdc_acm instance memory. */ + _ux_utility_memory_free(cdc_acm); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_DEACTIVATE, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(cdc_acm); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_endpoints_get.c new file mode 100644 index 0000000..69fb96d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_endpoints_get.c @@ -0,0 +1,212 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function distinguishes for either the Data or Control Class. */ +/* For the data class, we mount the bulk in and bulk out endpoints. */ +/* For the control class, we mount the optional interrupt endpoint. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_acm_activate Activate cdc_acm class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_endpoints_get(UX_HOST_CLASS_CDC_ACM *cdc_acm) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; +UX_TRANSFER *transfer_request; + + + /* Check what interface we are mounting. */ + if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_DATA_CLASS) + { + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(cdc_acm -> ux_host_class_cdc_acm_interface, endpoint_index, &endpoint); + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* We have found the bulk endpoint, save it. */ + cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint = endpoint; + + /* If found all, we break. */ + if (cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint) + break; + } + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the bulk endpoint, save it. */ + cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint = endpoint; + + /* If found all, we break. */ + if (cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint) + break; + } + } + + /* The both bulk endpoints are mandatory. */ + if (cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint == UX_NULL || + cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + } + else + { + /* Search the Interrupt endpoint. It is attached to the interface container of the control interface. It is not mandatory. */ + for (endpoint_index = 0; endpoint_index < cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(cdc_acm -> ux_host_class_cdc_acm_interface, endpoint_index, &endpoint); + + /* Check if endpoint is Interrupt and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the interrupt endpoint, save it. */ + cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint = endpoint; + + /* The endpoint is correct, Fill in the transfer request with the length requested for this endpoint. */ + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint -> ux_endpoint_transfer_request; + transfer_request -> ux_transfer_request_requested_length = cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* The direction is always IN for the CDC interrupt endpoint. */ + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) cdc_acm; + + /* Interrupt transactions have a completion routine. */ + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_cdc_acm_transfer_request_completed; + + /* Obtain a buffer for this transaction. The buffer will always be reused. */ + transfer_request -> ux_transfer_request_data_pointer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + transfer_request -> ux_transfer_request_requested_length); + + /* If the endpoint is available and we have memory, we start the interrupt endpoint. */ + if (transfer_request -> ux_transfer_request_data_pointer != UX_NULL) + { + + /* The transfer on the interrupt endpoint can be started. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check error, if endpoint interrupt IN transfer not successful, do not proceed. */ + if (status != UX_SUCCESS) + + /* Error, do not proceed. */ + return(status); + } + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We must return an error. */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + break; + } + } + } + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_entry.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_entry.c new file mode 100644 index 0000000..b74f88f --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_entry.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Acm Cdc Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the cdc_acm class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* cdc_acm on the bus or when the USB cdc_acm is removed. */ +/* */ +/* A CDC class can have multiple interfaces, one for Control and one */ +/* for Data. Here we filter for the Communication Class with ACM */ +/* subclass and the Communication Data Class. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* command Acm Cdc class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_cdc_acm_activate Activate cdc_acm class */ +/* _ux_host_class_cdc_acm_deactivate Deactivate cdc_acm class */ +/* */ +/* CALLED BY */ +/* */ +/* Acm Cdc Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + ((command -> ux_host_class_command_class == UX_HOST_CLASS_CDC_DATA_CLASS) || + ((command -> ux_host_class_command_class == UX_HOST_CLASS_CDC_CONTROL_CLASS) && + (command -> ux_host_class_command_subclass == UX_HOST_CLASS_CDC_ACM_SUBCLASS)) || + ((command -> ux_host_class_command_class == UX_HOST_CLASS_CDC_CONTROL_CLASS) && + (command -> ux_host_class_command_subclass == UX_HOST_CLASS_CDC_DLC_SUBCLASS)))) + { + /* Check for IAD presence. */ + if ((command -> ux_host_class_command_iad_class == 0) && (command -> ux_host_class_command_iad_subclass == 0)) + + /* No IAD, we accept this class. */ + return(UX_SUCCESS); + + else + { + + if ((command -> ux_host_class_command_iad_class == UX_HOST_CLASS_CDC_CONTROL_CLASS) && + (command -> ux_host_class_command_iad_subclass == UX_HOST_CLASS_CDC_ACM_SUBCLASS)) + + /* There is an IAD and this is for CDC-ACM. */ + return(UX_SUCCESS); + + else + + /* The IAD does not match with CDC-ACM. */ + return(UX_NO_CLASS_MATCH); + } + } + + else + + /* No match. */ + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_cdc_acm_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_cdc_acm_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_ioctl.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_ioctl.c new file mode 100644 index 0000000..ea54824 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_ioctl.c @@ -0,0 +1,294 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the ioctl entry point for the application to */ +/* configure the ACM device. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* acm Pointer to acm class */ +/* ioctl_function ioctl function */ +/* parameter pointer to structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* _ux_host_class_cdc_acm_command Send command to acm device */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_ioctl(UX_HOST_CLASS_CDC_ACM *cdc_acm, ULONG ioctl_function, + VOID *parameter) +{ + +UINT status; +UCHAR *data_buffer; +UX_HOST_CLASS_CDC_ACM_LINE_CODING *line_coding; +UX_HOST_CLASS_CDC_ACM_LINE_STATE *line_state; +VOID (*callback_function) (struct UX_HOST_CLASS_CDC_ACM_STRUCT *, ULONG, ULONG ); +ULONG value; + + /* Ensure the instance is valid. */ + if ((cdc_acm -> ux_host_class_cdc_acm_state != UX_HOST_CLASS_INSTANCE_LIVE) && + (cdc_acm -> ux_host_class_cdc_acm_state != UX_HOST_CLASS_INSTANCE_MOUNTING)) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* The command request will tell us what we need to do here. */ + switch (ioctl_function) + { + + case UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING, cdc_acm, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Allocate some cache safe memory for the control command. */ + data_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_CDC_ACM_LINE_CODING_LENGTH); + + /* Check if error. Return with error if no memory could be allocated. */ + if (data_buffer == UX_NULL) + + /* Do not proceed. Set error code. */ + status = UX_MEMORY_INSUFFICIENT; + else + { + + /* Build the buffer from the calling parameter. Cast the calling parameter. */ + line_coding = (UX_HOST_CLASS_CDC_ACM_LINE_CODING *) parameter; + + /* Put the data rate. */ + _ux_utility_long_put(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_RATE, + line_coding -> ux_host_class_cdc_acm_line_coding_dter); + + /* Then the stop bit. */ + *(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT) = + (UCHAR) line_coding -> ux_host_class_cdc_acm_line_coding_stop_bit; + + /* Then the parity. */ + *(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY) = + (UCHAR) line_coding -> ux_host_class_cdc_acm_line_coding_parity; + + /* Finally the data bits. */ + *(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_DATA_BIT) = + (UCHAR) line_coding -> ux_host_class_cdc_acm_line_coding_data_bits; + + /* Send the command to the device. */ + status = _ux_host_class_cdc_acm_command(cdc_acm, UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_CODING, + 0, data_buffer, UX_HOST_CLASS_CDC_ACM_LINE_CODING_LENGTH); + + /* We free the resources allocated no matter what. */ + _ux_utility_memory_free(data_buffer); + } + break; + + case UX_HOST_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING, cdc_acm, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Allocate some cache safe memory for the control command. */ + data_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_CDC_ACM_LINE_CODING_LENGTH); + + /* Check if error. Return with error if no memory could be allocated. */ + if (data_buffer == UX_NULL) + + /* Do not proceed. Set error code. */ + status = UX_MEMORY_INSUFFICIENT; + else + { + + /* Send the command to the device. */ + status = _ux_host_class_cdc_acm_command(cdc_acm, UX_HOST_CLASS_CDC_ACM_REQ_GET_LINE_CODING, + 0, data_buffer, UX_HOST_CLASS_CDC_ACM_LINE_CODING_LENGTH); + + /* Fill in the calling buffer if the result is successful. */ + if (status == UX_SUCCESS) + { + + /* Build the buffer from the calling parameter. Cast the calling parameter. */ + line_coding = (UX_HOST_CLASS_CDC_ACM_LINE_CODING *) parameter; + + /* Get the data rate. */ + line_coding -> ux_host_class_cdc_acm_line_coding_dter = _ux_utility_long_get(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_RATE); + + /* Then the stop bit. */ + line_coding -> ux_host_class_cdc_acm_line_coding_stop_bit = + (ULONG) *(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_STOP_BIT); + + /* Then the parity. */ + line_coding -> ux_host_class_cdc_acm_line_coding_parity = + (ULONG) *(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_PARITY); + + /* Finally the data bits. */ + line_coding -> ux_host_class_cdc_acm_line_coding_data_bits = + (ULONG) *(data_buffer + UX_HOST_CLASS_CDC_ACM_LINE_CODING_DATA_BIT); + } + + /* We free the resources allocated no matter what. */ + _ux_utility_memory_free(data_buffer); + } + break; + + case UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE, cdc_acm, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Cast the calling parameter. */ + line_state = (UX_HOST_CLASS_CDC_ACM_LINE_STATE *) parameter; + + /* Build the value field. */ + value = (line_state -> ux_host_class_cdc_acm_line_state_dtr | + (line_state -> ux_host_class_cdc_acm_line_state_rts << 1)); + + /* Send the command to the device. */ + status = _ux_host_class_cdc_acm_command(cdc_acm, UX_HOST_CLASS_CDC_ACM_REQ_SET_LINE_STATE, + value, UX_NULL,0); + break; + + case UX_HOST_CLASS_CDC_ACM_IOCTL_SEND_BREAK : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_SEND_BREAK, cdc_acm, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Build the value field. */ + value = *((ULONG *) parameter); + + /* Send the command to the device. */ + status = _ux_host_class_cdc_acm_command(cdc_acm, UX_HOST_CLASS_CDC_ACM_REQ_SEND_BREAK, + value, UX_NULL,0); + break; + + + + case UX_HOST_CLASS_CDC_ACM_IOCTL_ABORT_IN_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_ABORT_IN_PIPE, cdc_acm, cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_CDC_ACM_IOCTL_ABORT_OUT_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_ABORT_OUT_PIPE, cdc_acm, cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_CDC_ACM_IOCTL_NOTIFICATION_CALLBACK : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_NOTIFICATION_CALLBACK, cdc_acm, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Register a callback when the line state has changed. */ + callback_function = ((VOID (*) (struct UX_HOST_CLASS_CDC_ACM_STRUCT *, ULONG, ULONG )) (ALIGN_TYPE)parameter); + cdc_acm -> ux_host_class_cdc_acm_device_status_change_callback = callback_function; + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_CDC_ACM_IOCTL_GET_DEVICE_STATUS : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_IOCTL_GET_DEVICE_STATUS, cdc_acm, cdc_acm -> ux_host_class_cdc_acm_device_state, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Return the device status. */ + *((ULONG *) parameter) = cdc_acm -> ux_host_class_cdc_acm_device_state; + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_read.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_read.c new file mode 100644 index 0000000..5f62be2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_read.c @@ -0,0 +1,200 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** ACM CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the cdc_acm interface. The call is */ +/* blocking and only returns when there is either an error or when */ +/* the transfer is complete. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* data_pointer Pointer to buffer */ +/* requested_length Requested data read */ +/* actual_length Actual data read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_read (UX_HOST_CLASS_CDC_ACM *cdc_acm, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_READ, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (cdc_acm -> ux_host_class_cdc_acm_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* As further protection, we must ensure this instance of the interface is the data interface and not + the control interface ! */ + if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass != UX_HOST_CLASS_CDC_DATA_CLASS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by resetting the actual length of the transfer to zero. */ + *actual_length = 0; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk in endpoint until either the transfer is + completed or until there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer_request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_HOST_CLASS_CDC_ACM_CLASS_TRANSFER_TIMEOUT); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer request in case some data was actually received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked. */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually received and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to receive. */ + requested_length -= transfer_request_length; + } + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_callback.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_callback.c new file mode 100644 index 0000000..dbf6477 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_callback.c @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** ACM CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_reception_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback from the USBX transfer functions, */ +/* it is called when a full or partial transfer has been done for a */ +/* bulk in transfer. It calls back the application. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_cdc_acm_reception_callback (UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_CDC_ACM *cdc_acm; +UX_HOST_CLASS_CDC_ACM_RECEPTION *cdc_acm_reception; + + /* Get the class instance for this transfer request. */ + cdc_acm = (UX_HOST_CLASS_CDC_ACM *) transfer_request -> ux_transfer_request_class_instance; + + /* Get the pointer to the acm reception structure. */ + cdc_acm_reception = cdc_acm -> ux_host_class_cdc_acm_reception; + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* The reception is stopped. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_state = UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STOPPED; + + /* We do not proceed. */ + return; + + } + + /* Move to the next reception buffer. Check if we are at the end of the application buffer. */ + if (cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_head + cdc_acm_reception -> ux_host_class_cdc_acm_reception_block_size >= + cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_buffer + cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_buffer_size) + + /* Program the head to be at the beginning of the application buffer. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_head = cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_buffer; + + else + + /* Program the head to be after the current buffer. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_head += cdc_acm_reception -> ux_host_class_cdc_acm_reception_block_size; + + /* OVERFLOW check: if the head reaches the tail buffer that contains reception data */ + if (cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_tail == cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_head) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_BUFFER_OVERFLOW, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We have an overflow. We cannot continue. Report to the application. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_callback(cdc_acm, UX_BUFFER_OVERFLOW, UX_NULL, 0); + + /* And stop the transfer in progress flag. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_state = UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STOPPED; + + return; + } + + /* We need to report this transfer to the application. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_callback(cdc_acm, + transfer_request -> ux_transfer_request_completion_code, + transfer_request -> ux_transfer_request_data_pointer, + transfer_request -> ux_transfer_request_actual_length); + + /* Set data pointer to the next reception buffer. */ + transfer_request -> ux_transfer_request_data_pointer = cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_head; + + /* Arm another transfer. */ + _ux_host_stack_transfer_request(transfer_request); + + /* There is no status to be reported back to the stack. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_start.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_start.c new file mode 100644 index 0000000..c9ed01b --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_start.c @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** ACM CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_reception_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts a reception with the ACM modem. This mechanism */ +/* allows for non blocking calls based on a packet orientated round */ +/* robbin buffer. When a packet is fully or partially received, an */ +/* application callback function is invoked and a new transfer request */ +/* is rescheduled. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* cdc_acm_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_reception_start (UX_HOST_CLASS_CDC_ACM *cdc_acm, + UX_HOST_CLASS_CDC_ACM_RECEPTION *cdc_acm_reception) +{ + +UX_TRANSFER *transfer_request; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_RECEPTION_START, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (cdc_acm -> ux_host_class_cdc_acm_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* As further protection, we must ensure this instance of the interface is the data interface and not + the control interface ! */ + if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass != UX_HOST_CLASS_CDC_DATA_CLASS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by aligning the head and tail of buffers to the same address supplied by the application. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_head = cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_buffer; + cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_tail = cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_buffer; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) cdc_acm; + transfer_request -> ux_transfer_request_data_pointer = cdc_acm_reception -> ux_host_class_cdc_acm_reception_data_head; + transfer_request -> ux_transfer_request_requested_length = cdc_acm_reception -> ux_host_class_cdc_acm_reception_block_size; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_cdc_acm_reception_callback; + + /* Save the acm reception structure in the acm structure. */ + cdc_acm -> ux_host_class_cdc_acm_reception = cdc_acm_reception; + + /* And declare we have a transfer in progress. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_state = UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STARTED; + + /* Arm a first transfer on the bulk in endpoint. There is a callback to this function so we return to the caller + right away. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* We do not know if the first transfer was successful yet. If the status is not OK, we need to stop the transfer + in progress flag. */ + if (status != UX_SUCCESS) + cdc_acm_reception -> ux_host_class_cdc_acm_reception_state = UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STOPPED; + + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_stop.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_stop.c new file mode 100644 index 0000000..73af01a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_reception_stop.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** ACM CDC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_reception_stop PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function stops background reception previously started by */ +/* ux_host_class_cdc_acm_reception_start. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* cdc_acm_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_reception_stop(UX_HOST_CLASS_CDC_ACM *cdc_acm, + UX_HOST_CLASS_CDC_ACM_RECEPTION *cdc_acm_reception) +{ + +UX_TRANSFER *transfer_request; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_RECEPTION_STOP, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (cdc_acm -> ux_host_class_cdc_acm_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* As further protection, we must ensure this instance of the interface is the data interface and not + the control interface ! */ + if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass != UX_HOST_CLASS_CDC_DATA_CLASS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Check if we do have transfers for this application. If none, nothing to do. */ + if (cdc_acm_reception -> ux_host_class_cdc_acm_reception_state == UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STOPPED) + return(UX_SUCCESS); + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint); + + /* Declare the reception stopped. */ + cdc_acm_reception -> ux_host_class_cdc_acm_reception_state = UX_HOST_CLASS_CDC_ACM_RECEPTION_STATE_STOPPED; + + /* Obtain pointer to transfer request. */ + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Reset the completion callback function. */ + transfer_request -> ux_transfer_request_completion_function = UX_NULL; + + /* Clear semaphore counts that were (incorrectly) increased during each transfer + completion. */ + while (transfer_request -> ux_transfer_request_semaphore.tx_semaphore_count) + _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, 0); + + /* This function never really fails. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_transfer_request_completed.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_transfer_request_completed.c new file mode 100644 index 0000000..3c54429 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_transfer_request_completed.c @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC_ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_transfer_request_completed PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the completion thread when a transfer */ +/* request has been completed either because the transfer is */ +/* successful or there was an error. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_short_get Get 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USBX stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_cdc_acm_transfer_request_completed(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_CDC_ACM *cdc_acm; +ULONG notification_type; +ULONG notification_value; + + + /* Get the class instance for this transfer request. */ + cdc_acm = (UX_HOST_CLASS_CDC_ACM *) transfer_request -> ux_transfer_request_class_instance; + + /* Check the state of the transfer. If there is an error, we do not proceed with this notification. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + + /* We do not proceed. */ + return; + + /* Increment the notification count. */ + cdc_acm -> ux_host_class_cdc_acm_notification_count++; + + /* Get the notification. */ + notification_type = (ULONG) *(transfer_request -> ux_transfer_request_data_pointer + UX_HOST_CLASS_CDC_ACM_NPF_NOTIFICATION_TYPE); + + /* And the value. */ + notification_value = (ULONG) _ux_utility_short_get(transfer_request -> ux_transfer_request_data_pointer + UX_HOST_CLASS_CDC_ACM_NPF_VALUE); + + /* If there is a callback present, invoke it. */ + if (cdc_acm -> ux_host_class_cdc_acm_device_status_change_callback != UX_NULL) + + /* There is a callback, send the status change to the application. */ + cdc_acm -> ux_host_class_cdc_acm_device_status_change_callback(cdc_acm, notification_type, notification_value); + + /* Reactivate the CDC_ACM interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_acm_write.c b/common/usbx_host_classes/src/ux_host_class_cdc_acm_write.c new file mode 100644 index 0000000..ba2e7fa --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_acm_write.c @@ -0,0 +1,200 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ACM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_acm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the cdc_acm interface. The call is blocking */ +/* and only returns when there is either an error or when the transfer */ +/* is complete. */ +/* */ +/* INPUT */ +/* */ +/* cdc_acm Pointer to cdc_acm class */ +/* data_pointer Pointer to data to write */ +/* requested_length Length of data to write */ +/* actual_length Actual length of data written */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_acm_write(UX_HOST_CLASS_CDC_ACM *cdc_acm, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_WRITE, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (cdc_acm -> ux_host_class_cdc_acm_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* As further protection, we must ensure this instance of the interface is the data interface and not + the control interface ! */ + if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass != UX_HOST_CLASS_CDC_DATA_CLASS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_acm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by resetting the actual length of the transfer. */ + *actual_length = 0; + + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &cdc_acm -> ux_host_class_cdc_acm_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk out endpoint until either the transfer is + completed or when there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer_request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_HOST_CLASS_CDC_ACM_CLASS_TRANSFER_TIMEOUT); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer_request in case some data actually went out. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be sent. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually sent and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to send out. */ + requested_length -= transfer_request_length; + } + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_activate.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_activate.c new file mode 100644 index 0000000..e448107 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_activate.c @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + +UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the cdc_ecm instance, configure the device. */ +/* */ +/* INPUT */ +/* */ +/* command CDC ECM class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_class_cdc_ecm_endpoints_get Get endpoints of cdc_ecm */ +/* _ux_host_class_cdc_ecm_mac_address_get Get MAC address */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_delete Delete thread */ +/* _ux_utility_thread_resume Resume thread */ +/* _ux_network_driver_activate Activate NetX USB interface*/ +/* nx_packet_pool_create Create NetX packet pool */ +/* nx_packet_pool_delete Delete NetX packet pool */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_ecm_entry Entry of cdc_ecm class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_ecm_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_CDC_ECM *cdc_ecm; +UINT status; +UX_TRANSFER *transfer_request; +ULONG physical_address_msw = 0; +ULONG physical_address_lsw = 0; +UX_INTERFACE *control_interface; +UX_INTERFACE *cur_interface; + + /* The CDC ECM class is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Is this the control interface? */ + if (interface -> ux_interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_CONTROL_CLASS) + { + + /* We ignore the control interface. All activation is performed when + we receive the data interface. */ + return(UX_SUCCESS); + } + + /* Obtain memory for this class instance. */ + cdc_ecm = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, sizeof(UX_HOST_CLASS_CDC_ECM)); + if (cdc_ecm == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + cdc_ecm -> ux_host_class_cdc_ecm_class = command -> ux_host_class_command_class_ptr; + + /* Store the device container into the cdc_ecm class instance. */ + cdc_ecm -> ux_host_class_cdc_ecm_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* Store the interface container into the cdc_acm class instance. */ + cdc_ecm -> ux_host_class_cdc_ecm_interface_data = interface; + + /* We need to link the data and control interfaces together. In order + to do this, we first need to find the control interface. Per the spec, + it should be behind this one. */ + + /* Set the current interface to the second interface. */ + cur_interface = interface -> ux_interface_configuration -> ux_configuration_first_interface; + + /* Initialize to null. */ + control_interface = UX_NULL; + + /* Loop through all the interfaces until we find the current data interface. */ + while (cur_interface != interface) + { + + /* Is this a control interface? */ + if (cur_interface -> ux_interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_CONTROL_CLASS) + { + + /* Save it. */ + control_interface = cur_interface; + } + + /* Advance current interface. */ + cur_interface = cur_interface -> ux_interface_next_interface; + } + + /* Did we not find the control interface? */ + if (control_interface == UX_NULL) + { + + /* This in an invalid descriptor. */ + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error. */ + status = UX_DESCRIPTOR_CORRUPTED; + } + else + { + + /* We found the control interface. */ + status = UX_SUCCESS; + } + + if (status == UX_SUCCESS) + { + + /* Save the control interface. */ + cdc_ecm -> ux_host_class_cdc_ecm_interface_control = (UX_INTERFACE *) control_interface; + + /* Get the cdc_ecm endpoint(s) on the interface. */ + status = _ux_host_class_cdc_ecm_endpoints_get(cdc_ecm); + } + + if (status == UX_SUCCESS) + { + + /* Allocate a Thread stack. */ + cdc_ecm -> ux_host_class_cdc_ecm_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + if (cdc_ecm -> ux_host_class_cdc_ecm_thread_stack == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + if (status == UX_SUCCESS) + { + + /* Allocate some packet pool for reception. + * UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE overflow has been checked by + * UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT outside of function. + */ + cdc_ecm -> ux_host_class_cdc_ecm_pool_memory = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE); + if (cdc_ecm -> ux_host_class_cdc_ecm_pool_memory == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + if (status == UX_SUCCESS) + { + + /* Create the semaphore for aborting bulk in transfers. */ + status = _ux_utility_semaphore_create(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore, + "host CDC-ECM bulk in wait semaphore", 0); + if (status == UX_SUCCESS) + { + + /* Create the semaphore for aborting bulk out transfers. */ + status = _ux_utility_semaphore_create(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish_semaphore, + "host CDC-ECM bulk out wait semaphore", 0); + if (status == UX_SUCCESS) + { + + /* Create the semaphore to wake up the CDC ECM thread. */ + status = _ux_utility_semaphore_create(&cdc_ecm -> ux_host_class_cdc_ecm_interrupt_notification_semaphore, "host CDC-ECM interrupt notification semaphore", 0); + if (status == UX_SUCCESS) + { + + /* Create a packet pool. */ + status = nx_packet_pool_create(&cdc_ecm -> ux_host_class_cdc_ecm_packet_pool, "host CDC-ECM packet pool", + UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE, cdc_ecm -> ux_host_class_cdc_ecm_pool_memory, UX_HOST_CLASS_CDC_ECM_NX_ETHERNET_POOL_ALLOCSIZE); + if (status == UX_SUCCESS) + { + + /* Create the cdc_ecm class thread. We do not start it yet. */ + status = _ux_utility_thread_create(&cdc_ecm -> ux_host_class_cdc_ecm_thread, + "ux_host_cdc_ecm_thread", _ux_host_class_cdc_ecm_thread, + (ULONG) (ALIGN_TYPE) cdc_ecm, + cdc_ecm -> ux_host_class_cdc_ecm_thread_stack, + UX_THREAD_STACK_SIZE, + UX_THREAD_PRIORITY_CLASS, + UX_THREAD_PRIORITY_CLASS, + TX_NO_TIME_SLICE, TX_DONT_START); + if (status == UX_SUCCESS) + { + + UX_THREAD_EXTENSION_PTR_SET(&(cdc_ecm -> ux_host_class_cdc_ecm_thread), cdc_ecm) + + /* We now need to retrieve the MAC address of the node which is embedded in the ECM descriptor. + We will parse the entire configuration descriptor of the device and look for the ECM Ethernet Networking Functional Descriptor. */ + status = _ux_host_class_cdc_ecm_mac_address_get(cdc_ecm); + + if (status == UX_SUCCESS) + { + + /* Setup the physical address of this IP instance. */ + physical_address_msw = (ULONG)((cdc_ecm -> ux_host_class_cdc_ecm_node_id[0] << 8) | (cdc_ecm -> ux_host_class_cdc_ecm_node_id[1])); + physical_address_lsw = (ULONG)((cdc_ecm -> ux_host_class_cdc_ecm_node_id[2] << 24) | (cdc_ecm -> ux_host_class_cdc_ecm_node_id[3] << 16) | + (cdc_ecm -> ux_host_class_cdc_ecm_node_id[4] << 8) | (cdc_ecm -> ux_host_class_cdc_ecm_node_id[5])); + + /* The ethernet link is down by default. */ + cdc_ecm -> ux_host_class_cdc_ecm_link_state = UX_HOST_CLASS_CDC_ECM_LINK_STATE_DOWN; + } + + if (status == UX_SUCCESS) + { + + /* Register this interface to the NetX USB interface broker. */ + status = _ux_network_driver_activate((VOID *) cdc_ecm, _ux_host_class_cdc_ecm_write, + &cdc_ecm -> ux_host_class_cdc_ecm_network_handle, + physical_address_msw, physical_address_lsw); + } + + if (status == UX_SUCCESS) + { + + /* Mark the cdc_ecm data instance as live now. */ + cdc_ecm -> ux_host_class_cdc_ecm_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) cdc_ecm; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(cdc_ecm -> ux_host_class_cdc_ecm_class, (VOID *) cdc_ecm); + + /* Start the interrupt pipe now if it exists. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint != UX_NULL) + { + + /* Obtain the transfer request from the interrupt endpoint. */ + transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint -> ux_endpoint_transfer_request; + status = _ux_host_stack_transfer_request(transfer_request); + } + + if (status == UX_SUCCESS) + { + + /* Activation is complete. */ + + /* Now we can start the CDC-ECM thread. */ + _ux_utility_thread_resume(&cdc_ecm -> ux_host_class_cdc_ecm_thread); + + /* We need to inform the application if a function has been programmed + in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. Note that the application should + wait until the link state is up until using this instance. The + link state is changed to up by the CDC-ECM thread, which isn't + started until after the data interface has been processed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, cdc_ecm -> ux_host_class_cdc_ecm_class, (VOID *) cdc_ecm); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ECM_ACTIVATE, cdc_ecm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, cdc_ecm, 0, 0, 0) + + /* Activation was successful. */ + return(UX_SUCCESS); + } + + /* Error starting interrupt endpoint. */ + + /* Destroy this class instance. */ + _ux_host_stack_class_instance_destroy(cdc_ecm -> ux_host_class_cdc_ecm_class, (VOID *) cdc_ecm); + + /* Unmount instance. */ + interface -> ux_interface_class_instance = UX_NULL; + } + + /* Delete CDC-ECM thread. */ + _ux_utility_thread_delete(&cdc_ecm -> ux_host_class_cdc_ecm_thread); + } + + /* Delete packet pool. */ + nx_packet_pool_delete(&cdc_ecm -> ux_host_class_cdc_ecm_packet_pool); + } + else + { + + /* Packet pool creation failed. Notify application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ENUMERATOR, status); + } + + /* Delete interrupt notification semaphore. */ + _ux_utility_semaphore_delete(&cdc_ecm -> ux_host_class_cdc_ecm_interrupt_notification_semaphore); + } + + /* Delete class-level bulk out semaphore. */ + _ux_utility_semaphore_delete(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish_semaphore); + } + + /* Delete class-level bulk in semaphore. */ + _ux_utility_semaphore_delete(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore); + } + } + + /* An error occurred. We must clean up resources. */ + + if (cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint != UX_NULL && + cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer != UX_NULL) + _ux_utility_memory_free(cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer); + + if (cdc_ecm -> ux_host_class_cdc_ecm_pool_memory != UX_NULL) + _ux_utility_memory_free(cdc_ecm -> ux_host_class_cdc_ecm_pool_memory); + + if (cdc_ecm -> ux_host_class_cdc_ecm_thread_stack != UX_NULL) + _ux_utility_memory_free(cdc_ecm -> ux_host_class_cdc_ecm_thread_stack); + + _ux_utility_memory_free(cdc_ecm); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_deactivate.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_deactivate.c new file mode 100644 index 0000000..6b991b9 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_deactivate.c @@ -0,0 +1,203 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the cdc_ecm has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* and interrupt pipes will be destroyed and the instance */ +/* removed. */ +/* */ +/* INPUT */ +/* */ +/* command CDC ECM class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_ecm_entry Entry of cdc_ecm class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_ecm_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +TX_INTERRUPT_SAVE_AREA + +UX_HOST_CLASS_CDC_ECM *cdc_ecm; +UX_TRANSFER *transfer_request; + + /* This must be the data interface, since the control interface doesn't have + a class instance. */ + + /* Get the control instance for this class. */ + cdc_ecm = (UX_HOST_CLASS_CDC_ECM *) command -> ux_host_class_command_instance; + + /* The cdc_ecm is being shut down. */ + cdc_ecm -> ux_host_class_cdc_ecm_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* If the interrupt endpoint is defined, abort transfers so the link state + doesn't change. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint != UX_NULL) + { + + /* Get the transfer request. */ + transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint -> ux_endpoint_transfer_request; + + /* Abort any transfers. */ + _ux_host_stack_transfer_request_abort(transfer_request); + } + + /* Check if link was up to see if we should clean the transmit queue. If + the link is pending down, that means the CDC-ECM thread is in the process + of cleaning the transmit queue. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP) + + _ux_host_class_cdc_ecm_transmit_queue_clean(cdc_ecm); + + /* Get the bulk in transfer request. */ + transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Now abort all transfers. It's possible we're executing right before the transfer + is armed. If this is the case, then the transfer will not be aborted if we do the abort right now; instead, + we should wait until after the transfer is armed. We must look at the CDC-ECM thread's state. */ + + /* Disable interrupts while we check the link state and possibly set our state. */ + TX_DISABLE + + /* Is it in the process of checking the link state and arming the transfer? */ + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process == UX_TRUE) + { + + /* Yes. We must wait for it to finish arming the transfer. */ + + /* Let the CDC-ECM thread know we're waiting so it can wake us up. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish = UX_TRUE; + + /* Restore interrupts. */ + TX_RESTORE + + /* Wait for the transfer to be armed, or possibly an error. The CDC-ECM thread will wake us up. */ + _ux_utility_semaphore_get(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore, UX_WAIT_FOREVER); + + /* We're no longer waiting. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish = UX_FALSE; + } + else + { + + /* Restore interrupts. */ + TX_RESTORE + } + + /* Now we can abort the transfer. */ + _ux_host_stack_transfer_request_abort(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_endpoint -> ux_endpoint_transfer_request); + + /* De-register this interface to the NetX USB interface broker. */ + _ux_network_driver_deactivate((VOID *) cdc_ecm, cdc_ecm -> ux_host_class_cdc_ecm_network_handle); + + /* Destroy the control instance. */ + _ux_host_stack_class_instance_destroy(cdc_ecm -> ux_host_class_cdc_ecm_class, (VOID *) cdc_ecm); + + /* Now wait for all threads to leave the instance before freeing the resources. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Free the memory used by the interrupt endpoint. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint != UX_NULL) + _ux_utility_memory_free(cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer); + + /* Destroy the link monitoring thread. We should do this before destroying + the notification semaphore so that it does not run due to semaphore deletion. */ + _ux_utility_thread_delete(&cdc_ecm -> ux_host_class_cdc_ecm_thread); + + /* Free the CDC-ECM thread's stack memory. */ + _ux_utility_memory_free(cdc_ecm -> ux_host_class_cdc_ecm_thread_stack); + + /* Destroy the bulk semaphores. */ + _ux_utility_semaphore_delete(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish_semaphore); + _ux_utility_semaphore_delete(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore); + + /* Destroy the notification semaphore. */ + _ux_utility_semaphore_delete(&cdc_ecm -> ux_host_class_cdc_ecm_interrupt_notification_semaphore); + + /* Delete the packet pool. */ + nx_packet_pool_delete(&cdc_ecm -> ux_host_class_cdc_ecm_packet_pool); + + /* Free this pool of packets. */ + _ux_utility_memory_free(cdc_ecm -> ux_host_class_cdc_ecm_pool_memory); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, cdc_ecm -> ux_host_class_cdc_ecm_class, (VOID *) cdc_ecm); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ECM_DEACTIVATE, cdc_ecm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, unregister this object. */ + UX_TRACE_OBJECT_UNREGISTER(cdc_ecm); + + /* Free the cdc_ecm control instance memory. */ + _ux_utility_memory_free(cdc_ecm); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_endpoints_get.c new file mode 100644 index 0000000..cb5fcfb --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_endpoints_get.c @@ -0,0 +1,266 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function distinguishes for either the Data or Control Class. */ +/* For the data class, we mount the bulk in and bulk out endpoints. */ +/* For the control class, we mount the optional interrupt endpoint. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm Pointer to cdc_ecm class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_ecm_activate Activate cdc_ecm class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_ecm_endpoints_get(UX_HOST_CLASS_CDC_ECM *cdc_ecm) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; +UX_TRANSFER *transfer_request; +UX_INTERFACE *data_interface; + + + /* Get the endpoints from the data interface. */ + + /* Default data interface. */ + data_interface = cdc_ecm -> ux_host_class_cdc_ecm_interface_data; + + /* Some versions of cdc-ecm contain a default interface for data with 0 endpoints. Check if this the case and if so, + look for the next interface that has the 2 bulk endpoints. */ + + if (data_interface -> ux_interface_descriptor.bNumEndpoints == 0) + { + + /* We are in the case where the interface has the default set to 0 endpoints. */ + data_interface = data_interface -> ux_interface_next_interface; + + /* Check if invalid. */ + if (data_interface == UX_NULL || + data_interface -> ux_interface_descriptor.bInterfaceClass != UX_HOST_CLASS_CDC_DATA_CLASS || + data_interface -> ux_interface_descriptor.bAlternateSetting != 1) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, cdc_ecm -> ux_host_class_cdc_ecm_interface_data, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Descriptor is corrupted. */ + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* We have found the right alternate setting. Now we need to select it. */ + status = _ux_host_stack_interface_setting_select(data_interface); + + /* Check status. We don't continue if there is a problem with the selection. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + } + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < data_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + _ux_host_stack_interface_endpoint_get(data_interface, endpoint_index, &endpoint); + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_class_instance = (VOID *) cdc_ecm; + + /* The transfer request has a callback function. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_completion_function = _ux_host_class_cdc_ecm_transmission_callback; + + /* We have found the bulk endpoint, save it. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_endpoint = endpoint; + + break; + } + } + + /* The bulk out endpoint is mandatory. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, cdc_ecm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < data_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + _ux_host_stack_interface_endpoint_get(data_interface, endpoint_index, &endpoint); + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* Set the class instance in the transfer request. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_class_instance = (VOID *) cdc_ecm; + + /* The transfer request has NO callback function. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_completion_function = UX_NULL; + + /* We have found the bulk endpoint, save it. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_endpoint = endpoint; + + break; + } + } + + /* The bulk in endpoint is mandatory. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, cdc_ecm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Now get the endpoints from the control interface. */ + + /* Search the Interrupt endpoint. It is NOT mandatory. */ + for (endpoint_index = 0; endpoint_index < cdc_ecm -> ux_host_class_cdc_ecm_interface_control -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + _ux_host_stack_interface_endpoint_get(cdc_ecm -> ux_host_class_cdc_ecm_interface_control, endpoint_index, &endpoint); + + /* Check if endpoint is Interrupt and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the interrupt endpoint, save it. */ + cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint = endpoint; + + /* The endpoint is correct, Fill in the transfer request with the length requested for this endpoint. */ + transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint -> ux_endpoint_transfer_request; + transfer_request -> ux_transfer_request_requested_length = cdc_ecm -> ux_host_class_cdc_ecm_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* The direction is always IN for the CDC interrupt endpoint. */ + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) cdc_ecm; + + /* Interrupt transactions have a completion routine. */ + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_cdc_ecm_interrupt_notification; + + /* Obtain a buffer for this transaction. The buffer will always be reused. */ + transfer_request -> ux_transfer_request_data_pointer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + transfer_request -> ux_transfer_request_requested_length); + + /* If the endpoint is available and we have memory, we start the interrupt endpoint. */ + if (transfer_request -> ux_transfer_request_data_pointer == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We must return an error. */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + break; + } + } + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_entry.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_entry.c new file mode 100644 index 0000000..8ecfec9 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_entry.c @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the cdc_ecm class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* cdc_ecm ethernet device on the bus or when the it is removed. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* command CDC ECM class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_cdc_ecm_activate Activate cdc_ecm class */ +/* _ux_host_class_cdc_ecm_deactivate Deactivate cdc_ecm class */ +/* */ +/* CALLED BY */ +/* */ +/* CDC ECM Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_ecm_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if(command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) + { + + /* We are in CSP mode. Check if CDC-ECM Control or Data. */ + if (((command -> ux_host_class_command_class == UX_HOST_CLASS_CDC_DATA_CLASS) && (command -> ux_host_class_command_subclass == 0)) || + ((command -> ux_host_class_command_class == UX_HOST_CLASS_CDC_CONTROL_CLASS) && + (command -> ux_host_class_command_subclass == UX_HOST_CLASS_CDC_ECM_CONTROL_SUBCLASS))) + { + /* Check for IAD presence. */ + if ((command -> ux_host_class_command_iad_class == 0) && (command -> ux_host_class_command_iad_subclass == 0)) + + /* No IAD, we accept this class. */ + return(UX_SUCCESS); + + else + { + + if ((command -> ux_host_class_command_iad_class == UX_HOST_CLASS_CDC_CONTROL_CLASS) && + (command -> ux_host_class_command_iad_subclass == UX_HOST_CLASS_CDC_ECM_CONTROL_SUBCLASS)) + + /* There is an IAD and this is for CDC-ACM. */ + return(UX_SUCCESS); + + else + + /* The IAD does not match with CDC-ACM. */ + return(UX_NO_CLASS_MATCH); + } + } + + /* Not CDC-ECM control or data class. */ + return(UX_NO_CLASS_MATCH); + + } + + else + + /* No match. */ + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_cdc_ecm_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_cdc_ecm_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_interrupt_notification.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_interrupt_notification.c new file mode 100644 index 0000000..fb48507 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_interrupt_notification.c @@ -0,0 +1,174 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_interrupt_notification PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the stack when an interrupt packet as */ +/* been received. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_cdc_ecm_interrupt_notification(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_CDC_ECM *cdc_ecm; +ULONG notification_type; +ULONG notification_value; + + + /* Get the control class instance for this transfer request. */ + cdc_ecm = (UX_HOST_CLASS_CDC_ECM *) transfer_request -> ux_transfer_request_class_instance; + + /* Check the state of the transfer. If there is an error, we do not proceed with this notification. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + + /* We do not proceed. */ + return; + + /* Check if the class is in shutdown. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) + + /* We do not proceed. */ + return; + + /* Increment the notification count. */ + cdc_ecm -> ux_host_class_cdc_ecm_notification_count++; + + /* Get the notification. */ + notification_type = (ULONG) *(transfer_request -> ux_transfer_request_data_pointer + UX_HOST_CLASS_CDC_ECM_NPF_NOTIFICATION_TYPE); + + /* And the value. */ + notification_value = (ULONG) *(transfer_request -> ux_transfer_request_data_pointer + UX_HOST_CLASS_CDC_ECM_NPF_VALUE); + + /* Check if the notification is a Network notification. */ + if (notification_type == UX_HOST_CLASS_CDC_ECM_NOTIFICATION_NETWORK_CONNECTION) + { + + /* Check the state of the link. */ + if (notification_value == UX_HOST_CLASS_CDC_ECM_NOTIFICATION_NETWORK_LINK_UP) + { + + /* Link is up. See if we know about that. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_link_state != UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP && + cdc_ecm -> ux_host_class_cdc_ecm_link_state != UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_UP) + { + + /* Memorize the new link state. */ + cdc_ecm -> ux_host_class_cdc_ecm_link_state = UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_UP; + + /* We need to inform the cdc_ecm thread of this change. */ + _ux_utility_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_interrupt_notification_semaphore); + } + } + else + { + + /* Link is down. See if we know about that. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_link_state != UX_HOST_CLASS_CDC_ECM_LINK_STATE_DOWN && + cdc_ecm -> ux_host_class_cdc_ecm_link_state != UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_DOWN) + { + + /* We need to abort any transfers on the bulk in endpoint. */ + + /* Make sure no one does any more transfers. */ + cdc_ecm -> ux_host_class_cdc_ecm_link_state = UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_DOWN; + + /* Now abort all transfers. It's possible we're executing right before the transfer + is armed. If this is the case, then the transfer will not be aborted if we do the abort right now; instead, + we should wait until after the transfer is armed. We must look at the CDC-ECM thread's state. */ + + /* Is it in the process of checking the link state and arming the transfer? */ + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process == UX_TRUE) + { + + /* Yes. We must wait for it to finish arming the transfer. */ + + /* Let the CDC-ECM thread know we're waiting so it can wake us up. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish = UX_TRUE; + + /* Wait for the transfer to be armed, or possibly an error. The CDC-ECM thread will wake us up. */ + _ux_utility_semaphore_get(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore, UX_WAIT_FOREVER); + + /* We're no longer waiting. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish = UX_FALSE; + } + + /* Now we can abort the transfer. */ + _ux_host_stack_transfer_request_abort(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_endpoint -> ux_endpoint_transfer_request); + + /* We need to inform the CDC-ECM thread of this change. */ + _ux_utility_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_interrupt_notification_semaphore); + } + } + } + + /* Reactivate the CDC_ECM interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ECM_INTERRUPT_NOTIFICATION, cdc_ecm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_mac_address_get.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_mac_address_get.c new file mode 100644 index 0000000..d4dcfa4 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_mac_address_get.c @@ -0,0 +1,346 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_mac_address_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to retrieve the MAC address from */ +/* the configuration descriptor. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm Pointer to cdc_ecm class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_descriptor_parse Parse descriptors */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_cdc_ecm_activate CDC ECM class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_ecm_mac_address_get(UX_HOST_CLASS_CDC_ECM *cdc_ecm) +{ + +UINT status; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UX_CONFIGURATION_DESCRIPTOR configuration_descriptor; +UCHAR *descriptor; +UCHAR *start_descriptor = UX_NULL; +ULONG configuration_index; +ULONG total_configuration_length; +UINT descriptor_length; +UINT descriptor_type; +UINT descriptor_subtype; +UX_HOST_CLASS_ECM_INTERFACE_DESCRIPTOR ecm_interface_descriptor; +UCHAR *mac_address_string; +ULONG string_index; +ULONG string_length; +UCHAR element_content; +UCHAR element_hexa_upper; +UCHAR element_hexa_lower; + + /* We now need to retrieve the MAC address of the node which is embedded in the ECM descriptor. + We will parse the entire configuration descriptor of the device and look for the ECM Ethernet Networking Functional Descriptor. */ + configuration_index = cdc_ecm -> ux_host_class_cdc_ecm_interface_data -> ux_interface_configuration -> ux_configuration_descriptor.bConfigurationValue -1; + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &cdc_ecm -> ux_host_class_cdc_ecm_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. Since we do not know the size of the + descriptor, we first read the first bytes. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Memorize the descriptor start address. */ + start_descriptor = descriptor; + + /* Create a transfer request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_CONFIGURATION_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_CONFIGURATION_DESCRIPTOR_ITEM << 8) | configuration_index; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH)) + { + + /* Parse the descriptor so that we can read the total length. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration_descriptor); + + /* We don't need this descriptor now. */ + _ux_utility_memory_free(descriptor); + + /* Reallocate the memory necessary for the reading the entire descriptor. */ + total_configuration_length = configuration_descriptor.wTotalLength; + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, total_configuration_length); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save this descriptor address. */ + start_descriptor = descriptor; + + /* Read the descriptor again with the correct length this time. */ + transfer_request -> ux_transfer_request_requested_length = total_configuration_length; + + /* Since the address of the descriptor may have changed, reprogram it. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == configuration_descriptor.wTotalLength)) + { + + /* The ECM descriptor is embedded within the configuration descriptor. We parse the + entire descriptor to locate the ECM functional descriptor portion. */ + while (total_configuration_length) + { + + /* Gather the length and type of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Check the type for an interface descriptor and the subtype for a ECM functional descriptor. */ + if ((descriptor_type == UX_HOST_CLASS_CDC_ECM_CS_INTERFACE) && (descriptor_subtype == UX_HOST_CLASS_CDC_ECM_FUNCTIONAL_DESCRIPTOR)) + { + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, + _ux_system_ecm_interface_descriptor_structure, + UX_HOST_CLASS_CDC_ECM_INTERFACE_DESCRIPTOR_ENTRIES, + (UCHAR *) &ecm_interface_descriptor); + + + /* Release the memory. */ + _ux_utility_memory_free(start_descriptor); + + /* We now have the ECM functional descriptor in memory. We can retrieve the index of the iMACAddress + which we need for NetX. */ + + /* Allocate memory for the MAC address. */ + mac_address_string = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_CDC_ECM_MAC_ADDRESS_STRING_LENGTH); + + /* Check memory allocation. */ + if (mac_address_string == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = mac_address_string; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_CDC_ECM_MAC_ADDRESS_STRING_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_STRING_DESCRIPTOR_ITEM << 8) | ecm_interface_descriptor.iMACAddress; + transfer_request -> ux_transfer_request_index = 0x0409; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer. */ + if (status == UX_SUCCESS) + { + + /* Translate from Unicode to string. Length is in the first byte. We must take away 2 from it + and divide by 2 to find the right asciiz length. */ + string_length = (ULONG) *mac_address_string; + + /* Check the length of the mac address Unicode string. */ + if (string_length > 26) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* Return error. */ + status = UX_DESCRIPTOR_CORRUPTED; + } + else + { + + /* No error in length, decode the string. */ + string_length -=2; + string_length = string_length/2; + + /* Now we have a string of 12 hexa ASCII digits to be translated into 6 hexa digit bytes. + and copy into the node ID. */ + for (string_index = 0; string_index < string_length; string_index++) + { + + /* Get the upper element from the ASCII string. */ + element_content = *(mac_address_string + (string_index * 2) + 2); + + /* We have a valid element content. Turn it into a hexa decimal value. Note + that only hex digits are allowed. */ + if (element_content <= '9') + + /* We have a digit. */ + element_hexa_upper = (UCHAR)(element_content - '0'); + + else + { + /* We have a 'A' to 'F' or 'a' to 'f' value. */ + if (element_content >= 'a') + + /* We have a 'a' to 'f' char. */ + element_hexa_upper = (UCHAR)(element_content - 'a' + 10); + + else + + /* We have a 'A' to 'F' char. */ + element_hexa_upper = (UCHAR)(element_content - 'A' + 10); + + } + + /* Get the lower element from the ASCII string. */ + element_content = *(mac_address_string + ((string_index + 1) * 2) + 2); + + /* We have a valid element content. Turn it into a hexa decimal value. Note + that only hex digits are allowed. */ + if (element_content <= '9') + + /* We have a digit. */ + element_hexa_lower = (UCHAR)(element_content - '0'); + + else + { + /* We have a 'A' to 'F' or 'a' to 'f' value. */ + if (element_content >= 'a') + + /* We have a 'a' to 'f' char. */ + element_hexa_lower = (UCHAR)(element_content - 'a' + 10); + + else + + /* We have a 'A' to 'F' char. */ + element_hexa_lower = (UCHAR)(element_content - 'A' + 10); + + } + + /* Assemble the byte from the 2 nibbles and store it into the node_id. */ + *(cdc_ecm -> ux_host_class_cdc_ecm_node_id + string_index / 2) = (UCHAR)(element_hexa_upper << 4 | element_hexa_lower); + + /* Skip the lower nibble. */ + string_index++; + + } + + /* Operation was successful ! */ + status = UX_SUCCESS; + } + } + else + { + + /* We have a bad MAC address string. Do not proceed. */ + status = UX_ERROR; + } + + /* Free the MAC address string. */ + _ux_utility_memory_free(mac_address_string); + + /* Return completion status. */ + return(status); + } + else + { + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_configuration_length -= descriptor_length; + } + } + } + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, &configuration_descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Release the memory. */ + _ux_utility_memory_free(start_descriptor); + + /* Return an error. */ + return(UX_DESCRIPTOR_CORRUPTED); + +} + + + + + + + + + + + + + + diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_thread.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_thread.c new file mode 100644 index 0000000..78aab54 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_thread.c @@ -0,0 +1,244 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This is the CDC ECM thread that monitors the link change flag, */ +/* receives data from the device, and passes the data to the NetX-USB */ +/* broker. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm CDC ECM instance */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_cdc_ecm_transmit_queue_clean */ +/* Clean transmit queue */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_utility_short_get_big_endian Get 16-bit big endian */ +/* _ux_network_driver_link_up Set state link up */ +/* _ux_network_driver_link_down Set state link down */ +/* _ux_network_driver_packet_received Process received packet */ +/* nx_packet_allocate Allocate NetX packet */ +/* nx_packet_release Free NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* CDC ECM class initialization */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_cdc_ecm_thread(ULONG parameter) +{ + +UX_HOST_CLASS_CDC_ECM *cdc_ecm; +UX_TRANSFER *transfer_request; +NX_PACKET *packet; +ULONG ip_given_length; +UINT status; + + /* Cast the parameter passed in the thread into the cdc_ecm pointer. */ + UX_THREAD_EXTENSION_PTR_GET(cdc_ecm, UX_HOST_CLASS_CDC_ECM, parameter) + + /* Loop forever waiting for changes signaled through the semaphore. */ + while (1) + { + + /* Wait for the semaphore to be put by the cdc_ecm interrupt event. */ + _ux_utility_semaphore_get(&cdc_ecm -> ux_host_class_cdc_ecm_interrupt_notification_semaphore, UX_WAIT_FOREVER); + + /* Check the link state. It is either pending up or down. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_UP) + { + + /* Now the link is up. */ + cdc_ecm -> ux_host_class_cdc_ecm_link_state = UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP; + + /* Communicate the state with the network driver. */ + _ux_network_driver_link_up(cdc_ecm -> ux_host_class_cdc_ecm_network_handle); + + /* As long as we are connected, configured and link up ... do some work.... */ + while ((cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP) && + (cdc_ecm -> ux_host_class_cdc_ecm_device -> ux_device_state == UX_DEVICE_CONFIGURED)) + { + + /* We can accept reception. Get a NX Packet. */ + status = nx_packet_allocate(&cdc_ecm -> ux_host_class_cdc_ecm_packet_pool, &packet, + NX_RECEIVE_PACKET, MS_TO_TICK(UX_HOST_CLASS_CDC_ECM_PACKET_POOL_WAIT)); + + if (status == NX_SUCCESS) + { + + /* Adjust the prepend pointer to take into account the non 3 bit alignment of the ethernet header. */ + packet -> nx_packet_prepend_ptr += sizeof(USHORT); + + /* We have a packet. Link this packet to the reception transfer request on the bulk in endpoint. */ + transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Set the data pointer. */ + transfer_request -> ux_transfer_request_data_pointer = packet -> nx_packet_prepend_ptr; + + /* And length. */ + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* Store the packet that owns this transaction. */ + transfer_request -> ux_transfer_request_user_specific = packet; + + /* Reset the queue pointer of this packet. */ + packet -> nx_packet_queue_next = UX_NULL; + + /* We're arming the transfer now. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process = UX_TRUE; + + /* Is the link up? */ + if (cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP) + { + + /* Ask USB to schedule a reception. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Signal that we are done arming and resume waiting thread if necessary. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process = UX_FALSE; + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish == UX_TRUE) + _ux_utility_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore); + + /* Check if the transaction was armed successfully. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, TX_WAIT_FOREVER); + + /* Check the transfer status. If there is a transport error, we ignore the packet + and restart it. */ + if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS) + { + + /* Get the packet length. */ + packet -> nx_packet_length = transfer_request -> ux_transfer_request_actual_length; + + /* Adjust the prepend, length, and append fields. */ + packet -> nx_packet_append_ptr = + packet->nx_packet_prepend_ptr + transfer_request -> ux_transfer_request_actual_length; + + /* Calculate the accurate packet length from ip header */ + if ((*(packet -> nx_packet_prepend_ptr + 12) == 0x08) && + (*(packet -> nx_packet_prepend_ptr + 13) == 0)) + { + + ip_given_length = _ux_utility_short_get_big_endian(packet -> nx_packet_prepend_ptr + 16) + UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + packet->nx_packet_length = ip_given_length ; + packet->nx_packet_append_ptr = packet->nx_packet_prepend_ptr + ip_given_length; + } + + /* Send that packet to the NetX USB broker. */ + _ux_network_driver_packet_received(cdc_ecm -> ux_host_class_cdc_ecm_network_handle, packet); + } + else + { + + /* Free the packet that was not successfully received. */ + nx_packet_release(packet); + } + } + else + { + + /* Error arming transfer. */ + + /* Release packet. */ + nx_packet_release(packet); + } + } + else + { + + /* Link is down. */ + + /* Signal that we are done arming and resume waiting thread if necessary. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process = UX_FALSE; + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish == UX_TRUE) + _ux_utility_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore); + + /* Release packet. */ + nx_packet_release(packet); + } + } + else + { + + /* Packet allocation timed out. Note that the timeout value is + configurable. */ + + /* Error trap. No need for trace, since NetX does it. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + } + } + } + else + { + + /* The link state is pending down. We need to free the xmit queue. */ + _ux_host_class_cdc_ecm_transmit_queue_clean(cdc_ecm); + + /* Link state can now be set to down. */ + + /* Notify the network driver. */ + _ux_network_driver_link_down(cdc_ecm -> ux_host_class_cdc_ecm_network_handle); + + /* Set the link state. */ + cdc_ecm -> ux_host_class_cdc_ecm_link_state = UX_HOST_CLASS_CDC_ECM_LINK_STATE_DOWN; + } + } +} diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmission_callback.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmission_callback.c new file mode 100644 index 0000000..9802e33 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmission_callback.c @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC-ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_transmission_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback from the USBX transfer functions, */ +/* it is called when a full or partial transfer has been done for a */ +/* bulk out transfer. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* nx_packet_transmit_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_cdc_ecm_transmission_callback(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_CDC_ECM *cdc_ecm; +NX_PACKET *current_packet; +NX_PACKET *next_packet; +UCHAR *packet_header; + + /* Get the data and control class instances for this transfer request. */ + cdc_ecm = (UX_HOST_CLASS_CDC_ECM *) transfer_request -> ux_transfer_request_class_instance; + + /* Is the link not up, or the class is shutting down? */ + if (cdc_ecm -> ux_host_class_cdc_ecm_link_state != UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP || + cdc_ecm -> ux_host_class_cdc_ecm_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) + + /* The CDC-ECM thread or deactivation routine is in the process of freeing + the queue. Just return so we are not simultaneously accessing it. */ + return; + + /* Get the packet associated with this transfer. */ + current_packet = cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head; + + /* Do a sanity check on the packet. */ + if (current_packet == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FATAL_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, cdc_ecm, 0, 0, UX_FATAL_ERROR, 0, 0) + + /* Something went terribly wrong. Do not proceed. */ + return; + } + + /* Check the state of the transfer. */ + if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS) + { + + /* Get the next packet associated with the first packet. */ + next_packet = current_packet -> nx_packet_queue_next; + + /* Set the next packet (or a NULL value) as the head of the xmit queue. */ + cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head = next_packet; + + /* If there is nothing else or if the link is down no need to rearm a packet. */ + if (next_packet != UX_NULL) + { + + /* Load the address of the current packet header at the physical header. */ + packet_header = next_packet -> nx_packet_prepend_ptr; + + /* Prepare the values for this new transmission. */ + transfer_request -> ux_transfer_request_data_pointer = packet_header; + transfer_request -> ux_transfer_request_requested_length = next_packet -> nx_packet_length; + + /* Store the packet that owns this transaction. */ + transfer_request -> ux_transfer_request_user_specific = next_packet; + + /* If error log is enabled, insert this message into the log buffer. */ + UX_DEBUG_LOG("_ux_host_class_cdc_ecm_transmission_callback", "Sending packet", next_packet, next_packet, _ux_system -> ux_system_mutex.tx_mutex_suspended_count) + + /* If there is an error, the system will hang up. Not much we can do to + fix this. */ + _ux_host_stack_transfer_request(transfer_request); + } + + /* If error log is enabled, insert this message into the log buffer. */ + UX_DEBUG_LOG("_ux_host_class_cdc_ecm_transmission_callback", "Freeing transmitted packet", 0, transfer_request, current_packet) + + /* Free the packet that was just sent. First do some housekeeping. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + } + else + { + + /* The transfer failed. Retry it. Note that this can't be a transfer + abort, because otherwise the link is either down or the class is in + shutdown, both of which are checked for at the beginning. */ + _ux_host_stack_transfer_request(transfer_request); + } + + /* There is no status to be reported back to the stack. */ + return; +} diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmit_queue_clean.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmit_queue_clean.c new file mode 100644 index 0000000..843f7be --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_transmit_queue_clean.c @@ -0,0 +1,140 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC_ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_transmit_queue_clean PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cleans the transmit queue. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm CDC ECM instance */ +/* */ +/* OUTPUT */ +/* */ +/* No return value */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_get Get bulk out semaphore */ +/* _ux_host_stack_endpoint_transfer_abort Abort endpoint transfer */ +/* nx_packet_transmit_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* CDC ECM thread and deactivation */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_cdc_ecm_transmit_queue_clean(UX_HOST_CLASS_CDC_ECM *cdc_ecm) +{ + +TX_INTERRUPT_SAVE_AREA + +NX_PACKET *current_packet; +NX_PACKET *next_packet; + + /* Disable interrupts while we check the write in process flag and + set our own state. */ + TX_DISABLE + + /* Is there a write in process? */ + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_check_and_arm_in_process == UX_TRUE) + { + + /* Wait for writes to complete. Note that once these writes complete, + no more should occur since the link state is pending down. */ + + /* Mark this thread as suspended so it will be woken up. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish = UX_TRUE; + + /* Restore interrupts while we wait. */ + TX_RESTORE + + /* Wait for write function to resume us. */ + _ux_utility_semaphore_get(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish_semaphore, UX_WAIT_FOREVER); + + /* We're done waiting. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish = UX_FALSE; + } + else + { + + /* No writes are in process. Restore interrupts and go on to free + the xmit queue. */ + TX_RESTORE + } + + /* Abort transfers on the bulk out endpoint. Note we need to do this + before accessing the queue since the transmission callback might + modify it. */ + _ux_host_stack_transfer_request_abort(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_endpoint -> ux_endpoint_transfer_request); + + /* Get the first packet. */ + current_packet = cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head; + + /* We need to free the packets that will not be sent. */ + while (current_packet != UX_NULL) + { + + /* We must get the next packet before releasing the current packet + because nxe_packet_transmit_release sets the pointer we pass + to null. */ + next_packet = current_packet -> nx_packet_queue_next; + + /* Free the packet. First do some housekeeping. */ + current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + current_packet -> nx_packet_length = current_packet -> nx_packet_length - UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(current_packet); + + /* Next packet becomes the current one. */ + current_packet = next_packet; + } + + /* Clear the queue. */ + cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head = UX_NULL; +} diff --git a/common/usbx_host_classes/src/ux_host_class_cdc_ecm_write.c b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_write.c new file mode 100644 index 0000000..8a8f708 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_cdc_ecm_write.c @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** CDC ECM Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_cdc_ecm.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_ecm_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the cdc_ecm interface. The call is */ +/* non-blocking and queues the packet if there is an on-going write. */ +/* */ +/* INPUT */ +/* */ +/* cdc_ecm Pointer to cdc_ecm class */ +/* packet Packet to write or queue */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* nx_packet_transmit_release Release NetX packet */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_cdc_ecm_write(VOID *cdc_ecm_class, NX_PACKET *packet) +{ + +TX_INTERRUPT_SAVE_AREA + +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *packet_header; +UX_HOST_CLASS_CDC_ECM *cdc_ecm; + + /* Get the instance. */ + cdc_ecm = (UX_HOST_CLASS_CDC_ECM *) cdc_ecm_class; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ECM_WRITE, cdc_ecm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We're arming transfer now. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_check_and_arm_in_process = UX_TRUE; + + /* We need to disable interrupts here because we need to make sure that if the xmit + queue is non-null, it remains non-null until we have queued the packet. + Note that we do not have to worry about the case where the queue is null, + because we are the only ones that can change it from null to non-null. */ + TX_DISABLE + + /* Ensure the instance is valid. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Restore interrupts. */ + TX_RESTORE + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_ecm, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Are we in a valid state? */ + if (cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP) + { + + /* Check the queue. See if there is something that is being sent. */ + if (cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head == UX_NULL) + { + + /* Reset the queue pointer of this packet. */ + packet -> nx_packet_queue_next = UX_NULL; + + /* Memorize this packet at the beginning of the queue. */ + cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head = packet; + cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_tail = packet; + + /* Restore interrupts. */ + TX_RESTORE + + /* Now we need to arm the transfer. */ + + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Load the address of the current packet header at the physical header. */ + packet_header = packet -> nx_packet_prepend_ptr; + + /* Setup the transaction parameters. */ + transfer_request -> ux_transfer_request_data_pointer = packet_header; + transfer_request -> ux_transfer_request_requested_length = packet -> nx_packet_length; + + /* Store the packet that owns this transaction. */ + transfer_request -> ux_transfer_request_user_specific = packet; + + /* Arm the transfer request. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Did we successfully arm the transfer? */ + if (status != UX_SUCCESS) + { + + /* Clear the queue. No need to clear the tail. */ + cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head = UX_NULL; + + /* We cleared the queue, so we must free the packet. First + we need to clean it before passing it to NetX. */ + packet -> nx_packet_prepend_ptr = packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + packet -> nx_packet_length = packet -> nx_packet_length - UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + + /* And ask Netx to release it. */ + nx_packet_transmit_release(packet); + + /* Could not arm this transfer. */ + status = UX_ERROR; + } + } + else + { + + /* The packet to be sent is the last in the chain. */ + packet -> nx_packet_queue_next = NX_NULL; + + /* Memorize the packet to be sent. */ + cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_tail -> nx_packet_queue_next = packet; + + /* Set the tail. */ + cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_tail = packet; + + /* Restore interrupts. */ + TX_RESTORE + + /* Successfully added to queue. */ + status = UX_SUCCESS; + } + } + else + { + + /* Link was down. */ + + /* Restore interrupts. */ + TX_RESTORE + + /* Release the packet. */ + packet -> nx_packet_prepend_ptr = packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + packet -> nx_packet_length = packet -> nx_packet_length - UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE; + nx_packet_transmit_release(packet); + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_CDC_ECM_LINK_STATE_DOWN_ERROR); + + /* Return error. */ + status = UX_ERROR; + } + + /* Signal that we are done arming and resume waiting thread if necessary. */ + cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_check_and_arm_in_process = UX_FALSE; + if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish == UX_TRUE) + _ux_utility_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish_semaphore); + + /* We are done here. */ + return(status); +} diff --git a/common/usbx_host_classes/src/ux_host_class_gser_activate.c b/common/usbx_host_classes/src/ux_host_class_gser_activate.c new file mode 100644 index 0000000..fa49e51 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_activate.c @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to activate the class. */ +/* */ +/* INPUT */ +/* */ +/* command Dpump class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_gser_configure Configure gser class */ +/* _ux_host_class_gser_endpoints_get Get endpoints of gser */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_gser_entry Entry of gser class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_DEVICE *device; +UX_HOST_CLASS_GSER *gser; +UINT status; + + + /* The Generic Modem class is always activated by the device descriptor. */ + device = (UX_DEVICE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + gser = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_GSER)); + if (gser == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + gser -> ux_host_class_gser_class = command -> ux_host_class_command_class_ptr; + + /* Store the device container into the gser class instance. */ + gser -> ux_host_class_gser_device = device; + + /* Store the instance in the device container, this is for the USBX stack + when it needs to invoke the class for deactivation. */ + device -> ux_device_class_instance = (VOID *) gser; + + /* Create this class instance. */ + status = _ux_host_stack_class_instance_create(gser -> ux_host_class_gser_class, (VOID *) gser); + + /* Configure the gser class. */ + status = _ux_host_class_gser_configure(gser); + + /* Get the gser endpoint(s). We will need to search for Bulk Out and Bulk In endpoints on each interface . */ + if (status == UX_SUCCESS) + status = _ux_host_class_gser_endpoints_get(gser); + + /* Success things. */ + if (status == UX_SUCCESS) + { + + /* Mark the gser as live now. */ + gser -> ux_host_class_gser_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, gser -> ux_host_class_gser_class, (VOID *) gser); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_ACTIVATE, gser, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, gser, 0, 0, 0) + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Destroy class instance. */ + _ux_host_stack_class_instance_destroy(gser -> ux_host_class_gser_class, (VOID *) gser); + + /* Clear the instance in the device container. */ + device -> ux_device_class_instance = UX_NULL; + + /* Free instance memory. */ + _ux_utility_memory_free(gser); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_command.c b/common/usbx_host_classes/src/ux_host_class_gser_command.c new file mode 100644 index 0000000..6146204 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_command.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_command PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a command to the ACM device. The command */ +/* can be one of the following : */ +/* SET_CONTROL */ +/* SET_LINE */ +/* SEND_BREAK */ +/* */ +/* */ +/* INPUT */ +/* */ +/* acm Pointer to acm class */ +/* command command value */ +/* value value to be sent in the */ +/* command request */ +/* data_buffer buffer to be sent */ +/* data_length length of the buffer to send */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_command(UX_HOST_CLASS_GSER *gser, ULONG interface_index, ULONG command, + ULONG value, UCHAR *data_buffer, ULONG data_length) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +ULONG request_direction; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &gser -> ux_host_class_gser_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Check the direction of the command. */ + switch (command) + { + + case UX_HOST_CLASS_GSER_REQ_SEND_ENCAPSULATED_COMMAND : + case UX_HOST_CLASS_GSER_REQ_SET_COMM_FEATURE : + case UX_HOST_CLASS_GSER_REQ_CLEAR_COMM_FEATURE : + case UX_HOST_CLASS_GSER_REQ_SET_AUX_LINE_STATE : + case UX_HOST_CLASS_GSER_REQ_SET_HOOK_STATE : + case UX_HOST_CLASS_GSER_REQ_PULSE_SETUP : + case UX_HOST_CLASS_GSER_REQ_SEND_PULSE : + case UX_HOST_CLASS_GSER_REQ_SET_PUSLE_TIME : + case UX_HOST_CLASS_GSER_REQ_RING_AUX_JACK : + case UX_HOST_CLASS_GSER_REQ_SET_LINE_CODING : + case UX_HOST_CLASS_GSER_REQ_SET_LINE_STATE : + case UX_HOST_CLASS_GSER_REQ_SEND_BREAK : + case UX_HOST_CLASS_GSER_REQ_SET_RINGER_PARMS : + case UX_HOST_CLASS_GSER_REQ_SET_OPERATION_PARMS : + case UX_HOST_CLASS_GSER_REQ_SET_LINE_PARMS : + + /* Direction is out */ + request_direction = UX_REQUEST_OUT; + break; + + + case UX_HOST_CLASS_GSER_REQ_GET_ENCAPSULATED_COMMAND : + case UX_HOST_CLASS_GSER_REQ_GET_COMM_FEATURE : + case UX_HOST_CLASS_GSER_REQ_GET_LINE_CODING : + case UX_HOST_CLASS_GSER_REQ_GET_RINGER_PARMS : + case UX_HOST_CLASS_GSER_REQ_GET_OPERATION_PARMS : + case UX_HOST_CLASS_GSER_REQ_GET_LINE_PARMS : + + /* Direction is in */ + request_direction = UX_REQUEST_IN; + break; + + + default : + + return(UX_ERROR); + + } + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&gser -> ux_host_class_gser_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer_request for the request. */ + transfer_request -> ux_transfer_request_data_pointer = data_buffer; + transfer_request -> ux_transfer_request_requested_length = data_length; + transfer_request -> ux_transfer_request_function = command; + transfer_request -> ux_transfer_request_type = request_direction | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = value; + transfer_request -> ux_transfer_request_index = gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface->ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_configure.c b/common/usbx_host_classes/src/ux_host_class_gser_configure.c new file mode 100644 index 0000000..d0ab93b --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_configure.c @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* gser. Once the gser is configured, its interface will be */ +/* activated. The bulk endpoints enumerated(1 IN, 1 OUT ). */ +/* */ +/* INPUT */ +/* */ +/* gser Pointer to gser class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* _ux_utility_semaphore_create Create semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_gser_activate gser class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_configure(UX_HOST_CLASS_GSER *gser) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; +ULONG interface_index; + + /* If the device has been configured already, we don't need to do it + again. */ + if (gser -> ux_host_class_gser_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A gser normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(gser -> ux_host_class_gser_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, gser -> ux_host_class_gser_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the gser power source and check the parent power source for + incompatible connections. */ + if (gser -> ux_host_class_gser_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = gser -> ux_host_class_gser_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root gser and we don't have to worry + if the parent is not the root gser, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the gser default alternate setting for the gser interface is + active. We have to scan all interfaces and attach them. Each interface has an semaphore as well for protection. */ + for (interface_index = 0; interface_index < UX_HOST_CLASS_GSER_INTERFACE_NUMBER; interface_index++) + { + + /* Get that interface. */ + status = _ux_host_stack_configuration_interface_get(configuration, interface_index, 0, &gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface); + + /* Should not fail. But do a sanity check. */ + if (status == UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface -> ux_interface_class_instance = (VOID *) gser; + + /* Store the class container in the interface. The device has the correct class, duplicate it to the + interface. */ + gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface -> ux_interface_class = gser -> ux_host_class_gser_device -> ux_device_class ; + + /* Create the semaphore to protect 2 threads from accessing the same gser instance. */ + status = _ux_utility_semaphore_create(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore, "ux_host_class_gser_semaphore", 1); + + /* Check status. */ + if (status != UX_SUCCESS) + return(UX_SEMAPHORE_ERROR); + + } + } + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_deactivate.c b/common/usbx_host_classes/src/ux_host_class_gser_deactivate.c new file mode 100644 index 0000000..4ecdfb8 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_deactivate.c @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the gser has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* pipes will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Swar class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_gser_entry Entry of gser class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_GSER *gser; +ULONG interface_index; + + /* Get the instance for this class. */ + gser = (UX_HOST_CLASS_GSER *) command -> ux_host_class_command_instance; + + /* The gser class is being shut down. */ + gser -> ux_host_class_gser_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + for (interface_index = 0; interface_index < UX_HOST_CLASS_GSER_INTERFACE_NUMBER; interface_index++) + { + + /* We need to abort transactions on the bulk Out pipes. */ + _ux_host_stack_endpoint_transfer_abort(gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_out_endpoint); + + /* We need to abort transactions on the bulk In pipes. */ + _ux_host_stack_endpoint_transfer_abort(gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + } + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(gser -> ux_host_class_gser_class, (VOID *) gser); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, gser -> ux_host_class_gser_class, (VOID *) gser); + } + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_DEACTIVATE, gser, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(gser); + + /* Free the gser instance memory. */ + _ux_utility_memory_free(gser); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_gser_endpoints_get.c new file mode 100644 index 0000000..ca59f94 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_endpoints_get.c @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function search for the handle of the bulk out and bulk in */ +/* endpoints. The Generic Serial USB device has multiple interfaces. */ +/* */ +/* INPUT */ +/* */ +/* gser Pointer to gser class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_gser_activate Activate gser class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_endpoints_get(UX_HOST_CLASS_GSER *gser) +{ + +UINT status; +UX_ENDPOINT *endpoint; +ULONG endpoint_index; +ULONG interface_index; + + /* Search the endpoints on all interfaces. */ + for (interface_index = 0; interface_index < UX_HOST_CLASS_GSER_INTERFACE_NUMBER; interface_index++) + { + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* We have found the bulk endpoint, save it. */ + gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_out_endpoint = endpoint; + break; + } + } + } + + /* The bulk out endpoint is mandatory. */ + if (gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. It is attached to the interface container. */ + + for (endpoint_index = 0; endpoint_index < gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the bulk endpoint, save it. */ + gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint = endpoint; + break; + } + } + } + + /* The bulk in endpoint is mandatory. */ + if (gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint == UX_NULL) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + } + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_entry.c b/common/usbx_host_classes/src/ux_host_class_gser_entry.c new file mode 100644 index 0000000..122c9ff --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_entry.c @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the gser class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* gser on the bus or when the USB gser is removed. */ +/* */ +/* INPUT */ +/* */ +/* command gser class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_gser_activate Activate gser class */ +/* _ux_host_class_gser_deactivate Deactivate gser class */ +/* */ +/* CALLED BY */ +/* */ +/* Data pump Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if(((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_PIDVID) && + (command -> ux_host_class_command_pid == UX_HOST_CLASS_GSER_PRODUCT_ID) && + (command -> ux_host_class_command_vid == UX_HOST_CLASS_GSER_VENDOR_ID ))) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_gser_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_gser_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_ioctl.c b/common/usbx_host_classes/src/ux_host_class_gser_ioctl.c new file mode 100644 index 0000000..eff1cd7 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_ioctl.c @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the ioctl entry point for the application to */ +/* configure the ACM device. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* acm Pointer to acm class */ +/* ioctl_function ioctl function */ +/* parameter pointer to structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_gser_command Send command to acm device */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_ioctl(UX_HOST_CLASS_GSER *gser, ULONG interface_index, ULONG ioctl_function, + VOID *parameter) +{ + +UINT status; +UCHAR *data_buffer; +UX_HOST_CLASS_GSER_LINE_CODING *line_coding; +UX_HOST_CLASS_GSER_LINE_STATE *line_state; +VOID (*callback_function) (struct UX_HOST_CLASS_GSER_STRUCT *, ULONG, ULONG ); +ULONG value; + + /* Ensure the instance is valid. */ + if (gser -> ux_host_class_gser_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* The command request will tell us we need to do here. */ + switch (ioctl_function) + { + + case UX_HOST_CLASS_GSER_IOCTL_SET_LINE_CODING: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_SET_LINE_CODING, gser, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Allocate some cache safe memory for the control command. */ + data_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_GSER_LINE_CODING_LENGTH); + + /* Check if error. Return with error if no memory could be allocated. */ + if (data_buffer == UX_NULL) + + /* Do not proceed. Set error code. */ + status = UX_MEMORY_INSUFFICIENT; + else + { + + /* Build the buffer from the calling parameter. Cast the calling parameter. */ + line_coding = (UX_HOST_CLASS_GSER_LINE_CODING *) parameter; + + /* Put the data rate. */ + _ux_utility_long_put(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_RATE, + line_coding -> ux_host_class_gser_line_coding_dter); + + /* Then the stop bit. */ + *(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_STOP_BIT) = + (UCHAR) line_coding -> ux_host_class_gser_line_coding_stop_bit; + + /* Then the parity. */ + *(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_PARITY) = + (UCHAR) line_coding -> ux_host_class_gser_line_coding_parity; + + /* Finally the data bits. */ + *(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_DATA_BIT) = + (UCHAR) line_coding -> ux_host_class_gser_line_coding_data_bits; + + /* Send the command to the device. */ + status = _ux_host_class_gser_command(gser, interface_index, UX_HOST_CLASS_GSER_REQ_SET_LINE_CODING, + 0, data_buffer, UX_HOST_CLASS_GSER_LINE_CODING_LENGTH); + + /* We free the resources allocated no matter what. */ + _ux_utility_memory_free(data_buffer); + } + break; + + case UX_HOST_CLASS_GSER_IOCTL_GET_LINE_CODING: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_GET_LINE_CODING, gser, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Allocate some cache safe memory for the control command. */ + data_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_GSER_LINE_CODING_LENGTH); + + /* Check if error. Return with error if no memory could be allocated. */ + if (data_buffer == UX_NULL) + + /* Do not proceed. Set error code. */ + status = UX_MEMORY_INSUFFICIENT; + else + { + + /* Send the command to the device. */ + status = _ux_host_class_gser_command(gser, interface_index, UX_HOST_CLASS_GSER_REQ_GET_LINE_CODING, + 0, data_buffer, UX_HOST_CLASS_GSER_LINE_CODING_LENGTH); + + /* Fill in the calling buffer if the result is successful. */ + if (status == UX_SUCCESS) + { + + /* Build the buffer from the calling parameter. Cast the calling parameter. */ + line_coding = (UX_HOST_CLASS_GSER_LINE_CODING *) parameter; + + /* Get the data rate. */ + line_coding -> ux_host_class_gser_line_coding_dter = _ux_utility_long_get(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_RATE); + + /* Then the stop bit. */ + line_coding -> ux_host_class_gser_line_coding_stop_bit = + (ULONG) *(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_STOP_BIT); + + /* Then the parity. */ + line_coding -> ux_host_class_gser_line_coding_parity = + (ULONG) *(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_PARITY); + + /* Finally the data bits. */ + line_coding -> ux_host_class_gser_line_coding_data_bits = + (ULONG) *(data_buffer + UX_HOST_CLASS_GSER_LINE_CODING_DATA_BIT); + } + + /* We free the resources allocated no matter what. */ + _ux_utility_memory_free(data_buffer); + } + break; + + case UX_HOST_CLASS_GSER_IOCTL_SET_LINE_STATE: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_SET_LINE_STATE, gser, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Cast the calling parameter. */ + line_state = (UX_HOST_CLASS_GSER_LINE_STATE *) parameter; + + /* Build the value field. */ + value = (line_state -> ux_host_class_gser_line_state_dtr | + (line_state -> ux_host_class_gser_line_state_rts << 1)); + + /* Send the command to the device. */ + status = _ux_host_class_gser_command(gser, interface_index, UX_HOST_CLASS_GSER_REQ_SET_LINE_STATE, + value, UX_NULL,0); + break; + + case UX_HOST_CLASS_GSER_IOCTL_SEND_BREAK : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_SEND_BREAK, gser, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Build the value field. */ + value = *((ULONG *) parameter); + + /* Send the command to the device. */ + status = _ux_host_class_gser_command(gser, interface_index, UX_HOST_CLASS_GSER_REQ_SEND_BREAK, + value, UX_NULL,0); + break; + + case UX_HOST_CLASS_GSER_IOCTL_ABORT_IN_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_ABORT_IN_PIPE, gser, gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_GSER_IOCTL_ABORT_OUT_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_ABORT_OUT_PIPE, gser, gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_out_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_out_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_GSER_IOCTL_NOTIFICATION_CALLBACK : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_NOTIFICATION_CALLBACK, gser, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Register a callback when the line state has changed. */ + callback_function = ((VOID (*) (struct UX_HOST_CLASS_GSER_STRUCT *, ULONG, ULONG )) (ALIGN_TYPE)parameter); + gser -> ux_host_class_gser_device_status_change_callback = callback_function; + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_GSER_IOCTL_GET_DEVICE_STATUS : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_IOCTL_GET_DEVICE_STATUS, gser, gser -> ux_host_class_gser_device_state, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Return the device status. */ + * ((ULONG *) parameter) = gser -> ux_host_class_gser_device_state; + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_read.c b/common/usbx_host_classes/src/ux_host_class_gser_read.c new file mode 100644 index 0000000..933d046 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_read.c @@ -0,0 +1,210 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the gser interface. The call is */ +/* blocking and only returns when there is either an error or when */ +/* the transfer is complete. */ +/* */ +/* INPUT */ +/* */ +/* gser Pointer to gser class */ +/* data_pointer Pointer to buffer */ +/* requested_length Requested data read */ +/* actual_length Actual data read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_read(UX_HOST_CLASS_GSER *gser, + ULONG interface_index, + UCHAR *data_pointer, + ULONG requested_length, + ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_READ, gser, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (gser -> ux_host_class_gser_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Start by resetting the actual length of the transfer to zero. */ + *actual_length = 0; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Save the interface number in the Transfer Request. */ + transfer_request -> ux_transfer_request_user_specific = (VOID *)(ALIGN_TYPE)interface_index; + + /* Perform a transfer on the bulk in endpoint until either the transfer is + completed or until there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer_request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_GSER_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer request in case some data was actually received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* There was a non transfer error, no partial transfer to be checked. */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually received and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to receive. */ + requested_length -= transfer_request_length; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_reception_callback.c b/common/usbx_host_classes/src/ux_host_class_gser_reception_callback.c new file mode 100644 index 0000000..098aca0 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_reception_callback.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_reception_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback from the USBX transfer functions, */ +/* it is called when a full or partial transfer has been done for a */ +/* bulk in transfer. It calls back the application. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_gser_reception_callback (UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_GSER *gser; +UX_HOST_CLASS_GSER_RECEPTION *gser_reception; +ULONG interface_index; + + /* Get the class instance for this transfer request. */ + gser = (UX_HOST_CLASS_GSER *) transfer_request -> ux_transfer_request_class_instance; + + /* The interface index was stored into the user specific field. */ + interface_index = (ULONG) (ALIGN_TYPE) transfer_request -> ux_transfer_request_user_specific; + + /* Get the pointer to the acm reception structure. */ + gser_reception = gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_reception; + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* The reception is stopped. */ + gser_reception -> ux_host_class_gser_reception_state = UX_HOST_CLASS_GSER_RECEPTION_STATE_STOPPED; + + /* We do not proceed. */ + return; + + } + + /* And move to the next reception buffer. Check if we are at the end of the application buffer. */ + if (gser_reception -> ux_host_class_gser_reception_data_head + gser_reception -> ux_host_class_gser_reception_block_size >= + gser_reception -> ux_host_class_gser_reception_data_buffer + gser_reception -> ux_host_class_gser_reception_data_buffer_size) + { + + /* We are at the end of the buffer. Move back to the beginning if we have space available. */ + if (gser_reception -> ux_host_class_gser_reception_data_tail == gser_reception -> ux_host_class_gser_reception_data_buffer) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_BUFFER_OVERFLOW, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We have an overflow. We cannot continue. Report to the application. */ + gser_reception -> ux_host_class_gser_reception_callback(gser, UX_BUFFER_OVERFLOW, UX_NULL, 0); + + /* And stop the transfer in progress flag. */ + gser_reception -> ux_host_class_gser_reception_state = UX_HOST_CLASS_GSER_RECEPTION_STATE_STOPPED; + + return; + } + else + + /* Program the head to be at the beginning of the application buffer. */ + gser_reception -> ux_host_class_gser_reception_data_head = gser_reception -> ux_host_class_gser_reception_data_buffer; + + } + else + + /* Program the head to be after the current buffer. */ + gser_reception -> ux_host_class_gser_reception_data_head += gser_reception -> ux_host_class_gser_reception_block_size; + + + /* We need to report this transfer to the application. */ + gser_reception -> ux_host_class_gser_reception_callback(gser, + transfer_request -> ux_transfer_request_completion_code, + transfer_request -> ux_transfer_request_data_pointer, + transfer_request -> ux_transfer_request_actual_length); + + /* Arm another transfer. */ + _ux_host_stack_transfer_request(transfer_request); + + /* There is no status to be reported back to the stack. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_reception_start.c b/common/usbx_host_classes/src/ux_host_class_gser_reception_start.c new file mode 100644 index 0000000..551bd0e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_reception_start.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_reception_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts a reception with the generic serial class. This*/ +/* allows for non blocking calls based on a packet orientated round */ +/* robbin buffer. When a packet is fully or partially received, an */ +/* application callback function is invoked and a new transfer request */ +/* is rescheduled. */ +/* */ +/* INPUT */ +/* */ +/* gser Pointer to gser class */ +/* gser_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_reception_start (UX_HOST_CLASS_GSER *gser, + UX_HOST_CLASS_GSER_RECEPTION *gser_reception) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG interface_index; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_RECEPTION_START, gser, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (gser -> ux_host_class_gser_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Get the interface index. */ + interface_index = gser_reception -> ux_host_class_gser_reception_interface_index; + + /* Start by aligning the head and tail of buffers to the same address supplied by the application. */ + gser_reception -> ux_host_class_gser_reception_data_head = gser_reception -> ux_host_class_gser_reception_data_buffer; + gser_reception -> ux_host_class_gser_reception_data_tail = gser_reception -> ux_host_class_gser_reception_data_buffer; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Save the interface number in the Transfer Request. */ + transfer_request -> ux_transfer_request_user_specific = (VOID *) (ALIGN_TYPE) interface_index; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) gser; + transfer_request -> ux_transfer_request_data_pointer = gser_reception -> ux_host_class_gser_reception_data_head; + transfer_request -> ux_transfer_request_requested_length = gser_reception -> ux_host_class_gser_reception_block_size; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_gser_reception_callback; + + /* Save the acm reception structure in the acm structure. */ + gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_reception = gser_reception; + + /* And declare we have a transfer in progress. */ + gser_reception -> ux_host_class_gser_reception_state = UX_HOST_CLASS_GSER_RECEPTION_STATE_STARTED; + + /* Arm a first transfer on the bulk in endpoint. There is a callback to this function so we return to the caller + right away. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* We do not know if the first transfer was successful yet. If the status is not OK, we need to stop the transfer + in progress flag. */ + if (status != UX_SUCCESS) + gser_reception -> ux_host_class_gser_reception_state = UX_HOST_CLASS_GSER_RECEPTION_STATE_STOPPED; + + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_reception_stop.c b/common/usbx_host_classes/src/ux_host_class_gser_reception_stop.c new file mode 100644 index 0000000..7f9b4d4 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_reception_stop.c @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_reception_stop PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts a reception with the generic modem. This way */ +/* allows for non blocking calls based on a packet orientated round */ +/* robbin buffer. When a packet is fully or partially received, an */ +/* application callback function is invoked and a new transfer request */ +/* is rescheduled. */ +/* */ +/* INPUT */ +/* */ +/* gser Pointer to gser class */ +/* gser_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_reception_stop (UX_HOST_CLASS_GSER *gser, + UX_HOST_CLASS_GSER_RECEPTION *gser_reception) +{ + +ULONG interface_index; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_RECEPTION_STOP, gser, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (gser -> ux_host_class_gser_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Check if we do have transfers for this application. If none, nothing to do. */ + if (gser_reception -> ux_host_class_gser_reception_state == UX_HOST_CLASS_GSER_RECEPTION_STATE_STOPPED) + return(UX_SUCCESS); + + /* Get the interface index. */ + interface_index = gser_reception -> ux_host_class_gser_reception_interface_index; + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_in_endpoint); + + /* Declare the reception stopped. */ + gser_reception -> ux_host_class_gser_reception_state = UX_HOST_CLASS_GSER_RECEPTION_STATE_STOPPED; + + /* This function never really fails. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_gser_write.c b/common/usbx_host_classes/src/ux_host_class_gser_write.c new file mode 100644 index 0000000..9fbb1cc --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_gser_write.c @@ -0,0 +1,204 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Generic Serial Host module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_gser.h" +#include "ux_host_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_gser_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the gser interface. The call is blocking */ +/* and only returns when there is either an error or when the transfer */ +/* is complete. */ +/* */ +/* INPUT */ +/* */ +/* gser Pointer to gser class */ +/* data_pointer Pointer to data to write */ +/* requested_length Length of data to write */ +/* actual_length Actual length of data written */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_gser_write(UX_HOST_CLASS_GSER *gser, + ULONG interface_index, + UCHAR * data_pointer, + ULONG requested_length, + ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_GSER_WRITE, gser, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (gser -> ux_host_class_gser_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, gser, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Start by resetting the actual length of the transfer. */ + *actual_length = 0; + + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Save the interface number in the Transfer Request. */ + transfer_request -> ux_transfer_request_user_specific = (VOID *) (ALIGN_TYPE) interface_index; + + /* Perform a transfer on the bulk out endpoint until either the transfer is + completed or when there is an error. */ + do + { + + /* Program the maximum authorized length for this transfer_request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_GSER_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer_request in case some data actually went out. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be sent. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually sent and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to send out. */ + requested_length -= transfer_request_length; + + } while (requested_length != 0); + + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&gser -> ux_host_class_gser_interface_array[interface_index].ux_host_class_gser_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_activate.c b/common/usbx_host_classes/src/ux_host_class_hid_activate.c new file mode 100644 index 0000000..723e8a3 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_activate.c @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the enumeration of the HID class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_client_search HID client search */ +/* _ux_host_class_hid_configure Configure HID */ +/* _ux_host_class_hid_descriptor_parse Parse descriptor */ +/* _ux_host_class_hid_interrupt_endpoint_search Search endpoint */ +/* _ux_host_class_hid_instance_clean Clean up instance resources */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_semaphore_create Create semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_HID *hid; +UINT status; + + + /* The HID is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Instantiate this HID class */ + hid = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY,sizeof(UX_HOST_CLASS_HID)); + if (hid == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + hid -> ux_host_class_hid_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the HID class instance. */ + hid -> ux_host_class_hid_interface = interface; + + /* Store the device container into the HID class instance. */ + hid -> ux_host_class_hid_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) hid; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(command -> ux_host_class_command_class_ptr, (VOID *) hid); + + /* Configure the HID. */ + status = _ux_host_class_hid_configure(hid); + + /* If configure is done success, goes on. */ + if (status == UX_SUCCESS) + { + + /* Get the HID descriptor and parse it. */ + status = _ux_host_class_hid_descriptor_parse(hid); + } + + /* If HID descriptor parse is done success, goes on. */ + if (status == UX_SUCCESS) + { + + /* Search the HID interrupt endpoint but do not start it. */ + status = _ux_host_class_hid_interrupt_endpoint_search(hid); + } + + /* If HID interrupt endpoint is found, goes on. */ + if (status == UX_SUCCESS) + { + + /* Create the semaphore to protect multiple threads from accessing the same + storage instance. */ + status = _ux_utility_semaphore_create(&hid -> ux_host_class_hid_semaphore, "ux_host_class_hid_semaphore", 1); + + if (status == UX_SUCCESS) + { + + /* If all is fine, try to locate the HID client for this HID device. */ + _ux_host_class_hid_client_search(hid); + + /* Mark the HID class as live now. */ + hid -> ux_host_class_hid_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* We may need to inform the application if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, hid -> ux_host_class_hid_class, (VOID *) hid); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_ACTIVATE, hid, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, hid, 0, 0, 0) + + /* Return completion code. */ + return(status); + } + else + { + status = UX_SEMAPHORE_ERROR; + } + } + + /* Clean interrupt endpoint. */ + if (hid -> ux_host_class_hid_interrupt_endpoint && + hid -> ux_host_class_hid_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer) + _ux_utility_memory_free(hid -> ux_host_class_hid_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer); + + /* Clean instance. */ + _ux_host_class_hid_instance_clean(hid); + + /* Error, destroy the class instance and return error code. */ + _ux_host_stack_class_instance_destroy(hid -> ux_host_class_hid_class, (VOID *) hid); + + /* Unmount instance. */ + interface -> ux_interface_class_instance = UX_NULL; + + /* Free instance. */ + _ux_utility_memory_free(hid); + + /* Return error code. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_client_register.c b/common/usbx_host_classes/src/ux_host_class_hid_client_register.c new file mode 100644 index 0000000..945ef98 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_client_register.c @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + +UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(UX_HOST_CLASS_HID_MAX_CLIENTS, sizeof(UX_HOST_CLASS_HID_CLIENT)), UX_HOST_CLASS_HID_MAX_CLIENTS_mem_alloc_ovf) + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_client_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a USB HID client to the HID class. The */ +/* mechanism is similar to the USB stack class registration. The Class */ +/* must specify an entry point for the USB stack to send commands */ +/* such as: */ +/* */ +/* UX_HOST_CLASS_COMMAND_QUERY */ +/* UX_HOST_CLASS_COMMAND_ACTIVATE */ +/* UX_HOST_CLASS_COMMAND_DESTROY */ +/* */ +/* Note: The C string of hid_client_name must be NULL-terminated and */ +/* the length of it (without the NULL-terminator itself) must be no */ +/* larger than UX_HOST_CLASS_HID_MAX_CLIENT_NAME_LENGTH. */ +/* */ +/* INPUT */ +/* */ +/* hid_client_name Name of HID client */ +/* hid_client_handler Handler for HID client */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_get Get class */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_copy Copy memory block */ +/* _ux_utility_string_length_check Check C string and return */ +/* length if null-terminated */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_client_register(UCHAR *hid_client_name, + UINT (*hid_client_handler)(struct UX_HOST_CLASS_HID_CLIENT_COMMAND_STRUCT *)) +{ + +UX_HOST_CLASS *class; +ULONG hid_client_index; +UINT status; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UINT client_name_length = 0; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_CLIENT_REGISTER, hid_client_name, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Get the length of the client name (exclude null-terminator). */ + status = _ux_utility_string_length_check(hid_client_name, &client_name_length, UX_HOST_CLASS_HID_MAX_CLIENT_NAME_LENGTH); + if (status) + return(status); + + /* We need to locate our class container. */ + status = _ux_host_stack_class_get(_ux_system_host_class_hid_name, &class); + + /* If we cannot get the class container, it means the HID class was not registered. */ + if (status != UX_SUCCESS) + return(status); + + /* From the class container, we get the client pointer which has the list of + HID clients. If the pointer is NULL, the client list was not assigned. */ + if (class -> ux_host_class_client == UX_NULL) + { + + /* Allocate memory for the class client. + * Allocate size overflow static checked outside the function. + */ + class -> ux_host_class_client = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + sizeof(UX_HOST_CLASS_HID_CLIENT)*UX_HOST_CLASS_HID_MAX_CLIENTS); + + /* Check for successful allocation. */ + if (class -> ux_host_class_client == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + } + + /* De-reference the client pointer into a HID client array pointer. */ + hid_client = (UX_HOST_CLASS_HID_CLIENT *) class -> ux_host_class_client; + + /* We need to parse the HID client handler table to find an empty spot. */ + for (hid_client_index = 0; hid_client_index < UX_HOST_CLASS_HID_MAX_CLIENTS; hid_client_index++) + { + + /* Check if this HID client is already used. */ + if (hid_client -> ux_host_class_hid_client_status == UX_UNUSED) + { + + /* We have found a free container for the HID client. Copy the name (with null-terminator). */ + _ux_utility_memory_copy(hid_client -> ux_host_class_hid_client_name, hid_client_name, client_name_length + 1); + + /* Memorize the handler address of this client. */ + hid_client -> ux_host_class_hid_client_handler = hid_client_handler; + + /* Mark it as being in use. */ + hid_client -> ux_host_class_hid_client_status = UX_USED; + + /* Return successful completion. */ + return(UX_SUCCESS); + } + else + { + + /* Do a sanity check to make sure the handler is not already installed by + mistake. To verify this, we simple check for the handler entry point. */ + if (hid_client -> ux_host_class_hid_client_handler == hid_client_handler) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_ALREADY_INSTALLED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_ALREADY_INSTALLED, hid_client_name, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_ALREADY_INSTALLED); + } + } + + /* Try the next class. */ + hid_client++; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_ARRAY_FULL); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_ARRAY_FULL, hid_client_name, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* No more entries in the class table. */ + return(UX_MEMORY_ARRAY_FULL); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_client_search.c b/common/usbx_host_classes/src/ux_host_class_hid_client_search.c new file mode 100644 index 0000000..1d96ce9 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_client_search.c @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_client_search PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches for a HID client that wants to own this HID */ +/* device. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_hid_client_handler) HID client handler */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_client_search(UX_HOST_CLASS_HID *hid) +{ + +UX_HOST_CLASS_HID_CLIENT *hid_client; +ULONG hid_client_index; +UINT status; +UX_HOST_CLASS_HID_CLIENT_COMMAND hid_client_command; + + + /* In order to search a HID client, we need both the main page and the main usage. */ + hid_client_command.ux_host_class_hid_client_command_page = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_main_page; + hid_client_command.ux_host_class_hid_client_command_usage = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_main_usage & 0xffff; + hid_client_command.ux_host_class_hid_client_command_instance = hid; + hid_client_command.ux_host_class_hid_client_command_container = (VOID *) hid -> ux_host_class_hid_class; + hid_client_command.ux_host_class_hid_client_command_request = UX_HOST_CLASS_COMMAND_QUERY; + + /* Dereference the client pointer into a HID client pointer. */ + hid_client = (UX_HOST_CLASS_HID_CLIENT *) hid -> ux_host_class_hid_class -> ux_host_class_client; + + /* If the hid_client pointer is NULL, the array of clients was not initialized. */ + if (hid_client == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_UNKNOWN); + } + + /* We need to parse the hid client driver table to find a registered client. */ + for (hid_client_index = 0; hid_client_index < UX_HOST_CLASS_HID_MAX_CLIENTS; hid_client_index++) + { + + /* Check if this HID client is registered. */ + if (hid_client -> ux_host_class_hid_client_status == UX_USED) + { + + /* Call the HID client with a query command. */ + status = hid_client -> ux_host_class_hid_client_handler(&hid_client_command); + + /* Have we found a HID client? */ + if (status == UX_SUCCESS) + { + + /* Update the command to activate the client. */ + hid_client_command.ux_host_class_hid_client_command_request = UX_HOST_CLASS_COMMAND_ACTIVATE; + + /* Memorize the client for this HID device. */ + hid -> ux_host_class_hid_client = hid_client; + + /* Call the HID client with an activate command. */ + status = hid_client -> ux_host_class_hid_client_handler(&hid_client_command); + + /* Unmount the client if activation fail. */ + if (status != UX_SUCCESS) + hid -> ux_host_class_hid_client = UX_NULL; + + /* Return completion status. */ + return(status); + } + } + + /* Try the next HID client. */ + hid_client++; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_HOST_CLASS_HID_UNKNOWN); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_configure.c b/common/usbx_host_classes/src/ux_host_class_hid_configure.c new file mode 100644 index 0000000..71641a2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_configure.c @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* HID. Once the HID is configured, its interface will be activated */ +/* and all the endpoints enumerated (1 interrupt endpoint in the case */ +/* of the HID). */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_configure(UX_HOST_CLASS_HID *hid) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *device; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it again. */ + if (hid -> ux_host_class_hid_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A HID normally has one configuration. So retrieve the 1st configuration only. */ + status = _ux_host_stack_device_configuration_get(hid -> ux_host_class_hid_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, hid -> ux_host_class_hid_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Get the device container for this configuration. */ + device = configuration -> ux_configuration_device; + + /* Get the parent container for this device. */ + parent_device = device -> ux_device_parent; + + /* Check the HID power source and check the parent power source for + incompatible connections. */ + if (hid -> ux_host_class_hid_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* If the device is NULL, the parent is the root hid and we don't have to worry. + If the parent is not the root HID, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + + /* If the operation went well, the hid default alternate setting for the HID interface is active + and the interrupt endpoint is now enabled. We have to memorize the first interface since the + interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &hid -> ux_host_class_hid_interface); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_deactivate.c b/common/usbx_host_classes/src/ux_host_class_hid_deactivate.c new file mode 100644 index 0000000..7b8574a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_deactivate.c @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the HID has been */ +/* removed from the bus either directly or indirectly. The interrupt */ +/* pipe will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_hid_client_handler) HID client handler */ +/* _ux_host_class_hid_instance_clean HID instance clean */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT_COMMAND hid_client_command; +UX_TRANSFER *transfer_request; +UINT status; + + + /* Get the instance for this class. */ + hid = (UX_HOST_CLASS_HID *) command -> ux_host_class_command_instance; + + /* The HID is being shut down. */ + hid -> ux_host_class_hid_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We need to abort transactions on the interrupt pipe. */ + _ux_host_stack_endpoint_transfer_abort(hid -> ux_host_class_hid_interrupt_endpoint); + + /* We need to inform the HID client, if any, of the deactivation. */ + hid_client_command.ux_host_class_hid_client_command_instance = (VOID *) hid; + hid_client_command.ux_host_class_hid_client_command_container = (VOID *) hid -> ux_host_class_hid_class; + hid_client_command.ux_host_class_hid_client_command_request = UX_HOST_CLASS_COMMAND_DEACTIVATE; + + /* Call the HID client with a deactivate command if there was a client registered. */ + if (hid -> ux_host_class_hid_client != UX_NULL) + hid -> ux_host_class_hid_client -> ux_host_class_hid_client_handler(&hid_client_command); + + /* Clean all the HID memory fields. */ + _ux_host_class_hid_instance_clean(hid); + + /* If the Hid class instance has a interrupt pipe with a data payload associated with it + it must be freed. */ + transfer_request = &hid -> ux_host_class_hid_interrupt_endpoint -> ux_endpoint_transfer_request; + + /* Then de allocate the memory. */ + _ux_utility_memory_free(transfer_request -> ux_transfer_request_data_pointer); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(hid -> ux_host_class_hid_class, (VOID *) hid); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&hid -> ux_host_class_hid_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, hid -> ux_host_class_hid_class, (VOID *) hid); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_DEACTIVATE, hid, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(hid); + + /* The HID is now free again. */ + _ux_utility_memory_free(hid); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_descriptor_parse.c b/common/usbx_host_classes/src/ux_host_class_hid_descriptor_parse.c new file mode 100644 index 0000000..10f0dbb --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_descriptor_parse.c @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_descriptor_parse PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the HID descriptor and parses it. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_report_descriptor_get */ +/* Get HID report descriptor */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_descriptor_parse(UX_HOST_CLASS_HID *hid) +{ + +UCHAR *descriptor; +UCHAR *start_descriptor; +UX_CONFIGURATION_DESCRIPTOR configuration_descriptor; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UX_HID_DESCRIPTOR hid_descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UINT status; +ULONG total_configuration_length; +UINT descriptor_length; +UINT descriptor_type; +ULONG current_interface; + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hid -> ux_host_class_hid_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. Since we do not know the size of the + descriptor, we first read the first bytes. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Memorize the descriptor start address. */ + start_descriptor = descriptor; + + /* Reset the current interface to keep compiler warnings happy. */ + current_interface = 0; + + /* Create a transfer request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_CONFIGURATION_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_CONFIGURATION_DESCRIPTOR_ITEM << 8; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH)) + { + + /* Parse the descriptor so that we can read the total length. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration_descriptor); + + /* We don't need this descriptor now. */ + _ux_utility_memory_free(descriptor); + + /* Reallocate the memory necessary for the reading the entire descriptor. */ + total_configuration_length = configuration_descriptor.wTotalLength; + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, total_configuration_length); + if(descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Save this descriptor address. */ + start_descriptor = descriptor; + + /* Read the descriptor again with the correct length this time. */ + transfer_request -> ux_transfer_request_requested_length = total_configuration_length; + + /* Since the address of the descriptor may have changed, reprogram it. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == configuration_descriptor.wTotalLength)) + { + + /* The HID descriptor is embedded within the configuration descriptor. We parse the + entire descriptor to locate the HID portion. */ + while (total_configuration_length) + { + + /* Gather the length and type of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Error, release the memory and return an error. */ + _ux_utility_memory_free(start_descriptor); + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Check the type for an interface descriptor. */ + if (descriptor_type == UX_INTERFACE_DESCRIPTOR_ITEM) + { + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, + _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, + (UCHAR *) &interface_descriptor); + + /* Memorize the interface we are scanning. */ + current_interface = interface_descriptor.bInterfaceNumber; + + } + + /* Check the type for an interface descriptor. */ + /* Check if the descriptor belongs to interface attached to this instance. */ + if (descriptor_type == UX_HOST_CLASS_HID_DESCRIPTOR) + { + + /* Ensure this interface is the one we need to scan. */ + if (current_interface == hid -> ux_host_class_hid_interface -> ux_interface_descriptor.bInterfaceNumber) + { + + /* Obtain the length of the HID descriptor. We need to make machine + independent first. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_hid_descriptor_structure, + UX_HID_DESCRIPTOR_ENTRIES, (UCHAR *) &hid_descriptor); + + /* We have found the hid descriptor. Now we should get the HID report descriptor. */ + status = _ux_host_class_hid_report_descriptor_get(hid, hid_descriptor.wItemLength); + + /* Release the memory. */ + _ux_utility_memory_free(start_descriptor); + + /* Return completion status. */ + return(status); + } + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_configuration_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Release the memory. */ + _ux_utility_memory_free(start_descriptor); + + /* Return an error. */ + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_configuration_length -= descriptor_length; + } + } + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Free all used resources. */ + _ux_utility_memory_free(start_descriptor); + + /* Return an error. */ + return(UX_DESCRIPTOR_CORRUPTED); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_entry.c b/common/usbx_host_classes/src/ux_host_class_hid_entry.c new file mode 100644 index 0000000..c90d204 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_entry.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the HID class. It will be */ +/* called by the USB stack enumeration module when there is a new */ +/* device on the bus or when there is a device extraction. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_activate Activate HID class */ +/* _ux_host_class_hid_deactivate Deactivate HID class */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if ((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + (command -> ux_host_class_command_class == UX_HOST_CLASS_HID_CLASS)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + + status = _ux_host_class_hid_activate(command); + + return(status); + + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_hid_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error status. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_field_decompress.c b/common/usbx_host_classes/src/ux_host_class_hid_field_decompress.c new file mode 100644 index 0000000..9653466 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_field_decompress.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_field_decompress PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will decompress a field and return the usage/value. */ +/* */ +/* INPUT */ +/* */ +/* hid_field Pointer to HID field */ +/* report_buffer Pointer to report buffer */ +/* client_report Pointer to client report */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_field_decompress(UX_HOST_CLASS_HID_FIELD *hid_field, UCHAR *report_buffer, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report) +{ + +ULONG field_report_count; +ULONG field_report_size; +ULONG data_offset_byte; +ULONG data_offset_bit; +ULONG data_offset_bit_in_report; +ULONG field_value; +ULONG field_value_bit_shifting; +ULONG field_usage; +ULONG report_content; + + + /* Calculate the address of the beginning of the field in the report. */ + data_offset_byte = (hid_field -> ux_host_class_hid_field_report_offset >> 3); + + /* Calculate the bit start address. Hopefully things will be on a boundary but not necessary. */ + data_offset_bit = hid_field -> ux_host_class_hid_field_report_offset & 7; + + /* Each report field has a report_count value. This count is used to extract values from the + incoming report and build each usage/value instance. */ + for (field_report_count = 0; field_report_count < hid_field -> ux_host_class_hid_field_report_count; field_report_count++) + { + + /* Get the report size in bits. */ + field_report_size = hid_field -> ux_host_class_hid_field_report_size; + + /* We use the bit offset for this report and not the generic field bit offset. */ + data_offset_bit_in_report = data_offset_bit; + + /* Reset the local value. */ + field_value = 0; + + /* And start with bit 0 in the target value. */ + field_value_bit_shifting = 0; + + /* Build the value field bit by bit. */ + while (field_report_size-- != 0) + { + + /* Read the content. This method is redundant if we are not changing byte but it + makes the algorithm much easier. */ + report_content = (ULONG) *(report_buffer + data_offset_byte + (data_offset_bit_in_report >> 3)); + + /* Shift the current value content to allow space to store the new bit. */ + field_value |= ((report_content >> (data_offset_bit_in_report & 7)) & 1) << field_value_bit_shifting; + + /* Move to next bit in the report. */ + data_offset_bit_in_report++; + + /* And the next bit in the value. */ + field_value_bit_shifting++; + } + + /* The Usage value will depend if the data is defined as a variable or an array in the HID report. */ + if (hid_field -> ux_host_class_hid_field_value & UX_HOST_CLASS_HID_ITEM_VARIABLE) + { + + /* Take the usage directly from the usage array. */ + field_usage = *(hid_field -> ux_host_class_hid_field_usages + field_report_count); + } + else + { + + /* This is an array, so compute the usage from the min value, the report count and the + computed report value. */ + field_usage = hid_field -> ux_host_class_hid_field_usage_min + (ULONG)((SLONG)field_value - hid_field -> ux_host_class_hid_field_logical_min); + + /* Also add the usage page. */ + field_usage |= (hid_field -> ux_host_class_hid_field_usage_page << 16); + } + + /* Put the value and the usage into the caller's buffer. */ + *(client_report -> ux_host_class_hid_client_report_buffer + client_report -> ux_host_class_hid_client_report_actual_length) = field_usage; + client_report -> ux_host_class_hid_client_report_actual_length++; + *(client_report -> ux_host_class_hid_client_report_buffer + client_report -> ux_host_class_hid_client_report_actual_length) = field_value; + client_report -> ux_host_class_hid_client_report_actual_length++; + + /* Calculate the next address for this field. */ + data_offset_bit += hid_field -> ux_host_class_hid_field_report_size; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_global_item_parse.c b/common/usbx_host_classes/src/ux_host_class_hid_global_item_parse.c new file mode 100644 index 0000000..58b176f --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_global_item_parse.c @@ -0,0 +1,249 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_global_item_parse PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function parses a global item from the report descriptor. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* item Pointer to item */ +/* descriptor Pointer to descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_item_data_get Get data item */ +/* _ux_utility_memory_copy Copy memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_global_item_parse(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_ITEM *item, UCHAR *descriptor) +{ + +UX_HOST_CLASS_HID_PARSER *hid_parser; + + + /* Get the temporary parser structure pointer. */ + hid_parser = &hid -> ux_host_class_hid_parser; + + /* Get the tag of the item structure and process it. */ + switch(item -> ux_host_class_hid_item_report_tag) + { + + case UX_HOST_CLASS_HID_GLOBAL_TAG_USAGE_PAGE: + + /* Usage Page Tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_usage_page = + _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_LOGICAL_MINIMUM: + + /* Logical Minimum Tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_logical_min = + (SLONG) _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_LOGICAL_MAXIMUM: + + /* Logical Maximum Tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_logical_max = + (SLONG) _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_PHYSICAL_MINIMUM: + + /* Physical Minimum Tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_physical_min = + (SLONG) _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_PHYSICAL_MAXIMUM: + + /* Physical Maximum Tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_physical_max = + (SLONG) _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_UNIT_EXPONENT: + + /* Unit Exponent Tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_unit_expo = + _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_UNIT: + + /* Unit tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_unit = + _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_REPORT_SIZE: + + /* Report Size tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_size = + _ux_host_class_hid_item_data_get(descriptor, item); + + if (hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_size > UX_HOST_CLASS_HID_REPORT_SIZE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_REPORT_OVERFLOW); + } + + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_REPORT_ID: + + /* Report ID tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_id = + _ux_host_class_hid_item_data_get(descriptor, item); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_REPORT_COUNT: + + /* Report Count tag. */ + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count = + _ux_host_class_hid_item_data_get(descriptor, item); + + if (hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count > UX_HOST_CLASS_HID_USAGES) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_USAGE_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_USAGE_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_USAGE_OVERFLOW); + } + + break; + + case UX_HOST_CLASS_HID_GLOBAL_TAG_PUSH: + + /* Push tag. */ + if (hid_parser -> ux_host_class_hid_parser_number_global >= UX_HOST_CLASS_HID_MAX_GLOBAL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_PUSH_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_PUSH_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_PUSH_OVERFLOW); + } + + else + _ux_utility_memory_copy(&hid_parser -> ux_host_class_hid_parser_global_pool[hid_parser -> ux_host_class_hid_parser_number_global++], + &hid_parser -> ux_host_class_hid_parser_global, sizeof(UX_HOST_CLASS_HID_GLOBAL_ITEM)); + break; + + + case UX_HOST_CLASS_HID_GLOBAL_TAG_POP: + + /* Pop tag. */ + if(hid_parser -> ux_host_class_hid_parser_number_global == 0) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_POP_UNDERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_POP_UNDERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_POP_UNDERFLOW); + } + else + _ux_utility_memory_copy(&hid_parser -> ux_host_class_hid_parser_global, + &hid_parser -> ux_host_class_hid_parser_global_pool[--hid_parser -> ux_host_class_hid_parser_number_global], + sizeof(UX_HOST_CLASS_HID_GLOBAL_ITEM)); + + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_TAG_UNSUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_TAG_UNSUPPORTED, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* This tag was not recognized or is not supported. */ + return(UX_HOST_CLASS_HID_TAG_UNSUPPORTED); + } + + /* We get here when the tag has been processed successfully. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_idle_get.c b/common/usbx_host_classes/src/ux_host_class_hid_idle_get.c new file mode 100644 index 0000000..acf0f01 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_idle_get.c @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_idle_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a GET_IDLE to the HID device. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* idle_time Idle time */ +/* report_id Report ID */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_idle_get(UX_HOST_CLASS_HID *hid, USHORT *idle_time, USHORT report_id) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UCHAR *idle_byte; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_IDLE_GET, hid, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hid -> ux_host_class_hid_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the idle byte. */ + idle_byte = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 1); + if (idle_byte == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return error status. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Create a transfer request for the GET_IDLE request. */ + transfer_request -> ux_transfer_request_data_pointer = idle_byte; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_HID_GET_IDLE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = report_id; + transfer_request -> ux_transfer_request_index = hid -> ux_host_class_hid_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and for the entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 1)) + *idle_time = (USHORT) *idle_byte; + + /* Free used resources. */ + _ux_utility_memory_free(idle_byte); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return the completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_idle_set.c b/common/usbx_host_classes/src/ux_host_class_hid_idle_set.c new file mode 100644 index 0000000..a7fd64d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_idle_set.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_idle_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a SET_IDLE to the HID device. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* idle_time Idle time */ +/* report_id Report ID */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_idle_set(UX_HOST_CLASS_HID *hid, USHORT idle_time, USHORT report_id) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_IDLE_SET, hid, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hid -> ux_host_class_hid_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Create a transfer request for the SET_IDLE request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_HID_SET_IDLE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = (UINT)((idle_time << 8) | report_id); + transfer_request -> ux_transfer_request_index = hid -> ux_host_class_hid_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return the function status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_instance_clean.c b/common/usbx_host_classes/src/ux_host_class_hid_instance_clean.c new file mode 100644 index 0000000..5e9d52c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_instance_clean.c @@ -0,0 +1,206 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_instance_clean PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cleans all the components of a HID instance. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_instance_clean(UX_HOST_CLASS_HID *hid) +{ + +UX_HOST_CLASS_HID_PARSER *hid_parser; +UX_HOST_CLASS_HID_REPORT *hid_report; +UX_HOST_CLASS_HID_REPORT *hid_next_report; +UX_HOST_CLASS_HID_FIELD *hid_field; +UX_HOST_CLASS_HID_FIELD *hid_next_field; + + + /* Get the parser structure pointer. */ + hid_parser = &hid -> ux_host_class_hid_parser; + + /* Each report list of the parser should be cleaned: Input report. */ + hid_report = hid_parser -> ux_host_class_hid_parser_input_report; + while (hid_report != UX_NULL) + { + + /* Get the next report before we clean the current report. */ + hid_next_report = hid_report -> ux_host_class_hid_report_next_report; + + /* Get the first field in the report. */ + hid_field = hid_report -> ux_host_class_hid_report_field; + + /* Clean all the fields attached. */ + while (hid_field != UX_NULL) + { + + /* Get the next field before we clean the current field. */ + hid_next_field = hid_field -> ux_host_class_hid_field_next_field; + + /* Free the usage table. */ + if (hid_field -> ux_host_class_hid_field_usages != UX_NULL) + _ux_utility_memory_free(hid_field -> ux_host_class_hid_field_usages); + + /* Free the value table. */ + if (hid_field -> ux_host_class_hid_field_values != UX_NULL) + _ux_utility_memory_free(hid_field -> ux_host_class_hid_field_values); + + /* Now free the field memory. */ + _ux_utility_memory_free(hid_field); + + /* Next field. */ + hid_field = hid_next_field; + } + + /* Free the report. */ + _ux_utility_memory_free(hid_report); + + /* Next report. */ + hid_report = hid_next_report; + } + + /* Each report list of the parser should be cleaned: Output report. */ + hid_report = hid_parser -> ux_host_class_hid_parser_output_report; + while (hid_report != UX_NULL) + { + + /* Get the next report before we clean the current report. */ + hid_next_report = hid_report -> ux_host_class_hid_report_next_report; + + /* Get the first field in the report. */ + hid_field = hid_report -> ux_host_class_hid_report_field; + + /* Clean all the fields attached. */ + while (hid_field != UX_NULL) + { + + /* Get the next field before we clean the current field. */ + hid_next_field = hid_field -> ux_host_class_hid_field_next_field; + + /* Free the usage table. */ + if (hid_field -> ux_host_class_hid_field_usages != UX_NULL) + _ux_utility_memory_free(hid_field -> ux_host_class_hid_field_usages); + + /* Free the value table. */ + if (hid_field -> ux_host_class_hid_field_values != UX_NULL) + _ux_utility_memory_free(hid_field -> ux_host_class_hid_field_values); + + /* Now free the field memory. */ + _ux_utility_memory_free(hid_field); + + /* Next field. */ + hid_field = hid_next_field; + } + + /* Free the report. */ + _ux_utility_memory_free(hid_report); + + /* Next report. */ + hid_report = hid_next_report; + } + + /* Each report list of the parser should be cleaned: Feature report. */ + hid_report = hid_parser -> ux_host_class_hid_parser_feature_report; + while (hid_report != UX_NULL) + { + + /* Get the next report before we clean the current report. */ + hid_next_report = hid_report -> ux_host_class_hid_report_next_report; + + /* Get the first field in the report. */ + hid_field = hid_report -> ux_host_class_hid_report_field; + + /* Clean all the fields attached. */ + while (hid_field != UX_NULL) + { + + /* Get the next field before we clean the current field. */ + hid_next_field = hid_field -> ux_host_class_hid_field_next_field; + + /* Free the usage table. */ + if (hid_field -> ux_host_class_hid_field_usages != UX_NULL) + _ux_utility_memory_free(hid_field -> ux_host_class_hid_field_usages); + + /* Free the value table. */ + if (hid_field -> ux_host_class_hid_field_values != UX_NULL) + _ux_utility_memory_free(hid_field -> ux_host_class_hid_field_values); + + /* Now free the field memory. */ + _ux_utility_memory_free(hid_field); + + /* Next field. */ + hid_field = hid_next_field; + } + + /* Free the report. */ + _ux_utility_memory_free(hid_report); + + /* Next report. */ + hid_report = hid_next_report; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_interrupt_endpoint_search.c b/common/usbx_host_classes/src/ux_host_class_hid_interrupt_endpoint_search.c new file mode 100644 index 0000000..4e8988d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_interrupt_endpoint_search.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_interrupt_endpoint_search PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches for the handle of the only interrupt */ +/* endpoint in the default alternate setting of the HID interface. */ +/* The interrupt endpoint should always be there. The actual first */ +/* transfer on the interrupt endpoint does not start until a HID */ +/* client has claimed ownership of the HID device. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_interrupt_endpoint_search(UX_HOST_CLASS_HID *hid) +{ + +UINT status; +UX_INTERFACE *interface; +UX_ENDPOINT *endpoint; +UX_TRANSFER *transfer_request; + + + /* Search the interrupt endpoint. It is attached to the interface container. */ + interface = hid -> ux_host_class_hid_interface; + endpoint = interface -> ux_interface_first_endpoint; + while(endpoint != UX_NULL) + { + + /* Found interrupt IN endpoint. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* The endpoint is correct, save it. */ + hid -> ux_host_class_hid_interrupt_endpoint = endpoint; + + /* Fill in the transfer request with the length requested for this endpoint. */ + transfer_request = &endpoint -> ux_endpoint_transfer_request; + transfer_request -> ux_transfer_request_requested_length = hid -> ux_host_class_hid_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* The direction is always IN for the HID interrupt endpoint. */ + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) hid; + + /* Interrupt transactions have a completion routine. */ + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_hid_transfer_request_completed; + + /* Obtain a buffer for this transaction. The buffer will always be reused. */ + transfer_request -> ux_transfer_request_data_pointer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + transfer_request -> ux_transfer_request_requested_length); + + /* If the endpoint is available and we have memory, we mark the interrupt endpoint as ready. */ + if (transfer_request -> ux_transfer_request_data_pointer != UX_NULL) + { + hid -> ux_host_class_hid_interrupt_endpoint_status = UX_HOST_CLASS_HID_INTERRUPT_ENDPOINT_READY; + status = UX_SUCCESS; + } + else + status = UX_MEMORY_INSUFFICIENT; + + /* Return completion status. */ + return(status); + } + + /* Check next endpoint. */ + endpoint = endpoint -> ux_endpoint_next_endpoint; + } + + /* Return error completion. */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_item_data_get.c b/common/usbx_host_classes/src/ux_host_class_hid_item_data_get.c new file mode 100644 index 0000000..58bf853 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_item_data_get.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_item_data_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns a data from the descriptor based on its size, */ +/* format and whether it is signed or not. */ +/* */ +/* INPUT */ +/* */ +/* descriptor Pointer to descriptor */ +/* item Pointer to item */ +/* */ +/* OUTPUT */ +/* */ +/* ULONG data value */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_short_get Get 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_host_class_hid_item_data_get(UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item) +{ + +ULONG value; + + + /* Get the length of the item. */ + switch (item -> ux_host_class_hid_item_report_length) + { + + case 1: + + value = (ULONG) *descriptor; + break; + + + case 2: + + value = (ULONG) _ux_utility_short_get(descriptor); + break; + + + case 4: + + value = (ULONG) _ux_utility_long_get(descriptor); + break; + + + default: + + return(0); + } + + /* Return the value. */ + return(value); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_keyboard_activate.c b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_activate.c new file mode 100644 index 0000000..6712d00 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_activate.c @@ -0,0 +1,414 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Keyboard Client */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_keyboard.h" +#include "ux_host_stack.h" + +/* Define USB HID keyboard mapping tables. */ + +#ifndef UX_HOST_CLASS_HID_KEYBOARD_REGULAR_ARRAY_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_REGULAR_ARRAY_DEFAULT UX_HID_KEYBOARD_REGULAR_ARRAY_US +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_SHIFT_ARRAY_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_SHIFT_ARRAY_DEFAULT UX_HID_KEYBOARD_SHIFT_ARRAY_US +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_ON_ARRAY_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_ON_ARRAY_DEFAULT UX_HID_KEYBOARD_NUMLOCK_ON_ARRAY +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_OFF_ARRAY_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_OFF_ARRAY_DEFAULT UX_HID_KEYBOARD_NUMLOCK_OFF_ARRAY +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_KEYS_UPPER_RANGE_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_KEYS_UPPER_RANGE_DEFAULT UX_HID_KEYBOARD_KEYS_UPPER_RANGE +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_A_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_A_DEFAULT UX_HID_KEYBOARD_KEY_LETTER_A +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_Z_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_Z_DEFAULT UX_HID_KEYBOARD_KEY_LETTER_Z +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE_DEFAULT UX_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE +#endif +#ifndef UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE_DEFAULT +#define UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE_DEFAULT UX_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE +#endif + +UCHAR ux_host_class_hid_keyboard_regular_array[] = +{ + UX_HOST_CLASS_HID_KEYBOARD_REGULAR_ARRAY_DEFAULT +}; + +UCHAR ux_host_class_hid_keyboard_shift_array[] = +{ + UX_HOST_CLASS_HID_KEYBOARD_SHIFT_ARRAY_DEFAULT +}; + +UCHAR ux_host_class_hid_keyboard_numlock_on_array[] = +{ + UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_ON_ARRAY_DEFAULT +}; + +UCHAR ux_host_class_hid_keyboard_numlock_off_array[] = +{ + UX_HOST_CLASS_HID_KEYBOARD_NUMLOCK_OFF_ARRAY_DEFAULT +}; + +UX_HOST_CLASS_HID_KEYBOARD_LAYOUT ux_host_class_hid_keyboard_layout = +{ + ux_host_class_hid_keyboard_regular_array, + ux_host_class_hid_keyboard_shift_array, + ux_host_class_hid_keyboard_numlock_on_array, + ux_host_class_hid_keyboard_numlock_off_array, + UX_HOST_CLASS_HID_KEYBOARD_KEYS_UPPER_RANGE_DEFAULT, + UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_A_DEFAULT, + UX_HOST_CLASS_HID_KEYBOARD_KEY_LETTER_Z_DEFAULT, + UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_LOWER_RANGE_DEFAULT, + UX_HOST_CLASS_HID_KEYBOARD_KEYS_KEYPAD_UPPER_RANGE_DEFAULT, +}; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_keyboard_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the enumeration of a HID Keyboard Client. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_periodic_report_start */ +/* Start periodic report */ +/* _ux_host_class_hid_report_callback_register */ +/* Register callback */ +/* _ux_host_class_hid_report_id_get Get the report ID */ +/* _ux_host_class_hid_idle_set Set the idle rate */ +/* _ux_host_class_hid_report_set Do SET_REPORT */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_thread_create Create thread */ +/* _ux_utility_thread_delete Delete thread */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_keyboard_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UX_HOST_CLASS_HID_REPORT_CALLBACK call_back; +UX_HOST_CLASS_HID_CLIENT_REPORT client_report; +UX_HOST_CLASS_HID_REPORT_GET_ID report_id; +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance; +ULONG event_process_memory_size; +UINT status = UX_SUCCESS; +#ifdef UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE +UX_HOST_CLASS_HID_FIELD *field; +#endif + + + /* Get the instance to the HID class. */ + hid = command -> ux_host_class_hid_client_command_instance; + + /* And of the HID client. */ + hid_client = hid -> ux_host_class_hid_client; + + /* Get some memory for both the HID class instance of this client + and for the callback. */ + keyboard_instance = (UX_HOST_CLASS_HID_KEYBOARD *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + sizeof(UX_HOST_CLASS_HID_KEYBOARD)); + if (keyboard_instance == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Attach the remote control instance to the client instance. */ + hid_client -> ux_host_class_hid_client_local_instance = (VOID *) keyboard_instance; + + /* Save the HID instance in the client instance. */ + keyboard_instance -> ux_host_class_hid_keyboard_hid = hid; + + /* The instance is live now. */ + keyboard_instance -> ux_host_class_hid_keyboard_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* Allocate the round-robin buffer that the remote control instance will use + * to store the usages as they come in. + * Size calculation overflow is checked near where _USAGE_ARRAY_LENGTH is defined. + */ + keyboard_instance -> ux_host_class_hid_keyboard_usage_array = (ULONG *) + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH*4); + if (keyboard_instance -> ux_host_class_hid_keyboard_usage_array == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* Initialize the head and tail of this array. */ + keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head = keyboard_instance -> ux_host_class_hid_keyboard_usage_array; + keyboard_instance -> ux_host_class_hid_keyboard_usage_array_tail = keyboard_instance -> ux_host_class_hid_keyboard_usage_array; + + /* Get the report ID for the keyboard. The keyboard is a INPUT report. + This should be 0 but in case. */ + report_id.ux_host_class_hid_report_get_report = UX_NULL; + report_id.ux_host_class_hid_report_get_type = UX_HOST_CLASS_HID_REPORT_TYPE_INPUT; + status = _ux_host_class_hid_report_id_get(hid, &report_id); + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* Save the keyboard report ID. */ + keyboard_instance -> ux_host_class_hid_keyboard_id = (USHORT)(report_id.ux_host_class_hid_report_get_id); + + /* Set the idle rate of the keyboard to 0. This way a report is generated only when there is an activity. */ + status = _ux_host_class_hid_idle_set(hid, 0, keyboard_instance -> ux_host_class_hid_keyboard_id); + } + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + +#ifdef UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE + + /* Summarize number of usages to allocate state buffer for keyboard report. + We reserve 3 bits for lock status, 8 bits for modifier status. + We get number of keys from report of 8-bit array. + Key usages bytes then saved if it's down. + - 3 Locks - 8 Modifiers - N regular keys ... + */ + keyboard_instance -> ux_host_class_hid_keyboard_key_count = 3 + 8; + field = report_id.ux_host_class_hid_report_get_report -> ux_host_class_hid_report_field; + while(field) + { + if (field -> ux_host_class_hid_field_report_size == 8) + keyboard_instance -> ux_host_class_hid_keyboard_key_count += field -> ux_host_class_hid_field_report_count; + field = field -> ux_host_class_hid_field_next_field; + } + + /* Process memory includes: + - states for last [usage, value] and new [usage, value], (2 * 2) * max number of keys to log + - actions for last and new buffers, 2 * max number of keys to log + */ + UX_UTILITY_MULC_SAFE(keyboard_instance -> ux_host_class_hid_keyboard_key_count, 6, event_process_memory_size, status); + + /* Calculation overflow check. */ + if (status != UX_SUCCESS) + return(status); +#else + + /* We reserve 3 bytes for lock keys, 3 for processing. */ + keyboard_instance -> ux_host_class_hid_keyboard_key_count = 3; + + /* key count 3, multiply 2 is int safe. */ + event_process_memory_size = keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2; +#endif + + /* Allocate memory for usages states. */ + keyboard_instance -> ux_host_class_hid_keyboard_key_state = (UCHAR *) + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, event_process_memory_size); + if (keyboard_instance -> ux_host_class_hid_keyboard_key_state == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* Initialize the report callback. */ + call_back.ux_host_class_hid_report_callback_id = keyboard_instance -> ux_host_class_hid_keyboard_id; + call_back.ux_host_class_hid_report_callback_function = _ux_host_class_hid_keyboard_callback; + call_back.ux_host_class_hid_report_callback_buffer = UX_NULL; + call_back.ux_host_class_hid_report_callback_flags = UX_HOST_CLASS_HID_REPORT_DECOMPRESSED; + call_back.ux_host_class_hid_report_callback_length = 0; + + /* Register the report call back when data comes it on this report. */ + status = _ux_host_class_hid_report_callback_register(hid, &call_back); + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* We need a semaphore now. This will be used to synchronize the HID report with the keyboard thread. */ + status = _ux_utility_semaphore_create(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore, "ux_host_class_hid_keyboard_semaphore", 0); + if(status != UX_SUCCESS) + status = (UX_SEMAPHORE_ERROR); + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* The HID Keyboard needs a Thread to process LED changes. This process is asynchronous + to the callback because it involves using the commands on the Control Pipe which cannot + be done during callbacks. First allocate some stack memory. */ + keyboard_instance -> ux_host_class_hid_keyboard_thread_stack = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE); + + /* Check if stack memory was allocated. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_thread_stack == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + + /* Then create the actual thread. */ + status = _ux_utility_thread_create(&keyboard_instance -> ux_host_class_hid_keyboard_thread, "ux_host_stack_keyboard_thread",_ux_host_class_hid_keyboard_thread, + (ULONG) (ALIGN_TYPE) keyboard_instance, keyboard_instance -> ux_host_class_hid_keyboard_thread_stack, + UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_KEYBOARD, + UX_THREAD_PRIORITY_KEYBOARD, UX_NO_TIME_SLICE, TX_AUTO_START); + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + UX_THREAD_EXTENSION_PTR_SET(&(keyboard_instance -> ux_host_class_hid_keyboard_thread), keyboard_instance) + + /* Default state of keyboard is with NumLock on. */ + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_NUM_LOCK; + + /* We need to build the field for the LEDs. */ + keyboard_instance -> ux_host_class_hid_keyboard_led_mask = keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & + UX_HID_KEYBOARD_STATE_MASK_LOCK; + + /* We need to find the OUTPUT report for the keyboard LEDs. */ + report_id.ux_host_class_hid_report_get_report = UX_NULL; + report_id.ux_host_class_hid_report_get_type = UX_HOST_CLASS_HID_REPORT_TYPE_OUTPUT; + status = _ux_host_class_hid_report_id_get(hid, &report_id); + + /* The report ID should exist. If there is an error, we do not proceed. */ + if (status == UX_SUCCESS) + { + + /* Memorize the report pointer. */ + client_report.ux_host_class_hid_client_report = report_id.ux_host_class_hid_report_get_report; + + /* The report set is raw since the LEDs mask is already in the right format. */ + client_report.ux_host_class_hid_client_report_flags = UX_HOST_CLASS_HID_REPORT_RAW; + + /* The length of this report is 1 byte. */ + client_report.ux_host_class_hid_client_report_length = 1; + + /* The output report buffer is the LED mask field. */ + client_report.ux_host_class_hid_client_report_buffer = &keyboard_instance -> ux_host_class_hid_keyboard_led_mask; + + /* The HID class will perform the SET_REPORT command. Do not check for error here, this is + handled by the function itself. */ + status = _ux_host_class_hid_report_set(hid, &client_report); + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* Initialize the keyboard layout and use it to decode keys. */ + keyboard_instance -> ux_host_class_hid_keyboard_layout = &ux_host_class_hid_keyboard_layout; + keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable = UX_FALSE; + + /* Start the periodic report. */ + status = _ux_host_class_hid_periodic_report_start(hid); + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_INSERTION, hid -> ux_host_class_hid_class, (VOID *) hid_client); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_KEYBOARD_ACTIVATE, hid, keyboard_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Return completion status. */ + return(status); + } + + /* There is error, delete thread. */ + _ux_utility_thread_delete(&keyboard_instance -> ux_host_class_hid_keyboard_thread); + } + + /* We are here if there is error. */ + + /* Detach instance. */ + hid_client -> ux_host_class_hid_client_local_instance = UX_NULL; + + /* Free usage state. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_key_state) + _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_key_state); + + /* Free stack. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_thread_stack) + _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_thread_stack); + + /* Delete semaphore. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_semaphore.tx_semaphore_id != TX_EMPTY) + _ux_utility_semaphore_delete(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore); + + /* Free usage array. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_usage_array) + _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_usage_array); + + /* Free instance. */ + _ux_utility_memory_free(keyboard_instance); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_keyboard_callback.c b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_callback.c new file mode 100644 index 0000000..a9dedbd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_callback.c @@ -0,0 +1,570 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Keyboard Client */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_keyboard.h" +#include "ux_host_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_keyboard_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback mechanism for a report registration. */ +/* */ +/* INPUT */ +/* */ +/* callback Pointer to callback */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_set Set memory */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class when a report is generated */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hid_keyboard_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK *callback) +{ + +/* This array contains the bit for each alternate key (modifier or lock key) + that we report to the application. For example, if you wanted to set the + bit for the CAPS_LOCK key, you would do: + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= alternate_key_bits[0]; + Index 0 is used since it corresponds to the CAPS_LOCK bit in the array. Note + that _alternate_key_state is what we report to the application. */ +const ULONG alternate_key_bits[] = { + UX_HID_KEYBOARD_STATE_CAPS_LOCK, + UX_HID_KEYBOARD_STATE_NUM_LOCK, + UX_HID_KEYBOARD_STATE_SCROLL_LOCK, + UX_HID_KEYBOARD_STATE_LEFT_CTRL, + UX_HID_KEYBOARD_STATE_LEFT_SHIFT, + UX_HID_KEYBOARD_STATE_LEFT_ALT, + UX_HID_KEYBOARD_STATE_LEFT_GUI, + UX_HID_KEYBOARD_STATE_RIGHT_CTRL, + UX_HID_KEYBOARD_STATE_RIGHT_SHIFT, + UX_HID_KEYBOARD_STATE_RIGHT_ALT, + UX_HID_KEYBOARD_STATE_RIGHT_GUI, +}; + +/* Define the indices for each alternate key in the alternate key bits array. */ + +#define ALTERNATE_KEY_BITS_IDX_CAPS_LOCK ( 0) +#define ALTERNATE_KEY_BITS_IDX_NUM_LOCK ( 1) +#define ALTERNATE_KEY_BITS_IDX_SCROLL_LOCK ( 2) + +#define ALTERNATE_KEY_BITS_IDX_LEFT_CTRL ( 3) +#define ALTERNATE_KEY_BITS_IDX_LEFT_SHIFT ( 4) +#define ALTERNATE_KEY_BITS_IDX_LEFT_ALT ( 5) +#define ALTERNATE_KEY_BITS_IDX_LEFT_GUI ( 6) +#define ALTERNATE_KEY_BITS_IDX_RIGHT_CTRL ( 7) +#define ALTERNATE_KEY_BITS_IDX_RIGHT_SHIFT ( 8) +#define ALTERNATE_KEY_BITS_IDX_RIGHT_ALT ( 9) +#define ALTERNATE_KEY_BITS_IDX_RIGHT_GUI (10) + +/* Define a macro to get the index of a modifier key in the alternate key bits array. */ +#define GET_ALTERNATE_KEY_BITS_IDX(usage) ((usage) - UX_HID_MODIFIER_KEY_LEFT_CONTROL + ALTERNATE_KEY_BITS_IDX_LEFT_CTRL) + +/* Define key states. */ +#define KEY_STATE_REGULAR (11) +#define KEY_STATE_NO_KEY (12) + +#define KEY_UP ( 0) +#define KEY_KEEP ( 1) +#define KEY_DOWN ( 2) +#define KEY_DEL ( 3) + + +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance; +UX_HOST_CLASS_HID_KEYBOARD_LAYOUT *keyboard_layout; +UCHAR *keypad_array; +ULONG *array_head; +ULONG *array_tail; +ULONG *array_end; +ULONG *array_start; +ULONG *report_buffer; +ULONG *report_buffer_end; +ULONG keyboard_char = 0; +ULONG shift_on; +ULONG capslock_on; +ULONG numlock_on; +ULONG key_usage; +ULONG key_value; + +/* This variable either contains an index into the alternate key bit array, + or a value that describes the current key i.e. regular key or no key. */ +UINT key_state; + +#if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) +UCHAR *previous_lock_key_states; +UCHAR *current_lock_key_states; +#else +UINT i, i_save, new_count; +UCHAR *state_usage; +UCHAR *state_value; +UCHAR *state_action; +#endif + + + /* Get the HID client instance that issued the callback. */ + hid_client = callback -> ux_host_class_hid_report_callback_client; + + /* Get the keyboard local instance. */ + keyboard_instance = (UX_HOST_CLASS_HID_KEYBOARD *) hid_client -> ux_host_class_hid_client_local_instance; + + /* Get the report buffer. */ + report_buffer = (ULONG *)callback -> ux_host_class_hid_report_callback_buffer; + + /* Get the end of report buffer. */ + report_buffer_end = &report_buffer[callback -> ux_host_class_hid_report_callback_actual_length]; + +#if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) + + /* Get the previous states of the lock keys. */ + previous_lock_key_states = &keyboard_instance -> ux_host_class_hid_keyboard_key_state[0]; + + /* Get the current states of the lock keys and immediately initialize them to zero; + if a lock key is not pressed in this report, it will remain zero (not pressed). */ + current_lock_key_states = &keyboard_instance -> ux_host_class_hid_keyboard_key_state[3]; + _ux_utility_memory_set(current_lock_key_states, 0, 3); + + /* Scan the report buffer and decode it. */ + while(report_buffer < report_buffer_end) + { + + /* Get usage and value from buffer. */ + key_usage = *report_buffer ++; + key_value = *report_buffer ++; + + /* Analyze the usage we have received. We eliminate the page from the usage. */ + key_usage &= 0xFF; +#else + + /* Initialize key states for report processing. */ + state_usage = keyboard_instance -> ux_host_class_hid_keyboard_key_state; + state_value = state_usage + keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2; + state_action = state_value + keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2; + + /* Reset state actions to DEL(not received). */ + _ux_utility_memory_set(state_usage + keyboard_instance -> ux_host_class_hid_keyboard_key_count, 0, keyboard_instance -> ux_host_class_hid_keyboard_key_count); + _ux_utility_memory_set(state_value + keyboard_instance -> ux_host_class_hid_keyboard_key_count, 0, keyboard_instance -> ux_host_class_hid_keyboard_key_count); + _ux_utility_memory_set(state_action, KEY_DEL, keyboard_instance -> ux_host_class_hid_keyboard_key_count * 2); + + new_count = keyboard_instance -> ux_host_class_hid_keyboard_key_count; + while(report_buffer < report_buffer_end) + { + + /* Get usage and value from buffer. */ + key_usage = *report_buffer ++; + key_value = *report_buffer ++; + + /* Analyze the usage we have received. We eliminate the page from the usage. */ + key_usage &= 0xFF; + key_value &= 0xFF; + + /* If there is no key or in phantom state (roll over), skip. */ + if (key_usage <= UX_HID_KEYBOARD_PHANTOM_STATE) + continue; + + /* Check if the key is previously reported. */ + for (i = 0; i < keyboard_instance -> ux_host_class_hid_keyboard_key_count; i ++) + { + + /* Check if it's modified. */ + if (state_usage[i] == key_usage) + { + + /* Replace action state. */ + state_action[i] = (state_value[i] == key_value) ? KEY_KEEP : (key_value ? KEY_DOWN : KEY_UP); + + /* Replace key value. */ + state_value[i] = key_value; + break; + } + } + + /* When there is new key, add to new key list. */ + if (i == keyboard_instance -> ux_host_class_hid_keyboard_key_count) + { + + /* Add key value. */ + state_usage [new_count] = key_usage; + state_value [new_count] = key_value; + + /* Add key action. */ + state_action[new_count] = key_value ? KEY_DOWN : KEY_KEEP; + + new_count ++; + } + } /* while(report_buffer < report_buffer_end) */ + + /* Process pending key states. */ + i_save = 0; + for (i = 0; i < new_count; i ++) + { + + /* Get state value from buffer. */ + key_usage = state_usage[i]; + key_value = state_value[i]; + key_state = state_action[i]; + + /* If no key, just skip. */ + if (key_usage == 0) + continue; + + /* If key not reported, add up event if it's enabled. */ + if (key_state == KEY_DEL) + { + + /* Clear state, do not save. */ + state_usage[i] = 0; + state_action[i] = KEY_UP; + } + + /* Key reported, process it. */ + else + { + + /* We need to save key anyway. */ + if (i_save < i) + { + state_usage[i_save] = key_usage; + state_value[i_save] = key_value; + } + i_save ++; + } + + /* Skip keep keys. */ + if (state_action[i] == KEY_KEEP) + continue; + + /* Now handle key event. */ + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state &= ~(UX_HID_KEYBOARD_STATE_FUNCTION | UX_HID_KEYBOARD_STATE_KEY_UP); +#endif /* UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE */ + + /* Determine what key this is. */ + switch(key_usage) + { + + /* This is the usage of a modifier key. Set or clear the appropriate + bits in the alternate key state bitmap. */ + case UX_HID_MODIFIER_KEY_LEFT_SHIFT : + case UX_HID_MODIFIER_KEY_RIGHT_SHIFT : + case UX_HID_MODIFIER_KEY_LEFT_ALT : + case UX_HID_MODIFIER_KEY_RIGHT_ALT : + case UX_HID_MODIFIER_KEY_RIGHT_CONTROL : + case UX_HID_MODIFIER_KEY_LEFT_CONTROL : + case UX_HID_MODIFIER_KEY_RIGHT_GUI : + case UX_HID_MODIFIER_KEY_LEFT_GUI : + + key_state = GET_ALTERNATE_KEY_BITS_IDX(key_usage); + + /* We have received a modifier Key. Remember the state. */ + if (key_value > 0) + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= alternate_key_bits[key_state]; + else + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state &= ~alternate_key_bits[key_state]; + + break; + + /* This is the usage of a LOCK key. Just save the its index in the alternate + key bit array. */ + + case UX_HID_LED_KEY_CAPS_LOCK : + key_state = ALTERNATE_KEY_BITS_IDX_CAPS_LOCK; + break; + + case UX_HID_LED_KEY_NUM_LOCK : + key_state = ALTERNATE_KEY_BITS_IDX_NUM_LOCK; + break; + + case UX_HID_LED_KEY_SCROLL_LOCK : + key_state = ALTERNATE_KEY_BITS_IDX_SCROLL_LOCK; + break; + +#if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) + + /* Usage no key. */ + case UX_HID_KEYBOARD_NO_KEY : + case UX_HID_KEYBOARD_PHANTOM_STATE : + key_state = KEY_STATE_NO_KEY; + break; +#endif + + /* This is the usage of a regular key. Here, we just get the decoded + value; we will add it to the queue later. */ + default : + + /* By default the key will be saved. */ + key_state = KEY_STATE_REGULAR; + + /* Skip decode if decode is disabled. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable == UX_TRUE) + { + + /* Use raw data (scan code) as key code. */ + keyboard_char = key_value; + break; + } + + /* Get keyboard layout instance. */ + keyboard_layout = keyboard_instance -> ux_host_class_hid_keyboard_layout; + + /* Is this key outside the valid range? */ + if (key_value > keyboard_layout -> ux_host_class_hid_keyboard_layout_keys_upper_range) + { + + /* Set a flag to discard it. */ + key_state = KEY_STATE_NO_KEY; + break; + } + + /* We have received a regular key. Depending on the state of the shift or numlock status, the key should be mapped into + one of the translation tables. We verify if the key is within our mapping range. */ + + /* Get SHIFT, CAPS_LOCK and NUM_LOCK states. */ + shift_on = (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & UX_HID_KEYBOARD_STATE_SHIFT) ? UX_TRUE : UX_FALSE; + capslock_on = (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & UX_HID_KEYBOARD_STATE_CAPS_LOCK) ? UX_TRUE : UX_FALSE; + numlock_on = (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & UX_HID_KEYBOARD_STATE_NUM_LOCK) ? UX_TRUE : UX_FALSE; + + /* Check if we have letters ('a' to 'z'). */ + if (key_value >= keyboard_layout -> ux_host_class_hid_keyboard_layout_letters_lower_range && + key_value <= keyboard_layout -> ux_host_class_hid_keyboard_layout_letters_upper_range) + { + + /* We have letters, check the Shift and CapsLock state. */ + if (shift_on != capslock_on) + + /* Shift and CapsLock in different state: upper case. */ + keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_shift_array[key_value]; + else + + /* Lower case. */ + keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_regular_array[key_value]; + + break; /* default: */ + } + + /* Check if we have received a keypad key. They may be multiplexed. */ + if (key_value >= keyboard_layout -> ux_host_class_hid_keyboard_layout_keypad_lower_range && + key_value <= keyboard_layout -> ux_host_class_hid_keyboard_layout_keypad_upper_range) + { + + /* We have a keypad key. Check the NumLock state. */ + if (numlock_on) + + /* Numlock is on. */ + keypad_array = keyboard_layout -> ux_host_class_hid_keyboard_layout_numlock_on_array; + + else + + /* Numlock is off. */ + keypad_array = keyboard_layout -> ux_host_class_hid_keyboard_layout_numlock_off_array; + + /* Decode the keypad key. */ + keyboard_char = keypad_array[key_value - + keyboard_layout -> ux_host_class_hid_keyboard_layout_keypad_lower_range]; + + break; /* default: */ + } + + /* Check the state of the shift. */ + if (shift_on) + + /* We get the key from the shifted array. */ + keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_shift_array[key_value]; + + else + + /* We get the key from the regular array. */ + keyboard_char = keyboard_layout -> ux_host_class_hid_keyboard_layout_regular_array[key_value]; + + break; /* default: */ + + } /* switch(key_usage) */ + +#if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) + + if (state_action[i] == KEY_UP) + +#if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_KEY_DOWN_ONLY) + + /* Skip save. */ + continue; +#else + + /* Save key up state. */ + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_KEY_UP; +#endif +#endif + + /* If there is no key, just try next. */ + if (key_state == KEY_STATE_NO_KEY) + continue; + + /* Is this a LOCK key (i.e. caps lock, scroll lock or num lock)? */ + if (key_state <= ALTERNATE_KEY_BITS_IDX_SCROLL_LOCK) + { + + /* Skip decode if decode is disabled. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable == UX_TRUE) + { + + /* Use raw data (scan code) as key code. */ + keyboard_char = key_value; + } + else + { + +#if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) + + /* Reflect the press in the current lock key state. */ + current_lock_key_states[key_state] = (UCHAR)key_value; + + /* Take action only if key state changes from up to down (pressed). + Remember that the nothing happens when lock keys are released. */ + if (previous_lock_key_states[key_state] == 0) +#elif !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_KEY_DOWN_ONLY) + + /* Take action only if key state changes from up to down (pressed). */ + if (state_action[i] == KEY_DOWN) +#endif + { + + /* Reflect the change in the keyboard state. The state should be inverted. */ + if (keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & alternate_key_bits[key_state]) + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state &= (ULONG)~alternate_key_bits[key_state]; + else + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= alternate_key_bits[key_state]; + + /* Wake up the keyboard thread semaphore. */ + _ux_utility_semaphore_put(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore); + } + +#if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) && defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_LOCK_KEYS) + + /* Use usage and UX_HID_KEYBOARD_STATE_FUNCTION. */ + keyboard_char = key_usage; + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_FUNCTION; + +#else + + /* Check next usage & value. */ + continue; +#endif + } + } + + /* If it's modifier, check next usage & value. */ + else if (key_state < KEY_STATE_REGULAR) + { +#if defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) && defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE_REPORT_MODIFIER_KEYS) + + /* Use usage and UX_HID_KEYBOARD_STATE_FUNCTION. */ + keyboard_char = key_usage; + keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state |= UX_HID_KEYBOARD_STATE_FUNCTION; +#else + + /* Check next usage & value. */ + continue; +#endif + } + + /* If we get here, then we have a regular key. Now it's time to save + raw/decoded key in the key queue. */ + + /* This key should now be inserted in the circular array for the application to retrieve it. */ + array_start = keyboard_instance -> ux_host_class_hid_keyboard_usage_array; + array_end = array_start + UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH; + array_head = keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head; + array_tail = keyboard_instance -> ux_host_class_hid_keyboard_usage_array_tail; + + /* We have a single usage/value. We have to store it into the array. If the array overflows, + there is no mechanism for flow control here so we ignore the usage/value until the + applications makes more room in the array. */ + + /* Is the head at the end of the array and need to loop back? */ + if ((array_head + 2) >= array_end) + array_head = array_start; + else + array_head += 2; + + /* Do we have enough space to store the new usage? */ + if (array_head != array_tail) + { + + /* Yes, we have some space. */ + *keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head = keyboard_char; + *(keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head + 1) = keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state; + + /* Now update the array head. */ + keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head = array_head; + } + else + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + + } /* while(report_buffer < report_buffer_end) */ + +#if !defined(UX_HOST_CLASS_HID_KEYBOARD_EVENTS_KEY_CHANGES_MODE) + + /* Copy the current lock key states to the previous states. Note that if + a lock key wasn't down in this report, its current state would have + remained zero (not pressed). */ + _ux_utility_memory_copy(previous_lock_key_states, current_lock_key_states, 3); +#else + + /* Clear redundant data after last saved key. */ + _ux_utility_memory_set(state_usage + i_save, 0, keyboard_instance -> ux_host_class_hid_keyboard_key_count - i_save); +#endif + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_keyboard_deactivate.c b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_deactivate.c new file mode 100644 index 0000000..968cbd2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_deactivate.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Keyboard Client */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_keyboard.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_keyboard_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the deactivation of a HID Keyboard Client. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_periodic_report_stop */ +/* Stop periodic report */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_thread_delete Delete thread */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_keyboard_deactivate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance; +UINT status; + + + /* Get the instance to the HID class. */ + hid = command -> ux_host_class_hid_client_command_instance; + + /* Stop the periodic report. */ + status = _ux_host_class_hid_periodic_report_stop(hid); + + /* Get the HID client pointer. */ + hid_client = hid -> ux_host_class_hid_client; + + /* Get the remote control local instance. */ + keyboard_instance = (UX_HOST_CLASS_HID_KEYBOARD *) hid_client -> ux_host_class_hid_client_local_instance; + + /* Stop the semaphore. */ + status = _ux_utility_semaphore_delete(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore); + + /* Free memory for key states. */ + _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_key_state); + + /* Terminate the thread. */ + _ux_utility_thread_delete(&keyboard_instance -> ux_host_class_hid_keyboard_thread); + + /* Return to the pool the thread stack. */ + _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_thread_stack); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_KEYBOARD_DEACTIVATE, hid, keyboard_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Unload all the memory used by the keyboard client. */ + _ux_utility_memory_free(keyboard_instance -> ux_host_class_hid_keyboard_usage_array); + + /* Now free the instance memory. */ + _ux_utility_memory_free(hid_client -> ux_host_class_hid_client_local_instance); + + /* We may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_REMOVAL, hid -> ux_host_class_hid_class, (VOID *) hid_client); + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_keyboard_entry.c b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_entry.c new file mode 100644 index 0000000..ae9e4c6 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_entry.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Keyboard Client */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_keyboard.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_keyboard_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the HID keyboard client. */ +/* This function is called by the HID class after it has parsed a new */ +/* HID report descriptor and is searching for a HID client. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_keyboard_activate */ +/* Activate HID keyboard */ +/* _ux_host_class_hid_keyboard_deactivate */ +/* Deactivate HID keyboard */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_keyboard_entry(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_hid_client_command_request) + { + + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the HID class know if we want to own + this device or not */ + if ((command -> ux_host_class_hid_client_command_page == UX_HOST_CLASS_HID_PAGE_GENERIC_DESKTOP_CONTROLS) && + (command -> ux_host_class_hid_client_command_usage == UX_HOST_CLASS_HID_GENERIC_DESKTOP_KEYBOARD)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used by the HID class to start the HID client. */ + status = _ux_host_class_hid_keyboard_activate(command); + + /* Return completion status. */ + return(status); + + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used by the HID class when it received a deactivate + command from the USBX stack and there was a HID client attached to the HID instance */ + status = _ux_host_class_hid_keyboard_deactivate(command); + + /* Return completion status. */ + return(status); + } + + /* Return error status. */ + return(UX_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_keyboard_ioctl.c b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_ioctl.c new file mode 100644 index 0000000..5a7574e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_ioctl.c @@ -0,0 +1,124 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Keyboard Client */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_keyboard.h" +#include "ux_host_stack.h" + +extern UX_HOST_CLASS_HID_KEYBOARD_LAYOUT ux_host_class_hid_keyboard_layout; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_keyboard_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the ioctl entry point for the application to */ +/* configure the HID keyboard device. */ +/* */ +/* INPUT */ +/* */ +/* keyboard_instance Pointer to hid keyboard */ +/* ioctl_function ioctl function */ +/* parameter pointer to parameter/structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* HID Keyboard Client */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_keyboard_ioctl(UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance, + ULONG ioctl_function, VOID *parameter) +{ + +UINT status = UX_SUCCESS; + + + switch(ioctl_function) + { + + case UX_HID_KEYBOARD_IOCTL_SET_LAYOUT: + + /* Change the keyboard layout setting. */ + keyboard_instance -> ux_host_class_hid_keyboard_layout = (parameter == UX_NULL) ? + &ux_host_class_hid_keyboard_layout : + (UX_HOST_CLASS_HID_KEYBOARD_LAYOUT *) parameter; + + break; + + case UX_HID_KEYBOARD_IOCTL_DISABLE_KEYS_DECODE: + + /* Disable the keys decode setting. */ + keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable = UX_TRUE; + + break; + + case UX_HID_KEYBOARD_IOCTL_ENABLE_KEYS_DECODE: + + /* Enable the keys decode setting. */ + keyboard_instance -> ux_host_class_hid_keyboard_keys_decode_disable = UX_FALSE; + + break; + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_keyboard_key_get.c b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_key_get.c new file mode 100644 index 0000000..0e02029 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_key_get.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Remote Control Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_keyboard.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_keyboard_key_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the key and keyboard state from the */ +/* round-robin buffer. */ +/* */ +/* INPUT */ +/* */ +/* keyboard_instance Pointer to remote control */ +/* keyboard key Pointer to keyboard key */ +/* keyboard state Pointer to keyboard state */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance */ +/* */ +/* CALLED BY */ +/* */ +/* User application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_keyboard_key_get(UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance, + ULONG *keyboard_key, ULONG *keyboard_state) +{ + +ULONG *array_head; +ULONG *array_tail; +ULONG *array_end; +ULONG *array_start; +UX_HOST_CLASS_HID *hid; + + /* Get the HID class associated with the HID client. */ + hid = keyboard_instance -> ux_host_class_hid_keyboard_hid; + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Load the keyboard key and state from the usage array . */ + array_start = keyboard_instance -> ux_host_class_hid_keyboard_usage_array; + array_end = array_start + UX_HOST_CLASS_HID_KEYBOARD_USAGE_ARRAY_LENGTH; + array_head = keyboard_instance -> ux_host_class_hid_keyboard_usage_array_head; + array_tail = keyboard_instance -> ux_host_class_hid_keyboard_usage_array_tail; + + /* We want to extract a usage/value from the circular queue of the keyboard instance. */ + if (array_tail == array_head) + return(UX_ERROR); + + /* Get the usage/value from the current tail. */ + *keyboard_key = *array_tail; + *keyboard_state = *(array_tail + 1); + + /* Now we need to update the tail value. Are we at the end of the array? */ + if ((array_tail+2) >= array_end) + array_tail = array_start; + else + array_tail+=2; + + /* Set the tail pointer. */ + keyboard_instance -> ux_host_class_hid_keyboard_usage_array_tail = array_tail; + + /* The status will tell the application there is something valid in the key/state. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_keyboard_thread.c b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_thread.c new file mode 100644 index 0000000..fbd52e7 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_keyboard_thread.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Keyboard Client */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_keyboard.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_keyboard_thread PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains the keyboard thread used to process the changes */ +/* in the keyboard LEDs. */ +/* */ +/* INPUT */ +/* */ +/* keyboard_instance The keyboard instance for */ +/* there was a change in LEDs */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_get Get signal semaphore */ +/* _ux_host_class_hid_report_id_get Get report ID */ +/* _ux_host_class_hid_report_set Do SET_REPORT */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hid_keyboard_thread(ULONG thread_input) +{ +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT_REPORT client_report; +UX_HOST_CLASS_HID_REPORT_GET_ID report_id; +UINT status; +UX_HOST_CLASS_HID_KEYBOARD *keyboard_instance; + + /* Cast the thread_input variable into the proper format. */ + UX_THREAD_EXTENSION_PTR_GET(keyboard_instance, UX_HOST_CLASS_HID_KEYBOARD, thread_input) + + /* Loop forever waiting for changes signaled through the semaphore. */ + while (1) + { + + /* Wait for the semaphore to be put by the root hub or a regular hub. */ + status = _ux_utility_semaphore_get(&keyboard_instance -> ux_host_class_hid_keyboard_semaphore, UX_WAIT_FOREVER); + + /* The semaphore could be awaken because the semaphore was destroyed by the HID client + when the keyboard is removed. */ + if (status == UX_SUCCESS) + { + + /* We got awaken by the keyboard callback which indicates a change on the LEDs has to happen. + We need to build the field for the leds. */ + keyboard_instance -> ux_host_class_hid_keyboard_led_mask = keyboard_instance -> ux_host_class_hid_keyboard_alternate_key_state & + UX_HID_KEYBOARD_STATE_MASK_LOCK; + + /* Recall the HID instance for this client. */ + hid = keyboard_instance -> ux_host_class_hid_keyboard_hid; + + /* We need to find the OUTPUT report for the keyboard LEDs. */ + report_id.ux_host_class_hid_report_get_report = UX_NULL; + report_id.ux_host_class_hid_report_get_type = UX_HOST_CLASS_HID_REPORT_TYPE_OUTPUT; + status = _ux_host_class_hid_report_id_get(hid, &report_id); + + /* The report ID should exist. If there is an error, we do not proceed. */ + if (status == UX_SUCCESS) + { + + /* Memorize the report pointer. */ + client_report.ux_host_class_hid_client_report = report_id.ux_host_class_hid_report_get_report; + + /* The report set is raw since the LEDs mask is already in the right format. */ + client_report.ux_host_class_hid_client_report_flags = UX_HOST_CLASS_HID_REPORT_RAW; + + /* The length of this report is 1 byte. */ + client_report.ux_host_class_hid_client_report_length = 1; + + /* The output report buffer is the LED mask field. */ + client_report.ux_host_class_hid_client_report_buffer = &keyboard_instance -> ux_host_class_hid_keyboard_led_mask; + + /* The HID class will perform the SET_REPORT command. */ + _ux_host_class_hid_report_set(hid, &client_report); + } + } + else + + /* The thread should terminate upon a semaphore error. */ + return; + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_local_item_parse.c b/common/usbx_host_classes/src/ux_host_class_hid_local_item_parse.c new file mode 100644 index 0000000..16677de --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_local_item_parse.c @@ -0,0 +1,250 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_local_item_parse PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function parses a local item from the report descriptor. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* item Pointer to item */ +/* descriptor Pointer to descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_item_data_get Get data item */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_local_item_parse(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_ITEM *item, UCHAR *descriptor) +{ + +UX_HOST_CLASS_HID_PARSER *hid_parser; +ULONG usage; +ULONG usage_min; +ULONG usage_max; +ULONG delimiter_set; + + /* Get the temporary parser structure pointer. */ + hid_parser = &hid -> ux_host_class_hid_parser; + + /* Analyze the tag. */ + switch (item -> ux_host_class_hid_item_report_tag) + { + + case UX_HOST_CLASS_HID_LOCAL_TAG_USAGE: + + /* Local usage tag, check if we have an overflow. */ + if (hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage == UX_HOST_CLASS_HID_USAGES) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_USAGE_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_USAGE_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_USAGE_OVERFLOW); + } + + /* Obtain the usage from the descriptor. */ + usage = _ux_host_class_hid_item_data_get(descriptor, item); + + /* Combine the global usage with the local usage to form a unique usage ID. */ + usage |= (hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_usage_page << 16); + + /* Add the usage to the local usage table. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usages[hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage] = usage; + + /* We have one more usage now. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage++; + + break; + + + case UX_HOST_CLASS_HID_LOCAL_TAG_USAGE_MINIMUM: + + /* Usage Minimum tag. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_min = + (ULONG) _ux_host_class_hid_item_data_get(descriptor, item); + + break; + + + case UX_HOST_CLASS_HID_LOCAL_TAG_USAGE_MAXIMUM: + + /* Usage Maximum tag. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_max = + (ULONG) _ux_host_class_hid_item_data_get(descriptor, item); + + /* Check if the maximum value is coherent with the minimum. */ + if (hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_max < hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_min) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_MIN_MAX_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_MIN_MAX_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_MIN_MAX_ERROR); + } + + /* Get the boundaries for the usage values which are defined when encountering the USAGE MAX tag. */ + usage_min = (ULONG)(hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_min); + usage_max = (ULONG)(hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_max); + + while (usage_min <= usage_max) + { + + /* Check if we can still add this usage. */ + if (hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage == UX_HOST_CLASS_HID_USAGES) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_USAGE_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_USAGE_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_USAGE_OVERFLOW); + + } + + /* Combine the global usage with the local usage to form a unique usage ID. */ + usage = usage_min | (hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_usage_page << 16); + + /* Add the usage to the local usage table. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usages[hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage] = usage; + + /* We have one more usage now. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage++; + + /* Next usage value. */ + usage_min++; + } + + break; + + case UX_HOST_CLASS_HID_LOCAL_TAG_DELIMITER: + + /* Obtain the delimiter set from the descriptor. */ + delimiter_set = _ux_host_class_hid_item_data_get(descriptor, item); + + /* We should have either an open or a close. */ + switch (delimiter_set) + { + + case UX_HOST_CLASS_HID_DELIMITER_OPEN: + + /* Recursive delimiter opens are not supported. */ + if (hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_delimiter_level == 1) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_DELIMITER_ERROR); + + return(UX_HOST_CLASS_HID_DELIMITER_ERROR); + } + + /* Mark the opening of the delimiter. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_delimiter_level = 1; + + break; + + case UX_HOST_CLASS_HID_DELIMITER_CLOSE: + + /* Ensure we had an open delimiter before. */ + if (hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_delimiter_level == 0) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_DELIMITER_ERROR); + + return(UX_HOST_CLASS_HID_DELIMITER_ERROR); + } + + /* Mark the closing of the delimiter. */ + hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_delimiter_level = 0; + + break; + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_DELIMITER_ERROR); + + /* We got a wrong delimiter set. */ + return(UX_HOST_CLASS_HID_DELIMITER_ERROR); + } + break; + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_TAG_UNSUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_TAG_UNSUPPORTED, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* This tag is either unknown or unsupported. */ + return(UX_HOST_CLASS_HID_TAG_UNSUPPORTED); + } + + /* Return status. Always SUCCESS if we get here.*/ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_main_item_parse.c b/common/usbx_host_classes/src/ux_host_class_hid_main_item_parse.c new file mode 100644 index 0000000..352f900 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_main_item_parse.c @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_main_item_parse PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function parses a main item from the report descriptor. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* item Pointer to item */ +/* descriptor Pointer to descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_item_data_get Get data item */ +/* _ux_host_class_hid_report_add Add report */ +/* _ux_utility_memory_set Memory block set */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_main_item_parse(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_ITEM *item, UCHAR *descriptor) +{ + +UX_HOST_CLASS_HID_PARSER *hid_parser; +UINT status = UX_ERROR; +ULONG collection_type; + + + /* Get the temporary parser structure pointer. */ + hid_parser = &hid -> ux_host_class_hid_parser; + + /* Analyze the tag. */ + switch (item -> ux_host_class_hid_item_report_tag) + { + + case UX_HOST_CLASS_HID_MAIN_TAG_COLLECTION: + + /* We have a new collection to open. If the collection type is application, + we have to differentiate the first collection. */ + collection_type = _ux_host_class_hid_item_data_get(descriptor, item); + + /* Check the collection type. */ + if (collection_type == UX_HOST_CLASS_HID_COLLECTION_APPLICATION) + { + + /* We have a collection of type application, check if this is the first one */ + if ((hid_parser -> ux_host_class_hid_parser_main_page == 0) && (hid_parser -> ux_host_class_hid_parser_main_usage == 0)) + { + + /* It is the first application. Since the main usage and page have not yet + been defined, we use the global page and the current local usage. */ + hid_parser -> ux_host_class_hid_parser_main_page = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_usage_page; + hid_parser -> ux_host_class_hid_parser_main_usage = hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usages[0]; + } + + /* Memorize the application. */ + hid_parser -> ux_host_class_hid_parser_application = collection_type; + + /* Add one collection to this report */ + hid_parser -> ux_host_class_hid_parser_number_collection++; + + /* Set the status to success. */ + status = UX_SUCCESS; + } + else + { + + if (hid_parser -> ux_host_class_hid_parser_number_collection >= UX_HOST_CLASS_HID_MAX_COLLECTION) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_COLLECTION_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_COLLECTION_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_COLLECTION_OVERFLOW); + } + else + { + + hid_parser -> ux_host_class_hid_parser_collection[hid_parser -> ux_host_class_hid_parser_number_collection] = collection_type; + + /* Add one collection to this report. */ + hid_parser -> ux_host_class_hid_parser_number_collection++; + + /* Set the status to success. */ + status = UX_SUCCESS; + } + } + break; + + case UX_HOST_CLASS_HID_MAIN_TAG_END_COLLECTION: + + /* We need to pop back the last collection. */ + if (hid_parser -> ux_host_class_hid_parser_number_collection == 0) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_COLLECTION_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_COLLECTION_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_COLLECTION_OVERFLOW); + } + + else + + hid_parser -> ux_host_class_hid_parser_number_collection--; + + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_HID_MAIN_TAG_INPUT: + case UX_HOST_CLASS_HID_MAIN_TAG_OUTPUT: + case UX_HOST_CLASS_HID_MAIN_TAG_FEATURE: + + /* We need to add a report. */ + status = _ux_host_class_hid_report_add(hid, descriptor, item); + break; + } + + /* We have a new main item, so the local instances have to be cleaned. */ + _ux_utility_memory_set(&hid_parser -> ux_host_class_hid_parser_local, 0, sizeof(UX_HOST_CLASS_HID_LOCAL_ITEM)); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_mouse_activate.c b/common/usbx_host_classes/src/ux_host_class_hid_mouse_activate.c new file mode 100644 index 0000000..61cb418 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_mouse_activate.c @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_mouse.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_mouse_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the enumeration of a HID mouse. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_idle_set Set idle rate */ +/* _ux_host_class_hid_report_callback_register */ +/* Register report callback */ +/* _ux_host_class_hid_report_id_get Get report ID */ +/* _ux_host_class_hid_periodic_report_start */ +/* Start periodic report */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Mouse Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_mouse_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UX_HOST_CLASS_HID_REPORT_CALLBACK call_back; +UX_HOST_CLASS_HID_REPORT_GET_ID report_id; +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_MOUSE *mouse_instance; +UINT status; + + + /* Get the instance to the HID class. */ + hid = command -> ux_host_class_hid_client_command_instance; + + /* And of the HID client. */ + hid_client = hid -> ux_host_class_hid_client; + + /* Get some memory for both the HID class instance of this client + and for the callback. */ + mouse_instance = (UX_HOST_CLASS_HID_MOUSE *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + sizeof(UX_HOST_CLASS_HID_MOUSE)); + if(mouse_instance == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Attach the mouse instance to the client instance. */ + hid_client -> ux_host_class_hid_client_local_instance = (VOID *) mouse_instance; + + /* Save the HID instance in the client instance. */ + mouse_instance -> ux_host_class_hid_mouse_hid = hid; + + /* The instance is live now. */ + mouse_instance -> ux_host_class_hid_mouse_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* Get the report ID for the mouse. The mouse is a INPUT report. + This should be 0 but in case. */ + report_id.ux_host_class_hid_report_get_report = UX_NULL; + report_id.ux_host_class_hid_report_get_type = UX_HOST_CLASS_HID_REPORT_TYPE_INPUT; + status = _ux_host_class_hid_report_id_get(hid, &report_id); + + /* The report ID should exist. */ + if (status == UX_SUCCESS) + { + + /* Save the mouse report ID. */ + mouse_instance -> ux_host_class_hid_mouse_id = (USHORT)report_id.ux_host_class_hid_report_get_id; + + /* Set the idle rate of the mouse to 0. This way a report is generated only when there is an activity. */ + status = _ux_host_class_hid_idle_set(hid, 0, mouse_instance -> ux_host_class_hid_mouse_id); + + /* Check for error, accept protocol error since it's optional for mouse. */ + if (status == UX_TRANSFER_STALLED) + status = UX_SUCCESS; + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* Initialize the report callback. */ + call_back.ux_host_class_hid_report_callback_id = mouse_instance -> ux_host_class_hid_mouse_id; + call_back.ux_host_class_hid_report_callback_function = _ux_host_class_hid_mouse_callback; + call_back.ux_host_class_hid_report_callback_buffer = UX_NULL; + call_back.ux_host_class_hid_report_callback_flags = UX_HOST_CLASS_HID_REPORT_INDIVIDUAL_USAGE; + call_back.ux_host_class_hid_report_callback_length = 0; + + /* Register the report call back when data comes it on this report. */ + status = _ux_host_class_hid_report_callback_register(hid, &call_back); + } + + /* If we are OK, go on. */ + if (status == UX_SUCCESS) + { + + /* Start the periodic report. */ + status = _ux_host_class_hid_periodic_report_start(hid); + + if (status == UX_SUCCESS) + { + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_INSERTION, hid -> ux_host_class_hid_class, (VOID *) hid_client); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_MOUSE_ACTIVATE, hid, mouse_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Return completion status. */ + return(status); + } + } + + /* We are here if there is error. */ + + /* Detach the client instance. */ + hid_client -> ux_host_class_hid_client_local_instance = UX_NULL; + + /* Free mouse client instance. */ + _ux_utility_memory_free(mouse_instance); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_mouse_buttons_get.c b/common/usbx_host_classes/src/ux_host_class_hid_mouse_buttons_get.c new file mode 100644 index 0000000..139c9f0 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_mouse_buttons_get.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_mouse.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_mouse_buttons_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the mouse buttons and reports it to the user. */ +/* */ +/* INPUT */ +/* */ +/* mouse_instance Pointer to mouse instance */ +/* mouse_buttons Current Mouse Buttons */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance */ +/* */ +/* CALLED BY */ +/* */ +/* HID Mouse Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_mouse_buttons_get(UX_HOST_CLASS_HID_MOUSE *mouse_instance, + ULONG *mouse_buttons) +{ + +UX_HOST_CLASS_HID *hid; + + /* Get the HID class associated with the HID client. */ + hid = mouse_instance -> ux_host_class_hid_mouse_hid; + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Report the mouse buttons. */ + *mouse_buttons = mouse_instance -> ux_host_class_hid_mouse_buttons; + + /* The status will tell the application there is something valid in the usage/value. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_mouse_callback.c b/common/usbx_host_classes/src/ux_host_class_hid_mouse_callback.c new file mode 100644 index 0000000..9e453e1 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_mouse_callback.c @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_mouse.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_mouse_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback mechanism for a report registration. */ +/* For the mouse, we filter the mouse coordinate changes and the */ +/* state of the buttons. */ +/* */ +/* INPUT */ +/* */ +/* callback Pointer to callback */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hid_mouse_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK *callback) +{ + +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_MOUSE *mouse_instance; + + /* Get the HID client instance that issued the callback. */ + hid_client = callback -> ux_host_class_hid_report_callback_client; + + /* Get the mouse local instance */ + mouse_instance = (UX_HOST_CLASS_HID_MOUSE *) hid_client -> ux_host_class_hid_client_local_instance; + + /* Analyze the usage we have received. */ + switch (callback -> ux_host_class_hid_report_callback_usage) + { + + + /* X/Y Axis movement. */ + case UX_HOST_CLASS_HID_MOUSE_AXIS_X : + + /* Add the deplacement to the position. */ + mouse_instance -> ux_host_class_hid_mouse_x_position += (SCHAR) callback -> ux_host_class_hid_report_callback_value; + + break; + + case UX_HOST_CLASS_HID_MOUSE_AXIS_Y : + + /* Add the deplacement to the position. */ + mouse_instance -> ux_host_class_hid_mouse_y_position += (SCHAR) callback -> ux_host_class_hid_report_callback_value; + break; + + /* Buttons. */ + case UX_HOST_CLASS_HID_MOUSE_BUTTON_1 : + + /* Check the state of button 1. */ + if (callback -> ux_host_class_hid_report_callback_value == UX_TRUE) + mouse_instance -> ux_host_class_hid_mouse_buttons |= UX_HOST_CLASS_HID_MOUSE_BUTTON_1_PRESSED; + else + mouse_instance -> ux_host_class_hid_mouse_buttons &= (ULONG)~UX_HOST_CLASS_HID_MOUSE_BUTTON_1_PRESSED; + break; + + case UX_HOST_CLASS_HID_MOUSE_BUTTON_2 : + + /* Check the state of button 2. */ + if (callback -> ux_host_class_hid_report_callback_value == UX_TRUE) + mouse_instance -> ux_host_class_hid_mouse_buttons |= UX_HOST_CLASS_HID_MOUSE_BUTTON_2_PRESSED; + else + mouse_instance -> ux_host_class_hid_mouse_buttons &= (ULONG)~UX_HOST_CLASS_HID_MOUSE_BUTTON_2_PRESSED; + break; + + case UX_HOST_CLASS_HID_MOUSE_BUTTON_3 : + + /* Check the state of button 3. */ + if (callback -> ux_host_class_hid_report_callback_value == UX_TRUE) + mouse_instance -> ux_host_class_hid_mouse_buttons |= UX_HOST_CLASS_HID_MOUSE_BUTTON_3_PRESSED; + else + mouse_instance -> ux_host_class_hid_mouse_buttons &= (ULONG)~UX_HOST_CLASS_HID_MOUSE_BUTTON_3_PRESSED; + break; + + + /* Wheel movement. */ + case UX_HOST_CLASS_HID_MOUSE_WHEEL : + + mouse_instance -> ux_host_class_hid_mouse_wheel += (SCHAR) callback -> ux_host_class_hid_report_callback_value; + + break; + + default : + + /* We have received a Usage we don't know about. Ignore it. */ + break; + } + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_mouse_deactivate.c b/common/usbx_host_classes/src/ux_host_class_hid_mouse_deactivate.c new file mode 100644 index 0000000..8a06cf4 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_mouse_deactivate.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_mouse.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_mouse_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the deactivation of a HID mouse. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_periodic_report_stop */ +/* Stop periodic report */ +/* _ux_utility_memory_free Free memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Mouse Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_mouse_deactivate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UINT status; + + + /* Get the instance to the HID class. */ + hid = command -> ux_host_class_hid_client_command_instance; + + /* Stop the periodic report. */ + status = _ux_host_class_hid_periodic_report_stop(hid); + + /* Get the HID client pointer. */ + hid_client = hid -> ux_host_class_hid_client; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_MOUSE_DEACTIVATE, hid, hid_client -> ux_host_class_hid_client_local_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Now free the instance memory. */ + _ux_utility_memory_free(hid_client -> ux_host_class_hid_client_local_instance); + + /* We may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_REMOVAL, hid -> ux_host_class_hid_class, (VOID *) hid_client); + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_mouse_entry.c b/common/usbx_host_classes/src/ux_host_class_hid_mouse_entry.c new file mode 100644 index 0000000..a81662d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_mouse_entry.c @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_mouse.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_mouse_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the HID Mouse client. This */ +/* function is called by the HID class after it has parsed a new HID */ +/* report descriptor and is searching for a HID client. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_mouse_activate Activate HID mouse class */ +/* _ux_host_class_hid_mouse_deactivate Deactivate HID mouse class */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_mouse_entry(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_hid_client_command_request) + { + + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the HID class know if we want to own + this device or not. */ + if ((command -> ux_host_class_hid_client_command_page == UX_HOST_CLASS_HID_PAGE_GENERIC_DESKTOP_CONTROLS) && + (command -> ux_host_class_hid_client_command_usage == UX_HOST_CLASS_HID_GENERIC_DESKTOP_MOUSE)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used by the HID class to start the HID client. */ + status = _ux_host_class_hid_mouse_activate(command); + + /* Return completion status. */ + return(status); + + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used by the HID class when it received a deactivate + command from the USBX stack and there was a HID client attached to the HID instance. */ + status = _ux_host_class_hid_mouse_deactivate(command); + + /* Return completion status. */ + return(status); + } + + /* Return error status. */ + return(UX_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_mouse_position_get.c b/common/usbx_host_classes/src/ux_host_class_hid_mouse_position_get.c new file mode 100644 index 0000000..3b582f7 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_mouse_position_get.c @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_mouse.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_mouse_position_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the mouse position and reports it to the user. */ +/* We have the X and Y coordinates passed by the application. */ +/* */ +/* INPUT */ +/* */ +/* mouse_instance Pointer to mouse instance */ +/* mouse_x_position Current Mouse X Position */ +/* mouse_y_position Current Mouse Y Position */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Mouse Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_mouse_position_get(UX_HOST_CLASS_HID_MOUSE *mouse_instance, + SLONG *mouse_x_position, + SLONG *mouse_y_position) +{ + +UX_HOST_CLASS_HID *hid; + + /* Get the HID class associated with the HID client. */ + hid = mouse_instance -> ux_host_class_hid_mouse_hid; + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Report the mouse X position. */ + *mouse_x_position = mouse_instance -> ux_host_class_hid_mouse_x_position; + + /* Report the mouse Y position. */ + *mouse_y_position = mouse_instance -> ux_host_class_hid_mouse_y_position; + + /* The status will tell the application there is something valid in the usage/value. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_mouse_wheel_get.c b/common/usbx_host_classes/src/ux_host_class_hid_mouse_wheel_get.c new file mode 100644 index 0000000..7b20092 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_mouse_wheel_get.c @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Mouse Client Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_mouse.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_mouse_wheel_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the mouse wheel position (up or down). */ +/* */ +/* INPUT */ +/* */ +/* mouse_instance Pointer to mouse instance */ +/* mouse_wheel_movement Current Mouse wheel movement */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Mouse Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_mouse_wheel_get(UX_HOST_CLASS_HID_MOUSE *mouse_instance, + SLONG *mouse_wheel_movement) +{ + +UX_HOST_CLASS_HID *hid; + + /* Get the HID class associated with the HID client. */ + hid = mouse_instance -> ux_host_class_hid_mouse_hid; + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Report the mouse wheel movement. */ + *mouse_wheel_movement = (SLONG)(mouse_instance -> ux_host_class_hid_mouse_wheel); + + /* The status will tell the application there is something valid in the usage/value. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_periodic_report_start.c b/common/usbx_host_classes/src/ux_host_class_hid_periodic_report_start.c new file mode 100644 index 0000000..eb11b5d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_periodic_report_start.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_periodic_report_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts the interrupt endpoint to obtain the periodic */ +/* report. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_periodic_report_start(UX_HOST_CLASS_HID *hid) +{ + +UINT status; +UX_TRANSFER *transfer_request; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* Check the status of the interrupt endpoint. */ + if (hid -> ux_host_class_hid_interrupt_endpoint_status != UX_HOST_CLASS_HID_INTERRUPT_ENDPOINT_READY) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_PERIODIC_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_PERIODIC_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error status. */ + return(UX_HOST_CLASS_HID_PERIODIC_REPORT_ERROR); + } + + /* Get the address of the HID associated with the interrupt endpoint. */ + transfer_request = &hid -> ux_host_class_hid_interrupt_endpoint -> ux_endpoint_transfer_request; + + /* The transfer on the interrupt endpoint can be started. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the status. If no error, adjust the status of the periodic endpoint + to active. */ + if (status == UX_SUCCESS) + hid -> ux_host_class_hid_interrupt_endpoint_status = UX_HOST_CLASS_HID_INTERRUPT_ENDPOINT_ACTIVE; + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return the function status */ + return(status); +} diff --git a/common/usbx_host_classes/src/ux_host_class_hid_periodic_report_stop.c b/common/usbx_host_classes/src/ux_host_class_hid_periodic_report_stop.c new file mode 100644 index 0000000..5b5845c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_periodic_report_stop.c @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_periodic_report_stop PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function stops the interrupt endpoint of the HID class */ +/* instance. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_endpoint_transfer_abort Abort transfer on endpoint */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_periodic_report_stop(UX_HOST_CLASS_HID *hid) +{ + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Check the status of the interrupt endpoint. */ + if (hid -> ux_host_class_hid_interrupt_endpoint_status != UX_HOST_CLASS_HID_INTERRUPT_ENDPOINT_ACTIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_PERIODIC_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_PERIODIC_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_PERIODIC_REPORT_ERROR); + } + + /* Abort the transfer going on on the endpoint now. */ + _ux_host_stack_endpoint_transfer_abort(hid -> ux_host_class_hid_interrupt_endpoint); + + /* Regardless of the status, we update the status of the endpoint. */ + hid -> ux_host_class_hid_interrupt_endpoint_status = UX_HOST_CLASS_HID_INTERRUPT_ENDPOINT_READY; + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_remote_control_activate.c b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_activate.c new file mode 100644 index 0000000..0cb1032 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_activate.c @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Remote Control Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_remote_control.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_remote_control_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the enumeration of a HID Remote Control */ +/* class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_periodic_report_start Start periodic report */ +/* _ux_host_class_hid_report_callback_register Register callback */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Remote Control Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_remote_control_activate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UX_HOST_CLASS_HID_REPORT_CALLBACK call_back; +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_REMOTE_CONTROL *remote_control_instance; +UINT status = UX_SUCCESS; + + + /* Get the instance to the HID class. */ + hid = command -> ux_host_class_hid_client_command_instance; + + /* And of the HID client. */ + hid_client = hid -> ux_host_class_hid_client; + + /* Get some memory for both the HID class instance of this client + and for the callback. */ + remote_control_instance = (UX_HOST_CLASS_HID_REMOTE_CONTROL *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + sizeof(UX_HOST_CLASS_HID_REMOTE_CONTROL)); + if (remote_control_instance == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Attach the remote control instance to the client instance. */ + hid_client -> ux_host_class_hid_client_local_instance = (VOID *) remote_control_instance; + + /* Save the HID instance in the client instance. */ + remote_control_instance -> ux_host_class_hid_remote_control_hid = hid; + + /* The instance is live now. */ + remote_control_instance -> ux_host_class_hid_remote_control_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* Allocate the round-robin buffer that the remote control instance will use + * to store the usages as they come in. + * Size calculation overflow is checked near where _USAGE_ARRAY_LENGTH is defined. + */ + remote_control_instance -> ux_host_class_hid_remote_control_usage_array = (ULONG *) + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, + UX_HOST_CLASS_HID_REMOTE_CONTROL_USAGE_ARRAY_LENGTH*4); + + /* Check memory pointer. */ + if (remote_control_instance -> ux_host_class_hid_remote_control_usage_array == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + + /* If there is no error, go on. */ + if (status == UX_SUCCESS) + { + + /* Initialize the head and tail of this array. */ + remote_control_instance -> ux_host_class_hid_remote_control_usage_array_head = remote_control_instance -> ux_host_class_hid_remote_control_usage_array; + remote_control_instance -> ux_host_class_hid_remote_control_usage_array_tail = remote_control_instance -> ux_host_class_hid_remote_control_usage_array; + + /* Initialize the report callback. */ + call_back.ux_host_class_hid_report_callback_id = 0; + call_back.ux_host_class_hid_report_callback_function = _ux_host_class_hid_remote_control_callback; + call_back.ux_host_class_hid_report_callback_buffer = UX_NULL; + call_back.ux_host_class_hid_report_callback_flags = UX_HOST_CLASS_HID_REPORT_INDIVIDUAL_USAGE; + call_back.ux_host_class_hid_report_callback_length = 0; + + /* Register the report call back when data comes in on this report. */ + status = _ux_host_class_hid_report_callback_register(hid, &call_back); + } + + /* If there is no error, go on. */ + if (status == UX_SUCCESS) + { + + /* Start the periodic report. */ + status = _ux_host_class_hid_periodic_report_start(hid); + + /* If OK, we are done. */ + if (status == UX_SUCCESS) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_REMOTE_CONTROL_ACTIVATE, hid, remote_control_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_INSERTION, hid -> ux_host_class_hid_class, (VOID *) hid_client); + } + + /* We are done success. */ + return (UX_SUCCESS); + } + } + + /* We are here when there is error. */ + + /* Detach instance. */ + hid_client -> ux_host_class_hid_client_local_instance = UX_NULL; + + /* Free usage array. */ + if (remote_control_instance -> ux_host_class_hid_remote_control_usage_array) + _ux_utility_memory_free(remote_control_instance -> ux_host_class_hid_remote_control_usage_array); + + /* Free instance memory. */ + _ux_utility_memory_free(remote_control_instance); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_remote_control_callback.c b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_callback.c new file mode 100644 index 0000000..0997f2d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_callback.c @@ -0,0 +1,131 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Remote Control Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_remote_control.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_remote_control_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback mechanism for a report registration. */ +/* */ +/* INPUT */ +/* */ +/* callback Pointer to callback */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Remote Control Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hid_remote_control_callback(UX_HOST_CLASS_HID_REPORT_CALLBACK *callback) +{ + +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_REMOTE_CONTROL *remote_control_instance; +ULONG *array_head; +ULONG *array_tail; +ULONG *array_end; +ULONG *array_start; +ULONG *array_head_next; + + + /* Get the HID client instance that issued the callback. */ + hid_client = callback -> ux_host_class_hid_report_callback_client; + + /* Get the remote control local instance. */ + remote_control_instance = (UX_HOST_CLASS_HID_REMOTE_CONTROL *) hid_client -> ux_host_class_hid_client_local_instance; + + /* Load the remote control usage/value array info. */ + array_start = remote_control_instance -> ux_host_class_hid_remote_control_usage_array; + array_end = array_start + UX_HOST_CLASS_HID_REMOTE_CONTROL_USAGE_ARRAY_LENGTH; + array_head = remote_control_instance -> ux_host_class_hid_remote_control_usage_array_head; + array_tail = remote_control_instance -> ux_host_class_hid_remote_control_usage_array_tail; + + /* We have a single usage/value. We have to store it into the array. If the array overflows, + there is no mechanism for flow control here so we ignore the usage/value until the + applications makes more room in the array. */ + + /* Get position where next head will be. */ + array_head_next = array_head + 2; + + /* The head always points to where we will insert the next element. This + is the reason for the wrap. */ + if (array_head_next == array_end) + array_head_next = array_start; + + /* Do we have enough space to store the new usage? */ + if (array_head_next != array_tail) + { + + /* Yes, we have some space. */ + *array_head = callback -> ux_host_class_hid_report_callback_usage; + *(array_head + 1) = callback -> ux_host_class_hid_report_callback_value; + + /* Now update the array head. */ + remote_control_instance -> ux_host_class_hid_remote_control_usage_array_head = array_head_next; + } + else + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_REMOTE_CONTROL_CALLBACK, hid_client, remote_control_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Notify application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + } + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_remote_control_deactivate.c b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_deactivate.c new file mode 100644 index 0000000..8146336 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_deactivate.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Remote Control Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_remote_control.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_remote_control_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the deactivation of a HID Remote Control */ +/* class instance. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_periodic_report_stop Stop periodic report */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Remote Control Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_remote_control_deactivate(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_REMOTE_CONTROL *remote_control_instance; +UINT status; + + + /* Get the instance to the HID class. */ + hid = command -> ux_host_class_hid_client_command_instance; + + /* Stop the periodic report. */ + status = _ux_host_class_hid_periodic_report_stop(hid); + + /* Get the HID client pointer. */ + hid_client = hid -> ux_host_class_hid_client; + + /* Get the remote control local instance. */ + remote_control_instance = (UX_HOST_CLASS_HID_REMOTE_CONTROL *) hid_client -> ux_host_class_hid_client_local_instance; + + /* Unload all the memory used by the remote control client. */ + _ux_utility_memory_free(remote_control_instance -> ux_host_class_hid_remote_control_usage_array); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_REMOTE_CONTROL_DEACTIVATE, hid, remote_control_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Now free the instance memory. */ + _ux_utility_memory_free(hid_client -> ux_host_class_hid_client_local_instance); + + /* We may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_REMOVAL, hid -> ux_host_class_hid_class, (VOID *) hid_client); + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_remote_control_entry.c b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_entry.c new file mode 100644 index 0000000..6fb8812 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_entry.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Remote Control Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_remote_control.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_remote_control_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the HID Remote Control client. */ +/* This function is called by the HID class after it has parsed a new */ +/* HID report descriptor and is searching for a HID client. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_remote_control_activate */ +/* Activate HID RC class */ +/* _ux_host_class_hid_remote_control_deactivate */ +/* Deactivate HID RC class */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_remote_control_entry(UX_HOST_CLASS_HID_CLIENT_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_hid_client_command_request) + { + + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the HID class know if we want to own + this device or not */ + if ((command -> ux_host_class_hid_client_command_page == UX_HOST_CLASS_HID_PAGE_CONSUMER) && + (command -> ux_host_class_hid_client_command_usage == UX_HOST_CLASS_HID_CONSUMER_REMOTE_CONTROL)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used by the HID class to start the HID client. */ + status = _ux_host_class_hid_remote_control_activate(command); + + /* Return completion status. */ + return(status); + + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used by the HID class when it received a deactivate + command from the USBX stack and there was a HID client attached to the HID instance */ + status = _ux_host_class_hid_remote_control_deactivate(command); + + /* Return completion status. */ + return(status); + } + + /* Return error status. */ + return(UX_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_remote_control_usage_get.c b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_usage_get.c new file mode 100644 index 0000000..8fbd9a1 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_remote_control_usage_get.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Remote Control Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_class_hid_remote_control.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_remote_control_usage_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads the usage and value from the remote control */ +/* round-robin buffer. */ +/* */ +/* INPUT */ +/* */ +/* remote_control_instance Pointer to remote control */ +/* usage Pointer to usage */ +/* value Pointer to value */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Remote Control Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_remote_control_usage_get(UX_HOST_CLASS_HID_REMOTE_CONTROL *remote_control_instance, ULONG *usage, ULONG *value) +{ + +ULONG *array_head; +ULONG *array_tail; +ULONG *array_end; +ULONG *array_start; +UX_HOST_CLASS_HID *hid; + + /* Get the HID class associated with the HID client. */ + hid = remote_control_instance -> ux_host_class_hid_remote_control_hid; + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Load the remote control usage/value array info. */ + array_start = remote_control_instance -> ux_host_class_hid_remote_control_usage_array; + array_end = array_start + UX_HOST_CLASS_HID_REMOTE_CONTROL_USAGE_ARRAY_LENGTH; + array_head = remote_control_instance -> ux_host_class_hid_remote_control_usage_array_head; + array_tail = remote_control_instance -> ux_host_class_hid_remote_control_usage_array_tail; + + /* We want to extract a usage/value from the circular queue of the remote control instance. */ + if (array_tail == array_head) + return(UX_ERROR); + + /* Get the usage/value from the current tail. */ + *usage = *array_tail; + *value = *(array_tail + 1); + + /* Now we need to update the tail value. Are we at the end of the array? */ + if ((array_tail+2) >= array_end) + array_tail = array_start; + else + array_tail += 2; + + /* Set the tail pointer. */ + remote_control_instance -> ux_host_class_hid_remote_control_usage_array_tail = array_tail; + + /* The status will tell the application there is something valid in the usage/value. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_add.c b/common/usbx_host_classes/src/ux_host_class_hid_report_add.c new file mode 100644 index 0000000..59ca089 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_add.c @@ -0,0 +1,291 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_add PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function adds a report (input/output/feature) to the current */ +/* parser. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* descriptor Pointer to descriptor */ +/* item Pointer to item */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_item_data_get Get data item */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_copy Copy memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_add(UX_HOST_CLASS_HID *hid, UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item) +{ + +UX_HOST_CLASS_HID_PARSER *hid_parser; +ULONG hid_field_value; +ULONG hid_field_count; +UX_HOST_CLASS_HID_REPORT *new_hid_report; +UX_HOST_CLASS_HID_REPORT *hid_report; +UX_HOST_CLASS_HID_FIELD *hid_field; +UX_HOST_CLASS_HID_FIELD *new_hid_field; +ULONG current_field_address; + + + /* Get the parser structure pointer. */ + hid_parser = &hid -> ux_host_class_hid_parser; + + /* Obtain the field value from the report. */ + hid_field_value = _ux_host_class_hid_item_data_get(descriptor, item); + + /* Allocate some memory to store this new report. */ + new_hid_report = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_HID_REPORT)); + if (new_hid_report == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* We need to select the report entry based on the type of report. If the entry in the + report chain is NULL, this is the first report so update the start of the chain. */ + switch (item -> ux_host_class_hid_item_report_tag) + { + + case UX_HOST_CLASS_HID_MAIN_TAG_INPUT: + + hid_report = hid_parser -> ux_host_class_hid_parser_input_report; + if (hid_report == UX_NULL) + hid_parser -> ux_host_class_hid_parser_input_report = new_hid_report; + + /* This is a Input report. */ + new_hid_report -> ux_host_class_hid_report_type = UX_HOST_CLASS_HID_REPORT_TYPE_INPUT; + break; + + case UX_HOST_CLASS_HID_MAIN_TAG_OUTPUT: + + hid_report = hid_parser -> ux_host_class_hid_parser_output_report; + if (hid_report == UX_NULL) + hid_parser -> ux_host_class_hid_parser_output_report = new_hid_report; + + /* This is output report. */ + new_hid_report -> ux_host_class_hid_report_type = UX_HOST_CLASS_HID_REPORT_TYPE_OUTPUT; + break; + + case UX_HOST_CLASS_HID_MAIN_TAG_FEATURE: + + hid_report = hid_parser -> ux_host_class_hid_parser_feature_report; + if (hid_report == UX_NULL) + hid_parser -> ux_host_class_hid_parser_feature_report = new_hid_report; + + /* This is a Feature report. */ + new_hid_report -> ux_host_class_hid_report_type = UX_HOST_CLASS_HID_REPORT_TYPE_FEATURE; + break; + + + default: + + _ux_utility_memory_free(new_hid_report); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_HOST_CLASS_HID_REPORT_ERROR); + } + + /* If there is a preceding report, locate the end of the report chain. */ + if (hid_report != UX_NULL) + { + + while (hid_report -> ux_host_class_hid_report_next_report != UX_NULL) + hid_report = hid_report -> ux_host_class_hid_report_next_report; + } + + /* If this report is part of the current global report, use the last report + to add the fields. */ + if ((hid_report != UX_NULL) && (hid_report -> ux_host_class_hid_report_id == hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_id)) + { + + /* So we did not need a new report afterall! */ + _ux_utility_memory_free(new_hid_report); + new_hid_report = hid_report; + } + else + { + + /* We do have to build a new report. Add the new one to the chain. */ + if (hid_report != UX_NULL) + hid_report -> ux_host_class_hid_report_next_report = new_hid_report; + + /* Add the new report ID. */ + new_hid_report -> ux_host_class_hid_report_id = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_id; + } + + /* Compute the size of the report. The size is first calculated in bits. */ + current_field_address = new_hid_report -> ux_host_class_hid_report_bit_length; + new_hid_report -> ux_host_class_hid_report_bit_length += hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_size* + hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count; + + /* Now compute the size in bytes (easier for the end/receive reports functions). */ + new_hid_report -> ux_host_class_hid_report_byte_length = new_hid_report -> ux_host_class_hid_report_bit_length >> 3; + + /* Take care of the bit padding if necessary. */ + if (new_hid_report -> ux_host_class_hid_report_bit_length & 7) + new_hid_report -> ux_host_class_hid_report_byte_length++; + + /* Get the number of field usage for this report, this value depends on the report type. If this is + an array, we use the number of usages; if this a variable, we use the report count. */ + if (hid_field_value & UX_HOST_CLASS_HID_ITEM_VARIABLE) + hid_field_count = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count; + else + hid_field_count = hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_number_usage; + + /* If the field count is null, this is only padding and there is no field to be allocated to the report. */ + if (hid_field_count == 0) + return(UX_SUCCESS); + + /* Create the field structure. */ + new_hid_field = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_HID_FIELD)); + if (new_hid_field == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Attach the new field to the report. The report may already contain a field, if so parse the chain + until we reach the end of the chain. */ + new_hid_report -> ux_host_class_hid_report_number_item += hid_field_count; + if (new_hid_report -> ux_host_class_hid_report_field == UX_NULL) + { + + /* This is the first field for the report. */ + new_hid_report -> ux_host_class_hid_report_field = new_hid_field; + } + else + { + + /* We have previous HID fields, so search for the end of the chain. */ + hid_field = new_hid_report -> ux_host_class_hid_report_field; + while(hid_field -> ux_host_class_hid_field_next_field != UX_NULL) + hid_field = hid_field -> ux_host_class_hid_field_next_field; + + /* Attach the new field to the end of the chain. */ + hid_field -> ux_host_class_hid_field_next_field = new_hid_field; + } + + /* From the parser structure, update the new field values. Start with logical values. */ + new_hid_field -> ux_host_class_hid_field_logical_min = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_logical_min; + new_hid_field -> ux_host_class_hid_field_logical_max = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_logical_max; + + /* Then the usage values. Note that these are only used if the item is an array. */ + new_hid_field -> ux_host_class_hid_field_usage_page = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_usage_page; + new_hid_field -> ux_host_class_hid_field_usage_min = hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_min; + new_hid_field -> ux_host_class_hid_field_usage_max = hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usage_max; + + /* Then physical values. */ + new_hid_field -> ux_host_class_hid_field_physical_min = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_physical_min; + new_hid_field -> ux_host_class_hid_field_physical_max = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_physical_max; + + /* Then unit values. */ + new_hid_field -> ux_host_class_hid_field_unit = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_unit; + new_hid_field -> ux_host_class_hid_field_unit_expo = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_unit_expo; + + /* Then report values. */ + new_hid_field -> ux_host_class_hid_field_report_type = item -> ux_host_class_hid_item_report_tag; + new_hid_field -> ux_host_class_hid_field_report_id = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_id; + new_hid_field -> ux_host_class_hid_field_report_size = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_size; + new_hid_field -> ux_host_class_hid_field_report_count = hid_parser -> ux_host_class_hid_parser_global.ux_host_class_hid_global_item_report_count; + new_hid_field -> ux_host_class_hid_field_report_offset = current_field_address; + + /* Save the HID field value. */ + new_hid_field -> ux_host_class_hid_field_value = hid_field_value; + + /* We need some memory for the values. */ + new_hid_field -> ux_host_class_hid_field_values = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, + new_hid_field -> ux_host_class_hid_field_report_count, 4); + + /* Check the memory pointer. */ + if (new_hid_field -> ux_host_class_hid_field_values == UX_NULL) + { + + _ux_utility_memory_free(new_hid_field); + return(UX_MEMORY_INSUFFICIENT); + } + + /* We need some memory for the usages, but only for variable items; usage + values for array items can be calculated. */ + if (hid_field_value & UX_HOST_CLASS_HID_ITEM_VARIABLE) + { + + /* Allocate memory for the usages. */ + new_hid_field -> ux_host_class_hid_field_usages = _ux_utility_memory_allocate_mulc_safe(UX_NO_ALIGN, UX_REGULAR_MEMORY, hid_field_count, 4); + if (new_hid_field -> ux_host_class_hid_field_usages == UX_NULL) + { + + _ux_utility_memory_free(new_hid_field -> ux_host_class_hid_field_values); + _ux_utility_memory_free(new_hid_field); + return(UX_MEMORY_INSUFFICIENT); + } + + /* Copy the current usages in the field structure. */ + _ux_utility_memory_copy(new_hid_field -> ux_host_class_hid_field_usages, hid_parser -> ux_host_class_hid_parser_local.ux_host_class_hid_local_item_usages, hid_field_count*4); + } + + /* Save the number of usages. */ + new_hid_field -> ux_host_class_hid_field_number_usage = hid_field_count; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_callback_register.c b/common/usbx_host_classes/src/ux_host_class_hid_report_callback_register.c new file mode 100644 index 0000000..960d810 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_callback_register.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_callback_register PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will register a report callback to a HID report. */ +/* This function should be called by a HID client when it has been */ +/* instantiated by a new HID device. */ +/* */ +/* The registration process will allow the HID class to call a */ +/* function in the HID client when an asynchronous report is present. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* call_back HID report callback */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify class instance is valid*/ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_callback_register(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_REPORT_CALLBACK *call_back) +{ + +UINT status; +UX_HOST_CLASS_HID_REPORT *hid_report; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status!=UX_SUCCESS) + + /* Return error. */ + return(status); + + /* Search for the report ID. Note that this can only be an Input report! */ + hid_report = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_input_report; + + /* Parse all the report IDs in search of the one specified by the user. */ + while (hid_report != UX_NULL) + { + + /* Check report ID. */ + if (hid_report -> ux_host_class_hid_report_id == call_back -> ux_host_class_hid_report_callback_id) + { + + /* We have found the correct report. Set the call back function, buffer and flags. */ + hid_report -> ux_host_class_hid_report_callback_function = call_back -> ux_host_class_hid_report_callback_function; + hid_report -> ux_host_class_hid_report_callback_buffer = call_back -> ux_host_class_hid_report_callback_buffer; + hid_report -> ux_host_class_hid_report_callback_flags = call_back -> ux_host_class_hid_report_callback_flags; + hid_report -> ux_host_class_hid_report_callback_length = call_back -> ux_host_class_hid_report_callback_length; + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Tell the user the report was OK and the call back was registered. */ + return(UX_SUCCESS); + } + + /* Jump to next report. */ + hid_report = hid_report -> ux_host_class_hid_report_next_report; + } + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* The report ID could not be found amongst the reports. */ + return(UX_HOST_CLASS_HID_REPORT_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_compress.c b/common/usbx_host_classes/src/ux_host_class_hid_report_compress.c new file mode 100644 index 0000000..2264881 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_compress.c @@ -0,0 +1,207 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_compress PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will compress a client report into a report buffer. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* client_report Pointer to client report */ +/* report_buffer Pointer to report buffer */ +/* report_length Length of report */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_compress(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report, + UCHAR *report_buffer, ULONG report_length) +{ + +UX_HOST_CLASS_HID_REPORT *hid_report; +UX_HOST_CLASS_HID_FIELD *hid_field; +ULONG field_usage; +ULONG field_report_count; +ULONG field_report_size; +ULONG *client_buffer; +ULONG client_value; +UCHAR value; +ULONG data_offset_bit; +UCHAR is_valid_usage; + + UX_PARAMETER_NOT_USED(hid); + UX_PARAMETER_NOT_USED(report_length); + + /* Get the report pointer from the caller. */ + hid_report = client_report -> ux_host_class_hid_client_report; + + /* Get the pointer to the user buffer. */ + client_buffer = client_report -> ux_host_class_hid_client_report_buffer; + + /* Get the first field associated with the report. */ + hid_field = hid_report -> ux_host_class_hid_report_field; + + /* Set data offset bit. */ + data_offset_bit = 0; + + /* We need to compress each field defined in the report. */ + while (hid_field != UX_NULL) + { + + /* Each report field has a report count value. This count is used to extract + values from the incoming report and build each usage/value instance. */ + for (field_report_count = 0; field_report_count < hid_field -> ux_host_class_hid_field_report_count; field_report_count++) + { + + /* Ensure the usage in the client buffer is valid. How we determine + this depends on whether the field is VARIABLE or ARRAY. */ + is_valid_usage = UX_FALSE; + if (hid_field -> ux_host_class_hid_field_value & UX_HOST_CLASS_HID_ITEM_VARIABLE) + { + + /* Go through the usage array to try and find the client's usage. */ + for (field_usage = 0; field_usage < hid_field -> ux_host_class_hid_field_number_usage; field_usage++) + { + + /* Is this a usage we've recorded? */ + if (*client_buffer == hid_field -> ux_host_class_hid_field_usages[field_usage]) + { + + /* Yes, this is a valid usage. */ + is_valid_usage = UX_TRUE; + break; + } + } + } + else + { + + /* Is the usage page valid? */ + if (((*client_buffer & 0xffff0000) >> 16) == hid_field -> ux_host_class_hid_field_usage_page) + { + + /* Is the usage in the min and max range? */ + if ((*client_buffer & 0xffff) >= hid_field -> ux_host_class_hid_field_usage_min && + (*client_buffer & 0xffff) <= hid_field -> ux_host_class_hid_field_usage_max) + { + + /* Yes, this is a valid usage. */ + is_valid_usage = UX_TRUE; + } + } + } + + /* Is the usage invalid? */ + if (is_valid_usage == UX_FALSE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_HID_REPORT_ERROR); + } + + /* Skip the usage and point the buffer to the value. */ + client_buffer++; + + /* Read the client value. */ + client_value = *client_buffer++; + + /* Build the value field in the report buffer bit by bit. */ + for (field_report_size = hid_field -> ux_host_class_hid_field_report_size; field_report_size > 0; field_report_size--) + { + + /* Isolate each bit from the report value. */ + value = (UCHAR) client_value & 1; + + /* Shift the isolated bit to its right space in the report byte. */ + value = (UCHAR)(value << data_offset_bit); + + /* Update the report with the bit value. */ + *report_buffer |= value; + + /* Move to next bit. */ + data_offset_bit++; + + /* Are we on a byte boundary. */ + if ((data_offset_bit & 7) == 0) + { + + /* If so increment the report address. */ + report_buffer++; + + /* Reset offset bit. */ + data_offset_bit = 0; + } + + /* Move to the next bit. */ + client_value = client_value >> 1; + } + } + + /* Move to the next field. */ + hid_field = hid_field -> ux_host_class_hid_field_next_field; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_decompress.c b/common/usbx_host_classes/src/ux_host_class_hid_report_decompress.c new file mode 100644 index 0000000..e3eedcd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_decompress.c @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_decompress PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will decompress a raw report into a client buffer. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* client_report Pointer to client report */ +/* report_buffer Pointer to report buffer */ +/* report_length Length of report */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_field_decompress Decompress field */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_decompress(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report, + UCHAR *report_buffer, ULONG report_length) +{ + +UX_HOST_CLASS_HID_REPORT *hid_report; +UX_HOST_CLASS_HID_FIELD *hid_field; + + UX_PARAMETER_NOT_USED(hid); + + /* Get the report pointer from the caller. */ + hid_report = client_report -> ux_host_class_hid_client_report; + + /* Check if this report has a ID field in the front. An ID field is required + if report ID is non null. */ + if (hid_report -> ux_host_class_hid_report_id != 0) + { + + /* We have an ID tag in the report. The ID tag is the first byte of the report. Skip the + ID tag and adjust the length. */ + report_buffer++; + report_length--; + } + + /* Get the first field associated with the report. */ + hid_field = hid_report -> ux_host_class_hid_report_field; + + /* We need to decompress each field defined in the report. */ + while (hid_field != UX_NULL) + { + + /* Decompress a field. */ + _ux_host_class_hid_field_decompress(hid_field, report_buffer, client_report); + + /* Move to the next field. */ + hid_field = hid_field -> ux_host_class_hid_field_next_field; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_descriptor_get.c b/common/usbx_host_classes/src/ux_host_class_hid_report_descriptor_get.c new file mode 100644 index 0000000..628bebf --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_descriptor_get.c @@ -0,0 +1,204 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_descriptor_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the report descriptor and analyzes it. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* length Length of descriptor */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_global_item_parse Parse global item */ +/* _ux_host_class_hid_local_item_parse Parse local item */ +/* _ux_host_class_hid_report_item_analyse Analyze report */ +/* _ux_host_class_hid_resources_free Free HID resources */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_descriptor_get(UX_HOST_CLASS_HID *hid, ULONG length) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UCHAR *descriptor; +UCHAR *start_descriptor; +UX_HOST_CLASS_HID_ITEM item; +UINT status; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hid -> ux_host_class_hid_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the report descriptor. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, length); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* We need to save the descriptor starting address. */ + start_descriptor = descriptor; + + /* Create a transfer_request for the GET_DESCRIPTOR request */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = length; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_HID_REPORT_DESCRIPTOR << 8; + transfer_request -> ux_transfer_request_index = hid -> ux_host_class_hid_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == length)) + { + + /* Parse the report descriptor and build the report items. */ + while (length) + { + + /* Get one item from the report and analyze it. */ + _ux_host_class_hid_report_item_analyse(descriptor, &item); + + /* Point the descriptor right after the item identifier. */ + descriptor += item.ux_host_class_hid_item_report_format; + + /* Process relative to the item type. */ + switch (item.ux_host_class_hid_item_report_type) + { + + case UX_HOST_CLASS_HID_TYPE_GLOBAL: + + /* This is a global item. */ + status = _ux_host_class_hid_global_item_parse(hid, &item, descriptor); + break; + + + case UX_HOST_CLASS_HID_TYPE_MAIN: + + /* This is a main item. */ + status = _ux_host_class_hid_main_item_parse(hid, &item, descriptor); + break; + + + case UX_HOST_CLASS_HID_TYPE_LOCAL: + + /* This is a local item. */ + status = _ux_host_class_hid_local_item_parse(hid, &item, descriptor); + break; + + default: + + /* This is a reserved item, meaning it shouldn't be used! */ + + /* Set status to error. The check after this switch statement + will handle the rest. */ + status = UX_DESCRIPTOR_CORRUPTED; + break; + } + + /* Recheck the status code. */ + if (status != UX_SUCCESS) + { + break; + } + + /* Jump to the next item. */ + descriptor += item.ux_host_class_hid_item_report_length; + + /* Verify that the report descriptor is not corrupted. */ + if (length < item.ux_host_class_hid_item_report_length) + { + + /* Return error status. */ + status = (UX_DESCRIPTOR_CORRUPTED); + break; + } + + /* Adjust the length. */ + length -= (ULONG)(item.ux_host_class_hid_item_report_length + item.ux_host_class_hid_item_report_format); + } + } + else + + /* Descriptor length error! */ + status = UX_DESCRIPTOR_CORRUPTED; + + if (status != UX_SUCCESS) + { + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* The descriptor is corrupted! */ + _ux_host_class_hid_resources_free(hid); + } + + /* Free used resources. */ + _ux_utility_memory_free(start_descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_get.c b/common/usbx_host_classes/src/ux_host_class_hid_report_get.c new file mode 100644 index 0000000..add69ce --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_get.c @@ -0,0 +1,252 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets a report (input/output/feature) from the device. */ +/* The report can be either decompressed by the stack or raw. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* client_report Pointer to client report */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_report_decompress Decompress HID report */ +/* _ux_host_stack_class_instance_verify Verify the instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_copy Copy memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_get(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UCHAR *report_buffer; +UX_HOST_CLASS_HID_REPORT *hid_report; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_REPORT_GET, hid, client_report, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* Get the report pointer from the caller. */ + hid_report = client_report -> ux_host_class_hid_client_report; + + /* Ensure this is a INPUT report. */ + if (hid_report -> ux_host_class_hid_report_type != UX_HOST_CLASS_HID_REPORT_TYPE_INPUT) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error. */ + return(UX_HOST_CLASS_HID_REPORT_ERROR); + } + + /* Check the report length, if 0, we don't need to do anything. */ + if (hid_report -> ux_host_class_hid_report_byte_length == 0) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error code. */ + return(UX_HOST_CLASS_HID_REPORT_ERROR); + } + + /* Get some memory for reading the report. */ + report_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, hid_report -> ux_host_class_hid_report_byte_length); + if (report_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return error code. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hid -> ux_host_class_hid_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + { + + /* Something went wrong. */ + + /* Free all resources. */ + _ux_utility_memory_free(report_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + return(status); + } + + /* Create a transfer request for the GET_REPORT request. */ + transfer_request -> ux_transfer_request_data_pointer = report_buffer; + transfer_request -> ux_transfer_request_requested_length = hid_report -> ux_host_class_hid_report_byte_length; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_HID_GET_REPORT; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = (UINT)((USHORT) hid_report -> ux_host_class_hid_report_id | (USHORT) hid_report -> ux_host_class_hid_report_type << 8); + transfer_request -> ux_transfer_request_index = hid -> ux_host_class_hid_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == hid_report -> ux_host_class_hid_report_byte_length)) + { + + /* The report is now in memory in a raw format. The application may desire to keep it that way! */ + if (client_report -> ux_host_class_hid_client_report_flags & UX_HOST_CLASS_HID_REPORT_RAW) + { + + /* Ensure the user has given us enough memory for the raw buffer. */ + if (client_report -> ux_host_class_hid_client_report_length >= transfer_request -> ux_transfer_request_actual_length) + { + + /* We have enough memory to store the raw buffer. */ + _ux_utility_memory_copy(client_report -> ux_host_class_hid_client_report_buffer, report_buffer, transfer_request -> ux_transfer_request_actual_length); + + /* Set status to success. */ + status = UX_SUCCESS; + } + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_BUFFER_OVERFLOW, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Set overflow status. */ + status = UX_BUFFER_OVERFLOW; + } + } + else + { + + /* The report buffer must be parsed and decompressed. */ + status = _ux_host_class_hid_report_decompress(hid, client_report, report_buffer, transfer_request -> ux_transfer_request_actual_length); + } + } + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Set report error status. */ + status = UX_HOST_CLASS_HID_REPORT_ERROR; + } + + /* Free all resources. */ + _ux_utility_memory_free(report_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return the completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_id_get.c b/common/usbx_host_classes/src/ux_host_class_hid_report_id_get.c new file mode 100644 index 0000000..71d1a26 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_id_get.c @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_id_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves the report after */ +/* report_id -> ux_host_class_hid_report_get_id and stores it in the */ +/* same pointer. If report_id -> ux_host_class_hid_report_get_id is */ +/* null, retrieves the first report of the type specified by */ +/* report_id -> ux_host_class_hid_report_get_type. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* report_id Report id structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* _ux_host_stack_class_instance_verify Verify class instance is valid*/ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_id_get(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_REPORT_GET_ID *report_id) +{ + +UINT status; +UX_HOST_CLASS_HID_REPORT *next_hid_report; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Check if this is the first report to get. */ + if (report_id -> ux_host_class_hid_report_get_report == UX_NULL) + { + + /* Check for the type of report ID to get (Input, Output, Feature). */ + switch (report_id -> ux_host_class_hid_report_get_type) + { + + case UX_HOST_CLASS_HID_REPORT_TYPE_INPUT : + + /* Search for the input report ID. */ + next_hid_report = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_input_report; + break; + + case UX_HOST_CLASS_HID_REPORT_TYPE_OUTPUT : + + /* Search for the output report ID. */ + next_hid_report = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_output_report; + break; + + case UX_HOST_CLASS_HID_REPORT_TYPE_FEATURE : + + /* Search for the feature report ID. */ + next_hid_report = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_feature_report; + break; + + default : + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* The report ID could not be found amongst the reports. */ + next_hid_report = UX_NULL; + break; + } + } + else + { + + /* We had a report ID scan previously, point to the next report. */ + next_hid_report = report_id -> ux_host_class_hid_report_get_report -> ux_host_class_hid_report_next_report; + } + + /* Did we find the next report? */ + if (next_hid_report != UX_NULL) + { + + /* We want the first report, memorize the ID. */ + report_id -> ux_host_class_hid_report_get_id = next_hid_report -> ux_host_class_hid_report_id; + + /* And remember where we left. */ + report_id -> ux_host_class_hid_report_get_report = next_hid_report; + + /* Successfully found next report. */ + status = UX_SUCCESS; + } + else + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* No more reports. */ + status = UX_HOST_CLASS_HID_REPORT_ERROR; + } + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* The status variable has been set correctly. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_item_analyse.c b/common/usbx_host_classes/src/ux_host_class_hid_report_item_analyse.c new file mode 100644 index 0000000..d99351a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_item_analyse.c @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_item_analyse PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the report descriptor and analyzes it. */ +/* */ +/* INPUT */ +/* */ +/* descriptor Pointer to descriptor */ +/* item Pointer to item */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_item_analyse(UCHAR *descriptor, UX_HOST_CLASS_HID_ITEM *item) +{ + +UCHAR item_byte; + + + /* Get the first byte from the descriptor. */ + item_byte = *descriptor; + + /* We need to determine if this is a short or long item. + For long items, the tag is always 1111. */ + if ((item_byte & UX_HOST_CLASS_HID_ITEM_TAG_MASK) == UX_HOST_CLASS_HID_ITEM_TAG_LONG) + { + + /* We have a long item, mark its format. */ + item -> ux_host_class_hid_item_report_format = UX_HOST_CLASS_HID_ITEM_TAG_LONG; + + /* Set the type. */ + item -> ux_host_class_hid_item_report_type = (item_byte >> 2) & 3; + + /* Get its length (byte 1). */ + item -> ux_host_class_hid_item_report_length = (USHORT) *(descriptor + 1); + + /* Then the tag (byte 2). */ + item -> ux_host_class_hid_item_report_tag = *(descriptor + 2); + } + else + { + + /* We have a short item. Mark its format */ + item -> ux_host_class_hid_item_report_format = UX_HOST_CLASS_HID_ITEM_TAG_SHORT; + + /* Get the length of the item. */ + switch (item_byte & UX_HOST_CLASS_HID_ITEM_LENGTH_MASK) + { + + case 3: + + item -> ux_host_class_hid_item_report_length = 4; + break; + + default: + + item -> ux_host_class_hid_item_report_length = item_byte & UX_HOST_CLASS_HID_ITEM_LENGTH_MASK; + break; + } + + /* Set the type. */ + item -> ux_host_class_hid_item_report_type = (item_byte >> 2) & 3; + + /* Set the tag. */ + item -> ux_host_class_hid_item_report_tag = item_byte >> 4; + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_report_set.c b/common/usbx_host_classes/src/ux_host_class_hid_report_set.c new file mode 100644 index 0000000..688010a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_report_set.c @@ -0,0 +1,248 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_report_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets a report (input/output/feature) to the device. */ +/* The report can be either decompressed by the stack or raw. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* client_report Pointer to client report */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hid_report_compress Compress HID report */ +/* _ux_host_stack_class_instance_verify Verify the instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_copy Copy memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_report_set(UX_HOST_CLASS_HID *hid, UX_HOST_CLASS_HID_CLIENT_REPORT *client_report) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UCHAR *report_buffer; +UCHAR *current_report_buffer; +UX_HOST_CLASS_HID_REPORT *hid_report; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_REPORT_SET, hid, client_report, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_hid_name, (VOID *) hid) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Get the report pointer from the caller. */ + hid_report = client_report -> ux_host_class_hid_client_report; + + /* Ensure this is NOT an INPUT report. */ + if (hid_report -> ux_host_class_hid_report_type == UX_HOST_CLASS_HID_REPORT_TYPE_INPUT) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error code. */ + return(UX_HOST_CLASS_HID_REPORT_ERROR); + } + + /* Get some memory for sending the report. */ + if (hid_report -> ux_host_class_hid_report_id == 0) + report_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, hid_report -> ux_host_class_hid_report_byte_length); + else + report_buffer = _ux_utility_memory_allocate_add_safe(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, hid_report -> ux_host_class_hid_report_byte_length, 1); + + if (report_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return error code. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Memorize the start of the real report buffer. */ + current_report_buffer = report_buffer; + + /* Check if there is a report ID to be inserted in front of the buffer. */ + if (hid_report -> ux_host_class_hid_report_id != 0) + *current_report_buffer++ = (UCHAR)(hid_report -> ux_host_class_hid_report_id); + + /* The report is in the client's buffer. It may be raw or or decompressed. If decompressed, + we need to create the report. */ + if (client_report -> ux_host_class_hid_client_report_flags & UX_HOST_CLASS_HID_REPORT_RAW) + { + + /* Ensure the user is not trying to overflow the report buffer. */ + if (hid_report -> ux_host_class_hid_report_byte_length >= client_report -> ux_host_class_hid_client_report_length) + { + + /* We have enough memory to store the raw buffer. */ + _ux_utility_memory_copy(current_report_buffer, client_report -> ux_host_class_hid_client_report_buffer, hid_report -> ux_host_class_hid_report_byte_length); + } + else + { + + /* Free allocated buffer. */ + _ux_utility_memory_free(report_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_OVERFLOW, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error code. */ + return(UX_HOST_CLASS_HID_REPORT_OVERFLOW); + } + } + else + { + + /* The report buffer has to be compressed. */ + _ux_host_class_hid_report_compress(hid, client_report, current_report_buffer, client_report -> ux_host_class_hid_client_report_length); + } + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hid -> ux_host_class_hid_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&hid -> ux_host_class_hid_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + { + + /* Something went wrong. */ + + /* Free all resources. */ + _ux_utility_memory_free(report_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + return(status); + } + + /* Create a transfer request for the SET_REPORT request. */ + transfer_request -> ux_transfer_request_data_pointer = report_buffer; + transfer_request -> ux_transfer_request_requested_length = hid_report -> ux_host_class_hid_report_byte_length; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_HID_SET_REPORT; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = (UINT)((USHORT) hid_report -> ux_host_class_hid_report_id | (USHORT) hid_report -> ux_host_class_hid_report_type << 8); + transfer_request -> ux_transfer_request_index = hid -> ux_host_class_hid_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status != UX_SUCCESS) || (transfer_request -> ux_transfer_request_actual_length != hid_report -> ux_host_class_hid_report_byte_length)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_HID_REPORT_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_HID_REPORT_ERROR, hid, 0, 0, UX_TRACE_ERRORS, 0, 0) + + status = UX_HOST_CLASS_HID_REPORT_ERROR; + } + + /* Free all resources. */ + _ux_utility_memory_free(report_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&hid -> ux_host_class_hid_semaphore); + + /* Return the function status */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_resources_free.c b/common/usbx_host_classes/src/ux_host_class_hid_resources_free.c new file mode 100644 index 0000000..2cbacad --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_resources_free.c @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_resources_free PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function frees the resources allocated to a HID device. */ +/* */ +/* INPUT */ +/* */ +/* hid Pointer to HID class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hid_resources_free(UX_HOST_CLASS_HID *hid) +{ + + UX_PARAMETER_NOT_USED(hid); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hid_transfer_request_completed.c b/common/usbx_host_classes/src/ux_host_class_hid_transfer_request_completed.c new file mode 100644 index 0000000..6082c78 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hid_transfer_request_completed.c @@ -0,0 +1,269 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HID Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hid.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hid_transfer_request_completed PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the completion thread when a transfer */ +/* request has been completed either because the transfer is */ +/* successful or there was an error. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_hid_report_callback_function) */ +/* Callback function for report */ +/* _ux_host_class_hid_report_decompress Decompress HID report */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HID Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hid_transfer_request_completed(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_HID *hid; +UX_HOST_CLASS_HID_CLIENT *hid_client; +UX_HOST_CLASS_HID_REPORT *hid_report; +UINT status; +VOID *report_buffer; +UX_HOST_CLASS_HID_REPORT_CALLBACK callback; +UX_HOST_CLASS_HID_CLIENT_REPORT client_report; +ULONG *client_buffer; +UX_HOST_CLASS_HID_FIELD *hid_field; +ULONG field_report_count; + + /* Set Status to success. Optimistic view. */ + status = UX_SUCCESS; + + /* Get the class instance for this transfer request. */ + hid = (UX_HOST_CLASS_HID *) transfer_request -> ux_transfer_request_class_instance; + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* We have an error. We do not rehook another transfer if the device instance is shutting down or + if the transfer was aborted by the class. */ + if ((hid -> ux_host_class_hid_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) || + (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_ABORT)) + + /* We do not proceed. */ + return; + + else + { + + /* Reactivate the HID interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* We do not proceed. */ + return; + } + } + + /* Get the client instance attached to the HID. */ + hid_client = hid -> ux_host_class_hid_client; + + /* Get the pointer to the report buffer in the transfer request. */ + report_buffer = transfer_request -> ux_transfer_request_data_pointer; + + /* We know this incoming report is for the Input report. */ + hid_report = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_input_report; + + /* initialize some of the callback structure which are generic to any + reporting method. */ + callback.ux_host_class_hid_report_callback_client = hid_client; + callback.ux_host_class_hid_report_callback_id = hid_report -> ux_host_class_hid_report_id; + + /* For this report to be used, the HID client must have registered + the report. We check the call back function. */ + if (hid_report -> ux_host_class_hid_report_callback_function != UX_NULL) + { + + /* The report is now in memory in a raw format the application may desire to handle it that way! */ + if (hid_report -> ux_host_class_hid_report_callback_flags & UX_HOST_CLASS_HID_REPORT_RAW) + { + + /* Put the length of the report in raw form in the callers callback structure. */ + callback.ux_host_class_hid_report_callback_actual_length = transfer_request -> ux_transfer_request_actual_length; + callback.ux_host_class_hid_report_callback_buffer = report_buffer; + + /* Build the callback structure status. */ + callback.ux_host_class_hid_report_callback_status = status; + + /* Set the flags to indicate the type of report. */ + callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags; + + /* Call the report owner. */ + hid_report -> ux_host_class_hid_report_callback_function(&callback); + } + else + { + + /* The report may be decompressed, buffer length is based on number of items in report. + Each item is a pair of words (usage and the value itself), so the required decompress memory for each + item is 4 (word size) * 2 (number of word) = 8 bytes. + To accelerate we shift number of item by 3 to get the result. */ + if (UX_OVERFLOW_CHECK_MULC_ULONG(hid_report->ux_host_class_hid_report_number_item, 8)) + client_buffer = UX_NULL; + else + { + client_report.ux_host_class_hid_client_report_length = hid_report->ux_host_class_hid_report_number_item << 3; + + /* We need to allocate some memory to build the decompressed report. */ + client_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, client_report.ux_host_class_hid_client_report_length); + } + + /* Check completion status. */ + if (client_buffer == UX_NULL) + { + /* We have an error of memory, do not proceed */ + status = UX_MEMORY_INSUFFICIENT; + } + else + { + + /* We need to build a client structure to be used by the decompression engine. */ + client_report.ux_host_class_hid_client_report_buffer = client_buffer; + client_report.ux_host_class_hid_client_report_actual_length = 0; + client_report.ux_host_class_hid_client_report = hid_report; + + /* The report buffer must be parsed and decompressed into the local buffer. */ + _ux_host_class_hid_report_decompress(hid, &client_report, report_buffer, transfer_request -> ux_transfer_request_actual_length); + + /* The report may be decompressed and returned as individual Usages. */ + if (hid_report -> ux_host_class_hid_report_callback_flags & UX_HOST_CLASS_HID_REPORT_INDIVIDUAL_USAGE) + { + + /* Now, we need to call the HID client call back function with a usage/value couple. */ + hid_field = hid_report -> ux_host_class_hid_report_field; + + /* Set the flags to indicate the type of report (usage/value couple). */ + callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags; + + /* The length of the buffer is irrelevant here so we reset it. */ + callback.ux_host_class_hid_report_callback_actual_length = 0; + + /* Scan all the fields and send each usage/value. */ + while(hid_field != UX_NULL) + { + + /* Build each report item. */ + for (field_report_count = 0; field_report_count < hid_field -> ux_host_class_hid_field_report_count; field_report_count++) + { + + /* Insert the usage and the report value into the callback structure. */ + callback.ux_host_class_hid_report_callback_usage = *client_report.ux_host_class_hid_client_report_buffer++; + callback.ux_host_class_hid_report_callback_value = *client_report.ux_host_class_hid_client_report_buffer++; + + /* Build the callback structure status. */ + callback.ux_host_class_hid_report_callback_status = status; + + /* Call the report owner */ + hid_report -> ux_host_class_hid_report_callback_function(&callback); + } + + /* Get the next field. */ + hid_field = hid_field -> ux_host_class_hid_field_next_field; + } + } + else + { + + /* Add the length actually valid in the caller's buffer. */ + callback.ux_host_class_hid_report_callback_actual_length = client_report.ux_host_class_hid_client_report_actual_length; + + /* Add the caller's buffer address. */ + callback.ux_host_class_hid_report_callback_buffer = client_report.ux_host_class_hid_client_report_buffer; + + /* Build the callback structure status. */ + callback.ux_host_class_hid_report_callback_status = status; + + /* Set the flags to indicate the type of report. */ + callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags; + + /* Call the report owner. */ + hid_report -> ux_host_class_hid_report_callback_function(&callback); + } + + /* Free the memory resource we used. */ + _ux_utility_memory_free(client_buffer); + } + } + } + + /* Check latest status. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + /* Reactivate the HID interrupt pipe. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check latest status. */ + if (status != UX_SUCCESS) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_activate.c b/common/usbx_host_classes/src/ux_host_class_hub_activate.c new file mode 100644 index 0000000..af25734 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_activate.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs the enumeration of the HUB. The HUB */ +/* descriptor is read, the interrupt endpoint activated, power is set */ +/* to the downstream ports and the HUB instance will be awaken when */ +/* there is a status change on the HUB or one of the ports. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_configure Configure HUB */ +/* _ux_host_class_hub_descriptor_get Get descriptor */ +/* _ux_host_class_hub_interrupt_endpoint_start */ +/* Start interrupt endpoint */ +/* _ux_host_class_hub_ports_power Power ports */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_DEVICE *device; +UX_HOST_CLASS_HUB *hub; +UINT status; + + + /* We need to make sure that the enumeration thread knows about at least + one active HUB instance and the function to call when the thread + is awaken. */ + _ux_system_host -> ux_system_host_enum_hub_function = _ux_host_class_hub_change_detect; + + /* The HUB is always activated by the device descriptor and not the + instance descriptor. */ + device = (UX_DEVICE *) command -> ux_host_class_command_container; + + /* Instantiate this HUB class. */ + hub = (UX_HOST_CLASS_HUB *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_HUB)); + if (hub == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + hub -> ux_host_class_hub_class = command -> ux_host_class_command_class_ptr; + + /* Store the class container into this instance. */ + hub -> ux_host_class_hub_class = command -> ux_host_class_command_class_ptr; + + /* Store the device container instance in the HUB instance, this is for + the class instance when it needs to talk to the USBX stack. */ + hub -> ux_host_class_hub_device = device; + + /* Configure the HUB. */ + status = _ux_host_class_hub_configure(hub); + if (status == UX_SUCCESS) + { + + /* Get the HUB descriptor. */ + status = _ux_host_class_hub_descriptor_get(hub); + if (status == UX_SUCCESS) + { + + /* Power up the HUB downstream ports. This function always returns + success since we may be dealing with multiple ports. */ + _ux_host_class_hub_ports_power(hub); + + /* Search the HUB interrupt endpoint and start it. */ + status = _ux_host_class_hub_interrupt_endpoint_start(hub); + if (status == UX_SUCCESS) + { + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(hub -> ux_host_class_hub_class, (VOID *) hub); + + /* Store the instance in the device container, this is for the USBX stack + when it needs to invoke the class. */ + device -> ux_device_class_instance = (VOID *) hub; + + /* Mark the HUB as live now. */ + hub -> ux_host_class_hub_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, hub -> ux_host_class_hub_class, (VOID *) hub); + } + + /* Return success. */ + return(UX_SUCCESS); + } + } + } + + /* We get here when an error occurred. */ + + /* Free the hub instance. */ + _ux_utility_memory_free(hub); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_ACTIVATE, hub, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, hub, 0, 0, 0) + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_change_detect.c b/common/usbx_host_classes/src/ux_host_class_hub_change_detect.c new file mode 100644 index 0000000..265a1ec --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_change_detect.c @@ -0,0 +1,118 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_change_detect PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the enumeration thread when there has */ +/* been activity on the HUB. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_change_process Process HUB change */ +/* _ux_host_stack_class_get Get class */ +/* _ux_host_stack_class_instance_get Get class instance */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hub_change_detect(VOID) +{ + +UX_HOST_CLASS *class; +UX_HOST_CLASS_HUB *hub; +UINT status; +UINT class_index; + + /* Get the class container first. */ + _ux_host_stack_class_get(_ux_system_host_class_hub_name, &class); + + /* We start with the first index of the class instance. */ + class_index = 0; + + /* We have found the class, now parse the instances. */ + do + { + + /* Get class instance. */ + status = _ux_host_stack_class_instance_get(class, class_index++, (VOID **) &hub); + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* We have found an instance of a HUB, check if it is live and if the HUB has + detected a change before we proceed. */ + if (hub -> ux_host_class_hub_change_semaphore != 0) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_CHANGE_DETECT, hub, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Call the HUB function that will diagnose the origin of the change. */ + _ux_host_class_hub_change_process(hub); + + /* Decrement the HUB instance semaphore change so we don't get awaken again. */ + hub -> ux_host_class_hub_change_semaphore--; + } + } + } while (status == UX_SUCCESS); + + /* We have parsed all the HUB instances. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_change_process.c b/common/usbx_host_classes/src/ux_host_class_hub_change_process.c new file mode 100644 index 0000000..6fd50c5 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_change_process.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Hub Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_change_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the topology thread when there has been */ +/* activity on the HUB. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_port_change_process Port change process */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_short_get Get 16-bit word */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_change_process(UX_HOST_CLASS_HUB *hub) +{ + +UX_TRANSFER *transfer_request; +USHORT port_status_change_bits; +UINT port_index; +UINT status; + + + /* Now get the transfer_request attached to the interrupt endpoint. */ + transfer_request = &hub -> ux_host_class_hub_interrupt_endpoint -> ux_endpoint_transfer_request; + + /* The interrupt pipe buffer contains the status change for each of the ports + the length of the buffer can be 1 or 2 depending on the number of ports. + Usually, since HUBs can be bus powered the maximum number of ports is 4. + We must be taking precautions on how we read the buffer content for + big endian machines. */ + if (transfer_request -> ux_transfer_request_actual_length == 1) + port_status_change_bits = (USHORT) *transfer_request -> ux_transfer_request_data_pointer; + else + port_status_change_bits = (USHORT)_ux_utility_short_get(transfer_request -> ux_transfer_request_data_pointer); + + /* Scan all bits and report the change on each port. */ + for (port_index = 1; port_index <= hub -> ux_host_class_hub_descriptor.bNbPorts; port_index++) + { + + if (port_status_change_bits & (1< ux_transfer_request_actual_length = 0; + + /* Resend the request to the stack. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return completion status. */ + return(status); +} diff --git a/common/usbx_host_classes/src/ux_host_class_hub_configure.c b/common/usbx_host_classes/src/ux_host_class_hub_configure.c new file mode 100644 index 0000000..f5efdf5 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_configure.c @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* HUB. Once the HUB is configured, its interface will be activated */ +/* and all the endpoints enumerated (1 interrupt endpoint in the case */ +/* of the HUB). */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_device_configuration_get Get device configuration */ +/* _ux_host_stack_device_configuration_select */ +/* Select device configuration */ +/* _ux_host_stack_configuration_interface_get */ +/* Get interface */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_configure(UX_HOST_CLASS_HUB *hub) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *device; +UX_DEVICE *parent_device; +UX_ENDPOINT *control_endpoint; +UCHAR *device_status_data; +UX_TRANSFER *transfer_request; + + + /* A HUB normally has one configuration. So retrieve the 1st configuration + only. */ + _ux_host_stack_device_configuration_get(hub -> ux_host_class_hub_device, 0, &configuration); + + /* Get the device container for this configuration. */ + device = configuration -> ux_configuration_device; + + /* Get the parent container for this device. */ + parent_device = device -> ux_device_parent; + + /* To find the true source of the HUB power source, we need to do a GET_STATUS of + the device. */ + control_endpoint = &device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Allocate a buffer for the device status: 2 bytes. */ + device_status_data = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (device_status_data == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer_request for the GET_STATUS request, 2 bytes are returned. */ + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_data_pointer = device_status_data; + transfer_request -> ux_transfer_request_function = UX_GET_STATUS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the status and the length of the data returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* The data returned is good, now analyze power source. */ + if (*device_status_data & UX_STATUS_DEVICE_SELF_POWERED) + device -> ux_device_power_source = UX_DEVICE_SELF_POWERED; + else + device -> ux_device_power_source = UX_DEVICE_BUS_POWERED; + + /* Free the buffer resource now. */ + _ux_utility_memory_free(device_status_data); + } + else + { + + /* Free the buffer resource now. */ + _ux_utility_memory_free(device_status_data); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, hub, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_CONNECTION_INCOMPATIBLE); + } + + /* Check the HUB power source and check the parent power source for + incompatible connections. */ + if (hub -> ux_host_class_hub_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* If the device is NULL, the parent is the root HUB and we don't have to worry + if the parent is not the root HUB, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, hub, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + + /* If the operation went well, the HUB default alternate setting for the HUB interface is + active and the interrupt endpoint is now enabled. We have to memorize the first interface + since the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &hub -> ux_host_class_hub_interface); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_deactivate.c b/common/usbx_host_classes/src/ux_host_class_hub_deactivate.c new file mode 100644 index 0000000..ed4ef2d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_deactivate.c @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the HUB has been */ +/* removed from the bus either directly or indirectly. The interrupt */ +/* pipe will be destroyed and the instance removed. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_host_stack_device_remove Remove device */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_HUB *hub; +UX_HCD *hcd; +UX_TRANSFER *transfer_request; +UINT port_index; + + + /* Get the instance to the class. */ + hub = (UX_HOST_CLASS_HUB *) command -> ux_host_class_command_instance; + + /* Get the HCD used by this instance. */ + hcd = hub -> ux_host_class_hub_device -> ux_device_hcd; + + /* The HUB is being shut down. */ + hub -> ux_host_class_hub_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* We need to abort transactions on the interrupt pipe. */ + _ux_host_stack_endpoint_transfer_abort(hub -> ux_host_class_hub_interrupt_endpoint); + + /* Each device which is downstream on the HUB ports must be removed. */ + for (port_index = 1; port_index <= hub -> ux_host_class_hub_descriptor.bNbPorts; port_index++) + { + + /* Is there a device on this port? */ + if (hub -> ux_host_class_hub_port_state & (1UL << port_index)) + { + + /* The stack will remove the device and its resources. */ + _ux_host_stack_device_remove(hcd, hub -> ux_host_class_hub_device, port_index); + } + } + + /* If the Hub class instance has a interrupt pipe with a data payload associated with it + it must be freed. First get the transfer request. */ + transfer_request = &hub -> ux_host_class_hub_interrupt_endpoint -> ux_endpoint_transfer_request; + + /* Abort the data transfer on the interrupt endpoint. */ + _ux_host_stack_endpoint_transfer_abort(hub -> ux_host_class_hub_interrupt_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Then de allocate the memory. */ + _ux_utility_memory_free(transfer_request -> ux_transfer_request_data_pointer); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(hub -> ux_host_class_hub_class, (VOID *) hub); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, hub -> ux_host_class_hub_class, (VOID *) hub); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_DEACTIVATE, hub, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(hub); + + /* Free the memory block used by the class. */ + _ux_utility_memory_free(hub); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_descriptor_get.c b/common/usbx_host_classes/src/ux_host_class_hub_descriptor_get.c new file mode 100644 index 0000000..449e8a6 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_descriptor_get.c @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_descriptor_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the HUB descriptor. This descriptor contains */ +/* the number of downstream ports and the power characteristics of the */ +/* HUB (self powered or bus powered). */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_descriptor_get(UX_HOST_CLASS_HUB *hub) +{ + +UCHAR *descriptor; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +ULONG port_index; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hub -> ux_host_class_hub_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HUB_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_HUB_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_HUB_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = (UX_HUB_DESCRIPTOR_ITEM << 8); + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Did the transfer succeed? */ + if (status == UX_SUCCESS) + { + + /* Is the length valid? */ + if (transfer_request -> ux_transfer_request_actual_length == UX_HUB_DESCRIPTOR_LENGTH) + { + + /* Parse the device descriptor and create the local descriptor. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_hub_descriptor_structure, UX_HUB_DESCRIPTOR_ENTRIES, + (UCHAR *) &hub -> ux_host_class_hub_descriptor); + + /* Check the protocol used by the HUB. This will indicate if the HUB supports multiple TTs in high speed mode. */ + switch (hub -> ux_host_class_hub_device -> ux_device_descriptor.bDeviceProtocol) + { + + case UX_HOST_CLASS_HUB_PROTOCOL_FS: + + /* In the case of full speed hub, there are no TTs to declare */ + break; + + + case UX_HOST_CLASS_HUB_PROTOCOL_SINGLE_TT: + + /* In a single TT state, all the downstream ports report to the same + TT and share the 1.1 USB segment bandwidth. This is a very crude + but working method, we simply set all the ports bits to the first + TT. */ + hub -> ux_host_class_hub_device -> ux_device_hub_tt[0].ux_hub_tt_port_mapping = UX_TT_MASK; + hub -> ux_host_class_hub_device -> ux_device_hub_tt[0].ux_hub_tt_max_bandwidth = UX_TT_BANDWIDTH; + break; + + + case UX_HOST_CLASS_HUB_PROTOCOL_MULTIPLE_TT: + + /* In the case of multiple TTs, each downstream port can sustain the USB 1.1 + max bandwidth and therefore we allocate one TT per port with that bandwidth. */ + for (port_index = 0; port_index < hub -> ux_host_class_hub_descriptor.bNbPorts;port_index++) + { + + hub -> ux_host_class_hub_device -> ux_device_hub_tt[port_index].ux_hub_tt_port_mapping = (ULONG)(1 << port_index); + hub -> ux_host_class_hub_device -> ux_device_hub_tt[port_index].ux_hub_tt_max_bandwidth = UX_TT_BANDWIDTH; + } + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We should never get here. In this case the protocol value of the HUB is illegal. */ + status = UX_DESCRIPTOR_CORRUPTED; + } + } + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* The descriptor must be corrupted if we got an invalid length. */ + status = UX_DESCRIPTOR_CORRUPTED; + } + } + + /* Free the memory for the descriptor. */ + _ux_utility_memory_free(descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_entry.c b/common/usbx_host_classes/src/ux_host_class_hub_entry.c new file mode 100644 index 0000000..991612a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_entry.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the HUB class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* device on the bus or when there is a device extraction. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_activate Activate HUB class */ +/* _ux_host_class_hub_deactivate Deactivate HUB class */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if ((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + (command -> ux_host_class_command_class == UX_HOST_CLASS_HUB_CLASS)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_hub_activate(command); + + /* Return completion status. */ + return(status); + + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted */ + status = _ux_host_class_hub_deactivate(command); + + /* Return completion status. */ + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error status. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} diff --git a/common/usbx_host_classes/src/ux_host_class_hub_feature.c b/common/usbx_host_classes/src/ux_host_class_hub_feature.c new file mode 100644 index 0000000..fbf64d3 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_feature.c @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_feature PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a command to the HUB on a specific port. */ +/* The commands can be SET_FEATURE or CLEAR_FEATURE. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* command Command to send */ +/* feature Feature to send */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_feature(UX_HOST_CLASS_HUB *hub, UINT port, UINT command, UINT function) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT target; +UINT status; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hub -> ux_host_class_hub_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* The target is DEVICE for the HUB and OTHER for the downstream ports. */ + if (port == 0) + target = UX_REQUEST_TARGET_DEVICE; + else + target = UX_REQUEST_TARGET_OTHER; + + /* Create a transfer request for the SET_FEATURE or CLEAR_FEATURE request. */ + transfer_request -> ux_transfer_request_function = command; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | target; + transfer_request -> ux_transfer_request_value = function; + transfer_request -> ux_transfer_request_index = port; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_hub_change_process.c b/common/usbx_host_classes/src/ux_host_class_hub_hub_change_process.c new file mode 100644 index 0000000..b386998 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_hub_change_process.c @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_hub_change_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when there is a change on the HUB itself */ +/* (not a HUB port!). */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_hub_change_process(UX_HOST_CLASS_HUB *hub) +{ + + UX_PARAMETER_NOT_USED(hub); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_interrupt_endpoint_start.c b/common/usbx_host_classes/src/ux_host_class_hub_interrupt_endpoint_start.c new file mode 100644 index 0000000..e82a0f2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_interrupt_endpoint_start.c @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_interrupt_endpoint_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function search for the handle of the only interrupt endpoint */ +/* in the default alternate setting of the HUB interface. The */ +/* interrupt endpoint should always be there. When it is located, the */ +/* first transfer for this endpoint is made. The HUB will report status*/ +/* changes on this pipe. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get endpoint of interface */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_interrupt_endpoint_start(UX_HOST_CLASS_HUB *hub) +{ + +UINT status; +UX_TRANSFER *transfer_request; + + + /* Search the interrupt endpoint. It is attached to the interface container. */ + status = _ux_host_stack_interface_endpoint_get(hub -> ux_host_class_hub_interface, 0, &hub -> ux_host_class_hub_interrupt_endpoint); + + /* Check completion status. */ + if (status != UX_SUCCESS) + return(status); + + /* Do a sanity check on the nature of the endpoint. Must be interrupt and its direction must be IN. */ + if (((hub -> ux_host_class_hub_interrupt_endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((hub -> ux_host_class_hub_interrupt_endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* The endpoint is correct, fill in the transfer_request with the length requested for this endpoint. */ + transfer_request = &hub -> ux_host_class_hub_interrupt_endpoint -> ux_endpoint_transfer_request; + transfer_request -> ux_transfer_request_requested_length = hub -> ux_host_class_hub_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* Since this transfer_request has a callback, we need the HUB instance to be stored in the transfer request. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) hub; + + /* This transfer request always has the IN direction. */ + hub -> ux_host_class_hub_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* Interrupt transactions have a completion routine. */ + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_hub_transfer_request_completed; + + /* Obtain a buffer for this transaction. The buffer will always be reused. */ + transfer_request -> ux_transfer_request_data_pointer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + transfer_request -> ux_transfer_request_requested_length); + + /* Check the memory pointer. */ + if (transfer_request -> ux_transfer_request_data_pointer == UX_NULL) + { + + /* Clear the interrupt endpoint. */ + hub -> ux_host_class_hub_interrupt_endpoint = UX_NULL; + + /* Return error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Send the request to the stack. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return completion status. */ + return(status); + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, hub -> ux_host_class_hub_interrupt_endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error. */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); +} diff --git a/common/usbx_host_classes/src/ux_host_class_hub_port_change_connection_process.c b/common/usbx_host_classes/src/ux_host_class_hub_port_change_connection_process.c new file mode 100644 index 0000000..20a8831 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_port_change_connection_process.c @@ -0,0 +1,220 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_port_change_connection_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will process a connection change on the port. This */ +/* indicates that a device has been attached to the port. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* port_status Port status */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_feature Set HUB feature */ +/* _ux_host_class_hub_port_reset Reset port */ +/* _ux_host_class_hub_status_get Get status */ +/* _ux_host_stack_new_device_create Create new device */ +/* _ux_host_stack_device_remove Remove device */ +/* _ux_utility_delay_ms Thread sleep */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hub_port_change_connection_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status) +{ + +UINT device_speed; +UINT device_enumeration_retry; +USHORT port_power; +UINT status; +UX_HCD *hcd; +USHORT local_port_status; +USHORT local_port_change; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION_PROCESS, hub, port, port_status, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Get the HCD used by this instance. */ + hcd = hub -> ux_host_class_hub_device -> ux_device_hcd; + + /* If there is a device attached on this HUB, there is a new device and it should + be enumerated. */ + if (port_status & UX_HOST_CLASS_HUB_PORT_STATUS_CONNECTION) + { + + /* Check if there was a previous device already attached on this port. This may happen when a device + disconnects and reconnect very quickly before the hub has a chance to poll the port state. In this + case we do a device removal before doing a device connection. */ + if (hub -> ux_host_class_hub_port_state & (UINT)(1 << port)) + { + + /* There was a device attached previously. Perform a removal. */ + status = _ux_host_stack_device_remove(hcd, hub -> ux_host_class_hub_device, port); + + } + else + + /* Mark device connection. */ + hub -> ux_host_class_hub_port_state |= (UINT)(1 << port); + + /* Tell the hub to clear the change bit for this port so that we do + not process the same change event again. */ + _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_CONNECTION); + + /* Some devices are known to fail on the first try. */ + for (device_enumeration_retry = 0; device_enumeration_retry < UX_HOST_CLASS_HUB_ENUMERATION_RETRY; device_enumeration_retry++) + { + + /* Wait for debounce. */ + _ux_utility_delay_ms(UX_HOST_CLASS_HUB_ENUMERATION_DEBOUNCE_DELAY); + + /* The port must be reset. */ + status = _ux_host_class_hub_port_reset(hub, port); + if (status != UX_SUCCESS) + return; + + /* Reset succeeded, so perform a new port status after reset to force speed reevaluation. */ + status = _ux_host_class_hub_status_get(hub, port, &local_port_status, &local_port_change); + if (status != UX_SUCCESS) + return; + + /* Device connected. Get the device speed. */ + if (local_port_status & UX_HOST_CLASS_HUB_PORT_STATUS_LOW_SPEED) + device_speed = UX_LOW_SPEED_DEVICE; + else + { + + if (local_port_status & UX_HOST_CLASS_HUB_PORT_STATUS_HIGH_SPEED) + device_speed = UX_HIGH_SPEED_DEVICE; + else + device_speed = UX_FULL_SPEED_DEVICE; + } + + /* Decide how much power this device can draw. This depends if the HUB is self + powered or bus powered. */ + if (hub -> ux_host_class_hub_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + + /* Hub is bus powered. */ + port_power = UX_MAX_BUS_POWER; + else + + /* Hub is self powered. */ + port_power = UX_MAX_SELF_POWER; + + /* Wait for reset recovery. */ + _ux_utility_delay_ms(UX_HOST_CLASS_HUB_ENUMERATION_RESET_RECOVERY_DELAY); + + /* Perform the device creation. */ + status = _ux_host_stack_new_device_create(hub -> ux_host_class_hub_device -> ux_device_hcd, + hub -> ux_host_class_hub_device, + port, device_speed, port_power); + + /* Check return status. */ + if (status == UX_SUCCESS) + { + + /* Successful device creation. */ + /* Just return. */ + return; + } + else if (device_enumeration_retry < UX_HOST_CLASS_HUB_ENUMERATION_RETRY - 1) + { + + /* Simulate remove to free allocated resources before retry. */ + _ux_host_stack_device_remove(hcd, hub -> ux_host_class_hub_device, port); + + /* Wait for a while. */ + _ux_utility_delay_ms(UX_HOST_CLASS_HUB_ENUMERATION_RETRY_DELAY); + } + } + + /* If we get here, the device did not enumerate completely. */ + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_ROOT_HUB, UX_DEVICE_ENUMERATION_FAILURE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DEVICE_ENUMERATION_FAILURE, port, 0, 0, UX_TRACE_ERRORS, 0, 0) + } + else + { + + /* Check if there was a no previous device attached on this port. */ + if ((hub -> ux_host_class_hub_port_state & (UINT)(1 << port))) + { + /* Mark device disconnection. */ + hub -> ux_host_class_hub_port_state &= (UINT)~(1 << port); + + /* We get here when there is a device extraction. */ + status = _ux_host_stack_device_remove(hcd, hub -> ux_host_class_hub_device, port); + } + + /* The port should be disabled now. Power is still applied. */ + status = _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_PORT_ENABLE); + + /* We must clear the enable change condition so that we don't get awaken again. */ + _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_ENABLE); + + /* We must clear the connection change condition so that we don't get awaken again. */ + _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_CONNECTION); + } + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_port_change_enable_process.c b/common/usbx_host_classes/src/ux_host_class_hub_port_change_enable_process.c new file mode 100644 index 0000000..7a056e2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_port_change_enable_process.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_port_change_enable_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will process a enable condition change. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* port_status Port status */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_feature Set HUB feature */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hub_port_change_enable_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status) +{ + + UX_PARAMETER_NOT_USED(port_status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_ENABLE_PROCESS, hub, port, port_status, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Here we simply clear the condition so that we don't get awaken again. */ + _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_ENABLE); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_port_change_over_current_process.c b/common/usbx_host_classes/src/ux_host_class_hub_port_change_over_current_process.c new file mode 100644 index 0000000..ad198bd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_port_change_over_current_process.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_port_change_over_current_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will process a over current condition change. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* port_status Port status */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_feature Set HUB feature */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hub_port_change_over_current_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status) +{ + + UX_PARAMETER_NOT_USED(port_status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_OVER_CURRENT_PROCESS, hub, port, port_status, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Here we simply clear the condition so that we don't get awaken again. */ + _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_OVER_CURRENT); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, UX_OVER_CURRENT_CONDITION); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_port_change_process.c b/common/usbx_host_classes/src/ux_host_class_hub_port_change_process.c new file mode 100644 index 0000000..246db51 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_port_change_process.c @@ -0,0 +1,114 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_port_change_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will process a port change indication. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_port_change_connection_process */ +/* Process connection */ +/* _ux_host_class_hub_port_change_enable_process */ +/* Enable process */ +/* _ux_host_class_hub_port_change_over_current_process */ +/* Change over current process */ +/* _ux_host_class_hub_port_change_reset_process */ +/* Reset process */ +/* _ux_host_class_hub_port_change_suspend_process */ +/* Suspend process */ +/* _ux_host_class_hub_status_get Get HUB status */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_port_change_process(UX_HOST_CLASS_HUB *hub, UINT port) +{ + +USHORT port_status; +USHORT port_change; +UINT status; + + + /* First step is to retrieve the status on the port with a GET_STATUS. */ + status = _ux_host_class_hub_status_get(hub, port, &port_status, &port_change); + if (status != UX_SUCCESS) + return(status); + + /* On return of the GET_STATUS, the port change field has been updated + check for each of the bits it may contain. */ + if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_CONNECTION) + _ux_host_class_hub_port_change_connection_process(hub, port, port_status); + + if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_ENABLE) + _ux_host_class_hub_port_change_enable_process(hub, port, port_status); + + if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_SUSPEND) + _ux_host_class_hub_port_change_suspend_process(hub, port, port_status); + + if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_OVER_CURRENT) + _ux_host_class_hub_port_change_over_current_process(hub, port, port_status); + + if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_RESET) + _ux_host_class_hub_port_change_reset_process(hub, port, port_status); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_port_change_reset_process.c b/common/usbx_host_classes/src/ux_host_class_hub_port_change_reset_process.c new file mode 100644 index 0000000..733fbe8 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_port_change_reset_process.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_port_change_reset_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will process a reset condition change. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* port_status Status of port */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_feature Set HUB class feature */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hub_port_change_reset_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status) +{ + + UX_PARAMETER_NOT_USED(port_status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_RESET_PROCESS, hub, port, port_status, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Here we simply clear the condition so that we don't get awaken again. */ + _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_RESET); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_port_change_suspend_process.c b/common/usbx_host_classes/src/ux_host_class_hub_port_change_suspend_process.c new file mode 100644 index 0000000..270c954 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_port_change_suspend_process.c @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_port_change_suspend_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will process a suspend condition change. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* port_status Status of port */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_feature Set HUB class feature */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hub_port_change_suspend_process(UX_HOST_CLASS_HUB *hub, UINT port, UINT port_status) +{ + + UX_PARAMETER_NOT_USED(port_status); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HUB_PORT_CHANGE_SUSPEND_PROCESS, hub, port, port_status, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Here we simply clear the condition so that we don't get awaken again. */ + _ux_host_class_hub_feature(hub, port, UX_CLEAR_FEATURE, UX_HOST_CLASS_HUB_C_PORT_SUSPEND); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_port_reset.c b/common/usbx_host_classes/src/ux_host_class_hub_port_reset.c new file mode 100644 index 0000000..63106ba --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_port_reset.c @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_port_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reset a downstream port of the HUB. When the */ +/* port is reset, the hardware logic of the HUB also enables it. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port number */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_port_change_reset_process Reset HUB */ +/* _ux_host_class_hub_feature Set HUB class feature */ +/* _ux_host_class_hub_status_get Get HUB status */ +/* _ux_utility_delay_ms Thread sleep */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_port_reset(UX_HOST_CLASS_HUB *hub, UINT port) +{ + +UINT status; +UINT port_enable_retry; +USHORT port_status; +USHORT port_change; + + + /* Send the PORT_RESET command. */ + status = _ux_host_class_hub_feature(hub, port, UX_SET_FEATURE, UX_HOST_CLASS_HUB_PORT_RESET); + + /* Check the function result and update HUB status if there was a problem. */ + if (status != UX_SUCCESS) + return(status); + + /* We allow retrying of this as we may not get the port enable status bit + set on the first try. */ + port_enable_retry = UX_HOST_CLASS_HUB_ENABLE_RETRY_COUNT; + while (port_enable_retry--) + { + + /* Now get the status until we have a port enabled. */ + status = _ux_host_class_hub_status_get(hub, port, &port_status, &port_change); + if (status != UX_SUCCESS) + return(status); + + /* On return of the GET_STATUS, the port_change field has been updated. + Check for each of the bits it may contain. */ + if (port_change & UX_HOST_CLASS_HUB_PORT_CHANGE_RESET) + { + + /* The port has been successfully reset. */ + + /* Clear the RESET change bit. */ + _ux_host_class_hub_port_change_reset_process(hub, port, port_status); + + /* Return success. */ + return(UX_SUCCESS); + } + + /* We should wait a bit before retrying this operation. */ + _ux_utility_delay_ms(UX_HOST_CLASS_HUB_ENABLE_RETRY_DELAY); + } + + /* We get here when the port never reported RESET completion. */ + + /* Invoke error callback. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HUB, UX_PORT_RESET_FAILED); + + /* Return error. */ + return(UX_PORT_RESET_FAILED); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_ports_power.c b/common/usbx_host_classes/src/ux_host_class_hub_ports_power.c new file mode 100644 index 0000000..a10e458 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_ports_power.c @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_ports_power PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will power up each downstream port attached to the */ +/* HUB. There is a delay after powering up each port, otherwise we may */ +/* not detect device insertion. */ +/* */ +/* There are 3 port power modes: */ +/* */ +/* 1) Gang power: In this case we only power the first */ +/* port and all ports should be powered at */ +/* the same time */ +/* */ +/* 2) Individual power: In this case we power individually each */ +/* port */ +/* */ +/* 3) No power switching: In this case the power is applied to the */ +/* downstream ports when the upstream port */ +/* receives power. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_hub_feature Set HUB class feature */ +/* _ux_utility_delay_ms Thread sleep */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_ports_power(UX_HOST_CLASS_HUB *hub) +{ + +UINT nb_ports; +UINT port_index; +UINT status; + + + /* Check for the power management mode: no power switching. */ + if(hub -> ux_host_class_hub_descriptor.wHubCharacteristics & UX_HOST_CLASS_HUB_NO_POWER_SWITCHING) + return(UX_SUCCESS); + + /* All ports must be powered individually. */ + nb_ports = hub -> ux_host_class_hub_descriptor.bNbPorts; + + /* Perform the function to all ports: the port index starts from 1 as the port 0 is for the HUB. */ + for (port_index = 1; port_index <= nb_ports; port_index++) + { + + /* To apply port power, we send a SET_FEATURE to the port on the HUB. */ + status = _ux_host_class_hub_feature(hub, port_index, UX_SET_FEATURE, UX_HOST_CLASS_HUB_PORT_POWER); + + /* Check the function result and update HUB status if there was a problem. */ + if (status != UX_SUCCESS) + { + + /* Set the HUB status to not powered. */ + hub -> ux_host_class_hub_port_power &= (UINT)~(1 << port_index); + + } + else + { + + /* Now we need to wait for the power to be stable. */ + _ux_utility_delay_ms(((ULONG) (hub -> ux_host_class_hub_descriptor.bPwrOn2PwrGood) * 2)); + + /* Set the HUB status to powered. */ + hub -> ux_host_class_hub_port_power |= (UINT)(1 << port_index); + } + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_status_get.c b/common/usbx_host_classes/src/ux_host_class_hub_status_get.c new file mode 100644 index 0000000..fb165af --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_status_get.c @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_hub.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_status_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will do a GET_STATUS from the HUB. This function */ +/* retrieves the current status and the changed bits. */ +/* */ +/* INPUT */ +/* */ +/* hub Pointer to HUB class */ +/* port Port of device */ +/* port_status Destination for port status */ +/* port_change Destination for port change */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_get Get 16-bit word */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_hub_status_get(UX_HOST_CLASS_HUB *hub, UINT port, USHORT *port_status, USHORT *port_change) +{ + +UCHAR *port_data; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT target; +UINT status; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &hub -> ux_host_class_hub_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* The target is DEVICE for the HUB and OTHER for the downstream ports. */ + if (port == 0) + target = UX_REQUEST_TARGET_DEVICE; + else + target = UX_REQUEST_TARGET_OTHER; + + /* Allocate a buffer for the port status and change: 2 words. */ + port_data = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 4); + if(port_data == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer request for the GET_STATUS request. */ + transfer_request -> ux_transfer_request_requested_length = 4; + transfer_request -> ux_transfer_request_data_pointer = port_data; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_HUB_GET_STATUS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | target; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = port; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for error and completion of the transfer. */ + if (status == UX_SUCCESS) + { + + if (transfer_request -> ux_transfer_request_actual_length == 4) + { + + /* The 2 words are now in the buffer. We need to resolve their endianness + and report them as separate items to the called. */ + *port_status = (USHORT)_ux_utility_short_get(port_data); + *port_change = (USHORT)_ux_utility_short_get(port_data+2); + } + else + { + + /* Invalid length. Return error. */ + status = UX_TRANSFER_DATA_LESS_THAN_EXPECTED; + } + } + + /* Free the buffer resource now. */ + _ux_utility_memory_free(port_data); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_hub_transfer_request_completed.c b/common/usbx_host_classes/src/ux_host_class_hub_transfer_request_completed.c new file mode 100644 index 0000000..6addc55 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_hub_transfer_request_completed.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** HUB Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" +#include "ux_host_class_hub.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_hub_transfer_request_completed PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the completion thread when a transfer */ +/* request has been completed either because the transfer is */ +/* successful or there was an error. */ +/* */ +/* Because the HUB influences the topology of the USB, the insertion */ +/* or extraction of devices cannot be done during the transfer request */ +/* thread. We post a signal to the topology thread to wake up and */ +/* treat these changes on the HUB status. */ +/* */ +/* The interrupt pipe is not reactivated here. We will do this when */ +/* the topology thread has investigated the reason of the transfer */ +/* completion. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_semaphore_put Put the signaling semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* HUB Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_hub_transfer_request_completed(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_HUB *hub; + + + /* Get the class instance for this transfer request. */ + hub = (UX_HOST_CLASS_HUB *) transfer_request -> ux_transfer_request_class_instance; + + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* We have an error. We do not rehook another transfer if the device instance is shutting down or + if the transfer was aborted by the class. */ + if ((hub -> ux_host_class_hub_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) || + (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_ABORT) || + (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_NO_ANSWER)) + + /* We do not proceed. */ + return; + else + + { + + /* Reactivate the HID interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* We do not proceed. */ + return; + } + } + + /* We need to memorize which HUB instance has received a change signal. */ + hub -> ux_host_class_hub_change_semaphore++; + + /* Now we can set the semaphore, the enum thread will wake up and will + call the HUB instance which has a status change. */ + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_enum_semaphore); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_activate.c b/common/usbx_host_classes/src/ux_host_class_pima_activate.c new file mode 100644 index 0000000..4c94a23 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_activate.c @@ -0,0 +1,176 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the ACM instance, configure the device ... */ +/* */ +/* INPUT */ +/* */ +/* command Pima class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_configure Configure pima class */ +/* _ux_host_class_pima_endpoints_get Get endpoints of pima */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_pima_entry Entry of pima class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_PIMA *pima; +UINT status; + + /* The PIMA class is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + pima = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_PIMA)); + if (pima == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Allocate some DMA safe memory for sending/receiving headers. */ + pima -> ux_host_class_pima_container = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_CONTAINER_SIZE); + if (pima -> ux_host_class_pima_container == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + else + status = UX_SUCCESS; + + /* Allocate some DMA safe memory for receiving pima events. */ + if (status == UX_SUCCESS) + { + + pima -> ux_host_class_pima_event_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_AEI_MAX_LENGTH); + if (pima -> ux_host_class_pima_event_buffer == UX_NULL) + status = UX_MEMORY_INSUFFICIENT; + } + + /* Go on if no error. */ + if (status == UX_SUCCESS) + { + + /* Store the class container into this instance. */ + pima -> ux_host_class_pima_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the pima class instance. */ + pima -> ux_host_class_pima_interface = interface; + + /* Store the device container into the pima class instance. */ + pima -> ux_host_class_pima_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) pima; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(pima -> ux_host_class_pima_class, (VOID *) pima); + + /* Configure the pima. */ + status = _ux_host_class_pima_configure(pima); + } + + /* Get the pima endpoint(s). We will need to search for Bulk Out, Bulk In and interrupt endpoints. */ + if (status == UX_SUCCESS) + status = _ux_host_class_pima_endpoints_get(pima); + + /* Success things. */ + if (status == UX_SUCCESS) + { + + /* Mark the pima as live now. */ + pima -> ux_host_class_pima_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, pima -> ux_host_class_pima_class, (VOID *) pima); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_ACTIVATE, pima, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, pima, 0, 0, 0) + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Free existing resources. */ + if (pima -> ux_host_class_pima_event_buffer) + { + _ux_host_stack_class_instance_destroy(pima -> ux_host_class_pima_class, (VOID *) pima); + interface -> ux_interface_class_instance = UX_NULL; + _ux_utility_memory_free(pima -> ux_host_class_pima_event_buffer); + } + + if (pima -> ux_host_class_pima_container) + _ux_utility_memory_free(pima -> ux_host_class_pima_container); + + _ux_utility_memory_free(pima); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_command.c b/common/usbx_host_classes/src/ux_host_class_pima_command.c new file mode 100644 index 0000000..2e63ddd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_command.c @@ -0,0 +1,298 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_command PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a command to the PIMA device. */ +/* It will perform a data phase if necessary and the status phase. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* command pointer to command container */ +/* direction either IN or OUT */ +/* data_buffer buffer to be sent or received */ +/* data_length length of the buffer to send */ +/* or receive */ +/* max_payload_length maximum payload length */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_command(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_COMMAND *command, + ULONG direction, UCHAR *data_buffer, ULONG data_length, + ULONG max_payload_length) +{ + +UX_TRANSFER *transfer_request; +UCHAR *ptp_payload; +ULONG requested_length; +UINT status; + + /* We use the Bulk Out pipe for sending data out.. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE + ((ULONG)sizeof(ULONG) * command -> ux_host_class_pima_command_nb_parameters); + + /* Fill the command container. First the length of the total header and payload. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH, requested_length); + + /* Then the type of container : a command block here. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE, UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK); + + /* Now the command code to send. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE, (USHORT)command -> ux_host_class_pima_command_operation_code); + + /* Save the operation code. */ + pima -> ux_host_class_pima_operation_code = command -> ux_host_class_pima_command_operation_code; + + /* Put the transaction ID. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID, + pima -> ux_host_class_pima_transaction_id++); + + /* Then fill in all the parameters. To make it quick we fill the 5 parameters, regardless + of the number contained in the command. But when the payload is transmitted, only the + relevant data is sent over the Bulk Out pipe. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1, + command -> ux_host_class_pima_command_parameter_1); + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_2, + command -> ux_host_class_pima_command_parameter_2); + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_3, + command -> ux_host_class_pima_command_parameter_3); + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_4, + command -> ux_host_class_pima_command_parameter_4); + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_5, + command -> ux_host_class_pima_command_parameter_5); + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Check for completion of transfer. If the transfer is partial, return to caller. + Partial transfer is not OK. */ + if (requested_length == transfer_request -> ux_transfer_request_actual_length) + { + + /* The command was sent successfully. Now examine the direction and check + if we need a data phase. If so which direction. */ + + switch (direction) + { + + /* No data phase, proceed to response directly. */ + case UX_HOST_CLASS_PIMA_DATA_PHASE_NONE : + + /* No error here. */ + status = UX_SUCCESS; + break; + + /* We need to transfer data IN. */ + case UX_HOST_CLASS_PIMA_DATA_PHASE_IN : + status = _ux_host_class_pima_read(pima, data_buffer, data_length, max_payload_length); + break; + + /* We need to transfer data OUT. */ + case UX_HOST_CLASS_PIMA_DATA_PHASE_OUT : + status = _ux_host_class_pima_write(pima, data_buffer, data_length, command -> ux_host_class_pima_command_operation_code, + max_payload_length); + break; + + default : + return(UX_ERROR); + + } + + /* Analyze the status. If no errors during the data phase proceed to response code. */ + if (status == UX_SUCCESS) + { + + /* We use the Bulk In pipe for receiving the response payload .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_RESPONSE_HEADER_SIZE; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Check to ensure this is a Response packet. */ + if (_ux_utility_short_get(ptp_payload + UX_HOST_CLASS_PIMA_RESPONSE_HEADER_TYPE) != + UX_HOST_CLASS_PIMA_CT_RESPONSE_BLOCK) + + /* We have a wrong packet. */ + return(UX_ERROR); + + /* Then get all the response parameters. */ + command -> ux_host_class_pima_command_parameter_1 = _ux_utility_long_get(ptp_payload + + UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_1); + command -> ux_host_class_pima_command_parameter_2 = _ux_utility_long_get(ptp_payload + + UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_2); + command -> ux_host_class_pima_command_parameter_3 = _ux_utility_long_get(ptp_payload + + UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_3); + command -> ux_host_class_pima_command_parameter_4 = _ux_utility_long_get(ptp_payload + + UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_4); + command -> ux_host_class_pima_command_parameter_5 = _ux_utility_long_get(ptp_payload + + UX_HOST_CLASS_PIMA_RESPONSE_HEADER_PARAMETER_5); + + /* We are done with the command. */ + return(UX_SUCCESS); + } + /* Return the error. */ + return(status); + } + else + /* Truncated command. */ + return(UX_TRANSFER_ERROR); + +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_configure.c b/common/usbx_host_classes/src/ux_host_class_pima_configure.c new file mode 100644 index 0000000..90411f9 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_configure.c @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* pima. Once the pima is configured, its interface will be */ +/* activated. The bulk endpoints (1 IN, 1 OUT ) and the interrupt */ +/* endpoint are enumerated. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_pima_activate Pima class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_configure(UX_HOST_CLASS_PIMA *pima) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (pima -> ux_host_class_pima_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A pima normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(pima -> ux_host_class_pima_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, pima -> ux_host_class_pima_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the pima power source and check the parent power source for + incompatible connections. */ + if (pima -> ux_host_class_pima_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = pima -> ux_host_class_pima_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root pima and we don't have to worry + if the parent is not the root pima, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, pima, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the pima default alternate setting for the pima interface is + active and the interrupt endpoint is now enabled. We have to memorize the first interface since + the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &pima -> ux_host_class_pima_interface); + if (status != UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + pima -> ux_host_class_pima_interface -> ux_interface_class_instance = (VOID *) pima; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_deactivate.c b/common/usbx_host_classes/src/ux_host_class_pima_deactivate.c new file mode 100644 index 0000000..09fb56a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_deactivate.c @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the pima has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* and interrupt pipes will be destroyed and the instance removed. */ +/* */ +/* INPUT */ +/* */ +/* command PIMA class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_pima_entry Entry of pima class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_PIMA *pima; +UX_HOST_CLASS_PIMA_SESSION *pima_session; +UX_TRANSFER *transfer_request; +UINT status; + + + /* Get the instance for this class. */ + pima = (UX_HOST_CLASS_PIMA *) command -> ux_host_class_command_instance; + + /* The pima is being shut down. */ + pima -> ux_host_class_pima_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&pima -> ux_host_class_pima_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We come to this point when the device has been extracted. So there may have been a transaction + being scheduled. We make sure the transaction has been completed by the controller driver. + When the device is extracted, the controller tries multiple times the transaction and retires it + with a DEVICE_NOT_RESPONDING error code. + + First we take care of endpoint IN. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(pima -> ux_host_class_pima_bulk_in_endpoint); + + + /* Then endpoint OUT. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk Out pipe. We normally don't need that anymore. */ + _ux_host_stack_endpoint_transfer_abort(pima -> ux_host_class_pima_bulk_out_endpoint); + + + /* Then interrupt endpoint. */ + transfer_request = &pima -> ux_host_class_pima_interrupt_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the Interrupt pipe. */ + _ux_host_stack_endpoint_transfer_abort(pima -> ux_host_class_pima_interrupt_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Free the header container buffer. */ + if (pima -> ux_host_class_pima_container != UX_NULL) + _ux_utility_memory_free(pima -> ux_host_class_pima_container); + + /* Free the event data buffer. */ + if (pima -> ux_host_class_pima_event_buffer != UX_NULL) + _ux_utility_memory_free(pima -> ux_host_class_pima_event_buffer); + + /* Get the pointer to the PIMA session. */ + pima_session = pima -> ux_host_class_pima_session; + + /* Was there a PIMA session ? */ + if(pima_session != UX_NULL) + { + /* Clean the PIMA session and free the storage ID buffer in the session if it is opened. */ + if ((pima_session -> ux_host_class_pima_session_magic == UX_HOST_CLASS_PIMA_MAGIC_NUMBER) && + (pima_session -> ux_host_class_pima_session_state == UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED)) + + { + + /* Reset the magic field. */ + pima_session -> ux_host_class_pima_session_magic = 0; + + /* Declare the session closed. */ + pima_session -> ux_host_class_pima_session_state = UX_HOST_CLASS_PIMA_SESSION_STATE_CLOSED; + + } + } + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(pima -> ux_host_class_pima_class, (VOID *) pima); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&pima -> ux_host_class_pima_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, pima -> ux_host_class_pima_class, (VOID *) pima); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_DEACTIVATE, pima, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(pima); + + /* Free the pima instance memory. */ + _ux_utility_memory_free(pima); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_device_info_get.c b/common/usbx_host_classes/src/ux_host_class_pima_device_info_get.c new file mode 100644 index 0000000..5aa9be2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_device_info_get.c @@ -0,0 +1,351 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_device_info_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the device information block. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_device Device structure to fill */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* _ux_utility_descriptor_parse Unpack descriptor */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_free Free allocated memory */ +/* _ux_utility_short_get Get 16-bit value */ +/* _ux_utility_long_get Get 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_device_info_get(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_DEVICE *pima_device) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UCHAR *device_buffer; +UCHAR *device_pointer; +ULONG unicode_string_length; +ULONG array_length = 0; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_DEVICE_INFO_GET, pima, pima_device, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Issue command to get the device info. no parameter. */ + command.ux_host_class_pima_command_nb_parameters = 0; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_1 = 0; + command.ux_host_class_pima_command_parameter_2 = 0; + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to GET_DEVICE_INFO. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_DEVICE_INFO; + + /* Allocate some DMA safe memory for receiving the device info block. */ + device_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_DEVICE_MAX_LENGTH); + if (device_buffer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_IN , device_buffer, + UX_HOST_CLASS_PIMA_DEVICE_MAX_LENGTH, UX_HOST_CLASS_PIMA_DEVICE_MAX_LENGTH); + + /* Check the result. If the result is OK, the device info block was read properly. */ + if (status == UX_SUCCESS) + { + + /* Read and store the Standard Version field. */ + pima_device -> ux_host_class_pima_device_standard_version = _ux_utility_short_get(device_buffer + + UX_HOST_CLASS_PIMA_DEVICE_STANDARD_VERSION); + + /* Read and store the Vendor Extension ID . */ + pima_device -> ux_host_class_pima_device_vendor_extension_id = _ux_utility_long_get(device_buffer + + UX_HOST_CLASS_PIMA_DEVICE_VENDOR_EXTENSION_ID); + + /* Read and store the Vendor Extension Version . */ + pima_device -> ux_host_class_pima_device_vendor_extension_version = _ux_utility_long_get(device_buffer + + UX_HOST_CLASS_PIMA_DEVICE_VENDOR_EXTENSION_VERSION); + + + /* Copy the vendor extension descriptor. */ + device_pointer = device_buffer + UX_HOST_CLASS_PIMA_DEVICE_VENDOR_EXTENSION_DESC; + + /* Get the unicode string length. */ + unicode_string_length = (ULONG) *device_pointer; + + /* Check if the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the object description field. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_vendor_extension_desc, device_pointer, unicode_string_length); + + /* Point to the next field. */ + device_pointer += unicode_string_length + 1; + + /* Read and store the Functional Mode. */ + pima_device -> ux_host_class_pima_device_functional_mode = _ux_utility_short_get(device_pointer); + + /* Point to the next field. */ + device_pointer += sizeof(USHORT); + + /* Get the number of elements in array and compute total length. */ + array_length = (ULONG)sizeof(ULONG) + (_ux_utility_long_get(device_pointer) * 2); + + /* Ensure the array can fit in our buffer. */ + if (array_length > UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy the array of supported operations. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_operations_supported, device_pointer, array_length); + + /* Point to the next field. */ + device_pointer += array_length; + + /* Get the number of elements in array and compute total length. */ + array_length = (ULONG)sizeof(ULONG) + (_ux_utility_long_get(device_pointer) * 2); + + /* Ensure the array can fit in our buffer. */ + if (array_length > UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy the array of events supported. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_events_supported, device_pointer, array_length); + + /* Point to the next field. */ + device_pointer += array_length; + + /* Get the number of elements in array and compute total length. */ + array_length = (ULONG)sizeof(ULONG) + (_ux_utility_long_get(device_pointer) * 2); + + /* Ensure the array can fit in our buffer. */ + if (array_length > UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy the array of device properties. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_properties_supported, device_pointer, array_length); + + /* Point to the next field. */ + device_pointer += array_length; + + /* Get the number of elements in array and compute total length. */ + array_length = (ULONG)sizeof(ULONG) + (_ux_utility_long_get(device_pointer) * 2); + + /* Ensure the array can fit in our buffer. */ + if (array_length > UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy the array of capture formats. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_capture_formats, device_pointer, array_length); + + /* Point to the next field. */ + device_pointer += array_length; + + /* Get the number of elements in array and compute total length. */ + array_length = (ULONG)sizeof(ULONG) + (_ux_utility_long_get(device_pointer) * 2); + + /* Ensure the array can fit in our buffer. */ + if (array_length > UX_HOST_CLASS_PIMA_ARRAY_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy the array of supported operations. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_image_formats, device_pointer, array_length); + + /* Point to the next field. */ + device_pointer += array_length; + + /* Get the unicode string length. */ + unicode_string_length = (ULONG) *device_pointer; + + /* Ensure the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the manufacturer field. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_manufacturer, device_pointer, unicode_string_length); + + /* Point to the next field. */ + device_pointer += unicode_string_length + 1; + + /* Get the unicode string length. */ + unicode_string_length = (ULONG) *device_pointer ; + + /* Ensure the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the model date field. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_model, device_pointer, unicode_string_length); + + /* Point to the next field. */ + device_pointer += unicode_string_length + 1; + + /* Get the unicode string length. */ + unicode_string_length = (ULONG) *device_pointer ; + + /* Ensure the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the version field. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_version, device_pointer, unicode_string_length); + + /* Point to the next field. */ + device_pointer += unicode_string_length + 1; + + /* Get the unicode string length. */ + unicode_string_length = (ULONG) *device_pointer ; + + /* Ensure the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH) + + /* Return overflow error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + + /* Copy that string into the serial number field. */ + _ux_utility_memory_copy(pima_device -> ux_host_class_pima_device_serial_number, device_pointer, unicode_string_length); + + else + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + } + } + + /* Free the original object info buffer. */ + _ux_utility_memory_free(device_buffer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_device_reset.c b/common/usbx_host_classes/src/ux_host_class_pima_device_reset.c new file mode 100644 index 0000000..24cc876 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_device_reset.c @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_device_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function resets the device. */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_device_reset(UX_HOST_CLASS_PIMA *pima) +{ + +UX_TRANSFER *transfer_request; +UX_ENDPOINT *control_endpoint; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_DEVICE_RESET, pima, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &pima -> ux_host_class_pima_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Create a transfer_request for the CANCEL request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PIMA_REQUEST_RESET_DEVICE; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Reset the session pointer if it was opened. A device reset closes the session. */ + pima -> ux_host_class_pima_session = UX_NULL; + + /* Return completion status. */ + return(status); + +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_pima_endpoints_get.c new file mode 100644 index 0000000..abbd09e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_endpoints_get.c @@ -0,0 +1,233 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enables the bulk in bulk out and interrupt endpoints. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_pima_activate Activate pima class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_endpoints_get(UX_HOST_CLASS_PIMA *pima) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; +UX_TRANSFER *transfer_request; + + + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < pima -> ux_host_class_pima_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(pima -> ux_host_class_pima_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* We have found the bulk endpoint, save it. */ + pima -> ux_host_class_pima_bulk_out_endpoint = endpoint; + break; + } + } + } + + /* The bulk out endpoint is mandatory. */ + if (pima -> ux_host_class_pima_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, pima, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < pima -> ux_host_class_pima_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(pima -> ux_host_class_pima_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the bulk endpoint, save it. */ + pima -> ux_host_class_pima_bulk_in_endpoint = endpoint; + break; + } + } + } + + /* The bulk in endpoint is mandatory. */ + if (pima -> ux_host_class_pima_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, pima, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the Interrupt endpoint. It is attached to the interface container of the control interface. It is not mandatory. */ + for (endpoint_index = 0; endpoint_index < pima -> ux_host_class_pima_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(pima -> ux_host_class_pima_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is Interrupt and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the interrupt endpoint, save it. */ + pima -> ux_host_class_pima_interrupt_endpoint = endpoint; + + /* The endpoint is correct, Fill in the transfer request with the length requested for this endpoint. */ + transfer_request = &pima -> ux_host_class_pima_interrupt_endpoint -> ux_endpoint_transfer_request; + transfer_request -> ux_transfer_request_requested_length = pima -> ux_host_class_pima_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* The direction is always IN for the Pima interrupt endpoint. */ + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) pima; + + /* Interrupt transactions have a completion routine. */ + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_pima_notification; + + /* Obtain a buffer for this transaction. The buffer will always be reused. */ + transfer_request -> ux_transfer_request_data_pointer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + transfer_request -> ux_transfer_request_requested_length); + + /* Check memory allocation. */ + if (transfer_request -> ux_transfer_request_data_pointer != UX_NULL) + { + + /* The transfer on the interrupt endpoint can be started. */ + status = _ux_host_stack_transfer_request(transfer_request); + + } + + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We must return an error. */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + break; + } + } + } + + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_entry.c b/common/usbx_host_classes/src/ux_host_class_pima_entry.c new file mode 100644 index 0000000..909931a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_entry.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the pima class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* pima on the bus or when the USB pima is removed. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* command Pima class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_activate Activate pima class */ +/* _ux_host_class_pima_deactivate Deactivate pima class */ +/* */ +/* CALLED BY */ +/* */ +/* USBX stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + ((command -> ux_host_class_command_class == UX_HOST_CLASS_PIMA_CLASS) && + (command -> ux_host_class_command_subclass == UX_HOST_CLASS_PIMA_SUBCLASS) && + (command -> ux_host_class_command_protocol == UX_HOST_CLASS_PIMA_PROTOCOL))) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_pima_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_pima_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_notification.c b/common/usbx_host_classes/src/ux_host_class_pima_notification.c new file mode 100644 index 0000000..e7d60a3 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_notification.c @@ -0,0 +1,217 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_notification PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the completion thread when a transfer */ +/* request has been completed either because the transfer is */ +/* successful or there was an error. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_short_get Get 16-bit value */ +/* _ux_utility_long_get Get 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USBX stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_pima_notification(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_PIMA *pima; +UX_HOST_CLASS_PIMA_SESSION *pima_session; +UX_HOST_CLASS_PIMA_EVENT pima_event; + + /* Get the class instance for this transfer request. */ + pima = (UX_HOST_CLASS_PIMA *) transfer_request -> ux_transfer_request_class_instance; + + /* Check the state of the transfer. If there is an error, we do not proceed with this notification. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* We have an error. We do not rehook another transfer if the device instance is shutting down or + if the transfer was aborted by the class.. */ + if ((pima -> ux_host_class_pima_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) || + (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_ABORT)) + + /* We do not proceed. */ + return; + else + + { + + /* Reactivate the PIMA interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* We do not proceed. */ + return; + } + + } + + /* Check if this packet is the first, if so we have the total length expected in the event. */ + if (pima -> ux_host_class_pima_event_buffer_current_length == 0) + { + + /* First packet. Maybe the only one needed. It may happen that the notification event is split amongst + several interrupt packets. */ + pima -> ux_host_class_pima_event_buffer_expected_length = _ux_utility_long_get(transfer_request -> ux_transfer_request_data_pointer + + UX_HOST_CLASS_PIMA_AEI_DATA_LENGTH); + + /* Set the current offset to the beginning of the buffer. */ + pima -> ux_host_class_pima_event_buffer_current_offset = pima -> ux_host_class_pima_event_buffer; + + } + + /* Check the length of this payload and make sure we have enough space. */ + if ((pima -> ux_host_class_pima_event_buffer_current_length + transfer_request -> ux_transfer_request_actual_length) <= + UX_HOST_CLASS_PIMA_AEI_MAX_LENGTH) + { + + /* We copy the current payload into our event notification buffer. */ + _ux_utility_memory_copy(pima -> ux_host_class_pima_event_buffer_current_offset, transfer_request -> ux_transfer_request_data_pointer, + transfer_request -> ux_transfer_request_actual_length); + + /* Set the new offset address. */ + pima -> ux_host_class_pima_event_buffer_current_offset += transfer_request -> ux_transfer_request_actual_length; + + /* Adjust the length. */ + pima -> ux_host_class_pima_event_buffer_current_length += transfer_request -> ux_transfer_request_actual_length; + } + else + + /* We come here when we have a buffer overflow. Do not proceed. */ + return; + + /* Check if we have a complete notification event. */ + if (pima -> ux_host_class_pima_event_buffer_current_length == pima -> ux_host_class_pima_event_buffer_expected_length) + { + + /* Save the current event in the pima instance. First, unpack the event code. */ + pima -> ux_host_class_pima_event_code = _ux_utility_short_get(pima -> ux_host_class_pima_event_buffer + UX_HOST_CLASS_PIMA_AEI_EVENT_CODE); + + /* Unpack the Transaction ID. */ + pima -> ux_host_class_pima_event_transaction_id = _ux_utility_long_get(pima -> ux_host_class_pima_event_buffer + UX_HOST_CLASS_PIMA_AEI_TRANSACTION_ID); + + /* Unpack the parameter 1. */ + pima -> ux_host_class_pima_event_parameter_1 = _ux_utility_long_get(pima -> ux_host_class_pima_event_buffer + UX_HOST_CLASS_PIMA_AEI_PARAMETER_1); + + /* Unpack the parameter 2. */ + pima -> ux_host_class_pima_event_parameter_2 = _ux_utility_long_get(pima -> ux_host_class_pima_event_buffer + UX_HOST_CLASS_PIMA_AEI_PARAMETER_2); + + /* Unpack the parameter 3. */ + pima -> ux_host_class_pima_event_parameter_3 = _ux_utility_long_get(pima -> ux_host_class_pima_event_buffer + UX_HOST_CLASS_PIMA_AEI_PARAMETER_3); + + /* Check if a session is valid. */ + if (pima -> ux_host_class_pima_session != UX_NULL) + { + + /* Get session pointer. */ + pima_session = pima -> ux_host_class_pima_session; + + /* Check if this session is valid or not. */ + if ((pima_session -> ux_host_class_pima_session_magic == UX_HOST_CLASS_PIMA_MAGIC_NUMBER) && + (pima_session -> ux_host_class_pima_session_state == UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED)) + { + + /* If the application demanded a callback when event occur, create a event notification + message. */ + if (pima_session -> ux_host_class_pima_session_event_callback != UX_NULL) + { + + /* Fill in the pima event structure. First with pima instance. */ + pima_event.ux_host_class_pima_event_pima_instance = pima; + + /* Fill in the opened session pointer. Since the pima class only supports + one session, this address is hardwired to the opened session and is not coming from + the event buffer. */ + pima_event.ux_host_class_pima_event_session = pima_session; + + /* Fill in all the event parameters in the event callback structure. */ + pima_event.ux_host_class_pima_event_code = pima -> ux_host_class_pima_event_code; + pima_event.ux_host_class_pima_event_transaction_id = pima -> ux_host_class_pima_event_transaction_id; + pima_event.ux_host_class_pima_event_parameter_1 = pima -> ux_host_class_pima_event_parameter_1; + pima_event.ux_host_class_pima_event_parameter_2 = pima -> ux_host_class_pima_event_parameter_2; + pima_event.ux_host_class_pima_event_parameter_3 = pima -> ux_host_class_pima_event_parameter_3; + + /* Send this event to the application. */ + pima_session -> ux_host_class_pima_session_event_callback(&pima_event); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_NOTIFICATION, pima, + pima -> ux_host_class_pima_event_code, + pima -> ux_host_class_pima_event_transaction_id, + pima -> ux_host_class_pima_event_parameter_1, + UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + } + } + } + + /* We will receive a complete new transaction. */ + pima -> ux_host_class_pima_event_buffer_current_length = 0; + } + /* Reactivate the PIMA interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_num_objects_get.c b/common/usbx_host_classes/src/ux_host_class_pima_num_objects_get.c new file mode 100644 index 0000000..1131219 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_num_objects_get.c @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_num_object_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the number of objects for a specific storage. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_num_objects_get(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG storage_id, + ULONG object_format_code) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_NUM_OBJECTS_GET, pima, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to get the storage IDs. 2 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 2; + + /* Parameter 1 is the Storage ID. */ + command.ux_host_class_pima_command_parameter_1 = storage_id; + + /* Parameter 2 is the Object Format Code. */ + command.ux_host_class_pima_command_parameter_2 = object_format_code; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to GET_NUM_OBJETCS. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_NUM_OBJECTS; + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, 0 , UX_NULL, 0, 0); + + /* Check the result. If the result is OK, the parameter1 has the number of objects. */ + if (status == UX_SUCCESS) + + /* Store the number of objects into the current pima session. */ + pima_session -> ux_host_class_pima_session_nb_objects = command.ux_host_class_pima_command_parameter_1; + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_close.c b/common/usbx_host_classes/src/ux_host_class_pima_object_close.c new file mode 100644 index 0000000..d6843d7 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_close.c @@ -0,0 +1,219 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_close PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function closes an object, */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle The object handle */ +/* object Pointer to object info */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_close(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object) +{ + +UX_TRANSFER *transfer_request; +UCHAR *ptp_payload; +ULONG requested_length; +UINT status; + + UX_PARAMETER_NOT_USED(object_handle); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_CLOSE, pima, object, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if the object is already closed. */ + if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_OBJECT_ALREADY_CLOSED ); + + /* Close the object. */ + object -> ux_host_class_pima_object_state = UX_HOST_CLASS_PIMA_OBJECT_STATE_CLOSED; + + /* If the transfer was not ended prematurely, we need to do the status phase. */ + if ( object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED) + { + + /* The transfer for this transaction is now inactive. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_INACTIVE; + + /* Check if we had a ZLP condition during the data phase. */ + if (pima -> ux_host_class_pima_zlp_flag != UX_HOST_CLASS_PIMA_ZLP_NONE) + { + + /* We had a ZLP, so we now expect a zero length packet prior to the status phase. + We need to determine the direction. */ + if (pima -> ux_host_class_pima_zlp_flag == UX_HOST_CLASS_PIMA_ZLP_IN) + + /* We use the Bulk In pipe for receiving the zlp. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + else + + /* We use the Bulk Out pipe for sending the zlp. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Initialize the payload to zero length. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + + /* Reset the ZLP now. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* There was an error, return to the caller. */ + return(status); + } + } + } + + /* We use the Bulk In pipe for receiving the response payload. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_RESPONSE_HEADER_SIZE; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + } + + /* The transfer for this transaction is now inactive. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_INACTIVE; + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_copy.c b/common/usbx_host_classes/src/ux_host_class_pima_object_copy.c new file mode 100644 index 0000000..b97375f --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_copy.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_copy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function copies one object from one location to another. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle Object handle to move */ +/* storage_id destination storage ID */ +/* parent_object_handle Parent object handle */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_copy(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, + ULONG storage_id, + ULONG parent_object_handle) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_COPY, pima, object_handle, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to get the object info. 3 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 3; + + /* Parameter 1 is the Object Handle. */ + command.ux_host_class_pima_command_parameter_1 = object_handle; + + /* Parameter 2 is the Storage ID. */ + command.ux_host_class_pima_command_parameter_2 = storage_id; + + /* Parameter 3 is the Parent Object ID. */ + command.ux_host_class_pima_command_parameter_3 = parent_object_handle; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to COPY_OBJECT. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_COPY_OBJECT; + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_NONE , UX_NULL, + 0, 0); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_delete.c b/common/usbx_host_classes/src/ux_host_class_pima_object_delete.c new file mode 100644 index 0000000..e7341f3 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_delete.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_delete PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function deletes an object. If the object handle is set to */ +/* 0xFFFFFFFF then all objects on the media are deleted. */ +/* */ +/* */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle The object handle */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_delete(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_DELETE, pima, object_handle, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to get the object info. 1 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 1; + + /* Parameter 1 is the Object Handle. */ + command.ux_host_class_pima_command_parameter_1 = object_handle; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_2 = 0; + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to DELETE_OBJECT. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_DELETE_OBJECT; + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_NONE , UX_NULL, + 0, 0); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_get.c b/common/usbx_host_classes/src/ux_host_class_pima_object_get.c new file mode 100644 index 0000000..21c66cd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_get.c @@ -0,0 +1,412 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets an object identified by the object_handle */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle The object handle */ +/* object Pointer to object info */ +/* object_buffer Buffer to be used */ +/* object_buffer_length Buffer length */ +/* object_actual_length Length read in that */ +/* command */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_long_get Get 32-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_get(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object, + UCHAR *object_buffer, ULONG object_buffer_length, + ULONG *object_actual_length) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UX_TRANSFER *transfer_request; +UCHAR *ptp_payload; +ULONG requested_length; +ULONG total_length; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_GET, pima, object_handle, object, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if the object is already opened. */ + if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_OBJECT_NOT_OPENED); + + /* Check the transfer status. If there was an error or transfer is completed, refuse transfer. */ + if ((object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED) || + (object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED)) + return (UX_HOST_CLASS_PIMA_RC_ACCESS_DENIED); + + /* Reset the actual length. */ + *object_actual_length = 0; + + /* This variable will remain untouched if the offset is not 0 and the requested length is 0. */ + status = UX_SUCCESS; + + /* Check if the offset to be read is at 0, if so, prepare the first read command. */ + if (object -> ux_host_class_pima_object_offset == 0) + { + + /* Set the object transfer status to active. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE; + + /* Issue command to get the object info. 1 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 1; + + /* Parameter 1 is the Object Handle. */ + command.ux_host_class_pima_command_parameter_1 = object_handle; + + /* Then set the command to GET_OBJECT. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_OBJECT; + + /* We use the Bulk Out pipe for sending data out.. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE + ((ULONG)sizeof(ULONG) * command.ux_host_class_pima_command_nb_parameters); + + /* Fill the command container. First the length of the total header and payload. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH, requested_length); + + + /* Then the type of container : a command block here. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE, UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK); + + /* Now the command code to send. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE, (USHORT)command.ux_host_class_pima_command_operation_code); + + /* Put the transaction ID. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID, + pima -> ux_host_class_pima_transaction_id++); + + /* Then fill in the parameters. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1, + command.ux_host_class_pima_command_parameter_1); + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the object transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_ERROR); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Check for completion of transfer. If the transfer is partial, return to caller. + Partial transfer is not OK. */ + if (requested_length == transfer_request -> ux_transfer_request_actual_length) + { + + /* Obtain the first packet. This packet contains the header and some data. + We use the Bulk In pipe for receiving data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. It is the minimum + of the application's requested length and the container size. */ + if (object_buffer_length < UX_HOST_CLASS_PIMA_CONTAINER_SIZE) + requested_length = object_buffer_length; + else + requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the object transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_ERROR); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Ensure the transfer is larger than the header. */ + if (transfer_request -> ux_transfer_request_actual_length > UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) + { + + /* We need to skip the header. Copying the necessary partial memory only. */ + _ux_utility_memory_copy(object_buffer, ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE, + transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Get the size of the entire data payload to be expected in this object transfer (data + header). If the size is on a boundary + the pima protocol demands that the last packet is a ZLP. */ + total_length = _ux_utility_long_get(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH); + + /* Check for remainder in last packet. */ + if ((total_length % pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_descriptor.wMaxPacketSize) == 0) + + /* We have a ZLP condition on a IN. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_IN; + else + + /* Do not expect a ZLP. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + + /* Compute the total length of the data only. */ + total_length -= UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Check if the expected length remaining will be more than the current buffer. If so, we need to have a full payload + on a packet boundary. */ + if (total_length > object_buffer_length) + + /* Update what is left to be received. */ + object_buffer_length -= transfer_request -> ux_transfer_request_actual_length; + + else + + /* Update what is left to be received. */ + object_buffer_length -= transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Update the actual length. */ + *object_actual_length = transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Update where we will store the next data. */ + object_buffer += transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* And the offset. */ + object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + } + else + { + + /* The transfer is smaller than the header, which is an error. */ + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR); + + /* Return error. */ + return(UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR); + } + } + else + + /* We got a premature error. */ + return(UX_HOST_CLASS_PIMA_RC_INCOMPLETE_TRANSFER); + } + + /* We use the Bulk In pipe for receiving data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* We read a complete block. */ + while(object_buffer_length != 0) + { + + /* It may take several transactions. */ + if (object_buffer_length > UX_HOST_CLASS_PIMA_MAX_PAYLOAD) + + /* Set the requested length to the payload maximum. */ + requested_length = UX_HOST_CLASS_PIMA_MAX_PAYLOAD; + + else + + /* We can use the user supplied length to complete this request. */ + requested_length = object_buffer_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = object_buffer; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the object transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_ERROR); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the object length expected by the user with what we actually received. */ + object_buffer_length -= transfer_request -> ux_transfer_request_actual_length; + + /* Update the actual length. */ + *object_actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* And the offset. */ + object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length; + + /* And the object buffer pointer . */ + object_buffer += transfer_request -> ux_transfer_request_actual_length; + + /* Check to see if we are at the end of the object. */ + if (object -> ux_host_class_pima_object_offset == object -> ux_host_class_pima_object_compressed_size) + + /* The transfer for this transaction is completed. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED; + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_handles_get.c b/common/usbx_host_classes/src/ux_host_class_pima_object_handles_get.c new file mode 100644 index 0000000..447a7dd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_handles_get.c @@ -0,0 +1,176 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_storage_ids_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets a list if the current valid Storage IDS. There */ +/* is one Storage ID for each valid logical store. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handles_array Pointer to store handles */ +/* object_handles_length Length of arrays */ +/* object_format_code Object Format Code */ +/* object_handle_association Object Handle */ +/* Association */ +/* */ +/* The 2 last parameter are optional and should be set to 0 if not */ +/* used. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* _ux_utility_long_get Get 32 bit value */ +/* _ux_utility_memory_allocate Allocate some memory */ +/* _ux_utility_memory_free Free some memory */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_handles_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG *object_handles_array, ULONG object_handles_length, + ULONG storage_id, ULONG object_format_code, ULONG object_handle_association) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UCHAR *object_handles_array_raw; +ULONG object_handle_length_raw; +ULONG count_object_handles; +ULONG nb_object_handles; +UINT status; + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check the number of handles and compare with the size of the array given by the user. */ + if ((pima_session -> ux_host_class_pima_session_nb_objects * sizeof(ULONG)) > object_handles_length) + return(UX_MEMORY_INSUFFICIENT); + + /* Issue command to get the storage IDs. 3 parameters. */ + command.ux_host_class_pima_command_nb_parameters = 3; + + /* Parameter 1 is the Storage ID. */ + command.ux_host_class_pima_command_parameter_1 = storage_id; + + /* Parameter 2 is optional. It is the Object Format Code. */ + command.ux_host_class_pima_command_parameter_2 = object_format_code; + + /* Parameter 3 is optional. It is the object handle association. */ + command.ux_host_class_pima_command_parameter_3 = object_handle_association; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to GET_STORAGE_IDS. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_OBJECT_HANDLES; + + /* Calculate the length the raw array. We multiply the number of handles by the size on the handle and + add a ULONG for the number of handles stored at the beginning of the array. */ + status = UX_SUCCESS; + object_handle_length_raw = 0; + UX_UTILITY_ADD_SAFE(pima_session -> ux_host_class_pima_session_nb_objects, 1, object_handle_length_raw, status); + if (status != UX_SUCCESS) + return(status); + UX_UTILITY_MULC_SAFE(object_handle_length_raw, (ULONG)sizeof(ULONG), object_handle_length_raw, status); + if (status != UX_SUCCESS) + return(status); + + /* Allocate some DMA safe memory for receiving the handles */ + object_handles_array_raw = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, object_handle_length_raw); + if (object_handles_array_raw == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_IN , object_handles_array_raw, + object_handle_length_raw, object_handle_length_raw); + + /* Check the result. If OK, the object handles array is returned properly. */ + if (status == UX_SUCCESS) + { + + /* Read the number of Object handles in the returned array. */ + nb_object_handles = _ux_utility_long_get(object_handles_array_raw); + + /* Save the number of object handles. */ + pima_session -> ux_host_class_pima_session_nb_objects = nb_object_handles; + + /* Check if the user gave us enough memory. */ + if ((nb_object_handles * sizeof(ULONG)) > object_handles_length) + + /* No, not enough memory to store the array. */ + return(UX_MEMORY_INSUFFICIENT); + + /* Unpack all object handles. */ + for(count_object_handles = 0; count_object_handles < nb_object_handles; count_object_handles++) + + /* Unpack one object handle at a time */ + *(object_handles_array + count_object_handles) = _ux_utility_long_get(object_handles_array_raw + sizeof(ULONG) + + (count_object_handles * sizeof(ULONG))); + } + + /* Free the original raw array. */ + _ux_utility_memory_free(object_handles_array_raw); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_info_get.c b/common/usbx_host_classes/src/ux_host_class_pima_object_info_get.c new file mode 100644 index 0000000..dc52b40 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_info_get.c @@ -0,0 +1,234 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_info_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the current object information block. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle The object handle */ +/* object Object structure to fill */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* _ux_utility_descriptor_parse Unpack descriptor */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_free Free allocated memory */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_info_get(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UCHAR *object_buffer; +UCHAR *object_pointer; +ULONG unicode_string_length; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_INFO_GET, pima, object_handle, object, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to get the object info. 1 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 1; + + /* Parameter 1 is the Object Handle. */ + command.ux_host_class_pima_command_parameter_1 = object_handle; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_2 = 0; + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to GET_OBJECT_INFO. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_OBJECT_INFO; + + /* Allocate some DMA safe memory for receiving the object info block. */ + object_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_OBJECT_MAX_LENGTH); + if (object == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_IN , object_buffer, + UX_HOST_CLASS_PIMA_OBJECT_MAX_LENGTH, UX_HOST_CLASS_PIMA_OBJECT_MAX_LENGTH); + + /* Check the result. If the result is OK, the object info block was read properly. */ + if (status == UX_SUCCESS) + { + /* Uncompress the object descriptor, at least the fixed part. */ + _ux_utility_descriptor_parse(object_buffer, + _ux_system_class_pima_object_structure, + UX_HOST_CLASS_PIMA_OBJECT_ENTRIES, + (UCHAR *) object); + + /* Copy the object filename field. Point to the beginning of the object description string. */ + object_pointer = object_buffer + UX_HOST_CLASS_PIMA_OBJECT_VARIABLE_OFFSET; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_pointer * 2) + 1; + + /* Check if string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the object description field. */ + _ux_utility_memory_copy(object -> ux_host_class_pima_object_filename, object_pointer, unicode_string_length); + + /* Point to the next field. */ + object_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_pointer * 2) + 1; + + /* Ensure the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the capture date field. */ + _ux_utility_memory_copy(object -> ux_host_class_pima_object_capture_date, object_pointer, unicode_string_length); + + /* Point to the next field. */ + object_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_pointer * 2) + 1; + + /* Ensure the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_DATE_TIME_STRING_MAX_LENGTH) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the modification date field. */ + _ux_utility_memory_copy(object -> ux_host_class_pima_object_modification_date, object_pointer, unicode_string_length); + + /* Point to the next field. */ + object_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object_pointer * 2) + 1; + + /* Ensure the string can fit in our buffer. */ + if (unicode_string_length > UX_HOST_CLASS_PIMA_UNICODE_MAX_LENGTH) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the keywords field. */ + _ux_utility_memory_copy(object -> ux_host_class_pima_object_keywords, object_pointer, unicode_string_length); + + /* Make the object closed. */ + object -> ux_host_class_pima_object_state = UX_HOST_CLASS_PIMA_OBJECT_STATE_CLOSED; + + /* Reset the reading\writing offset */ + object -> ux_host_class_pima_object_offset = 0; + } + else + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + } + } + + /* Free the original object info buffer. */ + _ux_utility_memory_free(object_buffer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_info_send.c b/common/usbx_host_classes/src/ux_host_class_pima_object_info_send.c new file mode 100644 index 0000000..863090a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_info_send.c @@ -0,0 +1,252 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_info_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends an object information block prior to sending */ +/* a new object. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* storage_id StorageID where to store */ +/* parent_object_id Parent object id */ +/* object Object info structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_free Free allocated memory */ +/* _ux_utility_descriptor_pack Pack descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_info_send(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG storage_id, + ULONG parent_object_id, + UX_HOST_CLASS_PIMA_OBJECT *object) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UCHAR *object_buffer; +UCHAR *object_buffer_end; +UCHAR *object_pointer; +ULONG unicode_string_length; +ULONG object_info_length; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_INFO_SEND, pima, object, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to set the object info. 2 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 2; + + /* Parameter 1 is the Storage ID. */ + command.ux_host_class_pima_command_parameter_1 = storage_id; + + /* Parameter 2 is the Parent Object ID. */ + command.ux_host_class_pima_command_parameter_2 = parent_object_id; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to SEND_OBJECT_INFO. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_SEND_OBJECT_INFO; + + /* Allocate some DMA safe memory for sending the object info block. */ + object_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_OBJECT_MAX_LENGTH); + if (object == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Get the end of the object buffer. */ + object_buffer_end = object_buffer + UX_HOST_CLASS_PIMA_OBJECT_MAX_LENGTH; + + /* The object info structure coming from the application needs to be packed. */ + _ux_utility_descriptor_pack((UCHAR *) object, + _ux_system_class_pima_object_structure, + UX_HOST_CLASS_PIMA_OBJECT_ENTRIES, + object_buffer); + + /* Copy the object filename field. Point to the beginning of the object description string. */ + object_pointer = object_buffer + UX_HOST_CLASS_PIMA_OBJECT_VARIABLE_OFFSET; + + /* Get the unicode string length for the filename. */ + unicode_string_length = ((ULONG) *object -> ux_host_class_pima_object_filename * 2) + 1; + + /* Is there enough room for this string? */ + if (object_pointer + unicode_string_length > object_buffer_end) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + + else + + /* Continue. */ + status = UX_SUCCESS; + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the object description field. */ + _ux_utility_memory_copy(object_pointer, object -> ux_host_class_pima_object_filename, unicode_string_length); + + /* Point to the next field. */ + object_pointer += unicode_string_length; + + /* Get the unicode string length of the capture date. */ + unicode_string_length = ((ULONG) *object -> ux_host_class_pima_object_capture_date * 2) + 1; + + /* Is there enough room for this string? */ + if (object_pointer + unicode_string_length > object_buffer_end) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the capture date field. */ + _ux_utility_memory_copy(object_pointer, object -> ux_host_class_pima_object_capture_date, unicode_string_length); + + /* Point to the next field. */ + object_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object -> ux_host_class_pima_object_modification_date * 2) + 1; + + /* Is there enough room for this string? */ + if (object_pointer + unicode_string_length > object_buffer_end) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the modification date field. */ + _ux_utility_memory_copy(object_pointer, object -> ux_host_class_pima_object_modification_date, unicode_string_length); + + /* Point to the next field. */ + object_pointer += unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = ((ULONG) *object -> ux_host_class_pima_object_keywords * 2) + 1; + + /* Is there enough room for this string? */ + if (object_pointer + unicode_string_length > object_buffer_end) + + /* Return error. */ + status = UX_MEMORY_INSUFFICIENT; + } + + /* Is there enough space? */ + if (status == UX_SUCCESS) + { + + /* Copy that string into the keywords field. */ + _ux_utility_memory_copy(object_pointer, object -> ux_host_class_pima_object_keywords, unicode_string_length); + + /* Point to the next field. */ + object_pointer += unicode_string_length; + + /* Calculate the length of the payload. */ + object_info_length = (ULONG ) (object_pointer - object_buffer); + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_OUT , object_buffer, + object_info_length, object_info_length); + + /* If the status is OK, the device sent us the Object handle in the response. */ + if (status == UX_SUCCESS) + + /* Update the object handle. */ + object -> ux_host_class_pima_object_handle_id = command.ux_host_class_pima_command_parameter_2; + } + else + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + } + + /* Free the original object info buffer. */ + _ux_utility_memory_free(object_buffer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_move.c b/common/usbx_host_classes/src/ux_host_class_pima_object_move.c new file mode 100644 index 0000000..46768a2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_move.c @@ -0,0 +1,121 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_move PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function moves an object from one location to another. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle Object handle to move */ +/* storage_id destination storage ID */ +/* parent_object_handle Parent object handle */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_move(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, + ULONG storage_id, + ULONG parent_object_handle) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_MOVE, pima, object_handle, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to get the object info. 3 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 3; + + /* Parameter 1 is the Object Handle. */ + command.ux_host_class_pima_command_parameter_1 = object_handle; + + /* Parameter 2 is the Storage ID. */ + command.ux_host_class_pima_command_parameter_2 = storage_id; + + /* Parameter 3 is the Parent Object ID. */ + command.ux_host_class_pima_command_parameter_3 = parent_object_handle; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to MOVE_OBJECT. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_MOVE_OBJECT; + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_NONE , UX_NULL, + 0, 0); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_open.c b/common/usbx_host_classes/src/ux_host_class_pima_object_open.c new file mode 100644 index 0000000..b8e0569 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_open.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_open PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens an object for a read or write operation */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle The object handle */ +/* object Pointer to object info */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_open(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object) +{ + + UX_PARAMETER_NOT_USED(object_handle); + UX_PARAMETER_NOT_USED(pima); + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if the object is already opened. */ + if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_CLOSED) + return (UX_HOST_CLASS_PIMA_RC_OBJECT_ALREADY_OPENED); + + /* Open the object. */ + object -> ux_host_class_pima_object_state = UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED; + + /* Reset the object offset. */ + object -> ux_host_class_pima_object_offset = 0; + + /* Set the object transfer status to inactive. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_INACTIVE; + + /* Return completion status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_send.c b/common/usbx_host_classes/src/ux_host_class_pima_object_send.c new file mode 100644 index 0000000..a2cf18c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_send.c @@ -0,0 +1,376 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_send PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sends an object to the media. This commands should be */ +/* be proceeded by a object_info_send command. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object Pointer to object info */ +/* object_buffer Buffer to be used */ +/* object_buffer_length Buffer length */ +/* callback_function Application function to */ +/* callback when buffer */ +/* needs to be written */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_short_put Put 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_send(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + UX_HOST_CLASS_PIMA_OBJECT *object, + UCHAR *object_buffer, ULONG object_buffer_length) +{ + + +UX_HOST_CLASS_PIMA_COMMAND command; +UX_TRANSFER *transfer_request; +UCHAR *ptp_payload; +ULONG requested_length; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_SEND, pima, object, object_buffer, object_buffer_length, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if the object is already opened. */ + if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_OBJECT_NOT_OPENED); + + /* Check the transfer status. If there was an error or transfer is completed, refuse transfer. */ + if ((object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED) || + (object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED)) + return (UX_HOST_CLASS_PIMA_RC_ACCESS_DENIED); + + /* This variable will remain untouched if the offset is not 0 and the requested length is 0. */ + status = UX_SUCCESS; + + /* Check if the offset to be read is at 0, if so, prepare the first write command. */ + if (object -> ux_host_class_pima_object_offset == 0) + { + + /* Set the object transfer status to active. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE; + + /* Issue command to get the object info. no parameter. */ + command.ux_host_class_pima_command_nb_parameters = 0; + + /* Then set the command to SEND_OBJECT. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_SEND_OBJECT; + + /* We use the Bulk Out pipe for sending command out.. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE + ((ULONG)sizeof(ULONG) * command.ux_host_class_pima_command_nb_parameters); + + /* Fill the command container. First the length of the total header and payload. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH, requested_length); + + /* Then the type of container : a command block here. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE, UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK); + + /* Now the command code to send. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE, (USHORT)command.ux_host_class_pima_command_operation_code); + + /* Put the transaction ID. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID, + pima -> ux_host_class_pima_transaction_id++); + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the object transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Check for completion of transfer. If the transfer is partial, return to caller. + Partial transfer is not OK. */ + if (requested_length == transfer_request -> ux_transfer_request_actual_length) + { + + /* Send the first packet. This packet contains the header and some data. + We use the Bulk Out pipe for sending data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Container type is a data type. */ + *(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_TYPE) = UX_HOST_CLASS_PIMA_CT_DATA_BLOCK; + + /* Fill in the header values. Start with the length of the container. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH, + object -> ux_host_class_pima_object_compressed_size + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* If the total payload is on a boundary, we need a ZLP to mark the end. */ + if (((object -> ux_host_class_pima_object_compressed_size + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) % + pima -> ux_host_class_pima_bulk_out_endpoint-> ux_endpoint_descriptor.wMaxPacketSize) == 0) + + /* We have a ZLP condition. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_OUT; + else + + /* Do not expect a ZLP. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE; + + /* Check to see if we have enough to fill the first packet. */ + if (requested_length > (UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE + object_buffer_length)) + + /* This is a small object, enough to fit into the fist packet. */ + requested_length = UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE + object_buffer_length; + + /* We need to skip the header. Copying the necessary partial memory only. */ + _ux_utility_memory_copy(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE, object_buffer, + requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Update the offset. */ + object -> ux_host_class_pima_object_offset += requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* And the current object buffer pointer. */ + object_buffer += requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Adjust the remaining buffer length. */ + object_buffer_length -= requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the object transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + + } + else + + /* We got a premature error. */ + return(UX_HOST_CLASS_PIMA_RC_INCOMPLETE_TRANSFER); + } + + + /* We use the Bulk Out pipe for sending data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* We send a complete block. */ + while(object_buffer_length != 0) + { + + /* It may take several transactions. */ + if (object_buffer_length > UX_HOST_CLASS_PIMA_MAX_PAYLOAD) + + /* Set the requested length to the payload maximum. */ + requested_length = UX_HOST_CLASS_PIMA_MAX_PAYLOAD; + + else + + /* We can use the user supplied length to complete this request. */ + requested_length = object_buffer_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = object_buffer; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the object transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the object length expected by the user with what we actually sent. */ + object_buffer_length -= transfer_request -> ux_transfer_request_actual_length; + + /* And the offset. */ + object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length; + + /* And the object buffer pointer . */ + object_buffer += transfer_request -> ux_transfer_request_actual_length; + + /* Check to see if we are at the end of the object. */ + if (object -> ux_host_class_pima_object_offset == object -> ux_host_class_pima_object_compressed_size) + + /* The transfer for this transaction is completed. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED; + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_object_transfer_abort.c b/common/usbx_host_classes/src/ux_host_class_pima_object_transfer_abort.c new file mode 100644 index 0000000..1663b0d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_object_transfer_abort.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_object_transfer_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function aborts a pending transfer to\from an object. */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle The object handle */ +/* object Pointer to object info */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_request_cancel Cancel request */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_object_transfer_abort(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object) +{ + +UINT status; + + UX_PARAMETER_NOT_USED(object_handle); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_OBJECT_TRANSFER_ABORT, pima, object_handle, object, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if the object is already closed. */ + if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_OBJECT_ALREADY_CLOSED ); + + /* Cancel the current request. */ + status = _ux_host_class_pima_request_cancel(pima); + + /* The transfer for this transaction was aborted. No need to issue a status phase when the object is closed. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* Reset the potential ZLP condition. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_read.c b/common/usbx_host_classes/src/ux_host_class_pima_read.c new file mode 100644 index 0000000..d775fcd --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_read.c @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a data payload from the Pima device. This */ +/* function first read a header followed by some data. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* data_pointer Pointer to data to read */ +/* data_length Length of data to read */ +/* callback_function Application function to call */ +/* to get the rest of the data */ +/* max_payload_data Maximum data received in one */ +/* data payload. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_long_get Get a long 32 bit value */ +/* _ux_utility_short_get Get a short 16 bit value */ +/* _ux_utility_memory_copy Copy memory */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_read(UX_HOST_CLASS_PIMA *pima, UCHAR *data_pointer, + ULONG data_length, + ULONG max_payload_length) + +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG header_length; +UCHAR *ptp_payload; +ULONG requested_length; +ULONG payload_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_READ, pima, data_pointer, data_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We use the Bulk In pipe for receiving data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Ensure the transfer is greater than the size of a PIMA header. */ + if (transfer_request -> ux_transfer_request_actual_length <= UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) + + /* We have a malformed packet. Return error. */ + return(UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR); + + /* Get the expected length from the header. */ + header_length = _ux_utility_long_get(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH); + + /* Check for remainder in last packet. */ + if ((header_length % pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_descriptor.wMaxPacketSize) == 0) + + /* We have a ZLP condition on a IN. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_IN; + else + + /* Do not expect a ZLP. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + + /* The length returned should be smaller than the length requested. */ + if ((header_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE)> data_length) + return(UX_ERROR); + + /* We may have had data in the first packet, if so adjust the data_length. */ + data_length = header_length - transfer_request -> ux_transfer_request_actual_length; + + /* Copying the necessary partial memory. */ + _ux_utility_memory_copy(data_pointer, ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE, + transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Adjust the data payload pointer. */ + data_pointer += (transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Now we can read the data from the device. */ + while(data_length) + { + + /* Check if need to split the data payload into smaller packets. */ + if (data_length > max_payload_length) + + /* We cannot send everything in this payload. */ + payload_length = max_payload_length; + else + + /* Either this is the last packet or we we have a small packet to send. */ + payload_length = data_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = payload_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Check for completion of transfer. If the transfer is partial, return to caller. + Partial transfer is not OK. */ + if (payload_length != transfer_request -> ux_transfer_request_actual_length) + return(UX_TRANSFER_ERROR); + + /* Adjust the total length to transfer. */ + data_length -= payload_length; + + /* Adjust the data pointer. */ + data_pointer += payload_length; + + } + + /* If we have a ZLP condition, read from the device one more time with a zero packet. */ + if (pima -> ux_host_class_pima_zlp_flag == UX_HOST_CLASS_PIMA_ZLP_IN) + { + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* There was an error, return to the caller. */ + return(status); + } + + /* Reset the ZLP. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + } + } + + /* We have finished receiving the data. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_request_cancel.c b/common/usbx_host_classes/src/ux_host_class_pima_request_cancel.c new file mode 100644 index 0000000..892ca76 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_request_cancel.c @@ -0,0 +1,188 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_request_cancel PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cancels a request. */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_short_put Put 16-bit value */ +/* _ux_utility_short_get Get 16-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* _ux_utility_delay_ms Delay ms */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_request_cancel(UX_HOST_CLASS_PIMA *pima) +{ + +UX_TRANSFER *transfer_request; +UX_ENDPOINT *control_endpoint; +UCHAR *request_payload; +UINT status; +UINT device_status; +ULONG payload_length; +ULONG command_retry_counter; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_REQUEST_CANCEL, pima, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Allocate some DMA safe memory for the command data payload. We use mode than needed because + we use this buffer for both the command and the status phase. */ + request_payload = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_REQUEST_STATUS_DATA_LENGTH); + if (request_payload == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Fill in the payload buffer with cancellation code. */ + _ux_utility_short_put(request_payload + UX_HOST_CLASS_PIMA_REQUEST_CANCEL_OFFSET_CODE, + UX_HOST_CLASS_PIMA_REQUEST_CANCEL_CODE ); + + /* Fill in the payload buffer with transactionID. */ + _ux_utility_long_put(request_payload + UX_HOST_CLASS_PIMA_REQUEST_CANCEL_OFFSET_TRANSACTION_ID, + pima -> ux_host_class_pima_transaction_id++); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &pima -> ux_host_class_pima_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Create a transfer_request for the CANCEL request. */ + transfer_request -> ux_transfer_request_data_pointer = request_payload; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_PIMA_REQUEST_CANCEL_DATA_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PIMA_REQUEST_CANCEL_COMMAND; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check if status is OK. If the command did not work, we have a major problem. */ + if(status == UX_SUCCESS) + { + + /* Initialize the command retry counter. */ + command_retry_counter = UX_HOST_CLASS_PIMA_REQUEST_STATUS_COMMAND_COUNTER; + + /* This command may be retried a few times. */ + while (command_retry_counter-- != 0) + { + + /* We need to wait for the device to be OK. For that we issue a GET_DEVICE_STATUS command. */ + transfer_request -> ux_transfer_request_data_pointer = request_payload; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_PIMA_REQUEST_CANCEL_DATA_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PIMA_REQUEST_STATUS_COMMAND; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, abort if there is an error. */ + if (status != UX_SUCCESS) + break; + + /* Extract the payload length from the status buffer. */ + payload_length = _ux_utility_short_get(request_payload + UX_HOST_CLASS_PIMA_REQUEST_STATUS_OFFSET_LENGTH); + + /* Check the data payload length. */ + if (payload_length < UX_HOST_CLASS_PIMA_REQUEST_STATUS_OFFSET_CODE + sizeof(UINT)) + { + + /* We have a data format error. */ + status = UX_ERROR; + break; + } + + /* Extract the device status. */ + device_status = _ux_utility_short_get(request_payload + UX_HOST_CLASS_PIMA_REQUEST_STATUS_OFFSET_CODE); + + /* If the device status is OK, we have a successful cancellation. */ + if (device_status == UX_HOST_CLASS_PIMA_RC_OK) + { + + /* Status is OK. exit the command loop. */ + status = UX_SUCCESS; + break; + } + else + { + /* Force the status to error. */ + status = UX_ERROR; + + + /* We should wait a little bit before re-issuing the command. */ + _ux_utility_delay_ms(UX_HOST_CLASS_PIMA_REQUEST_STATUS_COMMAND_DELAY); + + } + } + + } + + /* Free allocated resources. */ + _ux_utility_memory_free(request_payload); + + /* Return completion status. */ + return(status); + +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_session_close.c b/common/usbx_host_classes/src/ux_host_class_pima_session_close.c new file mode 100644 index 0000000..8f8ed6a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_session_close.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_session_close PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function closes a session with the PIMA device. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_session_close(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_SESSION_CLOSE, pima, pima_session, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* The transaction ID in the PIMA instance should be reset. */ + pima -> ux_host_class_pima_transaction_id = 0; + + /* Issue command to close the session with the PIMA device. No parameter. */ + command.ux_host_class_pima_command_nb_parameters = 0; + + /* Then set the command to CLOSE_SESSION. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_CLOSE_SESSION; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_1 = 0; + command.ux_host_class_pima_command_parameter_2 = 0; + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, 0 , UX_NULL, 0, 0); + + /* Check the result. If OK, the session was closed properly. */ + if (status == UX_SUCCESS) + { + + /* Reset the PIMA session in the pima instance. */ + pima -> ux_host_class_pima_session = UX_NULL; + + /* Reset the magic field. */ + pima_session -> ux_host_class_pima_session_magic = 0; + + /* Mark the session as closed. */ + pima_session -> ux_host_class_pima_session_state = UX_HOST_CLASS_PIMA_SESSION_STATE_CLOSED; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_session_open.c b/common/usbx_host_classes/src/ux_host_class_pima_session_open.c new file mode 100644 index 0000000..e72a481 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_session_open.c @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_session_open PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens a session with the PIMA device. The session */ +/* is maintained in this state until the session is closed or the */ +/* device is unmounted. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_session_open(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +ULONG status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_SESSION_OPEN, pima, pima_session, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if there is already a session opened. */ + if (pima -> ux_host_class_pima_session != UX_NULL) + return (UX_HOST_CLASS_PIMA_RC_SESSION_ALREADY_OPENED); + + /* The transaction ID in the PIMA instance should be reset. */ + pima -> ux_host_class_pima_transaction_id = 0; + + /* Issue command to open the session with the PIMA device. First set the number of parameters. */ + command.ux_host_class_pima_command_nb_parameters = 1; + + /* Then set the command to OPEN_SESSION. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_OPEN_SESSION; + + /* The session ID is the handle to the session. */ + command.ux_host_class_pima_command_parameter_1 = (ULONG) (ALIGN_TYPE) pima_session; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_2 = 0; + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, 0 , UX_NULL, 0, 0); + + /* Check the result. If OK, the session was opened properly. */ + if (status == UX_SUCCESS) + { + + /* Store the session pointer in the PIMA instance. The PIMA class instance + only supports one opened session at a time at this stage. */ + pima -> ux_host_class_pima_session = pima_session; + + /* Save the session ID in the session container. This is not too useful since + the session ID is the session structure address. */ + pima_session -> ux_host_class_pima_session_id = (ALIGN_TYPE) pima_session; + + /* Put the magic number in the session instance. */ + pima_session -> ux_host_class_pima_session_magic = UX_HOST_CLASS_PIMA_MAGIC_NUMBER; + + /* Mark the session as opened. */ + pima_session -> ux_host_class_pima_session_state = UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_storage_ids_get.c b/common/usbx_host_classes/src/ux_host_class_pima_storage_ids_get.c new file mode 100644 index 0000000..ace2003 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_storage_ids_get.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + +UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH_ASSERT + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_storage_ids_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets a list if the current valid Storage IDS. There */ +/* is one Storage ID for each valid logical store. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_long_get Get 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_storage_ids_get(UX_HOST_CLASS_PIMA *pima, UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG *storage_ids_array, ULONG storage_id_length) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UINT status; +UCHAR *storage_ids; +ULONG count_storage_ids; +ULONG nb_storage_ids; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_STORAGE_IDS_GET, pima, storage_ids_array, storage_id_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to get the storage IDs. No parameter. */ + command.ux_host_class_pima_command_nb_parameters = 0; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_1 = 0; + command.ux_host_class_pima_command_parameter_2 = 0; + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to GET_STORAGE_IDS. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_STORAGE_IDS; + + /* Allocate some DMA safe memory for receiving the IDs. + * UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH_ASSERT checks if calculation of + * UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH is OK. + */ + storage_ids = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH); + if (storage_ids == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_IN , storage_ids, + UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH, UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH); + + /* Check the result. If OK, the storage ID array is returned properly. */ + if (status == UX_SUCCESS) + { + + /* Read the number of Storage IDs in the returned array. */ + nb_storage_ids = _ux_utility_long_get(storage_ids); + + /* Ensure we do read the array beyond the allocated memory. */ + if (((nb_storage_ids + 1) * sizeof(ULONG)) > UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH) + + /* If we get here we should probably increase the value for UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH */ + nb_storage_ids = (UX_HOST_CLASS_PIMA_STORAGE_IDS_LENGTH / sizeof(ULONG)) - 1; + + /* Save the number of storage IDs. */ + pima_session -> ux_host_class_pima_session_nb_storage_ids = nb_storage_ids; + + /* Check if the user gave us enough memory. */ + if ((nb_storage_ids * sizeof(ULONG)) > storage_id_length) + + /* No, not enough memory to store the array. */ + return(UX_MEMORY_INSUFFICIENT); + + /* Unpack all storage IDS. */ + for(count_storage_ids = 0; count_storage_ids < nb_storage_ids; count_storage_ids++) + + /* Unpack one ID at a time */ + *(storage_ids_array + count_storage_ids) = _ux_utility_long_get(storage_ids + sizeof(ULONG) + + (count_storage_ids * sizeof(ULONG))); + } + + /* Free the original storage ID buffer. */ + _ux_utility_memory_free(storage_ids); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_storage_info_get.c b/common/usbx_host_classes/src/ux_host_class_pima_storage_info_get.c new file mode 100644 index 0000000..3f60e0a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_storage_info_get.c @@ -0,0 +1,157 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_storage_info_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the current storage information block. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* storage_id The storage ID */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_pima_command Pima command function */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_memory_free Free memory */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_storage_info_get(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG storage_id, UX_HOST_CLASS_PIMA_STORAGE *storage) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UINT status; +UCHAR *storage_buffer; +UCHAR *storage_pointer; +ULONG unicode_string_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_STORAGE_INFO_GET, pima, storage_id, storage, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Issue command to get the storage IDs. 1 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 1; + + /* Parameter 1 is the Storage ID. */ + command.ux_host_class_pima_command_parameter_1 = storage_id; + + /* Other parameters unused. */ + command.ux_host_class_pima_command_parameter_2 = 0; + command.ux_host_class_pima_command_parameter_3 = 0; + command.ux_host_class_pima_command_parameter_4 = 0; + command.ux_host_class_pima_command_parameter_5 = 0; + + /* Then set the command to GET_STORAGE_INFO. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_STORAGE_INFO; + + /* Allocate some DMA safe memory for receiving the storage info block. */ + storage_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PIMA_STORAGE_MAX_LENGTH); + if (storage == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Issue the command. */ + status = _ux_host_class_pima_command(pima, &command, UX_HOST_CLASS_PIMA_DATA_PHASE_IN , storage_buffer, + UX_HOST_CLASS_PIMA_STORAGE_MAX_LENGTH, UX_HOST_CLASS_PIMA_STORAGE_MAX_LENGTH); + + /* Check the result. If the result is OK, the storage info block was read properly. */ + if (status == UX_SUCCESS) + { + /* Uncompress the storage descriptor, at least the fixed part. */ + _ux_utility_descriptor_parse(storage_buffer, + _ux_system_class_pima_object_structure, + UX_HOST_CLASS_PIMA_OBJECT_ENTRIES, + (UCHAR *) storage); + + /* Copy the storage description field. Point to the beginning of the storage description string. */ + storage_pointer = storage_buffer + UX_HOST_CLASS_PIMA_STORAGE_VARIABLE_OFFSET; + + /* Get the unicode string length. */ + unicode_string_length = (ULONG) *storage_pointer ; + + /* Copy that string into the storage description field. */ + _ux_utility_memory_copy(storage -> ux_host_class_pima_storage_description, storage_pointer, unicode_string_length); + + /* Point to the volume label. */ + storage_pointer = storage_buffer + UX_HOST_CLASS_PIMA_STORAGE_VARIABLE_OFFSET + unicode_string_length; + + /* Get the unicode string length. */ + unicode_string_length = (ULONG) *storage_pointer ; + + /* Copy that string into the storage volume label field. */ + _ux_utility_memory_copy(storage -> ux_host_class_pima_storage_description, storage_pointer, unicode_string_length); + + } + + /* Free the original storage info buffer. */ + _ux_utility_memory_free(storage_buffer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_thumb_get.c b/common/usbx_host_classes/src/ux_host_class_pima_thumb_get.c new file mode 100644 index 0000000..457aa87 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_thumb_get.c @@ -0,0 +1,372 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PIMA Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_thumb_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets a thumb image identified by the object_handle */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* pima_session Pointer to pima session */ +/* object_handle The object handle */ +/* object Pointer to object info */ +/* thumb_buffer Buffer to be used */ +/* thumb_buffer_length Buffer length */ +/* thumb_actual_length Length read in that */ +/* command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_short_put Put 16-bit value */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* USB application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_thumb_get(UX_HOST_CLASS_PIMA *pima, + UX_HOST_CLASS_PIMA_SESSION *pima_session, + ULONG object_handle, UX_HOST_CLASS_PIMA_OBJECT *object, + UCHAR *thumb_buffer, ULONG thumb_buffer_length, + ULONG *thumb_actual_length) +{ + +UX_HOST_CLASS_PIMA_COMMAND command; +UX_TRANSFER *transfer_request; +UCHAR *ptp_payload; +ULONG requested_length; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_THUMB_GET, pima, object_handle, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Check if this session is valid or not. */ + if (pima_session -> ux_host_class_pima_session_magic != UX_HOST_CLASS_PIMA_MAGIC_NUMBER) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if this session is opened or not. */ + if (pima_session -> ux_host_class_pima_session_state != UX_HOST_CLASS_PIMA_SESSION_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_SESSION_NOT_OPEN); + + /* Check if the object is already opened. */ + if (object -> ux_host_class_pima_object_state != UX_HOST_CLASS_PIMA_OBJECT_STATE_OPENED) + return (UX_HOST_CLASS_PIMA_RC_OBJECT_NOT_OPENED); + + /* Check the transfer status. If there was an error or transfer is completed, refuse transfer. */ + if ((object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED) || + (object -> ux_host_class_pima_object_transfer_status == UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED)) + return (UX_HOST_CLASS_PIMA_RC_ACCESS_DENIED); + + + /* Reset the actual length. */ + *thumb_actual_length = 0; + + /* This variable will remain untouched if the offset is not 0 and the requested length is 0. */ + status = UX_SUCCESS; + + /* Check if the offset to be read is at 0, if so, prepare the first read command. */ + if (object -> ux_host_class_pima_object_offset == 0) + { + + /* Set the object transfer status to active. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ACTIVE; + + /* Issue command to get the object info. 1 parameter. */ + command.ux_host_class_pima_command_nb_parameters = 1; + + /* Parameter 1 is the Object Handle. */ + command.ux_host_class_pima_command_parameter_1 = object_handle; + + /* Then set the command to GET_OBJECT. */ + command.ux_host_class_pima_command_operation_code = UX_HOST_CLASS_PIMA_OC_GET_THUMB; + + /* We use the Bulk Out pipe for sending data out.. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_COMMAND_HEADER_SIZE + ((ULONG)sizeof(ULONG) * command.ux_host_class_pima_command_nb_parameters); + + /* Fill the command container. First the length of the total header and payload. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_LENGTH, requested_length); + + /* Then the type of container : a command block here. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TYPE, UX_HOST_CLASS_PIMA_CT_COMMAND_BLOCK); + + /* Now the command code to send. */ + _ux_utility_short_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_CODE, (USHORT)command.ux_host_class_pima_command_operation_code); + + /* Put the transaction ID. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID, + pima -> ux_host_class_pima_transaction_id++); + + /* Then fill in the parameters. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1, + command.ux_host_class_pima_command_parameter_1); + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the thumb transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Check for completion of transfer. If the transfer is partial, return to caller. + Partial transfer is not OK. */ + if (requested_length == transfer_request -> ux_transfer_request_actual_length) + { + + /* Obtain the first packet. This packet contains the header and some data. + We use the Bulk In pipe for receiving data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Calculate the requested length for this payload. */ + requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the thumb transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Ensure transfer length is greater than the size of the header. */ + if (transfer_request -> ux_transfer_request_actual_length <= UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) + + /* We have a malformed packet. Return error. */ + return(UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR); + + /* Ensure there's enough room in the application's buffer. */ + if (thumb_buffer_length < transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) + + /* Not enough room. Return error. */ + return(UX_MEMORY_INSUFFICIENT); + + /* We need to skip the header. Copying the necessary partial memory only. */ + _ux_utility_memory_copy(thumb_buffer, ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE, + transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Update the actual length. */ + *thumb_actual_length = transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* And the offset. */ + object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + } + else + + /* We got a premature error. */ + return(UX_HOST_CLASS_PIMA_RC_INCOMPLETE_TRANSFER); + } + + else + { + + /* We use the Bulk In pipe for receiving data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* We read a complete block. */ + while(thumb_buffer_length != 0) + { + + /* It may take several transactions. */ + if (thumb_buffer_length > UX_HOST_CLASS_PIMA_MAX_PAYLOAD) + + /* Set the requested length to the payload maximum. */ + requested_length = UX_HOST_CLASS_PIMA_MAX_PAYLOAD; + + else + + /* We can use the user supplied length to complete this request. */ + requested_length = thumb_buffer_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = thumb_buffer; + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* Set the thumb transfer status to aborted. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_ABORTED; + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the object length expected by the user with what we actually received. */ + thumb_buffer_length -= transfer_request -> ux_transfer_request_actual_length; + + /* Update the actual length. */ + *thumb_actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* And the offset. */ + object -> ux_host_class_pima_object_offset += transfer_request -> ux_transfer_request_actual_length; + + /* Check to see if we are at the end of the object. */ + if (object -> ux_host_class_pima_object_offset == object -> ux_host_class_pima_object_thumb_compressed_size) + + /* The transfer for this transaction is completed. */ + object -> ux_host_class_pima_object_transfer_status = UX_HOST_CLASS_PIMA_OBJECT_TRANSFER_STATUS_COMPLETED; + + } + + } + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_pima_write.c b/common/usbx_host_classes/src/ux_host_class_pima_write.c new file mode 100644 index 0000000..2c464d8 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_pima_write.c @@ -0,0 +1,297 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Pima Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_pima.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_pima_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a data payload to the Pima device. This */ +/* function first write a header followed by some data. */ +/* */ +/* INPUT */ +/* */ +/* pima Pointer to pima class */ +/* data_pointer Pointer to data to write */ +/* data_length Length of data to write */ +/* operation_code Code to send in header (this */ +/* is from the command block */ +/* max_payload_data Maximum data sent in one load */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* _ux_utility_memory_copy Copy memory */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_long_put Put a long 32 bit value */ +/* _ux_utility_short_put Put a short 16 bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_pima_write(UX_HOST_CLASS_PIMA *pima, UCHAR *data_pointer, + ULONG data_length, + ULONG operation_code, + ULONG max_payload_length) + +{ + +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *ptp_payload; +ULONG requested_length; +ULONG payload_length; + + UX_PARAMETER_NOT_USED(operation_code); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PIMA_WRITE, pima, data_pointer, data_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We use the Bulk Out pipe for receiving data .. */ + transfer_request = &pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Get the pointer to the ptp payload. */ + ptp_payload = pima -> ux_host_class_pima_container ; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = ptp_payload; + + /* Fill in the header values. Start with the length of the container. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_LENGTH, data_length + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Check for remainder in last packet. */ + if (((data_length + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE) % pima -> ux_host_class_pima_bulk_out_endpoint -> ux_endpoint_descriptor.wMaxPacketSize) == 0) + + /* We have a ZLP condition on a OUT. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_OUT; + else + + /* Do not expect a ZLP. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + + /* Container type is a data type. */ + *(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_TYPE) = UX_HOST_CLASS_PIMA_CT_DATA_BLOCK; + + /* Put the operation code. */ + *(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_CODE) = (UCHAR)pima -> ux_host_class_pima_operation_code; + + /* Store the transaction ID. */ + _ux_utility_long_put(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_TRANSACTION_ID, + pima -> ux_host_class_pima_transaction_id); + + /* Calculate the requested length in the first container. */ + requested_length = data_length + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE; + + /* Can we fill the container ? */ + if(requested_length > UX_HOST_CLASS_PIMA_CONTAINER_SIZE) + + /* We have more data in the payload than the first container so adjust the length. */ + requested_length = UX_HOST_CLASS_PIMA_CONTAINER_SIZE; + + /* Add the data payload to fill that container. */ + _ux_utility_memory_copy(ptp_payload + UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE, data_pointer, + requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Initialize the transfer of that container. */ + transfer_request -> ux_transfer_request_requested_length = requested_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Isolate the length of the data payload that still needs to be sent. */ + data_length -= (requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Adjust the data payload pointer. */ + data_pointer += (requested_length - UX_HOST_CLASS_PIMA_DATA_HEADER_SIZE); + + /* Now we can send the data to the device. */ + while(data_length) + { + + /* Check if need to split the data payload into smaller packets. */ + if (data_length > max_payload_length) + + /* We cannot send everything in this payload. */ + payload_length = max_payload_length; + else + + /* Either this is the last packet or we we have a small packet to send. */ + payload_length = data_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = payload_length; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* There was an error, return to the caller. */ + return(status); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Check for completion of transfer. If the transfer is partial, return to caller. + Partial transfer is not OK. */ + if (payload_length != transfer_request -> ux_transfer_request_actual_length) + return(UX_TRANSFER_ERROR); + + /* Adjust the total length to transfer. */ + data_length -= payload_length; + + /* Adjust the data pointer. */ + data_pointer += payload_length; + + } + + /* If we have a ZLP condition, write the device one more time with a zero packet. */ + if (pima -> ux_host_class_pima_zlp_flag == UX_HOST_CLASS_PIMA_ZLP_OUT) + { + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PIMA_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_in_endpoint); + + /* The endpoint was halted by a transfer error and needs to be reset. */ + _ux_host_stack_endpoint_reset(pima -> ux_host_class_pima_bulk_out_endpoint); + + /* There was an error, return to the caller. */ + return(status); + } + + /* Reset the ZLP. */ + pima -> ux_host_class_pima_zlp_flag = UX_HOST_CLASS_PIMA_ZLP_NONE; + } + } + + /* We have finished receiving the data. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_activate.c b/common/usbx_host_classes/src/ux_host_class_printer_activate.c new file mode 100644 index 0000000..2cc8787 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_activate.c @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to activate the class. */ +/* */ +/* INPUT */ +/* */ +/* command Printer class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_printer_configure Configure printer class */ +/* _ux_host_class_printer_endpoints_get Get endpoints of printer */ +/* _ux_host_class_printer_name_get Get printer name */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create printer semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_printer_entry Entry of printer class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_PRINTER *printer; +UINT status; + + + /* The printer is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + printer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_PRINTER)); + if (printer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + printer -> ux_host_class_printer_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the printer class instance. */ + printer -> ux_host_class_printer_interface = interface; + + /* Store the device container into the printer class instance. */ + printer -> ux_host_class_printer_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) printer; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(printer -> ux_host_class_printer_class, (VOID *) printer); + + /* Configure the printer. */ + status = _ux_host_class_printer_configure(printer); + + /* Get the name of the printer from the 1284 descriptor. */ + if (status == UX_SUCCESS) + status = _ux_host_class_printer_name_get(printer); + + /* Get the printer endpoint(s). We may need to search for Bulk Out and Bulk In endpoints. */ + if (status == UX_SUCCESS) + status = _ux_host_class_printer_endpoints_get(printer); + + /* Create the semaphore to protect 2 threads from accessing the same printer instance. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&printer -> ux_host_class_printer_semaphore, "ux_host_class_printer_semaphore", 1); + if (status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Success things. */ + if (status == UX_SUCCESS) + { + + /* Mark the printer as live now. */ + printer -> ux_host_class_printer_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, printer -> ux_host_class_printer_class, (VOID *) printer); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PRINTER_ACTIVATE, printer, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, printer, 0, 0, 0) + + /* Return success. */ + return(UX_SUCCESS); + } + + /* On error, free resources. */ + _ux_host_stack_class_instance_destroy(printer -> ux_host_class_printer_class, (VOID *) printer); + interface -> ux_interface_class_instance = UX_NULL; + _ux_utility_memory_free(printer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_configure.c b/common/usbx_host_classes/src/ux_host_class_printer_configure.c new file mode 100644 index 0000000..00b7d1d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_configure.c @@ -0,0 +1,145 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* printer. Once the printer is configured, its interface will be */ +/* activated. The bulk endpoints enumerated(1 IN, 1 OUT ). */ +/* */ +/* INPUT */ +/* */ +/* printer Pointer to printer class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_printer_activate Printer class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_configure(UX_HOST_CLASS_PRINTER *printer) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (printer -> ux_host_class_printer_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A printer normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(printer -> ux_host_class_printer_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, printer -> ux_host_class_printer_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the printer power source and check the parent power source for + incompatible connections. */ + if (printer -> ux_host_class_printer_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = printer -> ux_host_class_printer_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root printer and we don't have to worry + if the parent is not the root printer, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, printer, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the printer default alternate setting for the printer interface is + active and the interrupt endpoint is now enabled. We have to memorize the first interface since + the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &printer -> ux_host_class_printer_interface); + if (status != UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + printer -> ux_host_class_printer_interface -> ux_interface_class_instance = (VOID *) printer; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_deactivate.c b/common/usbx_host_classes/src/ux_host_class_printer_deactivate.c new file mode 100644 index 0000000..1327c57 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_deactivate.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the printer has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* pipes will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Printer class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_printer_entry Entry of printer class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_PRINTER *printer; +UINT status; + + + /* Get the instance for this class. */ + printer = (UX_HOST_CLASS_PRINTER *) command -> ux_host_class_command_instance; + + /* The printer is being shut down. */ + printer -> ux_host_class_printer_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&printer -> ux_host_class_printer_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We need to abort transactions on the bulk out pipe. */ + _ux_host_stack_endpoint_transfer_abort(printer -> ux_host_class_printer_bulk_out_endpoint); + + /* If the printer is bidirectional, we need to abort transactions on the bulk in pipe. */ + if (printer -> ux_host_class_printer_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_PRINTER_PROTOCOL_BI_DIRECTIONAL) + _ux_host_stack_endpoint_transfer_abort(printer -> ux_host_class_printer_bulk_in_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(printer -> ux_host_class_printer_class, (VOID *) printer); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&printer -> ux_host_class_printer_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, printer -> ux_host_class_printer_class, (VOID *) printer); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PRINTER_DEACTIVATE, printer, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(printer); + + /* Free the printer instance memory. */ + _ux_utility_memory_free(printer); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_printer_endpoints_get.c new file mode 100644 index 0000000..6e86543 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_endpoints_get.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function search for the handle of the bulk out endpoint and */ +/* optionally the bulk in endpoint of the printer is bidirectional. */ +/* */ +/* INPUT */ +/* */ +/* printer Pointer to printer class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_printer_activate Activate printer class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_endpoints_get(UX_HOST_CLASS_PRINTER *printer) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; + + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < printer -> ux_host_class_printer_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(printer -> ux_host_class_printer_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* We have found the bulk endpoint, save it. */ + printer -> ux_host_class_printer_bulk_out_endpoint = endpoint; + break; + } + } + } + + /* The bulk out endpoint is mandatory. */ + if (printer -> ux_host_class_printer_bulk_out_endpoint == UX_NULL) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, printer, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. This endpoint is optional and only valid for + bidirectional printers. It is attached to the interface container. */ + if (printer -> ux_host_class_printer_interface -> ux_interface_descriptor.bInterfaceProtocol == + UX_HOST_CLASS_PRINTER_PROTOCOL_BI_DIRECTIONAL) + { + + for (endpoint_index = 0; endpoint_index < printer -> ux_host_class_printer_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(printer -> ux_host_class_printer_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the bulk endpoint, save it. */ + printer -> ux_host_class_printer_bulk_in_endpoint = endpoint; + break; + } + } + } + + /* The bulk in endpoint is not mandatory. */ + if ((printer -> ux_host_class_printer_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_PRINTER_PROTOCOL_BI_DIRECTIONAL) + && (printer -> ux_host_class_printer_bulk_in_endpoint == UX_NULL)) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, printer, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + } + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_entry.c b/common/usbx_host_classes/src/ux_host_class_printer_entry.c new file mode 100644 index 0000000..ee7fa18 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_entry.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the printer class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* printer on the bus or when the USB printer is removed. */ +/* */ +/* INPUT */ +/* */ +/* command Printer class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_printer_activate Activate printer class */ +/* _ux_host_class_printer_deactivate Deactivate printer class */ +/* */ +/* CALLED BY */ +/* */ +/* Printer Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + (command -> ux_host_class_command_class == UX_HOST_CLASS_PRINTER_CLASS)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_printer_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_printer_deactivate(command); + return(status); + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_name_get.c b/common/usbx_host_classes/src/ux_host_class_printer_name_get.c new file mode 100644 index 0000000..74d2e7a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_name_get.c @@ -0,0 +1,197 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_name_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the printer name. The name is used by the */ +/* application layer to identify the printer and loads its handler. */ +/* */ +/* INPUT */ +/* */ +/* printer Pointer to printer class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_compare Compare memory block */ +/* _ux_utility_memory_copy Copy memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_short_get_big_endian Get 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_name_get(UX_HOST_CLASS_PRINTER *printer) +{ + +UCHAR * descriptor_buffer; +UCHAR * printer_name_buffer; +UCHAR * printer_name; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +USHORT descriptor_length; +UINT printer_name_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PRINTER_NAME_GET, printer, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &printer -> ux_host_class_printer_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the 1284 descriptor. */ + descriptor_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PRINTER_DESCRIPTOR_LENGTH); + if(descriptor_buffer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer request for the GET_DEVICE_ID request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_PRINTER_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PRINTER_GET_DEVICE_ID; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer. We do not check for entire length as it may vary. */ + if(status == UX_SUCCESS) + { + + /* Cannot use descriptor buffer, so copy it to 1284 buffer. */ + printer_name_buffer = descriptor_buffer; + + /* Retrieve the printer name which is a USHORT at the beginning of the returned buffer. */ + descriptor_length = (USHORT)_ux_utility_short_get_big_endian(printer_name_buffer); + + /* Point the name buffer after the length. */ + printer_name_buffer += 2; + + /* Parse the name for a tag which is in the form DES: or DESCRIPTOR: */ + while (descriptor_length != 0) + { + + if (descriptor_length > 12) + { + + /* Compare the current pointer position with the DESCRIPTOR: tag. */ + if (_ux_utility_memory_compare(printer_name_buffer, UX_HOST_CLASS_PRINTER_TAG_DESCRIPTION, 12) == UX_SUCCESS) + { + + printer_name_buffer += 12; + descriptor_length = (USHORT)(descriptor_length - 12); + break; + } + } + + if (descriptor_length > 4) + { + + /* Compare the current pointer position with the DES: tag. */ + if (_ux_utility_memory_compare(printer_name_buffer, UX_HOST_CLASS_PRINTER_TAG_DES, 4) == UX_SUCCESS) + { + + printer_name_buffer += 4; + descriptor_length = (USHORT)(descriptor_length - 4); + break; + } + } + + /* And reduce the remaining length by 1. */ + descriptor_length--; + + /* Increment the descriptor pointer. */ + printer_name_buffer++; + } + + /* If the length remaining is 0, we have not found the descriptor tag we wanted. */ + if (descriptor_length == 0) + { + + /* Use the generic USB printer name. */ + _ux_utility_memory_copy(printer -> ux_host_class_printer_name, UX_HOST_CLASS_PRINTER_GENERIC_NAME, 11); + } + else + { + + /* We have found a tag and the name is right after delimited by ; or a 0. */ + printer_name = printer -> ux_host_class_printer_name; + printer_name_length = UX_HOST_CLASS_PRINTER_NAME_LENGTH; + + /* Parse the name and only copy the name until the max length is reached + or the delimiter is reached. */ + while ((descriptor_length--) && (printer_name_length--)) + { + + /* Check for the delimiter. */ + if((*printer_name_buffer == 0) || (*printer_name_buffer == ';')) + break; + else + /* Copy the name of the printer to the printer instance character + by character. */ + *printer_name++ = *printer_name_buffer++; + } + } + } + + /* Free all used resources. */ + _ux_utility_memory_free(descriptor_buffer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_read.c b/common/usbx_host_classes/src/ux_host_class_printer_read.c new file mode 100644 index 0000000..42a1076 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_read.c @@ -0,0 +1,227 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the printer interface. The call is */ +/* blocking and only returns when there is either an error or when */ +/* the transfer is complete. */ +/* */ +/* A read is only allowed on bidirectional printers. */ +/* */ +/* INPUT */ +/* */ +/* printer Pointer to printer class */ +/* data_pointer Pointer to buffer */ +/* requested_length Requested data read */ +/* actual_length Actual data read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_read (UX_HOST_CLASS_PRINTER *printer, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PRINTER_READ, printer, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (printer -> ux_host_class_printer_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, printer, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&printer -> ux_host_class_printer_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* Check if the printer is bidirectional. */ + if (printer -> ux_host_class_printer_interface -> ux_interface_descriptor.bInterfaceProtocol != + UX_HOST_CLASS_PRINTER_PROTOCOL_BI_DIRECTIONAL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } + + /* Start by resetting the actual length of the transfer to zero. */ + *actual_length = 0; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &printer -> ux_host_class_printer_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk in endpoint until either the transfer is + completed or until there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer_request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PRINTER_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer request in case some data was actually received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* There was a non transfer error, no partial transfer to be checked. */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually received and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to receive. */ + requested_length -= transfer_request_length; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_soft_reset.c b/common/usbx_host_classes/src/ux_host_class_printer_soft_reset.c new file mode 100644 index 0000000..450ce8d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_soft_reset.c @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_soft_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a soft reset of the printer in case the */ +/* printer gets into an error mode. */ +/* */ +/* INPUT */ +/* */ +/* printer Pointer to printer class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_soft_reset(UX_HOST_CLASS_PRINTER *printer) +{ + + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PRINTER_SOFT_RESET, printer, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (printer -> ux_host_class_printer_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, printer, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&printer -> ux_host_class_printer_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &printer -> ux_host_class_printer_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Create a transfer_request for the SOFT_RESET request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PRINTER_SOFT_RESET; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_status_get.c b/common/usbx_host_classes/src/ux_host_class_printer_status_get.c new file mode 100644 index 0000000..da8f071 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_status_get.c @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_status_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the printer status. The printer status is */ +/* similar to the LPT status (1284 standard). */ +/* */ +/* INPUT */ +/* */ +/* printer Pointer to printer class */ +/* printer_status Pointer to return status */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_long_get Get 32-bit long word */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_status_get(UX_HOST_CLASS_PRINTER *printer, ULONG *printer_status) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * printer_status_buffer; + + /* Ensure the instance is valid. */ + if (printer -> ux_host_class_printer_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, printer, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&printer -> ux_host_class_printer_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We need to get the default control endpoint transfer_request pointer. */ + control_endpoint = &printer -> ux_host_class_printer_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. */ + printer_status_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PRINTER_STATUS_LENGTH); + if (printer_status_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + return(UX_MEMORY_INSUFFICIENT); + } + + /* Create a transfer_request for the GET_STATUS request. */ + transfer_request -> ux_transfer_request_data_pointer = printer_status_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_PRINTER_STATUS_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PRINTER_GET_STATUS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && + (transfer_request -> ux_transfer_request_actual_length > 0) && + (transfer_request -> ux_transfer_request_actual_length <= UX_HOST_CLASS_PRINTER_STATUS_LENGTH)) + { + + /* Retrieve the printer status and translate it locally for endianness. */ + *printer_status = printer_status_buffer[0]; + if (transfer_request -> ux_transfer_request_actual_length > 1) + *printer_status |= (ULONG)(printer_status_buffer[1] << 8); + if (transfer_request -> ux_transfer_request_actual_length > 2) + *printer_status |= (ULONG)(printer_status_buffer[2] << 16); + if (transfer_request -> ux_transfer_request_actual_length > 3) + *printer_status |= (ULONG)(printer_status_buffer[3] << 24); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PRINTER_STATUS_GET, printer, *printer_status, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + } + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); + } + + /* Free all used resources. */ + _ux_utility_memory_free(printer_status_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_printer_write.c b/common/usbx_host_classes/src/ux_host_class_printer_write.c new file mode 100644 index 0000000..8fb6c6f --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_printer_write.c @@ -0,0 +1,206 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Printer Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_printer.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_printer_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the printer interface. The call is blocking */ +/* and only returns when there is either an error or when the transfer */ +/* is complete. */ +/* */ +/* INPUT */ +/* */ +/* printer Pointer to printer class */ +/* data_pointer Pointer to data to write */ +/* requested_length Length of data to write */ +/* actual_length Actual length of data written */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_printer_write(UX_HOST_CLASS_PRINTER *printer, UCHAR * data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PRINTER_WRITE, printer, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (printer -> ux_host_class_printer_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, printer, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&printer -> ux_host_class_printer_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* Start by resetting the actual length of the transfer. */ + *actual_length = 0; + + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &printer -> ux_host_class_printer_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk out endpoint until either the transfer is + completed or when there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer_request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PRINTER_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer_request in case some data actually went out. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be sent. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually sent and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to send out. */ + requested_length -= transfer_request_length; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&printer -> ux_host_class_printer_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_activate.c b/common/usbx_host_classes/src/ux_host_class_prolific_activate.c new file mode 100644 index 0000000..44883af --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_activate.c @@ -0,0 +1,192 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the prolific instance, configure the device. */ +/* */ +/* INPUT */ +/* */ +/* command DLC class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_prolific_configure Configure prolific class */ +/* _ux_host_class_prolific_endpoints_get Get endpoints of prolific */ +/* _ux_host_class_prolific_setup Set up prolific device */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create prolific semaphore */ +/* _ux_host_class_prolific_ioctl IOCTL function for DLC */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_prolific_entry Entry of prolific class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_DEVICE *device; +UX_HOST_CLASS_PROLIFIC *prolific; +UX_HOST_CLASS_PROLIFIC_LINE_CODING line_coding; +UX_HOST_CLASS_PROLIFIC_LINE_STATE line_state; +UINT status; + + /* The prolific class is always activated by the device descriptor. */ + device = (UX_DEVICE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + prolific = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, sizeof(UX_HOST_CLASS_PROLIFIC)); + if (prolific == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + prolific -> ux_host_class_prolific_class = command -> ux_host_class_command_class_ptr; + + /* Store the device container into the prolific class instance. */ + prolific -> ux_host_class_prolific_device = device; + + /* Store the instance in the device container, this is for the USBX stack + when it needs to invoke the class for deactivation. */ + device -> ux_device_class_instance = (VOID *) prolific; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(prolific -> ux_host_class_prolific_class, (VOID *) prolific); + + /* Configure the prolific. */ + status = _ux_host_class_prolific_configure(prolific); + + /* Get the prolific endpoint(s). We need to search for Bulk Out and Bulk In endpoints + and the interrupt endpoint. */ + if (status == UX_SUCCESS) + status = _ux_host_class_prolific_endpoints_get(prolific); + + /* Go on if success. */ + if (status == UX_SUCCESS) + { + + /* Store chip version for further reference. */ + prolific -> ux_host_class_prolific_version = device -> ux_device_descriptor.bcdDevice; + + /* Mark the prolific instance as mounting now. */ + prolific -> ux_host_class_prolific_state = UX_HOST_CLASS_INSTANCE_MOUNTING; + + /* The prolific chip needs to be setup properly. */ + status = _ux_host_class_prolific_setup(prolific); + } + + /* Set the default values to the device, first line coding. */ + if (status == UX_SUCCESS) + { + line_coding.ux_host_class_prolific_line_coding_dter = UX_HOST_CLASS_PROLIFIC_LINE_CODING_DEFAULT_RATE; + line_coding.ux_host_class_prolific_line_coding_stop_bit = UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT_0; + line_coding.ux_host_class_prolific_line_coding_parity = UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY_NONE; + line_coding.ux_host_class_prolific_line_coding_data_bits = UX_HOST_CLASS_PROLIFIC_LINE_CODING_DEFAULT_DATA_BIT; + status = _ux_host_class_prolific_ioctl(prolific, UX_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_CODING, (VOID *) &line_coding); + } + + /* Set the default values to the device, line state. For the Prolific chip to detect disconnection + and reconnection, RTS is low. */ + if (status == UX_SUCCESS) + { + line_state.ux_host_class_prolific_line_state_rts = 0; + line_state.ux_host_class_prolific_line_state_dtr = 1; + status = _ux_host_class_prolific_ioctl(prolific, UX_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_STATE, (VOID *) &line_state); + } + + /* Create the semaphore to protect 2 threads from accessing the same prolific instance. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&prolific -> ux_host_class_prolific_semaphore, "ux_host_class_prolific_semaphore", 1); + if (status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Success things. */ + if (status == UX_SUCCESS) + { + + /* Mark the prolific instance as live now. */ + prolific -> ux_host_class_prolific_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, prolific -> ux_host_class_prolific_class, (VOID *) prolific); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_ACTIVATE, prolific, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, prolific, 0, 0, 0) + + /* Return success. */ + return(UX_SUCCESS); + } + + /* There was a problem during the configuration, so free the resources. */ + /* The last resource, semaphore is not created or created error, no need to free. */ + _ux_host_stack_class_instance_destroy(prolific -> ux_host_class_prolific_class, (VOID *) prolific); + device -> ux_device_class_instance = UX_NULL; + _ux_utility_memory_free(prolific); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_command.c b/common/usbx_host_classes/src/ux_host_class_prolific_command.c new file mode 100644 index 0000000..8d8551f --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_command.c @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_command PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a command to the Prolific device. */ +/* The command can be one of the following : */ +/* SET_CONTROL */ +/* SET_LINE */ +/* SEND_BREAK */ +/* */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* command command value */ +/* value value to be sent in the */ +/* command request */ +/* data_buffer buffer to be sent */ +/* data_length length of the buffer to send */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_command(UX_HOST_CLASS_PROLIFIC *prolific, ULONG command, + ULONG value, UCHAR *data_buffer, ULONG data_length) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &prolific -> ux_host_class_prolific_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&prolific -> ux_host_class_prolific_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer_request for the request. */ + transfer_request -> ux_transfer_request_data_pointer = data_buffer; + transfer_request -> ux_transfer_request_requested_length = data_length; + transfer_request -> ux_transfer_request_function = command; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = value; + transfer_request -> ux_transfer_request_index = prolific -> ux_host_class_prolific_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_configure.c b/common/usbx_host_classes/src/ux_host_class_prolific_configure.c new file mode 100644 index 0000000..576aac0 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_configure.c @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* prolific. Once the prolific is configured, its interface will be */ +/* activated. The bulk endpoints (1 IN, 1 OUT ) and the optional */ +/* interrupt endpoint are enumerated. */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_prolific_activate Prolific class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_configure(UX_HOST_CLASS_PROLIFIC *prolific) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (prolific -> ux_host_class_prolific_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A prolific normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(prolific -> ux_host_class_prolific_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, prolific -> ux_host_class_prolific_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the prolific power source and check the parent power source for + incompatible connections. */ + if (prolific -> ux_host_class_prolific_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = prolific -> ux_host_class_prolific_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root prolific and we don't have to worry + if the parent is not the root prolific, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the prolific default alternate setting for the prolific interface is + active and the interrupt endpoint is now enabled. We have to memorize the first interface since + the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &prolific -> ux_host_class_prolific_interface); + + /* Check status for error. */ + if (status == UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + prolific -> ux_host_class_prolific_interface -> ux_interface_class_instance = (VOID *) prolific; + + /* Store the class container in the interface. The device has the correct class, duplicate it to the + interface. */ + prolific -> ux_host_class_prolific_interface -> ux_interface_class = prolific -> ux_host_class_prolific_device -> ux_device_class ; + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_deactivate.c b/common/usbx_host_classes/src/ux_host_class_prolific_deactivate.c new file mode 100644 index 0000000..9b168a1 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_deactivate.c @@ -0,0 +1,163 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the prolific has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* and optional interrupt pipes will be destroyed and the instance */ +/* removed. */ +/* */ +/* INPUT */ +/* */ +/* command Prolific class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_prolific_entry Entry of prolific class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_PROLIFIC *prolific; +UX_TRANSFER *transfer_request; +UINT status; + + + /* Get the instance for this class. */ + prolific = (UX_HOST_CLASS_PROLIFIC *) command -> ux_host_class_command_instance; + + /* The prolific is being shut down. */ + prolific -> ux_host_class_prolific_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&prolific -> ux_host_class_prolific_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* If the interrupt endpoint is defined, clean any pending transfer. */ + if (prolific -> ux_host_class_prolific_interrupt_endpoint != UX_NULL) + { + + /* Wait for any current transfer to be out of pending. */ + transfer_request = &prolific -> ux_host_class_prolific_interrupt_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* And abort any transfer. */ + _ux_host_stack_endpoint_transfer_abort(prolific -> ux_host_class_prolific_interrupt_endpoint); + + /* And free the memory used by the interrupt endpoint. */ + _ux_utility_memory_free(transfer_request -> ux_transfer_request_data_pointer); + + } + + + /* First we take care of cleaning endpoint IN. */ + transfer_request = &prolific -> ux_host_class_prolific_bulk_in_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(prolific -> ux_host_class_prolific_bulk_in_endpoint); + + + /* Then endpoint OUT. */ + transfer_request = &prolific -> ux_host_class_prolific_bulk_out_endpoint -> ux_endpoint_transfer_request; + if (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_PENDING) + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(prolific -> ux_host_class_prolific_bulk_out_endpoint); + + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(prolific -> ux_host_class_prolific_class, (VOID *) prolific); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&prolific -> ux_host_class_prolific_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, prolific -> ux_host_class_prolific_class, (VOID *) prolific); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_DEACTIVATE, prolific, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(prolific); + + /* Free the prolific instance memory. */ + _ux_utility_memory_free(prolific); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_prolific_endpoints_get.c new file mode 100644 index 0000000..f4d6463 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_endpoints_get.c @@ -0,0 +1,248 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function distinguishes for either the Data or Control Class. */ +/* For the data class, we mount the bulk in and bulk out endpoints. */ +/* For the control class, we mount the optional interrupt endpoint. */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* _ux_utility_memory_allocate Allocate memory */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_prolific_activate Activate prolific class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_endpoints_get(UX_HOST_CLASS_PROLIFIC *prolific) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; +UX_TRANSFER *transfer_request; + + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < prolific -> ux_host_class_prolific_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(prolific -> ux_host_class_prolific_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* We have found the bulk endpoint, save it. */ + prolific -> ux_host_class_prolific_bulk_out_endpoint = endpoint; + break; + } + } + } + + /* The bulk out endpoint is mandatory. */ + if (prolific -> ux_host_class_prolific_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < prolific -> ux_host_class_prolific_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(prolific -> ux_host_class_prolific_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the bulk endpoint, save it. */ + prolific -> ux_host_class_prolific_bulk_in_endpoint = endpoint; + break; + } + } + } + + /* The bulk in endpoint is mandatory. */ + if (prolific -> ux_host_class_prolific_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the Interrupt endpoint. It is mandatory. */ + for (endpoint_index = 0; endpoint_index < prolific -> ux_host_class_prolific_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(prolific -> ux_host_class_prolific_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is Interrupt and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the interrupt endpoint, save it. */ + prolific -> ux_host_class_prolific_interrupt_endpoint = endpoint; + + /* The endpoint is correct, Fill in the transfer request with the length requested for this endpoint. */ + transfer_request = &prolific -> ux_host_class_prolific_interrupt_endpoint -> ux_endpoint_transfer_request; + transfer_request -> ux_transfer_request_requested_length = prolific -> ux_host_class_prolific_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + transfer_request -> ux_transfer_request_actual_length = 0; + + /* The direction is always IN for the CDC interrupt endpoint. */ + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN; + + /* There is a callback function associated with the transfer request, so we need the class instance. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) prolific; + + /* Interrupt transactions have a completion routine. */ + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_prolific_transfer_request_completed; + + /* Obtain a buffer for this transaction. The buffer will always be reused. */ + transfer_request -> ux_transfer_request_data_pointer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + transfer_request -> ux_transfer_request_requested_length); + + /* If the endpoint is available and we have memory, we start the interrupt endpoint. */ + if (transfer_request -> ux_transfer_request_data_pointer != UX_NULL) + { + + /* The transfer on the interrupt endpoint can be started. */ + status = _ux_host_stack_transfer_request(transfer_request); + + } + + else + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We must return an error. */ + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + break; + } + } + } + + /* The interrupt endpoint is mandatory. */ + if (prolific -> ux_host_class_prolific_interrupt_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + else + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_entry.c b/common/usbx_host_classes/src/ux_host_class_prolific_entry.c new file mode 100644 index 0000000..71e72d9 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_entry.c @@ -0,0 +1,118 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the prolific class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* prolific on the bus or when the USB prolific is removed. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* command Prolific class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_prolific_activate Activate prolific class */ +/* _ux_host_class_prolific_deactivate Deactivate prolific class */ +/* */ +/* CALLED BY */ +/* */ +/* Acm Cdc Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if(((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_PIDVID) && + (command -> ux_host_class_command_pid == 0x2303) && + (command -> ux_host_class_command_vid == 0x67b))) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_prolific_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_prolific_deactivate(command); + return(status); + + default: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_ioctl.c b/common/usbx_host_classes/src/ux_host_class_prolific_ioctl.c new file mode 100644 index 0000000..6bcdb94 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_ioctl.c @@ -0,0 +1,332 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the ioctl entry point for the application to */ +/* configure the Prolific device. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* ioctl_function ioctl function */ +/* parameter pointer to structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* _ux_host_class_prolific_command Send command to device */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_long_put Put 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_ioctl(UX_HOST_CLASS_PROLIFIC *prolific, ULONG ioctl_function, + VOID *parameter) +{ + +UINT status; +UCHAR *data_buffer; +UX_HOST_CLASS_PROLIFIC_LINE_CODING *line_coding; +UX_HOST_CLASS_PROLIFIC_LINE_STATE *line_state; +ULONG value; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +VOID (*callback_function) (struct UX_HOST_CLASS_PROLIFIC_STRUCT *, ULONG ); + + /* Ensure the instance is valid. */ + if ((prolific -> ux_host_class_prolific_state != UX_HOST_CLASS_INSTANCE_LIVE) && + (prolific -> ux_host_class_prolific_state != UX_HOST_CLASS_INSTANCE_MOUNTING)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* The command request will tell us we need to do here. */ + switch (ioctl_function) + { + + case UX_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_CODING: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_CODING, prolific, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Allocate some cache safe memory for the control command. */ + data_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PROLIFIC_LINE_CODING_LENGTH); + + /* Check if error. Return with error if no memory could be allocated. */ + if (data_buffer == UX_NULL) + + /* Do not proceed. Set error code. */ + status = UX_MEMORY_INSUFFICIENT; + else + { + + /* Build the buffer from the calling parameter. Cast the calling parameter. */ + line_coding = (UX_HOST_CLASS_PROLIFIC_LINE_CODING *) parameter; + + /* Put the data rate. */ + _ux_utility_long_put(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_RATE, + line_coding -> ux_host_class_prolific_line_coding_dter); + + /* Then the stop bit. */ + *(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT) = + (UCHAR) line_coding -> ux_host_class_prolific_line_coding_stop_bit; + + /* Then the parity. */ + *(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY) = + (UCHAR) line_coding -> ux_host_class_prolific_line_coding_parity; + + /* Finally the data bits. */ + *(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_DATA_BIT) = + (UCHAR) line_coding -> ux_host_class_prolific_line_coding_data_bits; + + /* Send the command to the device. */ + status = _ux_host_class_prolific_command(prolific, UX_HOST_CLASS_PROLIFIC_REQ_SET_LINE_CODING, + 0, data_buffer, UX_HOST_CLASS_PROLIFIC_LINE_CODING_LENGTH); + + /* We free the resources allocated no matter what. */ + _ux_utility_memory_free(data_buffer); + } + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_GET_LINE_CODING: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_GET_LINE_CODING, prolific, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Allocate some cache safe memory for the control command. */ + data_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PROLIFIC_LINE_CODING_LENGTH); + + /* Check if error. Return with error if no memory could be allocated. */ + if (data_buffer == UX_NULL) + + /* Do not proceed. Set error code. */ + status = UX_MEMORY_INSUFFICIENT; + else + { + + /* Send the command to the device. */ + status = _ux_host_class_prolific_command(prolific, UX_HOST_CLASS_PROLIFIC_REQ_GET_LINE_CODING, + 0, data_buffer, UX_HOST_CLASS_PROLIFIC_LINE_CODING_LENGTH); + + /* Fill in the calling buffer if the result is successful. */ + if (status == UX_SUCCESS) + { + + /* Build the buffer from the calling parameter. Cast the calling parameter. */ + line_coding = (UX_HOST_CLASS_PROLIFIC_LINE_CODING *) parameter; + + /* Get the data rate. */ + line_coding -> ux_host_class_prolific_line_coding_dter = _ux_utility_long_get(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_RATE); + + /* Then the stop bit. */ + line_coding -> ux_host_class_prolific_line_coding_stop_bit = + (ULONG) *(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_STOP_BIT); + + /* Then the parity. */ + line_coding -> ux_host_class_prolific_line_coding_parity = + (ULONG) *(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_PARITY); + + /* Finally the data bits. */ + line_coding -> ux_host_class_prolific_line_coding_data_bits = + (ULONG) *(data_buffer + UX_HOST_CLASS_PROLIFIC_LINE_CODING_DATA_BIT); + } + + /* We free the resources allocated no matter what. */ + _ux_utility_memory_free(data_buffer); + } + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_STATE: + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_SET_LINE_STATE, prolific, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Cast the calling parameter. */ + line_state = (UX_HOST_CLASS_PROLIFIC_LINE_STATE *) parameter; + + /* Build the value field. */ + value = (line_state -> ux_host_class_prolific_line_state_rts | + (line_state -> ux_host_class_prolific_line_state_dtr << 1)); + + /* Send the command to the device. */ + status = _ux_host_class_prolific_command(prolific, UX_HOST_CLASS_PROLIFIC_REQ_SET_LINE_STATE, + value, UX_NULL,0); + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_SEND_BREAK : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_SEND_BREAK, prolific, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Build the value field. */ + value = *((ULONG *) parameter); + + /* Send the command to the device. */ + status = _ux_host_class_prolific_command(prolific, UX_HOST_CLASS_PROLIFIC_REQ_SEND_BREAK, + value, UX_NULL,0); + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_PURGE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_PURGE, prolific, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &prolific -> ux_host_class_prolific_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Reset upstream data pipes part 1. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PROLIFIC_VENDOR_WRITE_REQUEST; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_PIPE1_RESET; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Reset upstream data pipes part 1. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PROLIFIC_VENDOR_WRITE_REQUEST; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_PIPE2_RESET; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_ABORT_IN_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_ABORT_IN_PIPE, prolific, prolific -> ux_host_class_prolific_bulk_in_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(prolific -> ux_host_class_prolific_bulk_in_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_ABORT_OUT_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_ABORT_OUT_PIPE, prolific, prolific -> ux_host_class_prolific_bulk_out_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(prolific -> ux_host_class_prolific_bulk_out_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_REPORT_DEVICE_STATUS_CHANGE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_REPORT_DEVICE_STATUS_CHANGE, prolific, parameter, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Register a callback when the line state has changed. */ + callback_function = ((VOID (*) (struct UX_HOST_CLASS_PROLIFIC_STRUCT *, ULONG )) (ALIGN_TYPE)parameter); + prolific -> ux_host_class_prolific_device_status_change_callback = callback_function; + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_PROLIFIC_IOCTL_GET_DEVICE_STATUS : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_IOCTL_GET_DEVICE_STATUS, prolific, prolific -> ux_host_class_prolific_device_state, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Return the device status. */ + * ((ULONG *) parameter) = prolific -> ux_host_class_prolific_device_state; + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + break; + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_read.c b/common/usbx_host_classes/src/ux_host_class_prolific_read.c new file mode 100644 index 0000000..37f2a0e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_read.c @@ -0,0 +1,191 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the prolific interface. The call is */ +/* blocking and only returns when there is either an error or when */ +/* the transfer is complete. */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* data_pointer Pointer to buffer */ +/* requested_length Requested data read */ +/* actual_length Actual data read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_read (UX_HOST_CLASS_PROLIFIC *prolific, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_READ, prolific, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (prolific -> ux_host_class_prolific_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by resetting the actual length of the transfer to zero. */ + *actual_length = 0; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &prolific -> ux_host_class_prolific_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk in endpoint until either the transfer is + completed or until there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer_request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PROLIFIC_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer request in case some data was actually received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked. */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion status. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + return(transfer_request -> ux_transfer_request_completion_code); + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually received and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to receive. */ + requested_length -= transfer_request_length; + } + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_reception_callback.c b/common/usbx_host_classes/src/ux_host_class_prolific_reception_callback.c new file mode 100644 index 0000000..edeff6c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_reception_callback.c @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_reception_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback from the USBX transfer functions, */ +/* it is called when a full or partial transfer has been done for a */ +/* bulk in transfer. It calls back the application. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_prolific_reception_callback (UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_PROLIFIC *prolific; +UX_HOST_CLASS_PROLIFIC_RECEPTION *prolific_reception; + + /* Get the class instance for this transfer request. */ + prolific = (UX_HOST_CLASS_PROLIFIC *) transfer_request -> ux_transfer_request_class_instance; + + /* Get the pointer to the prolific reception structure. */ + prolific_reception = prolific -> ux_host_class_prolific_reception; + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* The reception is stopped. */ + prolific_reception -> ux_host_class_prolific_reception_state = UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STOPPED; + + /* We may have an device extraction. We cannot continue. Report to the application. */ + prolific_reception -> ux_host_class_prolific_reception_callback(prolific, transfer_request -> ux_transfer_request_completion_code, UX_NULL, 0); + + + /* We do not proceed. */ + return; + + } + + /* Check if the class is in shutdown. */ + if (prolific -> ux_host_class_prolific_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) + + /* We do not proceed. */ + return; + + /* And move to the next reception buffer. Check if we are at the end of the application buffer. */ + if (prolific_reception -> ux_host_class_prolific_reception_data_head + prolific_reception -> ux_host_class_prolific_reception_block_size >= + prolific_reception -> ux_host_class_prolific_reception_data_buffer + prolific_reception -> ux_host_class_prolific_reception_data_buffer_size) + { + + /* We are at the end of the buffer. Move back to the beginning if we have space available. */ + if (prolific_reception -> ux_host_class_prolific_reception_data_tail == prolific_reception -> ux_host_class_prolific_reception_data_buffer) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_BUFFER_OVERFLOW, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We have an overflow. We cannot continue. Report to the application. */ + prolific_reception -> ux_host_class_prolific_reception_callback(prolific, UX_BUFFER_OVERFLOW, UX_NULL, 0); + + /* And stop the transfer in progress flag. */ + prolific_reception -> ux_host_class_prolific_reception_state = UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STOPPED; + + return; + } + else + + /* Program the head to be at the beginning of the application buffer. */ + prolific_reception -> ux_host_class_prolific_reception_data_head = prolific_reception -> ux_host_class_prolific_reception_data_buffer; + + } + else + + /* Program the head to be after the current buffer. */ + prolific_reception -> ux_host_class_prolific_reception_data_head += prolific_reception -> ux_host_class_prolific_reception_block_size; + + + /* We need to report this transfer to the application. */ + prolific_reception -> ux_host_class_prolific_reception_callback(prolific, + transfer_request -> ux_transfer_request_completion_code, + transfer_request -> ux_transfer_request_data_pointer, + transfer_request -> ux_transfer_request_actual_length); + /* Update the next buffer for reception. */ + transfer_request -> ux_transfer_request_data_pointer = prolific_reception -> ux_host_class_prolific_reception_data_head; + + /* Arm another transfer. */ + _ux_host_stack_transfer_request(transfer_request); + + /* There is no status to be reported back to the stack. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_reception_start.c b/common/usbx_host_classes/src/ux_host_class_prolific_reception_start.c new file mode 100644 index 0000000..d930320 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_reception_start.c @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_reception_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts a reception with the DLC modem. This mechanism */ +/* allows for non blocking calls based on a packet orientated round */ +/* robbin buffer. When a packet is fully or partially received, an */ +/* application callback function is invoked and a new transfer request */ +/* is rescheduled. */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* prolific_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_reception_start (UX_HOST_CLASS_PROLIFIC *prolific, + UX_HOST_CLASS_PROLIFIC_RECEPTION *prolific_reception) +{ + +UX_TRANSFER *transfer_request; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_RECEPTION_START, prolific, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (prolific -> ux_host_class_prolific_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by aligning the head and tail of buffers to the same address supplied by the application. */ + prolific_reception -> ux_host_class_prolific_reception_data_head = prolific_reception -> ux_host_class_prolific_reception_data_buffer; + prolific_reception -> ux_host_class_prolific_reception_data_tail = prolific_reception -> ux_host_class_prolific_reception_data_buffer; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &prolific -> ux_host_class_prolific_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) prolific; + transfer_request -> ux_transfer_request_data_pointer = prolific_reception -> ux_host_class_prolific_reception_data_head; + transfer_request -> ux_transfer_request_requested_length = prolific_reception -> ux_host_class_prolific_reception_block_size; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_prolific_reception_callback; + + /* Save the prolific reception structure in the prolific structure. */ + prolific -> ux_host_class_prolific_reception = prolific_reception; + + /* And declare we have a transfer in progress. */ + prolific_reception -> ux_host_class_prolific_reception_state = UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STARTED; + + /* Arm a first transfer on the bulk in endpoint. There is a callback to this function so we return to the caller + right away. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* We do not know if the first transfer was successful yet. If the status is not OK, we need to stop the transfer + in progress flag. */ + if (status != UX_SUCCESS) + prolific_reception -> ux_host_class_prolific_reception_state = UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STOPPED; + + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_reception_stop.c b/common/usbx_host_classes/src/ux_host_class_prolific_reception_stop.c new file mode 100644 index 0000000..e3bbae3 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_reception_stop.c @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_reception_stop PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts a reception with a prolific device. This */ +/* mechanism allows for non blocking calls based on a packet */ +/* orientated round robbin buffer. When a packet is fully or partially */ +/* received, an application callback function is invoked and a new */ +/* transfer request is rescheduled. */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* prolific_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_reception_stop (UX_HOST_CLASS_PROLIFIC *prolific, + UX_HOST_CLASS_PROLIFIC_RECEPTION *prolific_reception) +{ + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_RECEPTION_STOP, prolific, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (prolific -> ux_host_class_prolific_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Check if we do have transfers for this application. If none, nothing to do. */ + if (prolific_reception -> ux_host_class_prolific_reception_state == UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STOPPED) + return(UX_SUCCESS); + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(prolific -> ux_host_class_prolific_bulk_in_endpoint); + + /* Declare the reception stopped. */ + prolific_reception -> ux_host_class_prolific_reception_state = UX_HOST_CLASS_PROLIFIC_RECEPTION_STATE_STOPPED; + + /* This function never really fails. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_setup.c b/common/usbx_host_classes/src/ux_host_class_prolific_setup.c new file mode 100644 index 0000000..1c5e76d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_setup.c @@ -0,0 +1,416 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_setup PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the entire prolific configuration descriptors.*/ +/* This is needed because the prolific class needs to know if commands */ +/* are routed through the comm interface or the data class. */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_prolific_activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_setup(UX_HOST_CLASS_PROLIFIC *prolific) +{ + +UCHAR *setup_buffer; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &prolific -> ux_host_class_prolific_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the buffer. */ + setup_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_PROLIFIC_SETUP_BUFFER_SIZE); + if (setup_buffer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set the device type. Can be either 0, 1 or HX. */ + if (prolific -> ux_host_class_prolific_device -> ux_device_descriptor.bDeviceClass == 0x02) + + /* Device is of type 0. */ + prolific -> ux_host_class_prolific_device_type = UX_HOST_CLASS_PROLIFIC_DEVICE_TYPE_0; + + else + { + + /* Check packet size. If 64, we are dealing with HX type device. */ + if (prolific -> ux_host_class_prolific_device -> ux_device_descriptor.bMaxPacketSize0 == 64) + + /* Device is of type HX. */ + prolific -> ux_host_class_prolific_device_type = UX_HOST_CLASS_PROLIFIC_DEVICE_TYPE_HX; + + else + + /* Default case : type 1. */ + prolific -> ux_host_class_prolific_device_type = UX_HOST_CLASS_PROLIFIC_DEVICE_TYPE_1; + + } + + /* Create a transfer request for the prolific setup request # 1. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_EEPROM_READ; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 2. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0x0404; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 3. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_EEPROM_READ; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 4. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0x8383; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 5. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_EEPROM_READ; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 6. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0x0404; + transfer_request -> ux_transfer_request_index = 1; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 7. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_EEPROM_READ; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 8. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0x8383; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 9. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 1; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 9. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = 1; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); + + } + + /* Create a transfer request for the prolific setup request # 10. */ + if (prolific -> ux_host_class_prolific_device_type == UX_HOST_CLASS_PROLIFIC_DEVICE_TYPE_HX) + + /* Chip is HX. */ + transfer_request -> ux_transfer_request_index = 0x44; + + else + + /* Chip is not HX. */ + transfer_request -> ux_transfer_request_index = 0x24; + + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = 1; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_REG_CONFIGURE; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + + /* Reset upstream data pipes part 1. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PROLIFIC_VENDOR_WRITE_REQUEST; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_PIPE1_RESET; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Reset upstream data pipes part 2. */ + transfer_request -> ux_transfer_request_data_pointer = setup_buffer; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_PROLIFIC_VENDOR_WRITE_REQUEST; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_VENDOR | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_PROLIFIC_COMMAND_PIPE2_RESET; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status, if error, do not proceed. */ + if (status != UX_SUCCESS || transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + + } + + /* Free all used resources. */ + _ux_utility_memory_free(setup_buffer); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_transfer_request_completed.c b/common/usbx_host_classes/src/ux_host_class_prolific_transfer_request_completed.c new file mode 100644 index 0000000..5bc4a72 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_transfer_request_completed.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** PROLIFIC Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_transfer_request_completed PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the completion thread when a transfer */ +/* request has been completed either because the transfer is */ +/* successful or there was an error. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* USBX stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_prolific_transfer_request_completed(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_PROLIFIC *prolific; + + + /* Get the class instance for this transfer request. */ + prolific = (UX_HOST_CLASS_PROLIFIC *) transfer_request -> ux_transfer_request_class_instance; + + /* Check the state of the transfer. If there is an error, we do not proceed with this notification. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + + /* We do not proceed. */ + return; + + /* Check if the class is in shutdown. */ + if (prolific -> ux_host_class_prolific_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) + + /* We do not proceed. */ + return; + + /* Increment the notification count. */ + prolific -> ux_host_class_prolific_notification_count++; + + /* Look at what the the status is. First ensure the length of our interrupt pipe data is correct. */ + if (transfer_request -> ux_transfer_request_actual_length == + prolific -> ux_host_class_prolific_interrupt_endpoint -> ux_endpoint_descriptor.wMaxPacketSize) + { + + /* Check if device is present. */ + if ((*(transfer_request -> ux_transfer_request_data_pointer + UX_HOST_CLASS_PROLIFIC_DEVICE_STATE_OFFSET) & + UX_HOST_CLASS_PROLIFIC_DEVICE_STATE_MASK) == 0) + + /* Device is not present. */ + prolific -> ux_host_class_prolific_device_state = UX_HOST_CLASS_PROLIFIC_DEVICE_NOT_PRESENT; + + else + + /* Device is present. */ + prolific -> ux_host_class_prolific_device_state = UX_HOST_CLASS_PROLIFIC_DEVICE_PRESENT; + + /* If there is a callback present, invoke it. */ + if (prolific -> ux_host_class_prolific_device_status_change_callback != UX_NULL) + + /* There is a callback, send the status change to the application. */ + prolific -> ux_host_class_prolific_device_status_change_callback(prolific, prolific -> ux_host_class_prolific_device_state); + + } + + /* Reactivate the PROLIFIC interrupt pipe. */ + _ux_host_stack_transfer_request(transfer_request); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_prolific_write.c b/common/usbx_host_classes/src/ux_host_class_prolific_write.c new file mode 100644 index 0000000..15f8bc0 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_prolific_write.c @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Prolific Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_prolific.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_prolific_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the prolific interface. The call is blocking*/ +/* and only returns when there is either an error or when the transfer */ +/* is complete. */ +/* */ +/* INPUT */ +/* */ +/* prolific Pointer to prolific class */ +/* data_pointer Pointer to data to write */ +/* requested_length Length of data to write */ +/* actual_length Actual length of data written */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_prolific_write(UX_HOST_CLASS_PROLIFIC *prolific, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_PROLIFIC_WRITE, prolific, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (prolific -> ux_host_class_prolific_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, prolific, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by resetting the actual length of the transfer. */ + *actual_length = 0; + + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &prolific -> ux_host_class_prolific_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk out endpoint until either the transfer is + completed or when there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer_request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_PROLIFIC_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer_request in case some data actually went out. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be sent. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion status. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + return(transfer_request -> ux_transfer_request_completion_code); + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually sent and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to send out. */ + requested_length -= transfer_request_length; + } + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_activate.c b/common/usbx_host_classes/src/ux_host_class_storage_activate.c new file mode 100644 index 0000000..d7fb0d2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_activate.c @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates an instance of the storage class. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_configure Configure storage device */ +/* _ux_host_class_storage_device_initialize */ +/* Initialize storage device */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_STORAGE *storage; +UINT status; + + + /* The storage is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. The memory used MUST BE allocated from a CACHE SAFE memory + since the buffer for the CSW is an array contained within each storage instance. */ + storage = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, sizeof(UX_HOST_CLASS_STORAGE)); + if (storage == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance */ + storage -> ux_host_class_storage_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the storage class instance. */ + storage -> ux_host_class_storage_interface = interface; + + /* Store the device container into the storage class instance. */ + storage -> ux_host_class_storage_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(command -> ux_host_class_command_class_ptr, (VOID *) storage); + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) storage; + + /* Configure the USB storage device. */ + status = _ux_host_class_storage_configure(storage); + + /* Create the semaphore to protect multiple threads from accessing the same storage instance. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&storage -> ux_host_class_storage_semaphore, "ux_host_class_storage_semaphore", 1); + if (status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Error case, free resources. */ + if (status != UX_SUCCESS) + { + + /* Last one, semaphore not created or created error, no need to free. */ + + /* Error, destroy the class and return an error. */ + _ux_host_stack_class_instance_destroy(storage -> ux_host_class_storage_class, (VOID *) storage); + + /* This instance of the device must also be removed in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) UX_NULL; + + /* Free memory for class instance. */ + _ux_utility_memory_free(storage); + + return(status); + } + + /* Mark the storage as mounting now. */ + storage -> ux_host_class_storage_state = UX_HOST_CLASS_INSTANCE_MOUNTING; + + /* Initialize the USB storage device. We do not check the status at this stage. We let the instance of this + class live even if there was a failure. Because the storage class has many media instance, we will let the + disconnection signal clean the instance at a later stage. */ + _ux_host_class_storage_device_initialize(storage); + + /* Mark the storage as live now. */ + storage -> ux_host_class_storage_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, storage -> ux_host_class_storage_class, (VOID *) storage); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_ACTIVATE, storage, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, storage, 0, 0, 0) + + /* Return completion status. Force it to success. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_cbw_initialize.c b/common/usbx_host_classes/src/ux_host_class_storage_cbw_initialize.c new file mode 100644 index 0000000..3892fc3 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_cbw_initialize.c @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_cbw_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will initialize the Command Block Wrapper (CBW) that */ +/* encapsulate the SCSI request to be sent to the storage device. */ +/* The CBW is normally used only for the BO protocol. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* direction Direction of transfer */ +/* data_transfer_length Length of data transfer */ +/* command_length Length of command */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_long_put Write a 32-bit value */ +/* _ux_utility_memory_set Set memory to a value */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_storage_cbw_initialize(UX_HOST_CLASS_STORAGE *storage, UINT direction, + ULONG data_transfer_length, UINT command_length) +{ + +UCHAR *cbw; + + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Store the signature of the CBW. */ + _ux_utility_long_put(cbw, UX_HOST_CLASS_STORAGE_CBW_SIGNATURE_MASK); + + /* Set the Tag, this value is simply an arbitrary number that is echoed by + the device in the CSW. */ + _ux_utility_long_put(cbw + UX_HOST_CLASS_STORAGE_CBW_TAG, UX_HOST_CLASS_STORAGE_CBW_TAG_MASK); + + /* Store the Data Transfer Length expected for the data payload. */ + _ux_utility_long_put(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH, data_transfer_length); + + /* Store the CBW Flag field that contains the transfer direction. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) = (UCHAR)direction; + + /* Store the LUN value. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_LUN) = (UCHAR)storage -> ux_host_class_storage_lun; + + /* Store the size of the SCSI command block that follows. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB_LENGTH) = (UCHAR)command_length; + + /* Reset the SCSI command block. */ + _ux_utility_memory_set(cbw + UX_HOST_CLASS_STORAGE_CBW_CB, 0, (ULONG) command_length); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_configure.c b/common/usbx_host_classes/src/ux_host_class_storage_configure.c new file mode 100644 index 0000000..62a147a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_configure.c @@ -0,0 +1,148 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* storage device. Once the storage is configured, its interface will */ +/* be activated. The bulk endpoints enumerated(1 IN, 1 OUT). */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get configuration */ +/* _ux_host_stack_device_configuration_get Get device config */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_configure(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (storage -> ux_host_class_storage_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A storage device normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(storage -> ux_host_class_storage_device, 0, &configuration); + + /* Check completion status. */ + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, storage -> ux_host_class_storage_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the storage device power source and check the parent power source for + incompatible connections. */ + if (storage -> ux_host_class_storage_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Pickup pointer to parent device. */ + parent_device = storage -> ux_host_class_storage_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root hub and we don't have to worry + if the parent is not the root hub, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the storage device default alternate setting + for the storage device interface is active and the interrupt endpoint is now enabled. + We have to memorize the first interface since the interrupt endpoint is hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, 0, 0, &storage -> ux_host_class_storage_interface); + + /* Check completion status. */ + if (status != UX_SUCCESS) + { + /* Store the instance in the interface container, this is for the USBX stack + when it needs to invoke the class. */ + storage -> ux_host_class_storage_interface -> ux_interface_class_instance = (VOID *) storage; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_deactivate.c b/common/usbx_host_classes/src/ux_host_class_storage_deactivate.c new file mode 100644 index 0000000..7ab5b0a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_deactivate.c @@ -0,0 +1,208 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the storage device */ +/* has been removed from the bus either directly or indirectly. The */ +/* bulk in\out pipes will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* fx_media_close Close media */ +/* _ux_host_stack_endpoint_transfer_abort Abort transfer request */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_STORAGE *storage; +UINT media_index; +FX_MEDIA *media; +UX_HOST_CLASS_STORAGE_MEDIA *storage_media; +UX_HOST_CLASS *class; +VOID *memory; + + + /* Get the instance for this class. */ + storage = (UX_HOST_CLASS_STORAGE *) command -> ux_host_class_command_instance; + + /* We need the class container. */ + class = storage -> ux_host_class_storage_class; + + /* Point the media structure to the first media in the container. */ + storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) class -> ux_host_class_media; + + /* The storage device is being shut down. */ + storage -> ux_host_class_storage_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* We come to this point when the device has been extracted. So there may have been a transaction + being scheduled. We make sure the transaction has been completed by the controller driver. + When the device is extracted, the controller tries multiple times the transaction and retires it + with a DEVICE_NOT_RESPONDING error code. + + First we take care of endpoint OUT. */ + + /* We need to abort transactions on the bulk pipes. */ + if (storage -> ux_host_class_storage_bulk_out_endpoint != UX_NULL) + _ux_host_stack_endpoint_transfer_abort(storage -> ux_host_class_storage_bulk_out_endpoint); + + /* Then endpoint IN. */ + if (storage -> ux_host_class_storage_bulk_in_endpoint != UX_NULL) + _ux_host_stack_endpoint_transfer_abort(storage -> ux_host_class_storage_bulk_in_endpoint); + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + /* Was the protocol CBI ? */ + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_STORAGE_PROTOCOL_CBI) + { + + /* Was there an interrupt endpoint? */ + if (storage -> ux_host_class_storage_interrupt_endpoint != UX_NULL) + { + + /* Then interrupt endpoint. */ + transfer_request = &storage -> ux_host_class_storage_interrupt_endpoint -> ux_endpoint_transfer_request; + + /* Abort the data transfer on the interrupt endpoint. */ + _ux_host_stack_endpoint_transfer_abort(storage -> ux_host_class_storage_interrupt_endpoint); + + /* Free the memory that was used by the interrupt endpoint. */ + if (storage -> ux_host_class_storage_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer != UX_NULL) + _ux_utility_memory_free(storage -> ux_host_class_storage_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer); + } + } +#endif + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Inform FileX of the deactivation of all Media attached to this instance. */ + for (media_index = 0; media_index < UX_HOST_CLASS_STORAGE_MAX_MEDIA; media_index++) + { + + /* Get the FileX Media attached to this media. */ + media = &storage_media -> ux_host_class_storage_media; + + /* Check if the media belongs to the device being removed. */ + if (((UX_HOST_CLASS_STORAGE *) media -> fx_media_driver_info) == storage) + { + + /* Check if the media was properly opened. */ + if (storage_media -> ux_host_class_storage_media_status == UX_HOST_CLASS_STORAGE_MEDIA_MOUNTED) + { + + /* We preserve the memory used by this media. */ + memory = storage_media -> ux_host_class_storage_media_memory; + + /* Ask FileX to unmount the partition. */ + fx_media_close(media); + + /* This device is now unmounted. */ + storage_media -> ux_host_class_storage_media_status = UX_HOST_CLASS_STORAGE_MEDIA_UNMOUNTED; + + /* Reset the media ID. */ + media -> fx_media_id = 0; + + /* Free the memory block used for data transfer on behalf of FileX. */ + _ux_utility_memory_free(memory); + } + } + + /* Move to next entry in the media array. */ + storage_media++; + } + + /* Protect thread reentry to this instance. */ + _ux_utility_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(storage -> ux_host_class_storage_class, (VOID *) storage); + + /* Destroy the protection semaphore. */ + _ux_utility_semaphore_delete(&storage -> ux_host_class_storage_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, storage -> ux_host_class_storage_class, (VOID *) storage); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_DEACTIVATE, storage, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(storage); + + /* Free the storage instance memory. */ + _ux_utility_memory_free(storage); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_device_initialize.c b/common/usbx_host_classes/src/ux_host_class_storage_device_initialize.c new file mode 100644 index 0000000..b2ddd55 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_device_initialize.c @@ -0,0 +1,169 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_device_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the USB storage device. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_device_reset Reset device */ +/* _ux_host_class_storage_device_support_check */ +/* Check protocol support */ +/* _ux_host_class_storage_endpoints_get Get all endpoints */ +/* _ux_host_class_storage_max_lun_get Get maximum number of LUNs */ +/* _ux_host_class_storage_media_characteristics_get */ +/* Get media characteristics */ +/* _ux_host_class_storage_media_format_capacity_get */ +/* Get format capacity */ +/* _ux_host_class_storage_media_mount Mount the media */ +/* _ux_utility_delay_ms Delay ms */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_device_initialize(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +ULONG lun_index; + + /* Check the device protocol support and initialize the transport layer. */ + status = _ux_host_class_storage_device_support_check(storage); + if (status != UX_SUCCESS) + return(status); + + /* Get the maximum number of LUN (Bulk Only device only, other device + will set the LUN number to 0). */ + status = _ux_host_class_storage_max_lun_get(storage); + if (status != UX_SUCCESS) + return(status); + + /* Search all the endpoints for the storage interface (Bulk Out, Bulk in, + and optional Interrupt endpoint). */ + status = _ux_host_class_storage_endpoints_get(storage); + if (status != UX_SUCCESS) + return(status); + + /* We need to wait for some device to settle. The INTUIX Flash disk is an example of + these device who fail the first Inquiry command if sent too quickly. + The timing does not have to be precise so we use the thread sleep function. + The default sleep value is 2 seconds. */ + _ux_utility_delay_ms(UX_HOST_CLASS_STORAGE_DEVICE_INIT_DELAY); + + /* Each LUN must be parsed and mounted. */ + for (lun_index = 0; lun_index <= storage -> ux_host_class_storage_max_lun; lun_index++) + { + + /* Set the LUN into the storage instance. */ + storage -> ux_host_class_storage_lun = lun_index; + + /* Get the media type supported by this storage device. */ + status = _ux_host_class_storage_media_characteristics_get(storage); + if (status == UX_HOST_CLASS_MEDIA_NOT_SUPPORTED) + { + /* Unsupported device. */ + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_MEDIA_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_MEDIA_NOT_SUPPORTED, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + + continue; + } + if (status != UX_SUCCESS) + return(status); + + /* Get the format capacity of this storage device. */ + status = _ux_host_class_storage_media_format_capacity_get(storage); + if (status != UX_SUCCESS) + return(status); + + /* Store the LUN type in the LUN type array. */ + storage -> ux_host_class_storage_lun_types[lun_index] = storage -> ux_host_class_storage_media_type; + + /* Check the media type. We support regular FAT drives and optical drives. + No CD-ROM support in this release. */ + switch (storage -> ux_host_class_storage_media_type) + { + + case UX_HOST_CLASS_STORAGE_MEDIA_FAT_DISK: + case UX_HOST_CLASS_STORAGE_MEDIA_OPTICAL_DISK: + case UX_HOST_CLASS_STORAGE_MEDIA_IOMEGA_CLICK: + + /* Try to read the device media in search for a partition table or boot sector. + We are at the root of the disk, so use sector 0 as the starting point. */ + _ux_host_class_storage_media_mount(storage, 0); + break; + + case UX_HOST_CLASS_STORAGE_MEDIA_CDROM: + default: + + /* In the case of CD-ROM, we do no need to mount any file system yet. The application + can read sectors directly. */ + + break; + } + } + + /* Some LUNs may succeed and some may fail. For simplicity's sake, we just + return success. The storage thread will try to remount the ones that failed. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_device_reset.c b/common/usbx_host_classes/src/ux_host_class_storage_device_reset.c new file mode 100644 index 0000000..1551a73 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_device_reset.c @@ -0,0 +1,117 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_device_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will perform a reset on the device if it is a */ +/* Bulk Only device. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_device_reset(UX_HOST_CLASS_STORAGE *storage) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; + + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + /* We need to perform a reset only for BO devices. */ + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_STORAGE_PROTOCOL_BO) + { +#endif + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &storage -> ux_host_class_storage_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* We need to prevent other threads from simultaneously using the endpoint. */ + _ux_utility_semaphore_get(&control_endpoint -> ux_endpoint_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Create a transfer_request for the RESET request. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_STORAGE_RESET; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Per the spec, reset the endpoints as well. */ + _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint); + _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint); + + /* Return completion status. */ + return(status); + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + } + + /* In case the device is not a BO device, we always succeed. */ + return(UX_SUCCESS); +#endif +} diff --git a/common/usbx_host_classes/src/ux_host_class_storage_device_support_check.c b/common/usbx_host_classes/src/ux_host_class_storage_device_support_check.c new file mode 100644 index 0000000..0f95bf4 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_device_support_check.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_device_support_check PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function verifies the support for the subclass and protocol */ +/* of the USB device. If the device is supported it will update the */ +/* storage class instances with the transport layer functions. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_device_support_check(UX_HOST_CLASS_STORAGE *storage) +{ + + /* Check for the protocol type (BO/CB/CBI) and update the transport functions. */ + switch(storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol) + { + + case UX_HOST_CLASS_STORAGE_PROTOCOL_BO: + + storage -> ux_host_class_storage_transport = _ux_host_class_storage_transport_bo; + break; + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + case UX_HOST_CLASS_STORAGE_PROTOCOL_CB: + + storage -> ux_host_class_storage_transport = _ux_host_class_storage_transport_cb; + break; + + + case UX_HOST_CLASS_STORAGE_PROTOCOL_CBI: + + /* In case of CBI, the subclass must be UFI, if not, default back to CB transport. */ + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + storage -> ux_host_class_storage_transport = _ux_host_class_storage_transport_cbi; + else + storage -> ux_host_class_storage_transport = _ux_host_class_storage_transport_cb; + break; +#endif + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_PROTOCOL_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_PROTOCOL_ERROR, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_PROTOCOL_ERROR); + } + + /* Check for the sub class and make sure we support it. */ + switch (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass) + { + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + case UX_HOST_CLASS_STORAGE_SUBCLASS_UFI: +#endif + case UX_HOST_CLASS_STORAGE_SUBCLASS_RBC: + case UX_HOST_CLASS_STORAGE_SUBCLASS_SCSI: + case UX_HOST_CLASS_STORAGE_SUBCLASS_SFF8020: + case UX_HOST_CLASS_STORAGE_SUBCLASS_SFF8070: + + return(UX_SUCCESS); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_PROTOCOL_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_PROTOCOL_ERROR, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_PROTOCOL_ERROR); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_driver_entry.c b/common/usbx_host_classes/src/ux_host_class_storage_driver_entry.c new file mode 100644 index 0000000..aa478d2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_driver_entry.c @@ -0,0 +1,214 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_driver_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point for the FileX file system. All */ +/* FileX driver I/O calls are are multiplexed here and rerouted to */ +/* the proper USB storage class functions. */ +/* */ +/* INPUT */ +/* */ +/* media FileX media pointer */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_sense_code_translate */ +/* Translate error status codes */ +/* _ux_host_class_storage_media_read Read sector(s) */ +/* _ux_host_class_storage_media_write Write sector(s) */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* FileX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_storage_driver_entry(FX_MEDIA *media) +{ + +UINT status; +UX_HOST_CLASS_STORAGE *storage; +UX_HOST_CLASS_STORAGE_MEDIA *storage_media; + + + /* Get the pointer to the storage instance. */ + storage = (UX_HOST_CLASS_STORAGE *) media -> fx_media_driver_info; + + /* Get the pointer to the media instance. */ + storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) media -> fx_media_reserved_for_user; + + /* Ensure the instance is valid. */ + if ((storage -> ux_host_class_storage_state != UX_HOST_CLASS_INSTANCE_LIVE) && + (storage -> ux_host_class_storage_state != UX_HOST_CLASS_INSTANCE_MOUNTING)) + { + + /* Class instance is invalid. Return an error! */ + media -> fx_media_driver_status = FX_PTR_ERROR; + return; + } + + /* Protect Thread reentry to this instance. */ + _ux_utility_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER); + + /* Restore the LUN number from the media instance. */ + storage -> ux_host_class_storage_lun = storage_media -> ux_host_class_storage_media_lun; + + /* And the sector size. */ + storage -> ux_host_class_storage_sector_size = storage_media -> ux_host_class_storage_media_sector_size; + + + /* Look at the request specified by the FileX caller. */ + switch (media -> fx_media_driver_request) + { + + case FX_DRIVER_READ: + + /* Read one or more sectors. */ + status = _ux_host_class_storage_media_read(storage, media -> fx_media_driver_logical_sector + + storage_media -> ux_host_class_storage_media_partition_start, + media -> fx_media_driver_sectors, media -> fx_media_driver_buffer); + + /* Check completion status. */ + if (status == UX_SUCCESS) + media -> fx_media_driver_status = FX_SUCCESS; + else + media -> fx_media_driver_status = _ux_host_class_storage_sense_code_translate(storage, status); + break; + + + case FX_DRIVER_WRITE: + + /* Write one or more sectors. */ + status = _ux_host_class_storage_media_write(storage, media -> fx_media_driver_logical_sector + + storage_media -> ux_host_class_storage_media_partition_start, + media -> fx_media_driver_sectors, media -> fx_media_driver_buffer); + + /* Check completion status. */ + if (status == UX_SUCCESS) + media -> fx_media_driver_status = FX_SUCCESS; + else + media -> fx_media_driver_status = _ux_host_class_storage_sense_code_translate(storage,status); + break; + + + case FX_DRIVER_FLUSH: + + /* Nothing to do. Just return a good status! */ + media -> fx_media_driver_status = FX_SUCCESS; + break; + + case FX_DRIVER_ABORT: + + /* Nothing to do. Just return a good status! */ + media -> fx_media_driver_status = FX_SUCCESS; + break; + + case FX_DRIVER_INIT: + + /* Check for media protection. We must do this operation here because FileX clears all the + media fields before init. */ + if (storage -> ux_host_class_storage_write_protected_media == UX_TRUE) + + /* The media is Write Protected. We tell FileX. */ + media -> fx_media_driver_write_protect = UX_TRUE; + + /* This function always succeeds. */ + media -> fx_media_driver_status = FX_SUCCESS; + break; + + + case FX_DRIVER_UNINIT: + + /* Nothing to do. Just return a good status! */ + media -> fx_media_driver_status = FX_SUCCESS; + break; + + + case FX_DRIVER_BOOT_READ: + + /* Read the media boot sector. */ + status = _ux_host_class_storage_media_read(storage, storage_media -> ux_host_class_storage_media_partition_start, 1, + media -> fx_media_driver_buffer); + + /* Check completion status. */ + if (status == UX_SUCCESS) + media -> fx_media_driver_status = FX_SUCCESS; + else + media -> fx_media_driver_status = _ux_host_class_storage_sense_code_translate(storage,status); + break; + + + case FX_DRIVER_BOOT_WRITE: + + /* Write the boot sector. */ + status = _ux_host_class_storage_media_write(storage, storage_media -> ux_host_class_storage_media_partition_start, 1, + media -> fx_media_driver_buffer); + + /* Check completion status. */ + if (status == UX_SUCCESS) + media -> fx_media_driver_status = FX_SUCCESS; + else + media -> fx_media_driver_status = _ux_host_class_storage_sense_code_translate(storage, status); + break; + + default: + + /* Invalid request from FileX */ + media -> fx_media_driver_status = FX_IO_ERROR; + break; + } + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&storage -> ux_host_class_storage_semaphore); +} diff --git a/common/usbx_host_classes/src/ux_host_class_storage_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_storage_endpoints_get.c new file mode 100644 index 0000000..0224a89 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_endpoints_get.c @@ -0,0 +1,210 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches for the handle of the bulk out endpoint and */ +/* optionally the bulk in endpoint. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get an endpoint pointer */ +/* _ux_utility_memory_allocate Allocate memory */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_endpoints_get(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT endpoint_index; +UX_ENDPOINT *endpoint; + + + /* Search for the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get an endpoint. */ + _ux_host_stack_interface_endpoint_get(storage -> ux_host_class_storage_interface, endpoint_index, &endpoint); + + /* We have an endpoint. Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* We have found the bulk OUT endpoint, save it! */ + storage -> ux_host_class_storage_bulk_out_endpoint = endpoint; + + /* This transfer request always has the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + break; + } + } + + /* The bulk OUT endpoint is mandatory. */ + if (storage -> ux_host_class_storage_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search for the bulk IN endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get an endpoint. */ + _ux_host_stack_interface_endpoint_get(storage -> ux_host_class_storage_interface, endpoint_index, &endpoint); + + /* We have an endpoint. Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* We have found the bulk IN endpoint, save it! */ + storage -> ux_host_class_storage_bulk_in_endpoint = endpoint; + + /* This transfer request always has the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + break; + } + } + + /* The bulk IN endpoint is mandatory */ + if (storage -> ux_host_class_storage_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + /* Search the Interrupt IN endpoint. This endpoint is optional and only valid for + storage devices that use the CBI protocol. */ + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_STORAGE_PROTOCOL_CBI) + { + + /* Yes, CBI protocol is present. Search for Interrupt IN endpoint. */ + for (endpoint_index = 0; endpoint_index < storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get an endpoint. */ + _ux_host_stack_interface_endpoint_get(storage -> ux_host_class_storage_interface, endpoint_index, &endpoint); + + /* Check if endpoint is Interrupt and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_INTERRUPT_ENDPOINT)) + { + + /* We have found the Interrupt IN endpoint, save it! */ + storage -> ux_host_class_storage_interrupt_endpoint = endpoint; + + /* This transfer request always has the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* The size of the transfer is fixed to the endpoint size. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_requested_length = + endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + + /* Get some memory for this data transfer. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer = + _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, + endpoint -> ux_endpoint_descriptor.wMaxPacketSize); + + /* Check the memory pointer. */ + if (endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + break; + } + } + + if (storage -> ux_host_class_storage_interrupt_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + } +#endif + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_entry.c b/common/usbx_host_classes/src/ux_host_class_storage_entry.c new file mode 100644 index 0000000..863c292 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_entry.c @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + +UX_COMPILE_TIME_ASSERT(!UX_OVERFLOW_CHECK_MULC_ULONG(sizeof(UX_HOST_CLASS_STORAGE_MEDIA), UX_HOST_CLASS_STORAGE_MAX_MEDIA), UX_HOST_CLASS_STORAGE_MAX_MEDIA_mul_ovf) + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the storage class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* USB disk on the bus or when the USB disk is removed. */ +/* */ +/* Version 2.0 of the storage class only supports USB FAT media and */ +/* not CD-ROM. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_activate Activate storage class */ +/* _ux_host_class_storage_deactivate Deactivate storage class */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_thread_create Create storage class thread */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if ((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + (command -> ux_host_class_command_class == UX_HOST_CLASS_STORAGE_CLASS)) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* We are assuming the device will mount. If this is the first activation of + the storage class, we have to fire up one thread for the media insertion + and acquire some memory for the media array. */ + + /* Allocate some memory for the media structures used by FileX. */ + if (command -> ux_host_class_command_class_ptr -> ux_host_class_media == UX_NULL) + { + + /* UX_HOST_CLASS_STORAGE_MAX_MEDIA*sizeof(UX_HOST_CLASS_STORAGE_MEDIA) overflow + * is checked outside of function. + */ + command -> ux_host_class_command_class_ptr -> ux_host_class_media = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_HOST_CLASS_STORAGE_MAX_MEDIA*sizeof(UX_HOST_CLASS_STORAGE_MEDIA)); + + /* Check the completion status. */ + if (command -> ux_host_class_command_class_ptr -> ux_host_class_media == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + } + + /* Allocate a Thread stack and create the thread. */ + if (command -> ux_host_class_command_class_ptr -> ux_host_class_thread_stack == UX_NULL) + { + + /* Allocate a Thread stack. */ + command -> ux_host_class_command_class_ptr -> ux_host_class_thread_stack = + _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_HOST_CLASS_STORAGE_THREAD_STACK_SIZE); + + /* Check the completion status. */ + if (command -> ux_host_class_command_class_ptr -> ux_host_class_thread_stack == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create the storage class thread. */ + status = _ux_utility_thread_create(&command -> ux_host_class_command_class_ptr -> ux_host_class_thread, + "ux_host_storage_thread", _ux_host_class_storage_thread_entry, + (ULONG) (ALIGN_TYPE) command -> ux_host_class_command_class_ptr, + command -> ux_host_class_command_class_ptr -> ux_host_class_thread_stack, + UX_HOST_CLASS_STORAGE_THREAD_STACK_SIZE, + UX_HOST_CLASS_STORAGE_THREAD_PRIORITY_CLASS, + UX_HOST_CLASS_STORAGE_THREAD_PRIORITY_CLASS, + TX_NO_TIME_SLICE, TX_DONT_START); + + /* Check the completion status. */ + if (status != UX_SUCCESS) + { + _ux_utility_memory_free(command -> ux_host_class_command_class_ptr -> ux_host_class_thread_stack); + command -> ux_host_class_command_class_ptr -> ux_host_class_thread_stack = UX_NULL; + return(UX_THREAD_ERROR); + } + + UX_THREAD_EXTENSION_PTR_SET(&(command -> ux_host_class_command_class_ptr -> ux_host_class_thread), command -> ux_host_class_command_class_ptr) + + /* Now that the extension pointer has been set, resume the thread. */ + tx_thread_resume(&command -> ux_host_class_command_class_ptr -> ux_host_class_thread); + } + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_storage_activate(command); + + /* Return the completion status. */ + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_storage_deactivate(command); + + /* Return the completion status. */ + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return an error. */ + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_max_lun_get.c b/common/usbx_host_classes/src/ux_host_class_storage_max_lun_get.c new file mode 100644 index 0000000..0a6542d --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_max_lun_get.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_max_lun_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves the maximum number of LUNs from the device. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_max_lun_get(UX_HOST_CLASS_STORAGE *storage) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *storage_data_buffer; + + + /* In the case of non BO device or an error on the command the number of lun should be + set to 0, indicating 1 lun. */ + storage -> ux_host_class_storage_max_lun = 0; + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + /* Check the device type. */ + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_STORAGE_PROTOCOL_BO) + { +#endif + + /* We need to get the default control endpoint transfer_request pointer. */ + control_endpoint = &storage -> ux_host_class_storage_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* We need to prevent other threads from simultaneously using the control endpoint. */ + _ux_utility_semaphore_get(&control_endpoint -> ux_endpoint_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Need to allocate memory for the descriptor. */ + storage_data_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 1); + if (storage_data_buffer == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer_request for the GET_MAX_LUN request. */ + transfer_request -> ux_transfer_request_data_pointer = storage_data_buffer; + transfer_request -> ux_transfer_request_requested_length = 1; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_STORAGE_GET_MAX_LUN; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 1)) + { + + /* Retrieve the number of max LUN for this device. */ + storage -> ux_host_class_storage_max_lun = *storage_data_buffer; + + /* Is the max LUN index greater than our LUN array's? */ + if (storage -> ux_host_class_storage_max_lun > UX_MAX_HOST_LUN - 1) + { + + /* Cap it off. */ + storage -> ux_host_class_storage_max_lun = UX_MAX_HOST_LUN - 1; + + /* Notify application so it knows to increase UX_MAX_HOST_LUN. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_MEMORY_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_MEMORY_ERROR, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + } + } + + /* Free all used resources. */ + _ux_utility_memory_free(storage_data_buffer); + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + } +#endif + + /* We always succeed. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_capacity_get.c b/common/usbx_host_classes/src/ux_host_class_storage_media_capacity_get.c new file mode 100644 index 0000000..d58848c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_capacity_get.c @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_capacity_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a READ_CAPACITY command to the storage */ +/* device. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_host_class_storage_media_format_capacity_get */ +/* Get format capacity */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_long_get_big_endian Get 32-bit big endian */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_capacity_get(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +UCHAR *cbw; +UCHAR *capacity_response; +UINT command_length; +ULONG command_retry; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_MEDIA_CAPACITY_GET, storage, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Set the default to either 512 or 2048 bytes. */ + switch (storage -> ux_host_class_storage_media_type) + { + + case UX_HOST_CLASS_STORAGE_MEDIA_FAT_DISK: + case UX_HOST_CLASS_STORAGE_MEDIA_IOMEGA_CLICK: + + storage -> ux_host_class_storage_sector_size = UX_HOST_CLASS_STORAGE_SECTOR_SIZE_FAT; + break; + + case UX_HOST_CLASS_STORAGE_MEDIA_CDROM: + case UX_HOST_CLASS_STORAGE_MEDIA_OPTICAL_DISK: + + storage -> ux_host_class_storage_sector_size = UX_HOST_CLASS_STORAGE_SECTOR_SIZE_OTHER; + break; + + default: + + /* Unsupported device. */ + return(UX_HOST_CLASS_MEDIA_NOT_SUPPORTED); + } + + /* First, we test if the device is ready. Then try to read its capacity. + On floppies, this operation tends to fail a few times. So we try harder. */ + for (command_retry = 0; command_retry < UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RETRY; command_retry++) + { + + /* Some devices require we do this. */ + status = _ux_host_class_storage_media_format_capacity_get(storage); + if (status != UX_SUCCESS) + return(status); + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the READ_CAPACITY command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_READ_CAPACITY_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_READ_CAPACITY_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_READ_CAPACITY_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_IN, UX_HOST_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH, command_length); + + /* Prepare the READ_CAPACITY command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_READ_CAPACITY_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_READ_CAPACITY; + + /* Obtain a block of memory for the answer. */ + capacity_response = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_READ_CAPACITY_RESPONSE_LENGTH); + if (capacity_response == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, capacity_response); + + /* Check for error during transfer. */ + if (status != UX_SUCCESS) + { + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(capacity_response); + + /* We return a sad status. */ + return(status); + } + + /* Check the sense code */ + if (storage -> ux_host_class_storage_sense_code == UX_SUCCESS) + { + + /* The data is valid, save the sector size. */ + storage -> ux_host_class_storage_sector_size = _ux_utility_long_get_big_endian(capacity_response + UX_HOST_CLASS_STORAGE_READ_CAPACITY_DATA_SECTOR_SIZE); + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(capacity_response); + + /* We return a happy status. */ + return(UX_SUCCESS); + } + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(capacity_response); + } + + /* We get here when we could not retrieve the sector size through the READ_CAPACITY command. + It's OK, we still calculated the default based on the device type. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_characteristics_get.c b/common/usbx_host_classes/src/ux_host_class_storage_media_characteristics_get.c new file mode 100644 index 0000000..3d5975a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_characteristics_get.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_characteristics_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a INQUIRY command to get the type of */ +/* device/media we are dealing with. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_host_class_storage_media_capacity_get */ +/* Get media capacity */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_characteristics_get(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +UCHAR *cbw; +UCHAR *inquiry_response; +UINT command_length; + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Write Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_INQUIRY_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_INQUIRY_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_INQUIRY_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_IN, UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH, command_length); + + /* Prepare the INQUIRY command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_INQUIRY_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_INQUIRY; + + /* Store the length of the Inquiry Response. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_INQUIRY_ALLOCATION_LENGTH) = UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH; + + /* Obtain a block of memory for the answer. */ + inquiry_response = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_LENGTH); + if (inquiry_response == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, inquiry_response); + + /* If we have a transport error, there is not much we can do, simply return the + error. */ + if (status == UX_SUCCESS) + { + + /* The Inquiry response contains the type of device/media and a media removable flag. */ + storage -> ux_host_class_storage_media_type = *(inquiry_response + UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_PERIPHERAL_TYPE); + storage -> ux_host_class_storage_lun_removable_media_flags[storage -> ux_host_class_storage_lun] = *(inquiry_response + UX_HOST_CLASS_STORAGE_INQUIRY_RESPONSE_REMOVABLE_MEDIA); + + /* Attempt to read the device capacity in order to retrieve the Sector Size. If the command fails, + we will default to 512 bytes for a regular drive and 2048 bytes for a CD-ROM or optical drive. */ + status = _ux_host_class_storage_media_capacity_get(storage); + } + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(inquiry_response); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_format_capacity_get.c b/common/usbx_host_classes/src/ux_host_class_storage_media_format_capacity_get.c new file mode 100644 index 0000000..959cc45 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_format_capacity_get.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_format_capacity_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a READ_FORMAT_CAPACITY command to the */ +/* device. Some USB storage disk require this function for the sanity */ +/* of their state machine. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_put_big_endian Put 32-bit big endian */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_format_capacity_get(UX_HOST_CLASS_STORAGE *storage) +{ + +UCHAR *cbw; +UCHAR *read_format_capacity_response; +UINT command_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_MEDIA_FORMAT_CAPACITY_GET, storage, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Write Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_READ_FORMAT_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_READ_FORMAT_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_READ_FORMAT_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_IN, UX_HOST_CLASS_STORAGE_READ_FORMAT_RESPONSE_LENGTH, command_length); + + /* Prepare the READ FORMAT CAPACITY command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_READ_FORMAT_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_READ_FORMAT_CAPACITY; + + /* Store the number of sectors to read. */ + _ux_utility_short_put_big_endian(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_READ_FORMAT_PARAMETER_LIST_LENGTH, UX_HOST_CLASS_STORAGE_READ_FORMAT_RESPONSE_LENGTH); + + /* Obtain a block of memory for the answer. */ + read_format_capacity_response = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_READ_FORMAT_RESPONSE_LENGTH); + if (read_format_capacity_response == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Send the command to transport layer. */ + _ux_host_class_storage_transport(storage, read_format_capacity_response); + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(read_format_capacity_response); + + /* If we have a transport error, there is not much we can do, we do not fail. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_mount.c b/common/usbx_host_classes/src/ux_host_class_storage_media_mount.c new file mode 100644 index 0000000..3b56772 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_mount.c @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_mount PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function tries to read the first sector on the media. It then */ +/* determines if this is a partition sector or a boot sector. If the */ +/* sector contains partition information, each partition is checked */ +/* for a DOS aware partition and mounted by FileX. */ +/* */ +/* If there is no partition sector or boot sector, we simply inform */ +/* FileX that a device is present and that its boot sector should be */ +/* periodically read to see if the device is mounted. This mechanism */ +/* applies to storage where the media can be removed (floppy, ZIP, */ +/* flash/smart media readers). */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* sector Boot sector start */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_media_mount Mount media */ +/* _ux_host_class_storage_media_open Open media */ +/* _ux_host_class_storage_partition_read Read partition */ +/* _ux_host_class_storage_media_read Read sectors of media */ +/* _ux_host_class_storage_start_stop Start the media */ +/* _ux_host_class_storage_unit_ready_test */ +/* Test unit ready */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_get Get 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_mount(UX_HOST_CLASS_STORAGE *storage, ULONG sector) +{ + +UCHAR *sector_memory; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_MEDIA_MOUNT, storage, sector, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + /* If the device is UFI, we need to start it. */ + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol == UX_HOST_CLASS_STORAGE_PROTOCOL_CBI) + { + + /* Start the media. */ + status = _ux_host_class_storage_start_stop(storage, UX_HOST_CLASS_STORAGE_START_MEDIA); + + /* Check the status. If the device does not start, fail the operation. + But we still return SUCCESS otherwise the storage thread has no chance to inspect this instance. */ + if (status != UX_SUCCESS) + return(UX_SUCCESS); + } +#endif + + /* Obtain memory for reading the partition sector, we do not use the storage instance + memory because this function is reentrant. */ + sector_memory = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, storage -> ux_host_class_storage_sector_size); + if (sector_memory == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Check if the device is now ready. */ + status = _ux_host_class_storage_unit_ready_test(storage); + + /* Check the status. There may be a transport error or the device is not ready. */ + if (status == UX_SUCCESS) + + /* Read the very first sector from the media. */ + status = _ux_host_class_storage_media_read(storage, sector, 1, sector_memory); + + /* Check the status. There may be a transport error or the device is not ready. */ + if (status != UX_SUCCESS) + { + + /* In any case, we free the sector memory. */ + _ux_utility_memory_free(sector_memory); + + if (status == UX_HOST_CLASS_STORAGE_SENSE_ERROR) + + /* The media is not there, so we will try to check for it at the storage + thread level every x seconds. */ + return(UX_SUCCESS); + + /* Since there was a transport error or command error, we do not try to mount + a fake media. */ + return(status); + } + + /* We need to examine this sector and determine if this is a partition sector + or a boot sector. */ + if (_ux_utility_short_get(sector_memory + 510) == UX_HOST_CLASS_STORAGE_PARTITION_SIGNATURE) + { + + /* If we have a signature, we know it is a valid media and it may be formatted. + If there is no signature, the media exist but maybe not formatted. Now determine + if this is a boot sector. The boot sector has a special signature in the first + couple of bytes. + BUT !!! The first boot sector of an iPod is actually a partition, so we need to + look further and see if the number of sector per FAT and the first partition are + set to 0. This will indicate a partition sector. + */ + if ((*sector_memory == 0xe9) || ((*sector_memory == 0xeb) && *(sector_memory + 2) == 0x90)) + { + + /* Check for fake boot sector. */ + if (_ux_utility_short_get(sector_memory + 0x16) != 0x0 || _ux_utility_long_get(sector_memory + 0x24) != 0x0) + { + + /* This is a boot sector signature. */ + _ux_host_class_storage_media_open(storage, sector); + _ux_utility_memory_free(sector_memory); + return(UX_SUCCESS); + } + } + + _ux_host_class_storage_partition_read(storage, sector_memory, sector); + _ux_utility_memory_free(sector_memory); + return(UX_SUCCESS); + } + else + { + + /* The drive does not contain a partition table or FAT MBR at LBA 0*/ + status = UX_ERROR; + } + + /* Free all resources used. */ + _ux_utility_memory_free(sector_memory); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_open.c b/common/usbx_host_classes/src/ux_host_class_storage_media_open.c new file mode 100644 index 0000000..9f9f963 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_open.c @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_open PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will ask FileX to mount a new partition for this */ +/* device. This function has some FileX dependencies. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* hidden_sectors Number of hidden sectors */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* fx_media_open Media open */ +/* _ux_host_class_storage_media_protection_check */ +/* Check for protection */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_storage_media_mount Media open */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_open(UX_HOST_CLASS_STORAGE *storage, ULONG hidden_sectors) +{ + +UINT status; +UINT media_index; +UX_HOST_CLASS_STORAGE_MEDIA *storage_media; +FX_MEDIA *media; +UX_HOST_CLASS *class; + + + /* We need the class container. */ + class = storage -> ux_host_class_storage_class; + + /* Point the media structure to the first media in the container. */ + storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) class -> ux_host_class_media; + + /* Locate a free partition in the storage instance. */ + for (media_index = 0; media_index < UX_HOST_CLASS_STORAGE_MAX_MEDIA; media_index++) + { + + /* Get the FileX media pointer for that media. */ + media = &storage_media -> ux_host_class_storage_media; + + /* Is the media valid? */ + if (media -> fx_media_id == 0) + { + + /* Save the storage instance in the media instance. */ + media -> fx_media_driver_info = (VOID *) storage; + + /* Save the number of hidden sectors in this partition. */ + storage_media -> ux_host_class_storage_media_partition_start = hidden_sectors; + + /* Save the LUN number in the storage media instance. */ + storage_media -> ux_host_class_storage_media_lun = storage -> ux_host_class_storage_lun; + + /* Save the Sector size in the storage media instance. */ + storage_media -> ux_host_class_storage_media_sector_size = storage -> ux_host_class_storage_sector_size; + + /* Save the storage media instance in the user reserved area in the FX_MEDIA structure. */ + media -> fx_media_reserved_for_user = (ALIGN_TYPE) storage_media; + + /* We now need to allocate a block of memory for FileX to use when doing transfers + The default buffer size is 8K. The value used for the definition is UX_HOST_CLASS_STORAGE_MEMORY_BUFFER_SIZE. + This value can be changed to save on memory space but should not be smaller than + the media sector size (which should be 512 bytes). Because USB devices are SCSI + devices and there is a great deal of overhead when doing read/writes, it is better + to leave the default buffer size or even increase it. */ + storage_media -> ux_host_class_storage_media_memory = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_MEMORY_BUFFER_SIZE); + if (storage_media -> ux_host_class_storage_media_memory == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_MEDIA_OPEN, storage, media, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ask FileX to mount the partition. */ + status = fx_media_open(media, UX_HOST_CLASS_STORAGE_MEDIA_NAME, _ux_host_class_storage_driver_entry, + storage, storage_media -> ux_host_class_storage_media_memory, + UX_HOST_CLASS_STORAGE_MEMORY_BUFFER_SIZE); + + /* If the media is mounted, update the status for the application. */ + if (status == UX_SUCCESS) + storage_media -> ux_host_class_storage_media_status = UX_HOST_CLASS_STORAGE_MEDIA_MOUNTED; + + else + + /* Free the memory resources. */ + _ux_utility_memory_free(storage_media -> ux_host_class_storage_media_memory); + + /* Return completion status. */ + return(status); + } + + /* Move to next entry in the media array. */ + storage_media++; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_MEMORY_ERROR); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_MEMORY_ERROR, storage, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Return error. */ + return(UX_HOST_CLASS_MEMORY_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_protection_check.c b/common/usbx_host_classes/src/ux_host_class_storage_media_protection_check.c new file mode 100644 index 0000000..f24d91a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_protection_check.c @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_protection_check PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a MODE_SENSE command to retrieve the medium */ +/* and device parameters. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_get_big_endian Get short value */ +/* _ux_utility_short_put_big_endian Put short value */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_protection_check(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +UCHAR *cbw; +UCHAR *mode_sense_response; +UINT command_length; +ULONG wp_parameter_location; + + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Write Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_IN, UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE_LENGTH, command_length); + + /* Prepare the MODE_SENSE command block. Distinguish between SUBCLASSES. */ + switch (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass) + { + + case UX_HOST_CLASS_STORAGE_SUBCLASS_RBC : +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + case UX_HOST_CLASS_STORAGE_SUBCLASS_UFI : +#endif + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_MODE_SENSE; + wp_parameter_location = UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_ATTRIBUTES; + break; + + default : + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT; + wp_parameter_location = UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_ATTRIBUTES_SHORT; + break; + } + + /* We ask for all pages. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_PC_PAGE_CODE) = UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE; + + /* Store the length of the Inquiry Response. */ + _ux_utility_short_put_big_endian(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_PARAMETER_LIST_LENGTH, UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE_LENGTH); + + /* Obtain a block of memory for the answer. */ + mode_sense_response = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE_LENGTH); + if (mode_sense_response == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, mode_sense_response); + + /* Reset the Write Protected flag. */ + storage -> ux_host_class_storage_write_protected_media = UX_FALSE; + + /* If we have a transport error, there is not much we can do, simply return the + error. The default will be non protected disk. */ + if (status == UX_SUCCESS) + { + /* Check to see that we have at least the header of the MODE_SENSE response, if not, ignore the data payload. + Some devices do not stall this command but rather return 0 byte length. */ + if(_ux_utility_short_get_big_endian(mode_sense_response + UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_MODE_DATA_LENGTH) >= UX_HOST_CLASS_STORAGE_MODE_SENSE_HEADER_PAGE_LENGTH) + { + + /* The Mode Sense response tells us if the media is Write protected or not. */ + if (*(mode_sense_response + wp_parameter_location) & UX_HOST_CLASS_STORAGE_MODE_SENSE_RESPONSE_ATTRIBUTES_WP) + + /* The Mode Sense response tells us if the media is Write protected or not. */ + storage -> ux_host_class_storage_write_protected_media = UX_TRUE; + } + } + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(mode_sense_response); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_read.c b/common/usbx_host_classes/src/ux_host_class_storage_media_read.c new file mode 100644 index 0000000..a96afc5 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_read.c @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will read one or more logical sector from the media. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* sector_start Starting sector */ +/* sector_count Number of sectors to read */ +/* data_pointer Pointer to data to read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize the CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_utility_long_put_big_endian Put 32-bit word */ +/* _ux_utility_short_put_big_endian Put 16-bit word */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_read(UX_HOST_CLASS_STORAGE *storage, ULONG sector_start, + ULONG sector_count, UCHAR *data_pointer) +{ + +UINT status; +UCHAR *cbw; +UINT command_length; +UINT media_retry; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_MEDIA_READ, storage, sector_start, sector_count, data_pointer, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Reset the retry count. */ + media_retry = UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RETRY; + + /* We may need several attempts. */ + while (media_retry-- != 0) + { + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Read Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_READ_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_READ_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_READ_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_IN, sector_count * storage -> ux_host_class_storage_sector_size, command_length); + + /* Prepare the MEDIA READ command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_READ_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_READ16; + + /* Store the sector start (LBA field). */ + _ux_utility_long_put_big_endian(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_READ_LBA, sector_start); + + /* Store the number of sectors to read. */ + _ux_utility_short_put_big_endian(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_READ_TRANSFER_LENGTH, (USHORT) sector_count); + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, data_pointer); + if (status != UX_SUCCESS) + return(status); + + /* Did the command succeed? */ + if (storage -> ux_host_class_storage_sense_code == UX_SUCCESS) + { + + /* Check for completeness of sector read. */ + if (storage -> ux_host_class_storage_data_phase_length != sector_count * storage -> ux_host_class_storage_sector_size) + { + + /* This can happen if the device sent less data than the host + requested. This does not fit our definition of success and + retrying shouldn't change the outcome, so we return an error. */ + + /* We got an error during read. Packet not complete. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_DATA_LESS_THAN_EXPECTED); + + /* Return to FileX. */ + return(UX_ERROR); + } + + /* The read succeeded. */ + return(UX_SUCCESS); + } + + /* The command did not succeed. Retry. */ + } + + /* Check if the media in the device has been removed. If so + we have to tell FileX that the media is closed. */ + return(UX_HOST_CLASS_STORAGE_SENSE_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_recovery_sense_get.c b/common/usbx_host_classes/src/ux_host_class_storage_media_recovery_sense_get.c new file mode 100644 index 0000000..a865008 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_recovery_sense_get.c @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_recovery_sense_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a MODE_SENSE command to recover from a read */ +/* and write error. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_put_big_endian Put short value */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_recovery_sense_get(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +UCHAR *cbw; +UCHAR *mode_sense_response; +UINT command_length; + + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Write Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_MODE_SENSE_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_IN, UX_HOST_CLASS_STORAGE_MODE_SENSE_TP_PAGE, command_length); + + /* Prepare the MODE_SENSE command block. Distinguish between SUBCLASSES. */ + switch (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass) + { + + case UX_HOST_CLASS_STORAGE_SUBCLASS_RBC : +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + case UX_HOST_CLASS_STORAGE_SUBCLASS_UFI : +#endif + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_MODE_SENSE; + break; + + default : + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_MODE_SENSE_SHORT; + break; + } + + /* We ask for a specific page page but we put the buffer to maximum size. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_PC_PAGE_CODE) = UX_HOST_CLASS_STORAGE_MODE_SENSE_TP_PAGE; + + /* Store the length of the Mode Sense Response. */ + _ux_utility_short_put_big_endian(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_MODE_SENSE_PARAMETER_LIST_LENGTH, UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE_LENGTH); + + /* Obtain a block of memory for the answer. */ + mode_sense_response = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_MODE_SENSE_ALL_PAGE_LENGTH); + if (mode_sense_response == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, mode_sense_response); + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(mode_sense_response); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_media_write.c b/common/usbx_host_classes/src/ux_host_class_storage_media_write.c new file mode 100644 index 0000000..d67fac9 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_media_write.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_media_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will write one or more logical sector to the media. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* sector_start Starting sector */ +/* sector_count Number of sectors to write */ +/* data_pointer Pointer to data to write */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize the CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_utility_long_put_big_endian Put 32-bit word */ +/* _ux_utility_short_put_big_endian Put 16-bit word */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_media_write(UX_HOST_CLASS_STORAGE *storage, ULONG sector_start, + ULONG sector_count, UCHAR *data_pointer) +{ + +UINT status; +UCHAR *cbw; +UINT media_retry; +UINT command_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_MEDIA_WRITE, storage, sector_start, sector_count, data_pointer, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Write Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_WRITE_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_WRITE_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_WRITE_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_OUT, sector_count * storage -> ux_host_class_storage_sector_size, + command_length); + + /* Prepare the MEDIA WRITE command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_WRITE_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_WRITE16; + + /* Store the sector start (LBA field). */ + _ux_utility_long_put_big_endian(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_WRITE_LBA, sector_start); + + /* Store the number of sectors to write. */ + _ux_utility_short_put_big_endian(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_WRITE_TRANSFER_LENGTH, (USHORT) sector_count); + + /* Reset the retry count. */ + media_retry = UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RETRY; + + /* We may need several attempts. */ + while (media_retry-- != 0) + { + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, data_pointer); + if (status != UX_SUCCESS) + return(status); + + /* Check the sense code */ + if (storage -> ux_host_class_storage_sense_code == UX_SUCCESS) + return(UX_SUCCESS); + } + + /* Return sense error. */ + return(UX_HOST_CLASS_STORAGE_SENSE_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_partition_read.c b/common/usbx_host_classes/src/ux_host_class_storage_partition_read.c new file mode 100644 index 0000000..7a79694 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_partition_read.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_partition_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will analyze the partition table and parse all the */ +/* partitions. It may happen that a partition entry points to a */ +/* secondary partition table. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* sector_memory Pointer to memory for sector */ +/* sector Sector number */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_media_mount Mount media */ +/* _ux_host_class_storage_media_open Open media */ +/* _ux_utility_long_get Get 32-bit word */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_storage_media_mount Mount media */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_partition_read(UX_HOST_CLASS_STORAGE *storage, UCHAR *sector_memory, ULONG sector) +{ + +UINT status = UX_ERROR; +UINT partition_index; + + + /* Point the sector buffer to the first partition entry. */ + sector_memory += UX_HOST_CLASS_STORAGE_PARTITION_TABLE_START; + + /* There are 4 partitions in a partition table. */ + for (partition_index = 0; partition_index < 4; partition_index++) + { + + /* Check if we recognize this partition entry. */ + switch(*(sector_memory + UX_HOST_CLASS_STORAGE_PARTITION_TYPE)) + { + + case UX_HOST_CLASS_STORAGE_PARTITION_FAT_12: + case UX_HOST_CLASS_STORAGE_PARTITION_FAT_16: + case UX_HOST_CLASS_STORAGE_PARTITION_FAT_16L: + case UX_HOST_CLASS_STORAGE_PARTITION_FAT_16_LBA_MAPPED: + case UX_HOST_CLASS_STORAGE_PARTITION_FAT_32_1: + case UX_HOST_CLASS_STORAGE_PARTITION_FAT_32_2: + + /* We have found a legal partition entry pointing to a potential boot sector. */ + status = _ux_host_class_storage_media_open(storage, sector + _ux_utility_long_get(sector_memory + UX_HOST_CLASS_STORAGE_PARTITION_SECTORS_BEFORE)); + break; + + case UX_HOST_CLASS_STORAGE_PARTITION_EXTENDED: + case UX_HOST_CLASS_STORAGE_PARTITION_EXTENDED_LBA_MAPPED: + + /* We have found an entry to an extended partition. We need to read that partition sector + and recursively mount all partitions found. */ + status = _ux_host_class_storage_media_mount(storage, sector + _ux_utility_long_get(sector_memory + UX_HOST_CLASS_STORAGE_PARTITION_SECTORS_BEFORE)); + break; + + default: + + /* We have found something which is not a DOS recognized partition, or an empty entry. + Ignore it and proceed with the rest. */ + break; + } + + /* Move to the next partition entry. */ + sector_memory += UX_HOST_CLASS_STORAGE_PARTITION_TABLE_SIZE; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_request_sense.c b/common/usbx_host_classes/src/ux_host_class_storage_request_sense.c new file mode 100644 index 0000000..bf0701c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_request_sense.c @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_request_sense PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will send a request sense to the device to see what */ +/* error happened during the last command. Request sense commands */ +/* cannot be nested. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize CBW */ +/* _ux_host_class_storage_transport Send command */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_copy Copy memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_request_sense(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +UCHAR *cbw; +UCHAR *request_sense_response; +ULONG sense_code; +UINT command_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_REQUEST_SENSE, storage, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Clear the former sense code value. */ + storage -> ux_host_class_storage_sense_code = 0; + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Request Sense Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_REQUEST_SENSE_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_REQUEST_SENSE_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_REQUEST_SENSE_COMMAND_LENGTH_SBC; +#endif + + /* Check of we are reentering a REQUEST SENSE command which is illegal. */ + if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_REQUEST_SENSE_OPERATION) == UX_HOST_CLASS_STORAGE_SCSI_REQUEST_SENSE) + return(UX_ERROR); + + /* Save the current command so that we can re-initiate it after the + REQUEST_SENSE command. */ + _ux_utility_memory_copy(storage -> ux_host_class_storage_saved_cbw, storage -> ux_host_class_storage_cbw, UX_HOST_CLASS_STORAGE_CBW_LENGTH); + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, UX_HOST_CLASS_STORAGE_DATA_IN, UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH, command_length); + + /* Prepare the REQUEST SENSE command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_REQUEST_SENSE_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_REQUEST_SENSE; + + /* Store the length of the Request Sense Response. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_REQUEST_SENSE_ALLOCATION_LENGTH) = UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH; + + /* Obtain a block of memory for the answer. */ + request_sense_response = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_LENGTH); + if (request_sense_response == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, request_sense_response); + + /* If we have a transport error, there is not much we can do, simply return the error. */ + if (status == UX_SUCCESS) + { + + /* We have a successful transaction, even though the sense code could reflect an error. The sense code + will be assembled and store in the device instance. */ + sense_code = (((ULONG) *(request_sense_response + UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_SENSE_KEY)) & 0x0f) << 16; + sense_code |= ((ULONG) *(request_sense_response + UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE)) << 8; + sense_code |= (ULONG) *(request_sense_response + UX_HOST_CLASS_STORAGE_REQUEST_SENSE_RESPONSE_CODE_QUALIFIER); + + /* Store the sense code in the storage instance. */ + storage -> ux_host_class_storage_sense_code = sense_code; + } + + /* Free the memory resource used for the command response. */ + _ux_utility_memory_free(request_sense_response); + + /* Restore the current CBW command. */ + _ux_utility_memory_copy(storage -> ux_host_class_storage_cbw, storage -> ux_host_class_storage_saved_cbw, UX_HOST_CLASS_STORAGE_CBW_LENGTH); + + /* Return completion code. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_sense_code_translate.c b/common/usbx_host_classes/src/ux_host_class_storage_sense_code_translate.c new file mode 100644 index 0000000..9d630ce --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_sense_code_translate.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_sense_code_translate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will perform a conversion between the SCSI error */ +/* codes and error codes expected by FileX. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* status Status to convert */ +/* */ +/* OUTPUT */ +/* */ +/* Converted Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_sense_code_translate(UX_HOST_CLASS_STORAGE *storage, UINT status) +{ + + UX_PARAMETER_NOT_USED(storage); + + /* Return status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_start_stop.c b/common/usbx_host_classes/src/ux_host_class_storage_start_stop.c new file mode 100644 index 0000000..53bf7e9 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_start_stop.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_start_stop.c PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts or stops the UFI device. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* start_stop_flag 1 or 0 if start/stop */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize the CBW */ +/* _ux_host_class_storage_transport Send transport layer command */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_start_stop(UX_HOST_CLASS_STORAGE *storage, + ULONG start_stop_signal) +{ + +UINT status; +UCHAR *cbw; +UINT command_length; +ULONG command_retry; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_START_STOP, storage, start_stop_signal, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Use a pointer for the CBW, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the START_STOP Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_START_STOP_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_START_STOP_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_START_STOP_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, 0, 0, command_length); + + /* Prepare the START STOP command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_START_STOP_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_START_STOP; + + /* Set the required signal. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_START_STOP_START_BIT) = (UCHAR) start_stop_signal; + + /* On floppies, this operation tends to fail a few times. So we try harder. */ + for (command_retry = 0; command_retry < 50; command_retry++) + { + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, UX_NULL); + + /* If we have a transport error give up. */ + if(status != UX_SUCCESS) + + /* Return completion status. */ + return(status); + + /* Check the CSW. We may learn something there about the state of the device. */ + if (storage -> ux_host_class_storage_sense_code == 0) + return(UX_SUCCESS); + } + + /* The start/Stop did not work. */ + return(UX_ERROR); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_thread_entry.c b/common/usbx_host_classes/src/ux_host_class_storage_thread_entry.c new file mode 100644 index 0000000..8d6b056 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_thread_entry.c @@ -0,0 +1,306 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_thread_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is awaken every 2 seconds to check if there was a */ +/* device insertion on a specific media. This is the only way we can */ +/* remount a media after the storage instance has opened the media to */ +/* FileX and the media is either not present or was removed and is */ +/* being re-)inserted. */ +/* */ +/* INPUT */ +/* */ +/* class_address Class address */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* fx_media_close Close FileX media */ +/* _ux_host_class_storage_device_reset Reset device */ +/* _ux_host_class_storage_media_mount Mount the media */ +/* _ux_host_class_storage_unit_ready_test */ +/* Test for unit ready */ +/* _ux_host_class_storage_media_characteristics_get */ +/* Get media characteristics */ +/* _ux_host_class_storage_media_format_capacity_get */ +/* Get media format capacity */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get a semaphore */ +/* _ux_utility_semaphore_put Put a semaphore */ +/* _ux_utility_delay_ms Thread sleep */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_storage_thread_entry(ULONG class_address) +{ + +UX_HOST_CLASS *class; +UX_HOST_CLASS_STORAGE *storage; +UINT status; +ULONG lun_index; +UX_HOST_CLASS_STORAGE_MEDIA *storage_media; +FX_MEDIA *media; +UCHAR *memory; +UINT media_index; + + + /* Setup pointer to class. */ + UX_THREAD_EXTENSION_PTR_GET(class, UX_HOST_CLASS, class_address) + + /* This thread goes on forever once started. */ + while(1) + { + + /* We need to wake every 2 seconds or so. */ + _ux_utility_delay_ms(UX_HOST_CLASS_STORAGE_THREAD_SLEEP_TIME); + + /* We need to parse all the storage instances and check for a removable + media flag. */ + storage = (UX_HOST_CLASS_STORAGE *) class -> ux_host_class_first_instance; + + while (storage != UX_NULL) + { + + /* Check if the instance is live and the device is removable. */ + if ((storage -> ux_host_class_storage_state == UX_HOST_CLASS_INSTANCE_LIVE)) + { + + /* We need to ensure nobody is accessing this storage instance. We use + the storage class instance semaphore to protect. */ + status = _ux_utility_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + break; + + /* Each LUN must be parsed and mounted. */ + for (lun_index = 0; lun_index <= storage -> ux_host_class_storage_max_lun; lun_index++) + { + + if (storage -> ux_host_class_storage_lun_removable_media_flags[lun_index] != UX_HOST_CLASS_STORAGE_MEDIA_REMOVABLE) + continue; + + /* Check the type of LUN, we only deal with the ones we know how to mount. */ + if ((storage -> ux_host_class_storage_lun_types[lun_index] == UX_HOST_CLASS_STORAGE_MEDIA_FAT_DISK) || + (storage -> ux_host_class_storage_lun_types[lun_index] == UX_HOST_CLASS_STORAGE_MEDIA_OPTICAL_DISK) || + (storage -> ux_host_class_storage_lun_types[lun_index] == UX_HOST_CLASS_STORAGE_MEDIA_IOMEGA_CLICK)) + { + + /* Set the LUN into the storage instance. */ + storage -> ux_host_class_storage_lun = lun_index; + + /* Check if the device is now ready. */ + status = _ux_host_class_storage_unit_ready_test(storage); + + /* If we have an transport failure here, we are in trouble! The storage device + is in an unstable state and should be reset completely. */ + if (status != UX_SUCCESS) + { + + /* Reset device. */ + _ux_host_class_storage_device_reset(storage); + break; + } + + /* Process relative to device status. */ + switch(storage -> ux_host_class_storage_sense_code >> 16) + { + + case UX_HOST_CLASS_STORAGE_SENSE_KEY_NOT_READY: + + /* We may need to unmount this partition if it was mounted before. + To do so, we need to parse the existing media instance and find out + if this partition was already mounted. */ + + storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) class -> ux_host_class_media; + + /* Scan all instances of media. */ + for (media_index = 0; media_index < UX_HOST_CLASS_STORAGE_MAX_MEDIA; media_index++) + { + + /* Get the FileX Media attached to this media. */ + media = &storage_media -> ux_host_class_storage_media; + + /* Check for the storage instance and lun number. */ + if ((media -> fx_media_id != 0) && (media -> fx_media_driver_info == (VOID *) storage) && + (storage_media -> ux_host_class_storage_media_lun == storage -> ux_host_class_storage_lun)) + { + + /* We preserve the memory used by this media. */ + memory = storage_media -> ux_host_class_storage_media_memory; + + /* Let FileX use this instance. */ + status = _ux_utility_semaphore_put(&storage -> ux_host_class_storage_semaphore); + + /* Ask FileX to unmount the partition. */ + fx_media_close(media); + + /* This device is now unmounted. */ + storage_media -> ux_host_class_storage_media_status = UX_HOST_CLASS_STORAGE_MEDIA_UNMOUNTED; + + /* Reset the media ID. */ + media -> fx_media_id = 0; + + /* Now, we protect the storage instance. */ + status = _ux_utility_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + break; + + /* Free the memory block used for data transfer on behalf of FileX. */ + _ux_utility_memory_free(memory); + } + + /* Next entry in the media array. */ + storage_media++; + } + break; + + case UX_HOST_CLASS_STORAGE_SENSE_KEY_UNIT_ATTENTION: + + /* We may need to unmount this partition if it was mounted before. + To do so, we need to parse the existing media instance and find out + if this partition was already mounted. */ + storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *) class -> ux_host_class_media; + + /* Scan all instances of media. */ + for (media_index = 0; media_index < UX_HOST_CLASS_STORAGE_MAX_MEDIA; media_index++) + { + + /* Get the FileX Media attached to this media. */ + media = &storage_media -> ux_host_class_storage_media; + + /* Check for the storage instance and lun number. */ + if ((media -> fx_media_id != 0) && (media -> fx_media_driver_info == (VOID *) storage) && + (storage_media -> ux_host_class_storage_media_lun == storage -> ux_host_class_storage_lun)) + { + + /* We preserve the memory used by this media. */ + memory = storage_media -> ux_host_class_storage_media_memory; + + /* Let FileX use this instance. */ + status = _ux_utility_semaphore_put(&storage -> ux_host_class_storage_semaphore); + + /* Ask FileX to unmount the partition. */ + fx_media_close(media); + + /* This device is now unmounted. */ + storage_media -> ux_host_class_storage_media_status = UX_HOST_CLASS_STORAGE_MEDIA_UNMOUNTED; + + /* Reset the media ID. */ + media -> fx_media_id = 0; + + /* Now, we protect the storage instance. */ + status = _ux_utility_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + break; + + /* Free the memory block used for data transfer on behalf of FileX. */ + _ux_utility_memory_free(memory); + } + + /* Next entry in the media array. */ + storage_media++; + } + + /* Now, we need to retest the media to see if it is there. */ + status = _ux_host_class_storage_unit_ready_test(storage); + + /* If we have an transport failure here, we are in trouble! The storage device + is in an unstable state and should be reset completely. */ + if (status != UX_SUCCESS) + { + + /* Reset device. */ + _ux_host_class_storage_device_reset(storage); + break; + } + + if (storage -> ux_host_class_storage_sense_code == 0) + { + + /* Get the media type supported by this storage device. */ + status = _ux_host_class_storage_media_characteristics_get(storage); + if (status != UX_SUCCESS) + break; + + /* Get the format capacity of this storage device. */ + status = _ux_host_class_storage_media_format_capacity_get(storage); + if (status != UX_SUCCESS) + break; + + /* Let FileX use this instance. */ + _ux_utility_semaphore_put(&storage -> ux_host_class_storage_semaphore); + + /* The device seems to have been inserted, try to mount it. */ + _ux_host_class_storage_media_mount(storage, 0); + + /* Now, we protect the storage instance. */ + _ux_utility_semaphore_get(&storage -> ux_host_class_storage_semaphore, UX_WAIT_FOREVER); + } + break; + + default: + break; + } + } + } + + /* Other threads are now allowed to access this storage instance. */ + status = _ux_utility_semaphore_put(&storage -> ux_host_class_storage_semaphore); + } + + /* Move to the next entry in the storage instances link. */ + storage = storage -> ux_host_class_storage_next_instance; + } + } +} diff --git a/common/usbx_host_classes/src/ux_host_class_storage_transport.c b/common/usbx_host_classes/src/ux_host_class_storage_transport.c new file mode 100644 index 0000000..27f9d62 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_transport.c @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_transport PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the transport layer for all protocols. It perform */ +/* the error recovery and retries if needed. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* data_pointer Pointer to data */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_storage_transport) Class storage transport */ +/* _ux_host_class_storage_device_reset Reset device */ +/* _ux_host_class_storage_request_sense Class request sense */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_transport(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer) +{ + +UINT status; +UINT csw_status; + + + /* Reset the sense code. */ + storage -> ux_host_class_storage_sense_code = UX_SUCCESS; + + /* Send the command to the appropriate transport. */ + status = storage -> ux_host_class_storage_transport(storage, data_pointer); + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + /* The treatment of errors is different according to the protocol used. BO and CB belong + to the CSW group. CBI is separate. */ + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceProtocol != UX_HOST_CLASS_STORAGE_PROTOCOL_CBI) + { +#endif + + /* Check the status. */ + if (status != UX_SUCCESS) + + /* There was a more serious error. Just give up! */ + return(status); + + /* The command transfer was OK but maybe we have a CSW error. */ + csw_status = storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS]; + if (csw_status == 0) + return(UX_SUCCESS); + + /* Check for a command failure. If so, we need to sense the error with + a REQUEST SENSE command. */ + status = _ux_host_class_storage_request_sense(storage); + + /* If we have an transport failure here, we are in trouble! The storage device is in + an unstable state and should be reset completely. */ + if (status != UX_SUCCESS) + { + + /* Reset device. */ + _ux_host_class_storage_device_reset(storage); + return(status); + } + + /* The sense code is saved in the device instance. We can return safely. */ + return(UX_SUCCESS); + +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + } + else + { + + switch (status) + { + + case UX_SUCCESS: + return(UX_SUCCESS); + + case UX_TRANSFER_STALLED: + + /* The endpoint was halted by a stall condition and needs to be reset. */ + _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint); + + /* The endpoint was halted by a stall condition and needs to be reset. */ + _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint); + + /* Check for a command failure. If so, we need to sense the error with + a REQUEST SENSE command. */ + status = _ux_host_class_storage_request_sense(storage); + return(status); + + default: + + /* There was a more serious error. Just give up! */ + return(status); + } + } +#endif +} diff --git a/common/usbx_host_classes/src/ux_host_class_storage_transport_bo.c b/common/usbx_host_classes/src/ux_host_class_storage_transport_bo.c new file mode 100644 index 0000000..26a6645 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_transport_bo.c @@ -0,0 +1,387 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_transport_bo PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the transport layer for the Bulk Only protocol. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* data_pointer Pointer to data */ +/* */ +/* OUTPUT */ +/* */ +/* The transfer completion status. It's possible for the transfer to */ +/* succeed but for the command to fail. The CSW must be checked to */ +/* determine command status. */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_device_reset Reset mass storage */ +/* _ux_host_stack_endpoint_reset Reset endpoint */ +/* _ux_host_stack_transfer_request Process host stack transfer */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_utility_long_get Get 32-bit word */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_transport_bo(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer) +{ + +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *cbw; +UINT retry; +ULONG data_phase_requested_length; +ULONG data_phase_transfer_size; +UCHAR *get_status_response; + + + /* Get the pointer to the transfer request. */ + transfer_request = &storage -> ux_host_class_storage_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Use a pointer for the cbw, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Fill in the transfer request parameters. */ + transfer_request -> ux_transfer_request_data_pointer = cbw; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_STORAGE_CBW_LENGTH; + + /* It's possible for the bulk out endpoint to stall; in that case, we clear + it and try again. */ + for (retry = 0; ; retry++) + { + + /* Send the CBW on the bulk out endpoint. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the status of the command (NB, the command is not sent yet since + the bulk transport is non blocking). */ + if (status != UX_SUCCESS) + return(status); + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + + /* Did we successfully send the CBW? */ + if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS) + + /* Jump to the data stage. */ + break; + + /* The transfer stalled. Is this the first time? */ + if (retry == 0) + { + + /* We must do a reset recovery. */ + _ux_host_class_storage_device_reset(storage); + + /* In virtually all cases, the reset should've fixed it, so just + resend the command. */ + } + else + { + + /* Well, we tried! */ + return(transfer_request -> ux_transfer_request_completion_code); + } + } + + /* Get the length of the data payload. */ + data_phase_requested_length = _ux_utility_long_get(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH); + + /* Reset the data phase memory size. */ + storage -> ux_host_class_storage_data_phase_length = 0; + + /* Perform the data stage - if there is any. */ + while (data_phase_requested_length != 0) + { + + /* Check if we can finish the transaction with one data phase. */ + if (data_phase_requested_length > UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE) + + /* We have too much data to send in one phase. Split into smaller chunks. */ + data_phase_transfer_size = UX_HOST_CLASS_STORAGE_MAX_TRANSFER_SIZE; + + else + + /* The transfer size can be the requested length. */ + data_phase_transfer_size = data_phase_requested_length; + + /* Check the direction and determine which endpoint to use. */ + if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN) + transfer_request = &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Fill in the transfer request data payload buffer. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + + /* Store the requested length in the transfer request. */ + transfer_request -> ux_transfer_request_requested_length = data_phase_transfer_size; + + /* Perform data payload transfer (in or out). */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the status of the data payload. */ + if (status == UX_SUCCESS) + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we may have a time out or if we had a problem during the preparation of the transaction + we should abort the SCSI transaction. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error that cannot be recovered, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + + /* Check the transfer status. If there is a transport error, we still need to read the CSW + and let the upper layer retry. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* This is most likely a STALL. We must clear it and go straight + to reading the CSW. Note this doesn't necessarily mean the transfer + failed completely failed. For example, if this was a read, it + could mean the device had less data to send than we requested, + but we still received data. */ + + /* Check the direction and determine which endpoint to reset. */ + if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN) + _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint); + else + _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint); + + /* We need to read the CSW now. */ + break; + } + + /* Adjust the total size that was requested. */ + data_phase_requested_length -= data_phase_transfer_size; + + /* And the data pointer. */ + data_pointer += data_phase_transfer_size; + } + + /* Now perform the status phase, i.e. try to get the CSW from the device. + According to the spec, if there is a failure the first time, we need to retry once + before reporting an error. */ + + /* Get the pointer to the transfer request, on the bulk in endpoint. */ + transfer_request = &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Fill in the transfer_request parameters. */ + transfer_request -> ux_transfer_request_data_pointer = (UCHAR *) &storage -> ux_host_class_storage_csw; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_STORAGE_CSW_LENGTH; + + /* Retry loop, we have 2 tries here. */ + for (retry = 0; retry < 2; retry++) + { + + /* Get the CSW on the bulk in endpoint. */ + status = _ux_host_stack_transfer_request(transfer_request); + if (status != UX_SUCCESS) + return(status); + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); + + /* Check semaphore status. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + + /* Did the transfer succeed? */ + if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS) + { + + /* The PASSED and COMMAND_FAILED error codes are nearly interchangeable + according to the spec, so we treat them similarly. */ + if (storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_PASSED || + storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_FAILED) + { + + /* Was this an OUT transport? */ + if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_OUT) + { + + /* Did the command fail, or succeed with non-zero data residue? */ + if (storage -> ux_host_class_storage_csw[UX_HOST_CLASS_STORAGE_CSW_STATUS] == UX_HOST_CLASS_STORAGE_CSW_FAILED || + _ux_utility_long_get(storage -> ux_host_class_storage_csw + UX_HOST_CLASS_STORAGE_CSW_DATA_RESIDUE) != 0) + { + + /* It's possible the bulk out endpoint is stalled. + This happens when 1) the device expects less data + than the host wants to send and 2) the endpoint + stalls after the last packet was sent (otherwise, + we'd have seen the stall during the data stage). + Query its status before clearing the stall. */ + + /* Allocate memory for Get Status response. */ + get_status_response = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (get_status_response == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Get the control endpoint's transfer request. */ + transfer_request = &storage -> ux_host_class_storage_device -> ux_device_control_endpoint.ux_endpoint_transfer_request; + + /* Setup transfer request for Get Status command. */ + transfer_request -> ux_transfer_request_data_pointer = get_status_response; + transfer_request -> ux_transfer_request_requested_length = 0x02; + transfer_request -> ux_transfer_request_function = UX_GET_STATUS; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_ENDPOINT; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = storage -> ux_host_class_storage_bulk_out_endpoint-> ux_endpoint_descriptor.bEndpointAddress; + + /* Send transfer request. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check status of transfer. */ + if (status == UX_SUCCESS) + { + + /* Check the Halt bit. */ + if (get_status_response[0] & 0x01) + { + + /* Clear the halt. */ + status = _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_out_endpoint); + } + } + + /* Free the get status memory. */ + _ux_utility_memory_free(get_status_response); + + /* Check result of Get Status and Clear Feature commands. */ + if (status != UX_SUCCESS) + return(status); + } + } + + /* Save the amount of relevant data. */ + storage -> ux_host_class_storage_data_phase_length = _ux_utility_long_get(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH) - + _ux_utility_long_get(storage -> ux_host_class_storage_csw + UX_HOST_CLASS_STORAGE_CSW_DATA_RESIDUE); + } + else + { + + /* Must be phase error. Need to reset the device. */ + _ux_host_class_storage_device_reset(storage); + } + + /* Return success since the transfer succeeded. The caller should + look at the CSW to determine if the command's status. */ + return(UX_SUCCESS); + } + + /* The transfer stalled. We must clear the stall and attempt to read + the CSW again. */ + _ux_host_stack_endpoint_reset(storage -> ux_host_class_storage_bulk_in_endpoint); + } + + /* If we get here, the CSW transfer stalled twice. We must reset the device. */ + _ux_host_class_storage_device_reset(storage); + + /* Return the error. */ + return(transfer_request -> ux_transfer_request_completion_code); +} diff --git a/common/usbx_host_classes/src/ux_host_class_storage_transport_cb.c b/common/usbx_host_classes/src/ux_host_class_storage_transport_cb.c new file mode 100644 index 0000000..ab9af48 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_transport_cb.c @@ -0,0 +1,176 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_transport_cb PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the transport layer for the Control/Bulk */ +/* transport. The command is sent on the control endpoint and the */ +/* data payload on the bulk endpoint. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* data_pointer Pointer to data */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process host stack transfer */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_long_get Get 32-bit word */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_transport_cb(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer) +{ + +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *ufi; +UCHAR *cbw; +ULONG data_phase_requested_length; +UX_ENDPOINT *control_endpoint; + + + /* Reset the data phase memory size. */ + storage -> ux_host_class_storage_data_phase_length = 0; + + /* We need to get the default control endpoint transfer request pointer */ + control_endpoint = &storage -> ux_host_class_storage_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Initialize the transfer request with the control SETUP values. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = 0; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Use a pointer for the ufi portion of the command. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + ufi = cbw + UX_HOST_CLASS_STORAGE_CBW_CB; + + /* Fill in the transfer request parameters. */ + transfer_request -> ux_transfer_request_data_pointer = ufi; + transfer_request -> ux_transfer_request_requested_length = (ULONG) * (cbw + UX_HOST_CLASS_STORAGE_CBW_CB_LENGTH); + + /* Send the ufi block on the control endpoint. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the transfer status. If there is a transport error, the host must perform + a reset recovery. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + return(transfer_request -> ux_transfer_request_completion_code); + + /* Get the length of the data payload. */ + data_phase_requested_length = _ux_utility_long_get(cbw + UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH); + + /* Perform the data stage - if there is any. */ + if (data_phase_requested_length != 0) + { + + /* Check the direction and determine which endpoint to use. */ + if (*(cbw + UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN) + transfer_request = &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request; + else + transfer_request = &storage -> ux_host_class_storage_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Fill in the transfer request data payload buffer. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + + /* Store the requested length in the transfer request. */ + transfer_request -> ux_transfer_request_requested_length = data_phase_requested_length; + + /* Perform data payload transfer (in or out). */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the status of the data payload. */ + if (status != UX_SUCCESS) + return(status); + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); + + /* Get the actual transfer length and update the cumulated stored value for upper layers. + This could be a non complete packet. But we don't test here because it only matters for + sector read. */ + storage -> ux_host_class_storage_data_phase_length += transfer_request -> ux_transfer_request_actual_length; + + /* If the semaphore did not succeed we probably have a timeout. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + + /* Return the status code. */ + return(transfer_request -> ux_transfer_request_completion_code); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_transport_cbi.c b/common/usbx_host_classes/src/ux_host_class_storage_transport_cbi.c new file mode 100644 index 0000000..5fba387 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_transport_cbi.c @@ -0,0 +1,199 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_transport_cbi PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the transport layer for the Control/Bulk/Interrupt */ +/* transport. The command is sent on the control endpoint, the data */ +/* payload on the bulk endpoint. The status from the command is */ +/* returned by the interrupt endpoint. This transport is mainly used */ +/* by storage devices that have very slow read/write commands. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* data_pointer Pointer to data */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process host stack transfer */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_long_get Get 32-bit word */ +/* _ux_utility_semaphore_get Get semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_transport_cbi(UX_HOST_CLASS_STORAGE *storage, UCHAR *data_pointer) +{ + +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *ufi; +UCHAR *cbw; +ULONG data_phase_requested_length; +UX_ENDPOINT *control_endpoint; + + + /* Reset the data phase memory size. */ + storage -> ux_host_class_storage_data_phase_length = 0; + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &storage -> ux_host_class_storage_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Initialize the transfer request with the control SETUP values. */ + transfer_request -> ux_transfer_request_data_pointer = UX_NULL; + transfer_request -> ux_transfer_request_requested_length = 0; + transfer_request -> ux_transfer_request_function = 0; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = 0; + transfer_request -> ux_transfer_request_index = 0; + + /* Use a pointer for the ufi portion of the command. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + ufi = cbw + UX_HOST_CLASS_STORAGE_CBW_CB; + + /* Fill in the transfer_request parameters. */ + transfer_request -> ux_transfer_request_data_pointer = ufi; + transfer_request -> ux_transfer_request_requested_length = (ULONG)*(cbw+UX_HOST_CLASS_STORAGE_CBW_CB_LENGTH); + + /* Send the ufi block on the control endpoint. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the transfer status. If there is a transport error, the host must perform + a reset recovery. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + return(transfer_request -> ux_transfer_request_completion_code); + + /* Get the length of the data payload. */ + data_phase_requested_length = _ux_utility_long_get(cbw+UX_HOST_CLASS_STORAGE_CBW_DATA_LENGTH); + + /* Perform the data stage - if there is any. */ + if (data_phase_requested_length != 0) + { + + /* Check the direction and determine which endpoint to use. */ + if (*(cbw+UX_HOST_CLASS_STORAGE_CBW_FLAGS) == UX_HOST_CLASS_STORAGE_DATA_IN) + transfer_request = &storage -> ux_host_class_storage_bulk_in_endpoint -> ux_endpoint_transfer_request; + else + transfer_request = &storage -> ux_host_class_storage_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Fill in the transfer_request data payload buffer. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + + /* Store the requested length in the transfer request. */ + transfer_request -> ux_transfer_request_requested_length = data_phase_requested_length; + + /* Perform data payload transfer (in or out). */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the status of the data payload. */ + if (status != UX_SUCCESS) + return(status); + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_STORAGE_TRANSFER_TIMEOUT)); + + /* Get the actual transfer length and update the cumulated stored value for upper layers. + This could be a non complete packet. But we don't test here because it only matters for + sector read. */ + storage -> ux_host_class_storage_data_phase_length += transfer_request -> ux_transfer_request_actual_length; + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + + /* We need to check the completion code as well. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + return(transfer_request -> ux_transfer_request_completion_code); + + } + + /* Arm the interrupt endpoint with a transfer request. */ + transfer_request = &storage -> ux_host_class_storage_interrupt_endpoint -> ux_endpoint_transfer_request; + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check the status of the data payload. */ + if (status != UX_SUCCESS) + return(status); + + /* We must wait for the interrupt endpoint to return the status stage now. This can + take a fairly long time. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_STORAGE_CBI_STATUS_TIMEOUT)); + + /* If the status is not successful, we may have a timeout error. */ + if (status != UX_SUCCESS) + return(status); + + /* Return the status code. */ + return(transfer_request -> ux_transfer_request_completion_code); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_storage_unit_ready_test.c b/common/usbx_host_classes/src/ux_host_class_storage_unit_ready_test.c new file mode 100644 index 0000000..ef97201 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_storage_unit_ready_test.c @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Storage Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_storage.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_storage_unit_ready_test PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will verify that a SCSI unit is ready for data */ +/* transfer. This command is used when the device does not mount */ +/* when power is supplied. */ +/* */ +/* INPUT */ +/* */ +/* storage Pointer to storage class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_storage_cbw_initialize Initialize the CBW */ +/* _ux_host_class_storage_transport Send transport layer command */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_storage_unit_ready_test(UX_HOST_CLASS_STORAGE *storage) +{ + +UINT status; +UCHAR *cbw; +UINT command_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_STORAGE_UNIT_READY_TEST, storage, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Use a pointer for the CBW, easier to manipulate. */ + cbw = (UCHAR *) storage -> ux_host_class_storage_cbw; + + /* Get the Unit Ready Test Command Length. */ +#ifdef UX_HOST_CLASS_STORAGE_INCLUDE_LEGACY_PROTOCOL_SUPPORT + if (storage -> ux_host_class_storage_interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_STORAGE_SUBCLASS_UFI) + command_length = UX_HOST_CLASS_STORAGE_TEST_READY_COMMAND_LENGTH_UFI; + else + command_length = UX_HOST_CLASS_STORAGE_TEST_READY_COMMAND_LENGTH_SBC; +#else + command_length = UX_HOST_CLASS_STORAGE_TEST_READY_COMMAND_LENGTH_SBC; +#endif + + /* Initialize the CBW for this command. */ + _ux_host_class_storage_cbw_initialize(storage, 0, 0, command_length); + + /* Prepare the TEST UNIT READY command block. */ + *(cbw + UX_HOST_CLASS_STORAGE_CBW_CB + UX_HOST_CLASS_STORAGE_TEST_READY_OPERATION) = UX_HOST_CLASS_STORAGE_SCSI_TEST_READY; + + /* Send the command to transport layer. */ + status = _ux_host_class_storage_transport(storage, UX_NULL); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_activate.c b/common/usbx_host_classes/src/ux_host_class_swar_activate.c new file mode 100644 index 0000000..c59f509 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_activate.c @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to activate the class. */ +/* */ +/* INPUT */ +/* */ +/* command Dpump class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_swar_configure Configure swar class */ +/* _ux_host_class_swar_endpoints_get Get endpoints of swar */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_create Create swar semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_swar_entry Entry of swar class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_DEVICE *device; +UX_HOST_CLASS_SWAR *swar; +UINT status; + + + /* The Sierra Wireless class is always activated by the device descriptor. */ + device = (UX_DEVICE *) command -> ux_host_class_command_container; + + /* Obtain memory for this class instance. */ + swar = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_SWAR)); + if (swar == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + swar -> ux_host_class_swar_class = command -> ux_host_class_command_class_ptr; + + /* Store the device container into the swar class instance. */ + swar -> ux_host_class_swar_device = device; + + /* Store the instance in the device container, this is for the USBX stack + when it needs to invoke the class for deactivation. */ + device -> ux_device_class_instance = (VOID *) swar; + + /* Create this class instance. */ + _ux_host_stack_class_instance_create(swar -> ux_host_class_swar_class, (VOID *) swar); + + /* Configure the swar. */ + status = _ux_host_class_swar_configure(swar); + + /* Get the swar endpoint(s). We will need to search for Bulk Out and Bulk In endpoints on interface . */ + if (status == UX_SUCCESS) + status = _ux_host_class_swar_endpoints_get(swar); + + /* Create the semaphore to protect 2 threads from accessing the same swar instance. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&swar -> ux_host_class_swar_semaphore, "ux_host_class_swar_semaphore", 1); + if (status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + /* Success things. */ + if (status == UX_SUCCESS) + { + + /* Mark the swar as live now. */ + swar -> ux_host_class_swar_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, swar -> ux_host_class_swar_class, (VOID *) swar); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_ACTIVATE, swar, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, swar, 0, 0, 0) + + /* Return success. */ + return(UX_SUCCESS); + } + + /* There was a problem during the configuration, so free the resources. */ + /* The last resource, semaphore is not created or created error, no need to free. */ + _ux_host_stack_class_instance_destroy(swar -> ux_host_class_swar_class, (VOID *) swar); + device -> ux_device_class_instance = UX_NULL; + _ux_utility_memory_free(swar); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_configure.c b/common/usbx_host_classes/src/ux_host_class_swar_configure.c new file mode 100644 index 0000000..bc118b0 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_configure.c @@ -0,0 +1,148 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to the */ +/* swar. Once the swar is configured, its interface will be */ +/* activated. The bulk endpoints enumerated(1 IN, 1 OUT ). */ +/* */ +/* INPUT */ +/* */ +/* swar Pointer to swar class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get Get interface */ +/* _ux_host_stack_device_configuration_get Get configuration */ +/* _ux_host_stack_device_configuration_select Select configuration */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_swar_activate swar class activate */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_configure(UX_HOST_CLASS_SWAR *swar) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_DEVICE *parent_device; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (swar -> ux_host_class_swar_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* A swar normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(swar -> ux_host_class_swar_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, swar -> ux_host_class_swar_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + } + + /* Check the swar power source and check the parent power source for + incompatible connections. */ + if (swar -> ux_host_class_swar_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device pointer. */ + parent_device = swar -> ux_host_class_swar_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root swar and we don't have to worry + if the parent is not the root swar, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration. */ + status = _ux_host_stack_device_configuration_select(configuration); + if (status != UX_SUCCESS) + return(status); + + /* If the operation went well, the swar default alternate setting for the swar interface is + active. We have to memorize the data interface since the bulk in/out endpoints are hooked to it. */ + status = _ux_host_stack_configuration_interface_get(configuration, UX_HOST_CLASS_SWAR_DATA_INTERFACE, 0, &swar -> ux_host_class_swar_interface); + if (status == UX_SUCCESS) + { + + /* Store the instance in the interface container, this is for the USB stack + when it needs to invoke the class. */ + swar -> ux_host_class_swar_interface -> ux_interface_class_instance = (VOID *) swar; + + /* Store the class container in the interface. The device has the correct class, duplicate it to the + interface. */ + swar -> ux_host_class_swar_interface -> ux_interface_class = swar -> ux_host_class_swar_device -> ux_device_class ; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_deactivate.c b/common/usbx_host_classes/src/ux_host_class_swar_deactivate.c new file mode 100644 index 0000000..4f3adda --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_deactivate.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the swar has been */ +/* removed from the bus either directly or indirectly. The bulk in\out */ +/* pipes will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Swar class command pointer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy the class instance */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort endpoint transfer */ +/* _ux_utility_memory_free Free memory block */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_delete Delete protection semaphore */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_swar_entry Entry of swar class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_SWAR *swar; +UINT status; + + + /* Get the instance for this class. */ + swar = (UX_HOST_CLASS_SWAR *) command -> ux_host_class_command_instance; + + /* The swar is being shut down. */ + swar -> ux_host_class_swar_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&swar -> ux_host_class_swar_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(swar -> ux_host_class_swar_bulk_out_endpoint); + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(swar -> ux_host_class_swar_bulk_in_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(swar -> ux_host_class_swar_class, (VOID *) swar); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&swar -> ux_host_class_swar_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, swar -> ux_host_class_swar_class, (VOID *) swar); + } + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_DEACTIVATE, swar, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_UNREGISTER(swar); + + /* Free the swar instance memory. */ + _ux_utility_memory_free(swar); + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_swar_endpoints_get.c new file mode 100644 index 0000000..819aeae --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_endpoints_get.c @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function search for the handle of the bulk out and bulk in */ +/* endpoints. The Sierra Wireless USB device has multiple interfaces. */ +/* We first need to find the interface which uses Bulk endpoints to */ +/* carry data. */ +/* */ +/* INPUT */ +/* */ +/* swar Pointer to swar class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* */ +/* CALLED BY */ +/* */ +/* _ux_host_class_swar_activate Activate swar class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_endpoints_get(UX_HOST_CLASS_SWAR *swar) +{ + +UINT status; +UX_ENDPOINT *endpoint; +UINT endpoint_index; + + /* Search the bulk OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < swar -> ux_host_class_swar_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(swar -> ux_host_class_swar_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the OUT direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_OUT; + + /* We have found the bulk endpoint, save it. */ + swar -> ux_host_class_swar_bulk_out_endpoint = endpoint; + break; + } + } + } + + /* The bulk out endpoint is mandatory. */ + if (swar -> ux_host_class_swar_bulk_out_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Search the bulk IN endpoint. It is attached to the interface container. */ + + for (endpoint_index = 0; endpoint_index < swar -> ux_host_class_swar_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get the endpoint handle. */ + status = _ux_host_stack_interface_endpoint_get(swar -> ux_host_class_swar_interface, endpoint_index, &endpoint); + + /* Check the completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is bulk and IN. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_IN) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_BULK_ENDPOINT)) + { + + /* This transfer_request always have the IN direction. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_type = UX_REQUEST_IN; + + /* We have found the bulk endpoint, save it. */ + swar -> ux_host_class_swar_bulk_in_endpoint = endpoint; + break; + } + } + } + + /* The bulk in endpoint is mandatory. */ + if (swar -> ux_host_class_swar_bulk_in_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* All endpoints have been mounted. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_entry.c b/common/usbx_host_classes/src/ux_host_class_swar_entry.c new file mode 100644 index 0000000..b4a754a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_entry.c @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the swar class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* swar on the bus or when the USB swar is removed. */ +/* */ +/* INPUT */ +/* */ +/* command swar class command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_swar_activate Activate swar class */ +/* _ux_host_class_swar_deactivate Deactivate swar class */ +/* */ +/* CALLED BY */ +/* */ +/* Data pump Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if(((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_PIDVID) && + (command -> ux_host_class_command_pid == UX_HOST_CLASS_SWAR_PRODUCT_ID) && + (command -> ux_host_class_command_vid == UX_HOST_CLASS_SWAR_VENDOR_ID ))) + return(UX_SUCCESS); + else + return(UX_NO_CLASS_MATCH); + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_swar_activate(command); + return(status); + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_swar_deactivate(command); + return(status); + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_FUNCTION_NOT_SUPPORTED); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_ioctl.c b/common/usbx_host_classes/src/ux_host_class_swar_ioctl.c new file mode 100644 index 0000000..af31172 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_ioctl.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the ioctl entry point for the application to */ +/* configure the Swar device. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* swar Pointer to swar class */ +/* ioctl_function ioctl function */ +/* parameter pointer to structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_ioctl(UX_HOST_CLASS_SWAR *swar, ULONG ioctl_function, + VOID *parameter) +{ + +UINT status; + + UX_PARAMETER_NOT_USED(parameter); + + /* The command request will tell us we need to do here. */ + switch (ioctl_function) + { + + case UX_HOST_CLASS_SWAR_IOCTL_ABORT_IN_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_IOCTL_ABORT_IN_PIPE, swar, swar -> ux_host_class_swar_bulk_in_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(swar -> ux_host_class_swar_bulk_in_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + case UX_HOST_CLASS_SWAR_IOCTL_ABORT_OUT_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_IOCTL_ABORT_OUT_PIPE, swar, swar -> ux_host_class_swar_bulk_out_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk Out pipe. */ + _ux_host_stack_endpoint_transfer_abort(swar -> ux_host_class_swar_bulk_out_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + break; + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_read.c b/common/usbx_host_classes/src/ux_host_class_swar_read.c new file mode 100644 index 0000000..a57ed4c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_read.c @@ -0,0 +1,205 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the swar interface. The call is */ +/* blocking and only returns when there is either an error or when */ +/* the transfer is complete. */ +/* */ +/* INPUT */ +/* */ +/* swar Pointer to swar class */ +/* data_pointer Pointer to buffer */ +/* requested_length Requested data read */ +/* actual_length Actual data read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_read(UX_HOST_CLASS_SWAR *swar, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_READ, swar, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_swar_name, (VOID *) swar) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&swar -> ux_host_class_swar_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Start by resetting the actual length of the transfer to zero. */ + *actual_length = 0; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &swar -> ux_host_class_swar_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk in endpoint until either the transfer is + completed or until there is an error. */ + while (requested_length) + { + + /* Program the maximum authorized length for this transfer request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer_request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_HOST_CLASS_SWAR_CLASS_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer request in case some data was actually received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* There was a non transfer error, no partial transfer to be checked. */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be received. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually received and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* Return success to caller. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to receive. */ + requested_length -= transfer_request_length; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_reception_callback.c b/common/usbx_host_classes/src/ux_host_class_swar_reception_callback.c new file mode 100644 index 0000000..a1807fa --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_reception_callback.c @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_reception_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the callback from the USBX transfer functions, */ +/* it is called when a full or partial transfer has been done for a */ +/* bulk in transfer. It calls back the application. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_swar_reception_callback (UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_SWAR *swar; +UX_HOST_CLASS_SWAR_RECEPTION *swar_reception; + + /* Get the class instance for this transfer request. */ + swar = (UX_HOST_CLASS_SWAR *) transfer_request -> ux_transfer_request_class_instance; + + /* Get the pointer to the acm reception structure. */ + swar_reception = swar -> ux_host_class_swar_reception; + + /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ + if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) + { + + /* The reception is stopped. */ + swar_reception -> ux_host_class_swar_reception_state = UX_HOST_CLASS_SWAR_RECEPTION_STATE_STOPPED; + + /* We do not proceed. */ + return; + + } + + /* And move to the next reception buffer. Check if we are at the end of the application buffer. */ + if (swar_reception -> ux_host_class_swar_reception_data_head + swar_reception -> ux_host_class_swar_reception_block_size >= + swar_reception -> ux_host_class_swar_reception_data_buffer + swar_reception -> ux_host_class_swar_reception_data_buffer_size) + { + + /* We are at the end of the buffer. Move back to the beginning if we have space available. */ + if (swar_reception -> ux_host_class_swar_reception_data_tail == swar_reception -> ux_host_class_swar_reception_data_buffer) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_BUFFER_OVERFLOW, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We have an overflow. We cannot continue. Report to the application. */ + swar_reception -> ux_host_class_swar_reception_callback(swar, UX_BUFFER_OVERFLOW, UX_NULL, 0); + + /* And stop the transfer in progress flag. */ + swar_reception -> ux_host_class_swar_reception_state = UX_HOST_CLASS_SWAR_RECEPTION_STATE_STOPPED; + + return; + } + else + + /* Program the head to be at the beginning of the application buffer. */ + swar_reception -> ux_host_class_swar_reception_data_head = swar_reception -> ux_host_class_swar_reception_data_buffer; + + } + else + + /* Program the head to be after the current buffer. */ + swar_reception -> ux_host_class_swar_reception_data_head += swar_reception -> ux_host_class_swar_reception_block_size; + + + /* We need to report this transfer to the application. */ + swar_reception -> ux_host_class_swar_reception_callback(swar, + transfer_request -> ux_transfer_request_completion_code, + transfer_request -> ux_transfer_request_data_pointer, + transfer_request -> ux_transfer_request_actual_length); + + /* Arm another transfer. */ + _ux_host_stack_transfer_request(transfer_request); + + /* There is no status to be reported back to the stack. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_reception_start.c b/common/usbx_host_classes/src/ux_host_class_swar_reception_start.c new file mode 100644 index 0000000..650a31a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_reception_start.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_reception_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts a reception with the Sierra modem. This way */ +/* allows for non blocking calls based on a packet orientated round */ +/* robbin buffer. When a packet is fully or partially received, an */ +/* application callback function is invoked and a new transfer request */ +/* is rescheduled. */ +/* */ +/* INPUT */ +/* */ +/* swar Pointer to swar class */ +/* swar_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_reception_start (UX_HOST_CLASS_SWAR *swar, + UX_HOST_CLASS_SWAR_RECEPTION *swar_reception) +{ + +UX_TRANSFER *transfer_request; +UINT status; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_RECEPTION_START, swar, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_swar_name, (VOID *) swar) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Start by aligning the head and tail of buffers to the same address supplied by the application. */ + swar_reception -> ux_host_class_swar_reception_data_head = swar_reception -> ux_host_class_swar_reception_data_buffer; + swar_reception -> ux_host_class_swar_reception_data_tail = swar_reception -> ux_host_class_swar_reception_data_buffer; + + /* Get the pointer to the bulk in endpoint in the transfer_request. */ + transfer_request = &swar -> ux_host_class_swar_bulk_in_endpoint -> ux_endpoint_transfer_request; + + /* Initialize the transfer request. */ + transfer_request -> ux_transfer_request_class_instance = (VOID *) swar; + transfer_request -> ux_transfer_request_data_pointer = swar_reception -> ux_host_class_swar_reception_data_head; + transfer_request -> ux_transfer_request_requested_length = swar_reception -> ux_host_class_swar_reception_block_size; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_swar_reception_callback; + + /* Save the acm reception structure in the acm structure. */ + swar -> ux_host_class_swar_reception = swar_reception; + + /* And declare we have a transfer in progress. */ + swar_reception -> ux_host_class_swar_reception_state = UX_HOST_CLASS_SWAR_RECEPTION_STATE_STARTED; + + /* Arm a first transfer on the bulk in endpoint. There is a callback to this function so we return to the caller + right away. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* We do not know if the first transfer was successful yet. If the status is not OK, we need to stop the transfer + in progress flag. */ + if (status != UX_SUCCESS) + swar_reception -> ux_host_class_swar_reception_state = UX_HOST_CLASS_SWAR_RECEPTION_STATE_STOPPED; + + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_reception_stop.c b/common/usbx_host_classes/src/ux_host_class_swar_reception_stop.c new file mode 100644 index 0000000..2eadac6 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_reception_stop.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_reception_stop PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts a reception with the Sierra modem. This way */ +/* allows for non blocking calls based on a packet orientated round */ +/* robbin buffer. When a packet is fully or partially received, an */ +/* application callback function is invoked and a new transfer request */ +/* is rescheduled. */ +/* */ +/* INPUT */ +/* */ +/* swar Pointer to swar class */ +/* swar_reception Pointer to reception struct */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_reception_stop (UX_HOST_CLASS_SWAR *swar, + UX_HOST_CLASS_SWAR_RECEPTION *swar_reception) +{ + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_RECEPTION_STOP, swar, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_swar_name, (VOID *) swar) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Check if we do have transfers for this application. If none, nothing to do. */ + if (swar_reception -> ux_host_class_swar_reception_state == UX_HOST_CLASS_SWAR_RECEPTION_STATE_STOPPED) + return(UX_SUCCESS); + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(swar -> ux_host_class_swar_bulk_in_endpoint); + + /* Declare the reception stopped. */ + swar_reception -> ux_host_class_swar_reception_state = UX_HOST_CLASS_SWAR_RECEPTION_STATE_STOPPED; + + /* This function never really fails. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_swar_write.c b/common/usbx_host_classes/src/ux_host_class_swar_write.c new file mode 100644 index 0000000..b7f2424 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_swar_write.c @@ -0,0 +1,207 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Host Sierra Wireless AR module class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_swar.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_swar_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes to the swar interface. The call is blocking */ +/* and only returns when there is either an error or when the transfer */ +/* is complete. */ +/* */ +/* INPUT */ +/* */ +/* swar Pointer to swar class */ +/* data_pointer Pointer to data to write */ +/* requested_length Length of data to write */ +/* actual_length Actual length of data written */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify the class instance */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_semaphore_get Get protection semaphore */ +/* _ux_utility_semaphore_put Release protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_swar_write(UX_HOST_CLASS_SWAR *swar, UCHAR * data_pointer, + ULONG requested_length, ULONG *actual_length) +{ + +UX_TRANSFER *transfer_request; +UINT status; +ULONG transfer_request_length; + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_SWAR_WRITE, swar, data_pointer, requested_length, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* Ensure the instance is valid. */ + if(_ux_host_stack_class_instance_verify((UCHAR *) _ux_system_host_class_swar_name, (VOID *) swar) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, swar, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&swar -> ux_host_class_swar_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Start by resetting the actual length of the transfer. */ + *actual_length = 0; + + /* Get the pointer to the bulk out endpoint transfer request. */ + transfer_request = &swar -> ux_host_class_swar_bulk_out_endpoint -> ux_endpoint_transfer_request; + + /* Perform a transfer on the bulk out endpoint until either the transfer is + completed or when there is an error. */ + do + { + + /* Program the maximum authorized length for this transfer_request. */ + if (requested_length > transfer_request -> ux_transfer_request_maximum_length) + transfer_request_length = transfer_request -> ux_transfer_request_maximum_length; + else + transfer_request_length = requested_length; + + /* Initialize the transfer_request. */ + transfer_request -> ux_transfer_request_data_pointer = data_pointer; + transfer_request -> ux_transfer_request_requested_length = transfer_request_length; + + /* Perform the transfer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* If the transfer is successful, we need to wait for the transfer request to be completed. */ + if (status == UX_SUCCESS) + { + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, UX_HOST_CLASS_SWAR_CLASS_TRANSFER_TIMEOUT); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* Update the length of the actual data transferred. We do this after the + abort of the transfer_request in case some data actually went out. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* Set the completion code. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* There was an error, return to the caller. */ + return(UX_TRANSFER_TIMEOUT); + } + } + else + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* There was a non transfer error, no partial transfer to be checked */ + return(status); + } + + /* Update the length of the transfer. Normally all the data has to be sent. */ + *actual_length += transfer_request -> ux_transfer_request_actual_length; + + /* Check for completion of transfer. If the transfer is partial, return to caller. + The transfer is marked as successful but the caller will need to check the length + actually sent and determine if a partial transfer is OK. */ + if (transfer_request_length != transfer_request -> ux_transfer_request_actual_length) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* Return success. */ + return(UX_SUCCESS); + } + + /* Update the data pointer for next transfer. */ + data_pointer += transfer_request_length; + + /* Update what is left to send out. */ + requested_length -= transfer_request_length; + + } while (requested_length != 0); + + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&swar -> ux_host_class_swar_semaphore); + + /* We get here when all the transfers went through without errors. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_activate.c b/common/usbx_host_classes/src/ux_host_class_video_activate.c new file mode 100644 index 0000000..440c4ca --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_activate.c @@ -0,0 +1,200 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + +UCHAR _ux_system_class_video_interface_descriptor_structure[] = {1,1,1,1,1,1,1,1}; +UCHAR _ux_system_class_video_input_terminal_descriptor_structure[] = {1,1,1,1,2,1,1}; +UCHAR _ux_system_class_video_input_header_descriptor_structure[] = {1,1,1,1,2,1,1,1,1,1,1,1}; +UCHAR _ux_system_class_video_processing_unit_descriptor_structure[] = {1,1,1,1,1,2,1,1}; +UCHAR _ux_system_class_video_streaming_interface_descriptor_structure[] = {1,1,1,1,1,1}; +UCHAR _ux_system_class_video_streaming_endpoint_descriptor_structure[] = {1,1,1,1,1,1}; +UCHAR _ux_system_class_video_frame_descriptor_structure[] = {1,1,1,1,1,2,2,4,4,4,4,1}; + +UCHAR _ux_system_host_class_video_name[] = "ux_host_class_video"; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function activates the video class. It may be called twice by */ +/* the same device if there is a video control interface to this */ +/* device. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_video_configure Configure the video class */ +/* _ux_host_class_video_descriptor_get Get video descriptor */ +/* _ux_host_class_video_input_terminal_get */ +/* Get input terminal */ +/* _ux_host_class_video_input_format_get Get input format */ +/* _ux_host_class_video_control_list_get Get controls */ +/* _ux_host_stack_class_instance_create Create class instance */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_utility_memory_allocate Allocate a memory block */ +/* _ux_utility_memory_free Free a memory block */ +/* _ux_utility_semaphore_create Create protection semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_activate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_INTERFACE *interface; +UX_HOST_CLASS_VIDEO *video; +UINT status; + + + /* The video is always activated by the interface descriptor and not the + device descriptor. */ + interface = (UX_INTERFACE *) command -> ux_host_class_command_container; + + /* Check the subclass of the new device. If it is a Video Control Interface, + we don't need to create an instance of this function. When we get the streaming interface, + we will search the video control interface for the device. */ + if (interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_VIDEO_SUBCLASS_CONTROL) + return(UX_SUCCESS); + + /* Obtain memory for this class instance. */ + video = (UX_HOST_CLASS_VIDEO *) _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HOST_CLASS_VIDEO)); + if (video == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Store the class container into this instance. */ + video -> ux_host_class_video_class = command -> ux_host_class_command_class_ptr; + + /* Store the interface container into the video class instance. */ + video -> ux_host_class_video_streaming_interface = interface; + + /* Store the device container into the video class instance. */ + video -> ux_host_class_video_device = interface -> ux_interface_configuration -> ux_configuration_device; + + /* This instance of the device must also be stored in the interface container. */ + interface -> ux_interface_class_instance = (VOID *) video; + + /* Create this class instance. */ + status = _ux_host_stack_class_instance_create(video -> ux_host_class_video_class, (VOID *) video); + + /* Configure the video. */ + status = _ux_host_class_video_configure(video); + + /* Get the video descriptor (all the class specific stuff) and memorize them + as we will need these descriptors to change settings. */ + if (status == UX_SUCCESS) + status = _ux_host_class_video_descriptor_get(video); + + /* Locate the video device streaming terminal. */ + if (status == UX_SUCCESS) + status = _ux_host_class_video_input_terminal_get(video); + + /* In the input terminal streaming interface get the number of formats supported. */ + if (status == UX_SUCCESS) + status = _ux_host_class_video_input_format_get(video); + + /* Get video controls. */ + if (status == UX_SUCCESS) + status = _ux_host_class_video_control_list_get(video); + + /* Create the semaphore to protect multiple threads from accessing the same + video instance. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&video -> ux_host_class_video_semaphore, "ux_video_semaphore", 1); + if (status != UX_SUCCESS) + status = UX_SEMAPHORE_ERROR; + } + + if (status == UX_SUCCESS) + { + + /* Mark the video as live now. */ + video -> ux_host_class_video_state = UX_HOST_CLASS_INSTANCE_LIVE; + + /* If all is fine and the device is mounted, we may need to inform the application + if a function has been programmed in the system structure. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Call system change function. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, video -> ux_host_class_video_class, (VOID *) video); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_VIDEO_ACTIVATE, video, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, video, 0, 0, 0) + + /* Return success. */ + return(UX_SUCCESS); + } + + /* There was error, free resources. */ + + /* The last resource, video -> ux_host_class_video_semaphore is not created or created error, + no need to free. */ + + /* Destroy the class instance. */ + _ux_host_stack_class_instance_destroy(video -> ux_host_class_video_class, (VOID *) video); + + /* This instance of the device must also be cleared in the interface container. */ + interface -> ux_interface_class_instance = UX_NULL; + + /* Free instance memory. */ + _ux_utility_memory_free(video); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_alternate_setting_locate.c b/common/usbx_host_classes/src/ux_host_class_video_alternate_setting_locate.c new file mode 100644 index 0000000..279edaa --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_alternate_setting_locate.c @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_alternate_setting_locate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the alternate setting for the specific format. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* alternate_setting Pointer to located alternate */ +/* setting */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_alternate_setting_locate(UX_HOST_CLASS_VIDEO *video, UINT max_payload_size, UINT *alternate_setting) +{ + +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UX_ENDPOINT *endpoint; +UINT streaming_interface; +UINT payload_size; +UINT current_payload_size = 0; +UINT alternate_setting_found; + + + configuration = video -> ux_host_class_video_streaming_interface -> ux_interface_configuration; + interface = configuration -> ux_configuration_first_interface; + streaming_interface = video -> ux_host_class_video_streaming_interface -> ux_interface_descriptor.bInterfaceNumber; + + alternate_setting_found = UX_FALSE; + + /* Scan all interfaces. */ + while (interface != UX_NULL) + { + + /* Search for the streaming interface with a endpoint. */ + if ((interface -> ux_interface_descriptor.bInterfaceNumber == streaming_interface) && + (interface -> ux_interface_first_endpoint != 0)) + { + + /* Get the max packet size of the endpoint. */ + endpoint = interface -> ux_interface_first_endpoint; + payload_size = (endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK) * + (((endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> + UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1); + + /* Check if the payload size is equal or greater than the required payload size. */ + if (payload_size >= max_payload_size) + { + + /* Check if we have found any alternate settings yet. */ + if(alternate_setting_found == UX_FALSE) + { + + /* Save the current payload size and mark the found flag. */ + alternate_setting_found = UX_TRUE; + current_payload_size = payload_size; + + /* Save the found alternate setting number. */ + *alternate_setting = interface -> ux_interface_descriptor.bAlternateSetting; + } + else + { + + /* Check if the payload size is smaller. */ + if (payload_size < current_payload_size) + { + + /* Smaller payload size found, select the current setting. */ + current_payload_size = payload_size; + + /* Save the found alternate setting number. */ + *alternate_setting = interface -> ux_interface_descriptor.bAlternateSetting; + } + } + } + } + + /* Move to next interface. */ + interface = interface -> ux_interface_next_interface; + } + + /* Check if we found the alternate setting. */ + if (alternate_setting_found) + { + + /* Return successful completion. */ + return(UX_SUCCESS); + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right video device. */ + return(UX_HOST_CLASS_VIDEO_WRONG_TYPE); +} diff --git a/common/usbx_host_classes/src/ux_host_class_video_channel_start.c b/common/usbx_host_classes/src/ux_host_class_video_channel_start.c new file mode 100644 index 0000000..d0e1f39 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_channel_start.c @@ -0,0 +1,268 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_channel_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts the video channel. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* channel_parameter Pointer to video channel */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_video_alternate_setting_locate */ +/* Search alternate setting */ +/* _ux_host_stack_interface_setting_select */ +/* Select alternate setting */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_long_get Get 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_channel_start(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_CHANNEL *video_parameter) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR *control_buffer; +UINT alternate_setting; +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UX_ENDPOINT *endpoint; +ULONG endpoint_index; +UINT streaming_interface; +UINT max_payload_size; + + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &video -> ux_host_class_video_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Get the interface number of the video streaming interface. */ + streaming_interface = video -> ux_host_class_video_streaming_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Need to allocate memory for the control_buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Check the result, did we find the alternate setting ? */ + if (status == UX_SUCCESS) + { + + *(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FORMAT_INDEX) = (UCHAR)video_parameter -> ux_host_class_video_parameter_format_requested; + *(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FRAME_INDEX) = (UCHAR)video_parameter -> ux_host_class_video_parameter_frame_requested; + _ux_utility_long_put(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FRAME_INTERVAL, + video_parameter -> ux_host_class_video_parameter_frame_interval_requested); + + /* Create a transfer request for the GET_CUR_buffer request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_SET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_VIDEO_VS_PROBE_CONTROL << 8; + transfer_request -> ux_transfer_request_index = streaming_interface; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer. Buffer may not be all what we asked for. */ + if (status == UX_SUCCESS) + { + + + /* Create a transfer request for the SET_CUR_buffer request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_GET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_VIDEO_VS_PROBE_CONTROL << 8; + transfer_request -> ux_transfer_request_index = streaming_interface; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer. */ + if (status == UX_SUCCESS) + { + + /* We did the GET_CUR and SET_CUR for Probe Control. Now we can commit to the bandwidth. */ + /* Create a transfer request for the SET_CUR_buffer request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_SET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_VIDEO_VS_COMMIT_CONTROL << 8; + transfer_request -> ux_transfer_request_index = streaming_interface; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer. */ + if (status == UX_SUCCESS) + { + + /* Check if user has request specific bandwidth selection. */ + if (video_parameter -> ux_host_class_video_parameter_channel_bandwidth_selection == 0) + { + + /* Get the max payload transfer size returned from video device. */ + max_payload_size = _ux_utility_long_get(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_MAX_PAYLOAD_TRANSFER_SIZE); + } + else + { + + /* Set the max payload transfer size to the user requested one. */ + max_payload_size = video_parameter -> ux_host_class_video_parameter_channel_bandwidth_selection; + } + + /* Search for the non zero alternate setting of the video stream. */ + status = _ux_host_class_video_alternate_setting_locate(video, max_payload_size, &alternate_setting); + + /* Now the Commit has been done, the alternate setting can be requested. */ + /* We found the alternate setting for the sampling values demanded, now we need + to search its container. */ + configuration = video -> ux_host_class_video_streaming_interface -> ux_interface_configuration; + interface = configuration -> ux_configuration_first_interface; + + + /* Scan all interfaces. */ + while (interface != UX_NULL) + { + + /* We search for both the right interface and alternate setting. */ + if ((interface -> ux_interface_descriptor.bInterfaceNumber == streaming_interface) && + (interface -> ux_interface_descriptor.bAlternateSetting == alternate_setting)) + { + + /* We have found the right interface/alternate setting combination + The stack will select it for us. */ + status = _ux_host_stack_interface_setting_select(interface); + + /* If the alternate setting for the streaming interface could be selected, we memorize it. */ + if (status == UX_SUCCESS) + { + + /* Memorize the interface. */ + video -> ux_host_class_video_streaming_interface = interface; + + /* We need to research the isoch endpoint now. */ + for (endpoint_index = 0; endpoint_index < interface -> ux_interface_descriptor.bNumEndpoints; endpoint_index++) + { + + /* Get the list of endpoints one by one. */ + status = _ux_host_stack_interface_endpoint_get(video -> ux_host_class_video_streaming_interface, + endpoint_index, &endpoint); + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is ISOCH, regardless of the direction. */ + if ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_ISOCHRONOUS_ENDPOINT) + { + + /* We have found the isoch endpoint, save it. */ + video -> ux_host_class_video_isochronous_endpoint = endpoint; + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + } + } + } + + /* Move to next interface. */ + interface = interface -> ux_interface_next_interface; + } + } + } + } + } + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_configure.c b/common/usbx_host_classes/src/ux_host_class_video_configure.c new file mode 100644 index 0000000..e9cca7b --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_configure.c @@ -0,0 +1,179 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_configure PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the USBX stack to do a SET_CONFIGURATION to */ +/* the device. Once the device is configured, its interface(s) will */ +/* be activated. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_configuration_interface_get */ +/* Get interface */ +/* _ux_host_stack_device_configuration_get */ +/* Get configuration */ +/* _ux_host_stack_device_configuration_select */ +/* Select configuration */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_configure(UX_HOST_CLASS_VIDEO *video) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UX_DEVICE *parent_device; +ULONG interface_number; + + + /* If the device has been configured already, we don't need to do it + again. */ + if (video -> ux_host_class_video_device -> ux_device_state == UX_DEVICE_CONFIGURED) + return(UX_SUCCESS); + + /* An video device normally has one configuration. So retrieve the 1st configuration + only. */ + status = _ux_host_stack_device_configuration_get(video -> ux_host_class_video_device, 0, &configuration); + if (status != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONFIGURATION_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONFIGURATION_HANDLE_UNKNOWN, video -> ux_host_class_video_device, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONFIGURATION_HANDLE_UNKNOWN); + + } + /* Check the video power source and check the parent power source for + incompatible connections. */ + if (video -> ux_host_class_video_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED) + { + + /* Get parent device. */ + parent_device = video -> ux_host_class_video_device -> ux_device_parent; + + /* If the device is NULL, the parent is the root video and we don't have to worry + if the parent is not the root video, check for its power source. */ + if ((parent_device != UX_NULL) && (parent_device -> ux_device_power_source == UX_DEVICE_BUS_POWERED)) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CONNECTION_INCOMPATIBLE); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONNECTION_INCOMPATIBLE, video, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONNECTION_INCOMPATIBLE); + } + } + + /* We have the valid configuration. Ask the USBX stack to set this configuration */ + status = _ux_host_stack_device_configuration_select(configuration); + + /* Start with interface number 0. */ + interface_number = 0; + + /* We only need to retrieve the video streaming interface. */ + do + { + + /* Pickup interface. */ + status = _ux_host_stack_configuration_interface_get(configuration, (UINT) interface_number, 0, &interface); + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* Check the type of interface we have found - is it streaming? */ + if (interface -> ux_interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_VIDEO_SUBCLASS_STREAMING) + { + + video -> ux_host_class_video_streaming_interface = interface; + video -> ux_host_class_video_streaming_interface -> ux_interface_class_instance = (VOID *)video; + break; + } + } + + /* Move to next interface. */ + interface_number++; + } while(status == UX_SUCCESS); + + /* After we have parsed the video interfaces, ensure the streaming interfaces is resent. */ + if (video -> ux_host_class_video_streaming_interface == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_INTERFACE_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_INTERFACE_HANDLE_UNKNOWN, interface, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We get here when we could not locate the interface(s) handle */ + return(UX_INTERFACE_HANDLE_UNKNOWN); + } + + return(UX_SUCCESS); + +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_control_get.c b/common/usbx_host_classes/src/ux_host_class_video_control_get.c new file mode 100644 index 0000000..26602a3 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_control_get.c @@ -0,0 +1,212 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_control_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the static values for a single video control */ +/* on either the master channel or a specific channel. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* video_control Pointer to video control */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_get Read 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_control_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_CONTROL *video_control) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * control_buffer; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_video_name, (VOID *) video) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, video, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &video -> ux_host_class_video_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the control buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return an error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer request for the GET_MIN request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_GET_MIN; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = (video_control -> ux_host_class_video_control << 8); + transfer_request -> ux_transfer_request_index = video -> ux_host_class_video_control_interface_number | (video -> ux_host_class_video_feature_unit_id << 8); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the MIN static value for the caller. */ + video_control -> ux_host_class_video_control_min = (LONG)(SHORT)_ux_utility_short_get(control_buffer); + } + else + { + + /* Free the previous control buffer. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + } + + /* Create a transfer request for the GET_MAX request. */ + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_GET_MAX; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the MAX static value for the caller. */ + video_control -> ux_host_class_video_control_max = (LONG)(SHORT)_ux_utility_short_get(control_buffer); + } + else + { + + /* Free the previous control buffer. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + } + + /* Create a transfer request for the GET_RES request. */ + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_GET_RES; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the RES static value for the caller. */ + video_control -> ux_host_class_video_control_res = (LONG)(SHORT)_ux_utility_short_get(control_buffer); + } + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_control_list_get.c b/common/usbx_host_classes/src/ux_host_class_video_control_list_get.c new file mode 100644 index 0000000..184324e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_control_list_get.c @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_control_list_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the controls for the video device. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_control_list_get(UX_HOST_CLASS_VIDEO *video) +{ + +UCHAR *descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_VIDEO_PROCESSING_UNIT_DESCRIPTOR processing_unit_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; +ULONG interface_found; + + + /* Get the descriptor to the selected format. */ + descriptor = video -> ux_host_class_video_configuration_descriptor; + total_descriptor_length = video -> ux_host_class_video_configuration_descriptor_length; + + /* Haven't found it yet. */ + interface_found = UX_FALSE; + + /* Scan the descriptor for the Video Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to descriptor type. */ + switch (descriptor_type) + { + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Video Control. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_VIDEO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_VIDEO_SUBCLASS_CONTROL)) + { + + /* Mark we have found it. */ + interface_found = UX_TRUE; + + /* Get the interface number of this descriptor and save it in the video + instance. This will be useful to program the video controls. */ + video -> ux_host_class_video_control_interface_number = interface_descriptor.bInterfaceNumber; + } + else + { + + /* Haven't found it. */ + interface_found = UX_FALSE; + } + break; + + + case UX_HOST_CLASS_VIDEO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if ((interface_found == UX_TRUE) && (descriptor_subtype == UX_HOST_CLASS_VIDEO_VC_PROCESSING_UNIT)) + { + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_video_input_terminal_descriptor_structure, + UX_HOST_CLASS_VIDEO_PROCESSING_UNIT_DESCRIPTOR_ENTRIES, (UCHAR *) &processing_unit_descriptor); + + video -> ux_host_class_video_feature_unit_id = processing_unit_descriptor.bUnitID; + + /* We are done here. */ + return(UX_SUCCESS); + } + + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right video device. */ + return(UX_HOST_CLASS_VIDEO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_control_value_get.c b/common/usbx_host_classes/src/ux_host_class_video_control_value_get.c new file mode 100644 index 0000000..6e74320 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_control_value_get.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_control_value_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the current values for a single video control.*/ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* video_control Pointer to video control */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_get Read 16-bit value */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_control_value_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_CONTROL *video_control) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * control_buffer; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_video_name, (VOID *) video) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, video, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &video -> ux_host_class_video_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the control buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return an error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer request for the GET_MIN request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_GET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = (video_control -> ux_host_class_video_control << 8); + transfer_request -> ux_transfer_request_index = video -> ux_host_class_video_control_interface_number | (video -> ux_host_class_video_feature_unit_id << 8); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire control buffer returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == 2)) + { + + /* Update the MIN static value for the caller. */ + video_control -> ux_host_class_video_control_cur = (LONG)(SHORT)_ux_utility_short_get(control_buffer); + } + else + { + + /* Free the previous control buffer. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + } + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_control_value_set.c b/common/usbx_host_classes/src/ux_host_class_video_control_value_set.c new file mode 100644 index 0000000..e5eaa5c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_control_value_set.c @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_control_value_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function updates the current values for a single video control.*/ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* video_control Pointer to video control */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_short_put Put 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_control_value_set(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_CONTROL *video_control) +{ + +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT status; +UCHAR * control_buffer; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_video_name, (VOID *) video) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, video, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &video -> ux_host_class_video_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the control buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, 2); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return an error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + /* Update the control buffer with the current control value. */ + _ux_utility_short_put(control_buffer, (USHORT) video_control -> ux_host_class_video_control_cur); + + /* Protect the control endpoint semaphore here. It will be unprotected in the + transfer request function. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_device -> ux_device_protection_semaphore, UX_WAIT_FOREVER); + + /* Check for status. */ + if (status != UX_SUCCESS) + + /* Something went wrong. */ + return(status); + + /* Create a transfer request for the GET_MIN request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = 2; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_SET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = (video_control -> ux_host_class_video_control << 8); + transfer_request -> ux_transfer_request_index = video -> ux_host_class_video_control_interface_number | (video -> ux_host_class_video_feature_unit_id << 8); + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer. */ + if (status != UX_SUCCESS) + { + + /* Free the previous control buffer. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(UX_TRANSFER_ERROR); + } + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_deactivate.c b/common/usbx_host_classes/src/ux_host_class_video_deactivate.c new file mode 100644 index 0000000..928a77e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_deactivate.c @@ -0,0 +1,131 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called when this instance of the video has been */ +/* removed from the bus either directly or indirectly. The iso pipes */ +/* will be destroyed and the instanced removed. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_destroy Destroy class instance */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort outstanding transfer */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_thread_schedule_other Schedule other threads */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_deactivate(UX_HOST_CLASS_COMMAND *command) +{ + +UX_HOST_CLASS_VIDEO *video; +UINT status; + + /* Get the instance for this class. */ + video= (UX_HOST_CLASS_VIDEO *) command -> ux_host_class_command_instance; + + /* The video is being shut down. */ + video -> ux_host_class_video_state = UX_HOST_CLASS_INSTANCE_SHUTDOWN; + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + + /* Return error. */ + return(status); + + /* We need to abort transactions on the iso out pipe. */ + _ux_host_stack_endpoint_transfer_abort(video -> ux_host_class_video_isochronous_endpoint); + + /* The enumeration thread needs to sleep a while to allow the application or the class that may be using + endpoints to exit properly. */ + _ux_utility_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); + + /* Destroy the instance. */ + _ux_host_stack_class_instance_destroy(video -> ux_host_class_video_class, (VOID *) video); + + /* Destroy the semaphore. */ + _ux_utility_semaphore_delete(&video -> ux_host_class_video_semaphore); + + /* Before we free the device resources, we need to inform the application + that the device is removed. */ + if (_ux_system_host -> ux_system_host_change_function != UX_NULL) + { + + /* Inform the application the device is removed. */ + _ux_system_host -> ux_system_host_change_function(UX_DEVICE_REMOVAL, video -> ux_host_class_video_class, (VOID *) video); + } + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_VIDEO_DEACTIVATE, video, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* If trace is enabled, register this object. */ + //UX_TRACE_OBJECT_UNREGISTER(video); + + /* Free the video instance memory. */ + _ux_utility_memory_free(video); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_descriptor_get.c b/common/usbx_host_classes/src/ux_host_class_video_descriptor_get.c new file mode 100644 index 0000000..b2e68a0 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_descriptor_get.c @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_descriptor_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains the entire video configuration descriptors. */ +/* This is needed because the video class has many class specific */ +/* descriptors which describe the alternate settings that can be used. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_descriptor_get(UX_HOST_CLASS_VIDEO *video) +{ + +UCHAR * descriptor; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UX_CONFIGURATION configuration; +UINT status; +ULONG total_configuration_length; + + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &video -> ux_host_class_video_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Need to allocate memory for the descriptor. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_CONFIGURATION_DESCRIPTOR_LENGTH); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Create a transfer request for the GET_DESCRIPTOR request. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + transfer_request -> ux_transfer_request_requested_length = UX_CONFIGURATION_DESCRIPTOR_LENGTH; + transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE; + transfer_request -> ux_transfer_request_value = UX_CONFIGURATION_DESCRIPTOR_ITEM << 8; + transfer_request -> ux_transfer_request_index = 0; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == UX_CONFIGURATION_DESCRIPTOR_LENGTH)) + { + + /* The descriptor is in a packed format, parse it locally. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_configuration_descriptor_structure, + UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration.ux_configuration_descriptor); + + /* Now we have the configuration descriptor which will tell us how many + bytes there are in the entire descriptor. */ + total_configuration_length = configuration.ux_configuration_descriptor.wTotalLength; + + /* Free the previous descriptor. */ + _ux_utility_memory_free(descriptor); + + /* Allocate enough memory to read all descriptors attached + to this configuration. We will save it until the class is unmounted. */ + descriptor = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, total_configuration_length); + if (descriptor == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set the length we need to retrieve. */ + transfer_request -> ux_transfer_request_requested_length = total_configuration_length; + + /* And reprogram the descriptor buffer address. */ + transfer_request -> ux_transfer_request_data_pointer = descriptor; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer and entire descriptor returned. */ + if ((status == UX_SUCCESS) && (transfer_request -> ux_transfer_request_actual_length == total_configuration_length)) + { + + /* Save the address of the entire configuration descriptor of the video device. */ + video -> ux_host_class_video_configuration_descriptor = descriptor; + + /* Save the length of the entire descriptor too. */ + video -> ux_host_class_video_configuration_descriptor_length = total_configuration_length; + + /* We do not free the resource for the descriptor until the device is + plugged out. */ + return(UX_SUCCESS); + } + } + + /* Free all used resources. */ + _ux_utility_memory_free(descriptor); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_endpoints_get.c b/common/usbx_host_classes/src/ux_host_class_video_endpoints_get.c new file mode 100644 index 0000000..f67c2d2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_endpoints_get.c @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_endpoints_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function searches for the handle of the isochronous endpoints. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_interface_endpoint_get Get interface endpoint */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_endpoints_get(UX_HOST_CLASS_VIDEO *video) +{ + +UINT status; +UINT endpoint_index; +UX_ENDPOINT *endpoint; + + + /* Search the ISO OUT endpoint. It is attached to the interface container. */ + for (endpoint_index = 0; endpoint_index < video -> ux_host_class_video_streaming_interface -> ux_interface_descriptor.bNumEndpoints; + endpoint_index++) + { + + /* Get interface endpoint. */ + status = _ux_host_stack_interface_endpoint_get(video -> ux_host_class_video_streaming_interface, endpoint_index, &endpoint); + + /* Check completion status. */ + if (status == UX_SUCCESS) + { + + /* Check if endpoint is iso and OUT. */ + if (((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == UX_ENDPOINT_OUT) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_ISOCHRONOUS_ENDPOINT)) + { + + /* We have found the bulk endpoint, save it. */ + video -> ux_host_class_video_isochronous_endpoint = endpoint; + break; + } + } + } + + /* The isochronous endpoint is mandatory. If we didn't find it, return an error. */ + if (video -> ux_host_class_video_isochronous_endpoint == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Return successful status. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_entry.c b/common/usbx_host_classes/src/ux_host_class_video_entry.c new file mode 100644 index 0000000..bc2049b --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_entry.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point of the Video class. It will be */ +/* called by the USBX stack enumeration module when there is a new */ +/* video device (speaker, microphone ...) on the bus or when the video */ +/* device is removed. */ +/* */ +/* INPUT */ +/* */ +/* command Pointer to command */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_video_activate Activate video class */ +/* _ux_host_class_video_deactivate Deactivate video class */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_entry(UX_HOST_CLASS_COMMAND *command) +{ + +UINT status = UX_SUCCESS; + + + /* The command request will tell us we need to do here, either a enumeration + query, an activation or a deactivation. */ + switch (command -> ux_host_class_command_request) + { + + + case UX_HOST_CLASS_COMMAND_QUERY: + + /* The query command is used to let the stack enumeration process know if we want to own + this device or not. */ + if ((command -> ux_host_class_command_usage == UX_HOST_CLASS_COMMAND_USAGE_CSP) && + (command -> ux_host_class_command_class == UX_HOST_CLASS_VIDEO_CLASS)) + status = UX_SUCCESS; + else + status = UX_NO_CLASS_MATCH; + break; + + case UX_HOST_CLASS_COMMAND_ACTIVATE: + + /* The activate command is used when the device inserted has found a parent and + is ready to complete the enumeration. */ + status = _ux_host_class_video_activate(command); + + break; + + + case UX_HOST_CLASS_COMMAND_DEACTIVATE: + + /* The deactivate command is used when the device has been extracted either + directly or when its parents has been extracted. */ + status = _ux_host_class_video_deactivate(command); + + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + status = UX_FUNCTION_NOT_SUPPORTED; + } + + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_format_data_get.c b/common/usbx_host_classes/src/ux_host_class_video_format_data_get.c new file mode 100644 index 0000000..9c86a1c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_format_data_get.c @@ -0,0 +1,176 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_format_data_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the format data within the input terminal. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* format_parameter Format Parameter structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_format_data_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_FORMAT_DATA *format_parameter) +{ + +UCHAR *descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; + + + /* Get the descriptor to the selected format. */ + descriptor = video -> ux_host_class_video_format_address; + total_descriptor_length = video -> ux_host_class_video_length_formats; + + /* Descriptors are arranged in order. First FORMAT then FRAME. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Must be CS_INTERFACE. */ + if (descriptor_type == UX_HOST_CLASS_VIDEO_CS_INTERFACE) + { + + /* Process relative to descriptor type. */ + switch (descriptor_subtype) + { + + case UX_HOST_CLASS_VIDEO_VS_FORMAT_UNCOMPRESSED : + case UX_HOST_CLASS_VIDEO_VS_FORMAT_MJPEG : + case UX_HOST_CLASS_VIDEO_VS_FORMAT_MPEG2TS : + case UX_HOST_CLASS_VIDEO_VS_FORMAT_DV : + case UX_HOST_CLASS_VIDEO_VS_FORMAT_FRAME_BASED : + case UX_HOST_CLASS_VIDEO_VS_FORMAT_STREAM_BASED : + + + /* We found a Format descriptor. Is it the right one ? */ + if (format_parameter -> ux_host_class_video_parameter_format_requested == *(descriptor + 3)) + { + + /* Save the subtype for this format. */ + format_parameter -> ux_host_class_video_parameter_format_subtype = descriptor_subtype; + + /* Save the number of frame descriptors associated with this format. */ + format_parameter -> ux_host_class_video_parameter_number_frame_descriptors = *(descriptor + 4); + + /* Save the address of this format. */ + video -> ux_host_class_video_current_format_address = descriptor; + + /* And the current format index. */ + video -> ux_host_class_video_current_format = format_parameter -> ux_host_class_video_parameter_format_requested; + + /* We are done here. */ + return(UX_SUCCESS); + + } + + break; + + } + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right video device. */ + return(UX_HOST_CLASS_VIDEO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_frame_data_get.c b/common/usbx_host_classes/src/ux_host_class_video_frame_data_get.c new file mode 100644 index 0000000..7abb99c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_frame_data_get.c @@ -0,0 +1,176 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_frame_data_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the frame data within the input terminal. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* frame_data Frame request structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler Log system error */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_frame_data_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_DATA *frame_parameter) +{ + +UCHAR *descriptor; +UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR frame_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; + + + /* Get the descriptor to the selected format. */ + descriptor = video -> ux_host_class_video_current_format_address; + total_descriptor_length = video -> ux_host_class_video_length_formats; + + /* Descriptors are arranged in order. First FORMAT then FRAME. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Must be CS_INTERFACE. */ + if (descriptor_type == UX_HOST_CLASS_VIDEO_CS_INTERFACE) + { + + /* Process relative to descriptor type. */ + switch (descriptor_subtype) + { + + case UX_HOST_CLASS_VIDEO_VS_FRAME_UNCOMPRESSED : + case UX_HOST_CLASS_VIDEO_VS_FRAME_MJPEG : + case UX_HOST_CLASS_VIDEO_VS_FRAME_FRAME_BASED : + + /* We found a Frame descriptor. Is it the right one ? */ + if (frame_parameter -> ux_host_class_video_parameter_frame_requested == *(descriptor + 3)) + { + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_video_frame_descriptor_structure, + UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR_ENTRIES, (UCHAR *) &frame_descriptor); + + + /* Save the frame subtype. */ + frame_parameter -> ux_host_class_video_parameter_frame_subtype = descriptor_type; + + /* Save useful frame parameters. */ + frame_parameter -> ux_host_class_video_parameter_frame_width = frame_descriptor.wWidth; + frame_parameter -> ux_host_class_video_parameter_frame_height = frame_descriptor.wHeight; + frame_parameter -> ux_host_class_video_parameter_default_frame_interval = frame_descriptor.dwDefaultFrameInterval; + frame_parameter -> ux_host_class_video_parameter_frame_interval_type = frame_descriptor.bFrameIntervalType; + video -> ux_host_class_video_current_frame_address = descriptor; + video -> ux_host_class_video_current_frame_interval = frame_descriptor.dwDefaultFrameInterval; + /* We are done here. */ + return(UX_SUCCESS); + } + + break; + + } + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + // UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right video device. */ + return(UX_HOST_CLASS_VIDEO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_frame_interval_get.c b/common/usbx_host_classes/src/ux_host_class_video_frame_interval_get.c new file mode 100644 index 0000000..22a801a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_frame_interval_get.c @@ -0,0 +1,199 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_frame_interval_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the frame intervals within the frame. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* interval_parameter Interval request structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler Log system error */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_long_get Get 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_frame_interval_get(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_INTERVAL *interval_parameter) +{ + +UCHAR *descriptor; +UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR frame_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; +ULONG intervals_to_copy; +ULONG i; + + /* Get the descriptor to the selected format. */ + descriptor = video -> ux_host_class_video_current_format_address; + total_descriptor_length = video -> ux_host_class_video_length_formats; + + /* Descriptors are arranged in order. First FORMAT then FRAME. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Must be CS_INTERFACE. */ + if (descriptor_type == UX_HOST_CLASS_VIDEO_CS_INTERFACE) + { + + /* Process relative to descriptor type. */ + switch (descriptor_subtype) + { + + case UX_HOST_CLASS_VIDEO_VS_FRAME_UNCOMPRESSED : + case UX_HOST_CLASS_VIDEO_VS_FRAME_MJPEG : + case UX_HOST_CLASS_VIDEO_VS_FRAME_FRAME_BASED : + + /* We found a Frame descriptor. Is it the right one ? */ + if (interval_parameter -> ux_host_class_video_parameter_frame_requested == *(descriptor + 3)) + { + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_video_frame_descriptor_structure, + UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR_ENTRIES, (UCHAR *) &frame_descriptor); + + /* Check the frame interval type. */ + if (frame_descriptor.bFrameIntervalType == 0) + { + + /* Frame type is continuous, copy Min, Max and Step. */ + intervals_to_copy = 3; + } + else + { + + /* Frame type is discrete, copy number of frame intervals. */ + intervals_to_copy = frame_descriptor.bFrameIntervalType; + } + + /* Check if we have enough space to copy. */ + if (intervals_to_copy * sizeof(ULONG) > interval_parameter -> ux_host_class_video_parameter_frame_interval_buffer_length) + { + intervals_to_copy = interval_parameter -> ux_host_class_video_parameter_frame_interval_buffer_length / sizeof(ULONG); + } + + /* Return bytes copied. */ + interval_parameter -> ux_host_class_video_parameter_frame_interval_buffer_length_written = intervals_to_copy * sizeof(ULONG); + + /* Loop to copy interval data. */ + for (i = 0; i < intervals_to_copy; i++) + { + + /* Copy intervals to user buffer. */ + interval_parameter -> ux_host_class_video_parameter_frame_interval_buffer[i] = + _ux_utility_long_get(descriptor + 26 + i * sizeof(ULONG)); + } + + /* We are done here. */ + return(UX_SUCCESS); + } + + break; + + } + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right video device. */ + return(UX_HOST_CLASS_VIDEO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_frame_parameters_set.c b/common/usbx_host_classes/src/ux_host_class_video_frame_parameters_set.c new file mode 100644 index 0000000..a9a86b2 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_frame_parameters_set.c @@ -0,0 +1,290 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_frame_parameters_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the video parameters for the video device. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* frame_format Video format to use */ +/* width Video frame width */ +/* height Video frame height */ +/* frame_interval Video frame interval */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_video_format_data_get Get format data */ +/* _ux_host_class_video_frame_data_get Get frame data */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_utility_long_get Read 32-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_frame_parameters_set(UX_HOST_CLASS_VIDEO *video, ULONG frame_format, ULONG width, ULONG height, ULONG frame_interval) +{ + +UX_HOST_CLASS_VIDEO_PARAMETER_FORMAT_DATA format_parameter; +UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_DATA frame_parameter; +UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR frame_descriptor; +ULONG format_index; +ULONG frame_index; +UINT status; +ULONG min_frame_interval; +ULONG max_frame_interval; +ULONG frame_interval_step; +ULONG i; +UX_ENDPOINT *control_endpoint; +UX_TRANSFER *transfer_request; +UINT streaming_interface; +UCHAR *control_buffer; +ULONG max_payload_size; + + + /* Find the requested frame format. */ + for (format_index = 1; format_index <= video -> ux_host_class_video_number_formats; format_index++) + { + + /* Get format data for current format index. */ + format_parameter.ux_host_class_video_parameter_format_requested = format_index; + status = _ux_host_class_video_format_data_get(video, &format_parameter); + if (status != UX_SUCCESS) + { + return(status); + } + + /* Check if the format type is the one requested. */ + if (format_parameter.ux_host_class_video_parameter_format_subtype == frame_format) + { + + /* Save number of frames in the video instance. */ + video -> ux_host_class_video_number_frames = format_parameter.ux_host_class_video_parameter_number_frame_descriptors; + + /* The format is found and break the loop. */ + break; + } + } + + /* Check if the requested format is found. */ + if (format_index > video -> ux_host_class_video_number_formats) + { + return(UX_HOST_CLASS_VIDEO_PARAMETER_ERROR); + } + + /* Find the requested frame resolution. */ + for (frame_index = 1; frame_index <= video -> ux_host_class_video_number_frames; frame_index++) + { + + /* Get frame data for current frame index. */ + frame_parameter.ux_host_class_video_parameter_frame_requested = frame_index; + status = _ux_host_class_video_frame_data_get(video, &frame_parameter); + if (status != UX_SUCCESS) + { + return(status); + } + + /* Check the frame resolution. */ + if (frame_parameter.ux_host_class_video_parameter_frame_width == width && + frame_parameter.ux_host_class_video_parameter_frame_height == height) + { + + /* Save the current frame index. */ + video -> ux_host_class_video_current_frame = frame_index; + + /* The requested resolution is found, break the loop. */ + break; + } + } + + /* Check if the requested resolution is found. */ + if (frame_index > video -> ux_host_class_video_number_frames) + { + return(UX_HOST_CLASS_VIDEO_PARAMETER_ERROR); + } + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(video -> ux_host_class_video_current_frame_address, + _ux_system_class_video_frame_descriptor_structure, + UX_HOST_CLASS_VIDEO_FRAME_DESCRIPTOR_ENTRIES, (UCHAR *) &frame_descriptor); + + /* Initial status for frame interval checking. */ + status = UX_HOST_CLASS_VIDEO_PARAMETER_ERROR; + + /* Check the frame interval type. */ + if (frame_descriptor.bFrameIntervalType == 0) + { + + /* Frame interval type is continuous. */ + min_frame_interval = _ux_utility_long_get(video -> ux_host_class_video_current_frame_address + 26); + max_frame_interval = _ux_utility_long_get(video -> ux_host_class_video_current_frame_address + 30); + frame_interval_step = _ux_utility_long_get(video -> ux_host_class_video_current_frame_address + 34); + + /* Check if the frame interval is valid. */ + if (frame_interval >= min_frame_interval && frame_interval <= max_frame_interval && + ((frame_interval - min_frame_interval) % frame_interval_step == 0)) + { + + /* Save the frame interval. */ + video -> ux_host_class_video_current_frame_interval = frame_interval; + status = UX_SUCCESS; + } + } + else + { + + /* Frame interval type is discrete. */ + for(i = 0; i < frame_descriptor.bFrameIntervalType; i++) + { + + /* Check if the requested frame interval is valid. */ + if (frame_interval == _ux_utility_long_get(video -> ux_host_class_video_current_frame_address + 26 + i * sizeof(ULONG))) + { + + /* Save the frame interval. */ + video -> ux_host_class_video_current_frame_interval = frame_interval; + status = UX_SUCCESS; + } + } + } + + if (status != UX_SUCCESS) + return(UX_HOST_CLASS_VIDEO_PARAMETER_ERROR); + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + + /* We need to get the default control endpoint transfer request pointer. */ + control_endpoint = &video -> ux_host_class_video_device -> ux_device_control_endpoint; + transfer_request = &control_endpoint -> ux_endpoint_transfer_request; + + /* Get the interface number of the video streaming interface. */ + streaming_interface = video -> ux_host_class_video_streaming_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* Need to allocate memory for the control_buffer. */ + control_buffer = _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH); + if (control_buffer == UX_NULL) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return error. */ + return(UX_MEMORY_INSUFFICIENT); + } + + *(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FORMAT_INDEX) = (UCHAR)format_index; + *(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FRAME_INDEX) = (UCHAR)frame_index; + _ux_utility_long_put(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_FRAME_INTERVAL, frame_interval); + + /* Create a transfer request for the SET_CUR buffer request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_SET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_OUT | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_VIDEO_VS_PROBE_CONTROL << 8; + transfer_request -> ux_transfer_request_index = streaming_interface; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Check for correct transfer. Buffer may not be all what we asked for. */ + if (status == UX_SUCCESS) + { + + + /* Create a transfer request for the GET_CUR buffer request. */ + transfer_request -> ux_transfer_request_data_pointer = control_buffer; + transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_VIDEO_PROBE_COMMIT_LENGTH; + transfer_request -> ux_transfer_request_function = UX_HOST_CLASS_VIDEO_GET_CUR; + transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_CLASS | UX_REQUEST_TARGET_INTERFACE; + transfer_request -> ux_transfer_request_value = UX_HOST_CLASS_VIDEO_VS_PROBE_CONTROL << 8; + transfer_request -> ux_transfer_request_index = streaming_interface; + + /* Send request to HCD layer. */ + status = _ux_host_stack_transfer_request(transfer_request); + + } + + /* Get the max payload transfer size returned from video device. */ + max_payload_size = _ux_utility_long_get(control_buffer + UX_HOST_CLASS_VIDEO_PROBE_COMMIT_MAX_PAYLOAD_TRANSFER_SIZE); + + /* Free all used resources. */ + _ux_utility_memory_free(control_buffer); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Check the transfer status. */ + if (status == UX_SUCCESS) + { + + /* Save the maximum payload size returned by the device. */ + video -> ux_host_class_video_current_max_payload_size = max_payload_size; + return(UX_SUCCESS); + } + else + { + + /* The probe process failed. Return error. */ + return(UX_HOST_CLASS_VIDEO_PARAMETER_ERROR); + } +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_input_format_get.c b/common/usbx_host_classes/src/ux_host_class_video_input_format_get.c new file mode 100644 index 0000000..e44338c --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_input_format_get.c @@ -0,0 +1,201 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_input_format_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the input format(s) for the input terminal. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_input_format_get(UX_HOST_CLASS_VIDEO *video) +{ + +UCHAR *descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_VIDEO_INPUT_HEADER_DESCRIPTOR input_header_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; +ULONG descriptor_found; + + + /* Get the descriptor to the entire configuration. */ + descriptor = video -> ux_host_class_video_configuration_descriptor; + total_descriptor_length = video -> ux_host_class_video_configuration_descriptor_length; + + /* Default is Interface descriptor not yet found. */ + descriptor_found = UX_FALSE; + + /* Scan the descriptor for the Video Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to the descriptor type. */ + switch (descriptor_type) + { + + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Video Streaming. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_VIDEO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_VIDEO_SUBCLASS_STREAMING)) + { + + /* Mark we have found it. */ + descriptor_found = UX_TRUE; + } + else + { + + descriptor_found = UX_FALSE; + } + break; + + + case UX_HOST_CLASS_VIDEO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if (descriptor_found == UX_TRUE) + { + + /* Check the sub type. */ + switch (descriptor_subtype) + { + + + case UX_HOST_CLASS_VIDEO_VS_INPUT_HEADER: + + /* Make the descriptor machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_video_input_header_descriptor_structure, + UX_HOST_CLASS_VIDEO_INPUT_HEADER_DESCRIPTOR_ENTRIES, (UCHAR *) &input_header_descriptor); + + /* Get the number of formats. */ + video -> ux_host_class_video_number_formats = input_header_descriptor.bNumFormats; + + /* Get the length of formats. */ + video -> ux_host_class_video_length_formats = input_header_descriptor.wTotalLength; + + /* Save the descriptor where the formats reside. */ + video -> ux_host_class_video_format_address = descriptor; + + /* We are done here. */ + return(UX_SUCCESS); + + } + } + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right video device. */ + return(UX_HOST_CLASS_VIDEO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_input_terminal_get.c b/common/usbx_host_classes/src/ux_host_class_video_input_terminal_get.c new file mode 100644 index 0000000..426764a --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_input_terminal_get.c @@ -0,0 +1,195 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_input_terminal_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the input terminal in the config descriptor. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_system_error_handler System error log */ +/* _ux_utility_descriptor_parse Parse descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_input_terminal_get(UX_HOST_CLASS_VIDEO *video) +{ + +UCHAR *descriptor; +UX_INTERFACE_DESCRIPTOR interface_descriptor; +UX_HOST_CLASS_VIDEO_INPUT_TERMINAL_DESCRIPTOR input_terminal_descriptor; +ULONG total_descriptor_length; +ULONG descriptor_length; +ULONG descriptor_type; +ULONG descriptor_subtype; +ULONG interface_found; + + + /* Get the descriptor to the selected format. */ + descriptor = video -> ux_host_class_video_configuration_descriptor; + total_descriptor_length = video -> ux_host_class_video_configuration_descriptor_length; + + /* Haven't found it yet. */ + interface_found = UX_FALSE; + + /* Scan the descriptor for the Video Streaming interface. */ + while (total_descriptor_length) + { + + /* Gather the length, type and subtype of the descriptor. */ + descriptor_length = *descriptor; + descriptor_type = *(descriptor + 1); + descriptor_subtype = *(descriptor + 2); + + /* Make sure this descriptor has at least the minimum length. */ + if (descriptor_length < 3) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Process relative to descriptor type. */ + switch (descriptor_type) + { + + case UX_INTERFACE_DESCRIPTOR_ITEM: + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_interface_descriptor_structure, + UX_INTERFACE_DESCRIPTOR_ENTRIES, (UCHAR *) &interface_descriptor); + + /* Ensure we have the correct interface for Video Control. */ + if ((interface_descriptor.bInterfaceClass == UX_HOST_CLASS_VIDEO_CLASS) && + (interface_descriptor.bInterfaceSubClass == UX_HOST_CLASS_VIDEO_SUBCLASS_CONTROL)) + { + + /* Mark we have found it. */ + interface_found = UX_TRUE; + + /* Get the interface number of this descriptor and save it in the video + instance. This will be useful to program the video controls. */ + video -> ux_host_class_video_control_interface_number = interface_descriptor.bInterfaceNumber; + } + else + { + + /* Haven't found it. */ + interface_found = UX_FALSE; + } + break; + + + case UX_HOST_CLASS_VIDEO_CS_INTERFACE: + + /* First make sure we have found the correct generic interface descriptor. */ + if ((interface_found == UX_TRUE) && (descriptor_subtype == UX_HOST_CLASS_VIDEO_VC_INPUT_TERMINAL)) + { + + /* Parse the interface descriptor and make it machine independent. */ + _ux_utility_descriptor_parse(descriptor, _ux_system_class_video_input_terminal_descriptor_structure, + UX_HOST_CLASS_VIDEO_INPUT_TERMINAL_DESCRIPTOR_ENTRIES, (UCHAR *) &input_terminal_descriptor); + + /* Save the video terminal ID. */ + video -> ux_host_class_video_terminal_id = input_terminal_descriptor.bTerminalID; + + /* Save the video terminal type. */ + video -> ux_host_class_video_terminal_type = input_terminal_descriptor.wTerminalType; + + + /* We are done here. */ + return(UX_SUCCESS); + } + + break; + } + + /* Verify if the descriptor is still valid. */ + if (descriptor_length > total_descriptor_length) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_DESCRIPTOR_CORRUPTED, descriptor, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_DESCRIPTOR_CORRUPTED); + } + + /* Jump to the next descriptor if we have not reached the end. */ + descriptor += descriptor_length; + + /* And adjust the length left to parse in the descriptor. */ + total_descriptor_length -= descriptor_length; + } + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_TYPE); + + /* We get here when either the report descriptor has a problem or we could + not find the right video device. */ + return(UX_HOST_CLASS_VIDEO_WRONG_TYPE); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_ioctl.c b/common/usbx_host_classes/src/ux_host_class_video_ioctl.c new file mode 100644 index 0000000..a3dbd7e --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_ioctl.c @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** VIDEO Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_cdc_acm_ioctl PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the ioctl entry point for the application to */ +/* configure the video class based device. */ +/* */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* ioctl_function Ioctl function */ +/* parameter Pointer to structure */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_video_format_data_get Get video format data. */ +/* _ux_host_class_video_frame_data_get Get video frame data */ +/* _ux_host_class_video_frame_interval_get */ +/* Get video frame internal data.*/ +/* _ux_host_class_video_channel_start Start the video. */ +/* _ux_host_class_video_stop Stop the video. */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort the transfer */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Storage Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_ioctl(UX_HOST_CLASS_VIDEO *video, ULONG ioctl_function, + VOID *parameter) +{ + +UINT status; +UX_HOST_CLASS_VIDEO_PARAMETER_INPUT_TERMINAL *input_terminal; +UX_HOST_CLASS_VIDEO_PARAMETER_NUMBER_FORMATS *number_formats; +UX_HOST_CLASS_VIDEO_PARAMETER_FORMAT_DATA *format_parameter; +UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_DATA *frame_parameter; +UX_HOST_CLASS_VIDEO_PARAMETER_CHANNEL *channel_parameter; +UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_INTERVAL *interval_parameter; + + /* Ensure the instance is valid. */ + if ((video -> ux_host_class_video_state != UX_HOST_CLASS_INSTANCE_LIVE) && + (video -> ux_host_class_video_state != UX_HOST_CLASS_INSTANCE_MOUNTING)) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, video, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* The command request will tell us what we need to do here. */ + switch (ioctl_function) + { + + case UX_HOST_CLASS_VIDEO_IOCTL_GET_INPUT_TERMINAL: + + /* Set status to error by default. */ + status = UX_ERROR; + + /* Is the video terminal defined ? */ + if (video -> ux_host_class_video_terminal_id != 0) + { + + /* Cast answer. */ + input_terminal = (UX_HOST_CLASS_VIDEO_PARAMETER_INPUT_TERMINAL *) parameter; + + /* Return input terminal id. */ + input_terminal -> ux_host_class_video_parameter_input_terminal_id = video -> ux_host_class_video_terminal_id; + + /* Return input terminal type. */ + input_terminal -> ux_host_class_video_parameter_input_terminal_type = video -> ux_host_class_video_terminal_type; + + /* Status ok. */ + status = UX_SUCCESS; + } + + break; + + case UX_HOST_CLASS_VIDEO_IOCTL_GET_FORMAT_NUMBER: + + /* Cast answer. */ + number_formats = (UX_HOST_CLASS_VIDEO_PARAMETER_NUMBER_FORMATS *) parameter; + + /* Save the number of formats. */ + number_formats -> ux_host_class_video_parameter_number_formats = video -> ux_host_class_video_number_formats; + + /* Status ok. */ + status = UX_SUCCESS; + + break; + + case UX_HOST_CLASS_VIDEO_IOCTL_GET_FORMAT_DATA: + + /* Cast answer. */ + format_parameter = (UX_HOST_CLASS_VIDEO_PARAMETER_FORMAT_DATA *) parameter; + + /* Get the format data for the format index requested. */ + status = _ux_host_class_video_format_data_get(video, format_parameter); + break; + + case UX_HOST_CLASS_VIDEO_IOCTL_GET_FRAME_DATA: + + /* Cast answer. */ + frame_parameter = (UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_DATA *) parameter; + + /* Get the frame data for the frame index requested. */ + status = _ux_host_class_video_frame_data_get(video, frame_parameter); + break; + + case UX_HOST_CLASS_VIDEO_IOCTL_GET_FRAME_INTERVAL: + + /* Cast answer. */ + interval_parameter = (UX_HOST_CLASS_VIDEO_PARAMETER_FRAME_INTERVAL *) parameter; + + /* Get the frame intervals for the frame index requested. */ + status = _ux_host_class_video_frame_interval_get(video, interval_parameter); + break; + + case UX_HOST_CLASS_VIDEO_IOCTL_CHANNEL_START: + + /* Cast answer. */ + channel_parameter = (UX_HOST_CLASS_VIDEO_PARAMETER_CHANNEL *) parameter; + + /* Start the channel for reading video input. */ + status = _ux_host_class_video_channel_start(video, channel_parameter); + break; + + case UX_HOST_CLASS_VIDEO_IOCTL_CHANNEL_STOP: + + /* Stop the channel. */ + status = _ux_host_class_video_stop(video); + break; + + + case UX_HOST_CLASS_VIDEO_IOCTL_ABORT_IN_PIPE : + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_VIDEO_IOCTL_ABORT_IN_PIPE, video, video -> ux_host_class_video_isochronous_endpoint, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0) + + /* We need to abort transactions on the bulk In pipe. */ + _ux_host_stack_endpoint_transfer_abort(video -> ux_host_class_video_isochronous_endpoint); + + /* Status is successful. */ + status = UX_SUCCESS; + break; + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + //UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Function not supported. Return an error. */ + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_max_payload_get.c b/common/usbx_host_classes/src/ux_host_class_video_max_payload_get.c new file mode 100644 index 0000000..8402d8f --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_max_payload_get.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_max_payload_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the maximum transfer size in a single payload */ +/* transfer. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Maximum payload transfer size */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_host_class_video_max_payload_get(UX_HOST_CLASS_VIDEO *video) +{ + + /* Return the maximum payload size. */ + return(video ->ux_host_class_video_current_max_payload_size); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_read.c b/common/usbx_host_classes/src/ux_host_class_video_read.c new file mode 100644 index 0000000..1e92c98 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_read.c @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads from the video streaming interface. */ +/* */ +/* Note if the transfer request is not linked (next pointer is NULL), */ +/* a single request is performed. If the transfer request links into a */ +/* list, the whole list is added. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* video_transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_video_transfer_request Video transfer request */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_system_error_handler System error log */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_read(UX_HOST_CLASS_VIDEO *video, UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST *video_transfer_request) +{ + +UINT status; +ULONG packet_size; +UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST *transfer_list; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_video_name, (VOID *) video) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* By default the transmission will be successful. */ + status = UX_SUCCESS; + + /* Ensure we have a selected interface that allows isoch transmission. */ + if ((video -> ux_host_class_video_isochronous_endpoint == UX_NULL) || + (video -> ux_host_class_video_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize == 0)) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_INTERFACE); + + /* Return error status. */ + return(UX_HOST_CLASS_VIDEO_WRONG_INTERFACE); + } + + /* Calculate max transfer size on the endpoint. */ + packet_size = video -> ux_host_class_video_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + if (packet_size & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) + packet_size = (packet_size & UX_MAX_PACKET_SIZE_MASK) * (((packet_size & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1); + + /* Save list head. */ + transfer_list = video_transfer_request; + + /* Handle the request (list). */ + while(video_transfer_request) + { + + /* For video in, we read packets at a time, so the transfer request size is the size of the + endpoint. */ + video_transfer_request -> ux_host_class_video_transfer_request_requested_length = packet_size; + + /* Next request. */ + video_transfer_request = video_transfer_request -> ux_host_class_video_transfer_request_next_video_transfer_request; + + } + + /* Ask the stack to hook this transfer request to the iso ED. */ + status = _ux_host_class_video_transfer_request(video, transfer_list); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_start.c b/common/usbx_host_classes/src/ux_host_class_video_start.c new file mode 100644 index 0000000..f3396cc --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_start.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_start PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function starts the video streaming. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_class_video_channel_start Start video device */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_start(UX_HOST_CLASS_VIDEO *video) +{ + +UX_HOST_CLASS_VIDEO_PARAMETER_CHANNEL channel_parameter; +UINT status; + + + /* Get current parameters from the video instance. */ + channel_parameter.ux_host_class_video_parameter_format_requested = video -> ux_host_class_video_current_format; + channel_parameter.ux_host_class_video_parameter_frame_requested = video -> ux_host_class_video_current_frame; + channel_parameter.ux_host_class_video_parameter_frame_interval_requested = video -> ux_host_class_video_current_frame_interval; + channel_parameter.ux_host_class_video_parameter_channel_bandwidth_selection = 0; + + /* Start the video with the parameters. */ + status = _ux_host_class_video_channel_start(video, &channel_parameter); + + /* Reset indices for transfer requests. */ + video -> ux_host_class_video_transfer_request_start_index = 0; + video -> ux_host_class_video_transfer_request_end_index = 0; + + /* Return status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_stop.c b/common/usbx_host_classes/src/ux_host_class_video_stop.c new file mode 100644 index 0000000..6789430 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_stop.c @@ -0,0 +1,144 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_stop PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function stops the video channel. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_endpoint_transfer_abort */ +/* Abort outstanding transfer */ +/* _ux_host_stack_interface_setting_select */ +/* Select interface */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_stop(UX_HOST_CLASS_VIDEO *video) +{ + +UINT status; +UX_CONFIGURATION *configuration; +UX_INTERFACE *interface; +UINT streaming_interface; + + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + + /* Get the interface number of the video streaming interface. */ + streaming_interface = video -> ux_host_class_video_streaming_interface -> ux_interface_descriptor.bInterfaceNumber; + + /* We need to abort transactions on the iso pipe. */ + if (video -> ux_host_class_video_isochronous_endpoint != UX_NULL) + { + + /* Abort the iso transfer. */ + _ux_host_stack_endpoint_transfer_abort(video -> ux_host_class_video_isochronous_endpoint); + } + + /* We found the alternate setting for the sampling values demanded, now we need + to search its container. */ + configuration = video -> ux_host_class_video_streaming_interface -> ux_interface_configuration; + interface = configuration -> ux_configuration_first_interface; + + /* Scan all interfaces. */ + while (interface != UX_NULL) + { + + /* We search for both the right interface and alternate setting. */ + if ((interface -> ux_interface_descriptor.bInterfaceNumber == streaming_interface) && + (interface -> ux_interface_descriptor.bAlternateSetting == 0)) + { + + /* We have found the right interface/alternate setting combination + The stack will select it for us. */ + status = _ux_host_stack_interface_setting_select(interface); + + /* If the alternate setting for the streaming interface could be selected, we memorize it. */ + if (status == UX_SUCCESS) + { + + /* Memorize the interface. */ + video -> ux_host_class_video_streaming_interface = interface; + + /* There is no endpoint for the alternate setting 0. */ + video -> ux_host_class_video_isochronous_endpoint = UX_NULL; + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + + /* Move to next interface. */ + interface = interface -> ux_interface_next_interface; + } + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_transfer_buffer_add.c b/common/usbx_host_classes/src/ux_host_class_video_transfer_buffer_add.c new file mode 100644 index 0000000..d7f6daa --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_transfer_buffer_add.c @@ -0,0 +1,161 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_transfer_buffer_add PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function adds a buffer for video transfer requests. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* buffer Pointer to data buffer */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_transfer_buffer_add(UX_HOST_CLASS_VIDEO *video, UCHAR* buffer) +{ + +UINT status; +UX_TRANSFER *transfer_request; +ULONG transfer_index; +ULONG packet_size; + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_video_name, (VOID *) video) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + // UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, video, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Ensure we have a selected interface that allows isoch transmission. */ + if ((video -> ux_host_class_video_isochronous_endpoint == UX_NULL) || + (video -> ux_host_class_video_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize == 0)) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_INTERFACE); + + /* Return error status. */ + return(UX_HOST_CLASS_VIDEO_WRONG_INTERFACE); + } + + transfer_index = video->ux_host_class_video_transfer_request_start_index; + transfer_index ++; + if (transfer_index == UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT) + transfer_index = 0; + if (transfer_index == video->ux_host_class_video_transfer_request_end_index) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return error status. */ + return(UX_MEMORY_ARRAY_FULL); + } + + transfer_request = &video->ux_host_class_video_transfer_requests[video->ux_host_class_video_transfer_request_start_index]; + video->ux_host_class_video_transfer_request_start_index = transfer_index; + + /* Select the direction. We do this by taking the endpoint direction. */ + transfer_request -> ux_transfer_request_type = video -> ux_host_class_video_isochronous_endpoint -> + ux_endpoint_descriptor.bEndpointAddress & UX_REQUEST_DIRECTION; + + /* Calculate packet size. */ + packet_size = video -> ux_host_class_video_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + if (packet_size & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) + packet_size = (packet_size & UX_MAX_PACKET_SIZE_MASK) * (((packet_size & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1); + + /* Fill the transfer request with all the required fields. */ + transfer_request -> ux_transfer_request_endpoint = video -> ux_host_class_video_isochronous_endpoint; + transfer_request -> ux_transfer_request_data_pointer = buffer; + transfer_request -> ux_transfer_request_requested_length = packet_size; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_video_transfer_request_callback; + transfer_request -> ux_transfer_request_class_instance = video; + + /* Add single transfer. */ + transfer_request -> ux_transfer_request_next_transfer_request = UX_NULL; + + /* Transfer the transfer request. */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_transfer_buffers_add.c b/common/usbx_host_classes/src/ux_host_class_video_transfer_buffers_add.c new file mode 100644 index 0000000..1d36642 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_transfer_buffers_add.c @@ -0,0 +1,208 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_transfer_buffers_add PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function adds buffers for video transfer requests. */ +/* */ +/* Usually it's the very first step to start a video stream on a high */ +/* bandwidth isochronous endpoint. Since adding new buffer while */ +/* the prepared buffers in progress helps to improve performance. */ +/* */ +/* Note the maximum number of transfers could be buffered is */ +/* UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT - 1. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* buffers Pointer to data buffer */ +/* pointers array */ +/* num_buffers Number of data buffers */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_class_instance_verify Verify instance is valid */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* _ux_system_error_handler Log system error */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_transfer_buffers_add(UX_HOST_CLASS_VIDEO *video, UCHAR** buffers, ULONG num_buffers) +{ + +UINT status; +UX_TRANSFER *transfer_request; +UX_TRANSFER *previous_transfer; +ULONG transfer_index; +ULONG packet_size; +UINT i; + + + /* Ensure the instance is valid. */ + if (_ux_host_stack_class_instance_verify(_ux_system_host_class_video_name, (VOID *) video) != UX_SUCCESS) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN); + + return(UX_HOST_CLASS_INSTANCE_UNKNOWN); + } + + /* Protect thread reentry to this instance. */ + status = _ux_utility_semaphore_get(&video -> ux_host_class_video_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return(status); + + /* Ensure we have a selected interface that allows isoch transmission. */ + if ((video -> ux_host_class_video_isochronous_endpoint == UX_NULL) || + (video -> ux_host_class_video_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize == 0)) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_VIDEO_WRONG_INTERFACE); + + /* Return error status. */ + return(UX_HOST_CLASS_VIDEO_WRONG_INTERFACE); + } + + /* Check if there is enough requests available. */ + if (video -> ux_host_class_video_transfer_request_start_index >= + video -> ux_host_class_video_transfer_request_end_index) + { + + if (video -> ux_host_class_video_transfer_request_start_index + num_buffers >= + video -> ux_host_class_video_transfer_request_end_index + UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT) + status = (UX_MEMORY_ARRAY_FULL); + } + else + { + if (video -> ux_host_class_video_transfer_request_start_index + num_buffers >= + video -> ux_host_class_video_transfer_request_end_index) + status = (UX_MEMORY_ARRAY_FULL); + } + + /* Check error. */ + if (status != UX_SUCCESS) + { + + /* Unprotect thread reentry to this instance. */ + status = _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return error status. */ + return(status); + } + + /* Calculate packet size. */ + packet_size = video -> ux_host_class_video_isochronous_endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + if (packet_size & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) + packet_size = (packet_size & UX_MAX_PACKET_SIZE_MASK) * (((packet_size & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT) + 1); + + /* Add buffers one by one. */ + for (i = 0, + transfer_index = video -> ux_host_class_video_transfer_request_start_index, + previous_transfer = UX_NULL; + i < num_buffers; i ++) + { + + transfer_request = &video->ux_host_class_video_transfer_requests[transfer_index]; + + /* Select the direction. We do this by taking the endpoint direction. */ + transfer_request -> ux_transfer_request_type = video -> ux_host_class_video_isochronous_endpoint -> + ux_endpoint_descriptor.bEndpointAddress & UX_REQUEST_DIRECTION; + + /* Fill the transfer request with all the required fields. */ + transfer_request -> ux_transfer_request_endpoint = video -> ux_host_class_video_isochronous_endpoint; + transfer_request -> ux_transfer_request_data_pointer = buffers[i]; + transfer_request -> ux_transfer_request_requested_length = packet_size; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_video_transfer_request_callback; + transfer_request -> ux_transfer_request_class_instance = video; + + /* Confirm the transfer request is single one. */ + transfer_request -> ux_transfer_request_next_transfer_request = UX_NULL; + + /* Link to transfer request tail. */ + if (previous_transfer) + previous_transfer -> ux_transfer_request_next_transfer_request = transfer_request; + + /* Save as previous request. */ + previous_transfer = transfer_request; + + /* Move to next transfer request. */ + transfer_index ++; + if (transfer_index >= UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT) + transfer_index = 0; + } + + /* Get request list head. */ + transfer_request = &video -> ux_host_class_video_transfer_requests[video->ux_host_class_video_transfer_request_start_index]; + + /* Move request index. */ + video->ux_host_class_video_transfer_request_start_index = transfer_index; + + /* Transfer the transfer request (list). */ + status = _ux_host_stack_transfer_request(transfer_request); + + /* Unprotect thread reentry to this instance. */ + _ux_utility_semaphore_put(&video -> ux_host_class_video_semaphore); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_transfer_callback_set.c b/common/usbx_host_classes/src/ux_host_class_video_transfer_callback_set.c new file mode 100644 index 0000000..5702cff --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_transfer_callback_set.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_transfer_callback_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the callback function for video transfers. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* callback_function Pointer to callback function */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_video_transfer_callback_set(UX_HOST_CLASS_VIDEO *video, VOID (*callback_function)(UX_TRANSFER*)) +{ + + /* Save the callback function in the video instance. */ + video -> ux_host_class_video_transfer_completion_function = callback_function; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_transfer_request.c b/common/usbx_host_classes/src/ux_host_class_video_transfer_request.c new file mode 100644 index 0000000..fc2f989 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_transfer_request.c @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_transfer_request PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function submits an isochronous video transfer request or */ +/* isochronous video transfer request list to the USBX stack. */ +/* */ +/* Note if the transfer request is not linked (next pointer is NULL), */ +/* a single request is submitted. If the transfer request links into a */ +/* list, the whole list is submitted. */ +/* */ +/* INPUT */ +/* */ +/* video Pointer to video class */ +/* video_transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_host_stack_transfer_request Process transfer request */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_host_class_video_transfer_request(UX_HOST_CLASS_VIDEO *video, + UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST *video_transfer_request) +{ + +UINT status; +UX_TRANSFER *transfer_list; +UX_TRANSFER *transfer_request; +UX_TRANSFER *previous_transfer; + + /* Get transfer request list head. */ + transfer_list = &video_transfer_request -> ux_host_class_video_transfer_request; + + /* Process the transfer request list (if multiple found). */ + previous_transfer = UX_NULL; + while(video_transfer_request) + { + + /* The transfer request is embedded in the application transfer request. */ + transfer_request = &video_transfer_request -> ux_host_class_video_transfer_request; + + /* Select the direction. We do this by taking the endpoint direction. */ + transfer_request -> ux_transfer_request_type = video -> ux_host_class_video_isochronous_endpoint -> + ux_endpoint_descriptor.bEndpointAddress & UX_REQUEST_DIRECTION; + + /* Fill the transfer request with all the required fields. */ + transfer_request -> ux_transfer_request_endpoint = video -> ux_host_class_video_isochronous_endpoint; + transfer_request -> ux_transfer_request_data_pointer = video_transfer_request -> ux_host_class_video_transfer_request_data_pointer; + transfer_request -> ux_transfer_request_requested_length = video_transfer_request -> ux_host_class_video_transfer_request_requested_length; + transfer_request -> ux_transfer_request_completion_function = _ux_host_class_video_transfer_request_completed; + transfer_request -> ux_transfer_request_class_instance = video; + + /* We memorize the application transfer request in the local transfer request. */ + transfer_request -> ux_transfer_request_user_specific = (VOID *) video_transfer_request; + + /* Confirm transfer is not linking to others */ + transfer_request -> ux_transfer_request_next_transfer_request = UX_NULL; + + /* Add it to transfer list tail. */ + if (previous_transfer != UX_NULL) + previous_transfer -> ux_transfer_request_next_transfer_request = transfer_request; + + /* Save as previous transfer. */ + previous_transfer = transfer_request; + + /* Check next transfer request. */ + video_transfer_request = video_transfer_request -> ux_host_class_video_transfer_request_next_video_transfer_request; + } + + /* Transfer the transfer request (list). */ + status = _ux_host_stack_transfer_request(transfer_list); + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_transfer_request_callback.c b/common/usbx_host_classes/src/ux_host_class_video_transfer_request_callback.c new file mode 100644 index 0000000..620ea34 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_transfer_request_callback.c @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_transfer_request_callback PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function receives a completion call back on an isoch transfer */ +/* request. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_video_transfer_completion_function) */ +/* Transfer request completion */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_video_transfer_request_callback(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_VIDEO *video; +ULONG transfer_index; + + + /* Get the pointer to the video instance. */ + video = (UX_HOST_CLASS_VIDEO *) transfer_request -> ux_transfer_request_class_instance; + + /* Do a sanity check on the transfer request, if NULL something is wrong. */ + if (video == UX_NULL) + return; + + /* The caller's transfer request needs to be updated. */ + transfer_index = video -> ux_host_class_video_transfer_request_end_index; + transfer_index++; + if (transfer_index == UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST_COUNT) + transfer_index = 0; + + /* Update the transfer index. */ + video -> ux_host_class_video_transfer_request_end_index = transfer_index; + + /* Call the completion routine. */ + if (video -> ux_host_class_video_transfer_completion_function) + video -> ux_host_class_video_transfer_completion_function(transfer_request); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_classes/src/ux_host_class_video_transfer_request_completed.c b/common/usbx_host_classes/src/ux_host_class_video_transfer_request_completed.c new file mode 100644 index 0000000..088eb58 --- /dev/null +++ b/common/usbx_host_classes/src/ux_host_class_video_transfer_request_completed.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Video Class */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_class_video.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_host_class_video_transfer_request_completed PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function receives a completion call back on an isoch transfer */ +/* request. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_host_class_video_transfer_request_completion_function) */ +/* Transfer request completion */ +/* */ +/* CALLED BY */ +/* */ +/* Video Class */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_host_class_video_transfer_request_completed(UX_TRANSFER *transfer_request) +{ + +UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST *video_transfer_request; + + + /* Get the pointer to the video specific transfer request, by nature of the lined transfer requests, + the corresponding transfer request has to be the head transfer request in the video instance. */ + video_transfer_request = (UX_HOST_CLASS_VIDEO_TRANSFER_REQUEST *) transfer_request -> ux_transfer_request_user_specific; + + /* Do a sanity check on the transfer request, if NULL something is wrong. */ + if (video_transfer_request == UX_NULL) + return; + + /* The caller's transfer request needs to be updated. */ + video_transfer_request -> ux_host_class_video_transfer_request_actual_length = transfer_request -> ux_transfer_request_actual_length; + video_transfer_request -> ux_host_class_video_transfer_request_completion_code = transfer_request -> ux_transfer_request_completion_code; + + /* Call the completion routine. */ + video_transfer_request -> ux_host_class_video_transfer_request_completion_function(video_transfer_request); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/CMakeLists.txt b/common/usbx_host_controllers/CMakeLists.txt new file mode 100644 index 0000000..cdd0f20 --- /dev/null +++ b/common/usbx_host_controllers/CMakeLists.txt @@ -0,0 +1,93 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_asynch_td_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_done_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_door_bell_wait.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_ed_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_fsisochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_fsisochronous_tds_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_hsisochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_hsisochronous_tds_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_interrupt_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_isochronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_next_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_periodic_descriptor_link.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_poll_rate_entry_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_interrupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_request_transfer_add.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ehci_transfer_request_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_asynchronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_asynchronous_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_controller_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_done_queue_process.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_ed_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_endpoint_error_clear.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_endpoint_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_entry.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_frame_number_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_frame_number_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_interrupt_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_interrupt_handler.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_isochronous_endpoint_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_isochronous_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_least_traffic_list_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_next_td_clean.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_periodic_endpoint_destroy.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_periodic_tree_create.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_disable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_reset.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_resume.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_port_suspend.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_power_down_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_power_on_port.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_power_root_hubs.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_register_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_register_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_regular_td_obtain.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_bulk_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_control_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_interupt_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_isochronous_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_request_transfer.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_transfer_abort.c + ${CMAKE_CURRENT_LIST_DIR}/src/ux_hcd_ohci_transfer_request_process.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/common/usbx_host_controllers/inc/ux_hcd_ehci.h b/common/usbx_host_controllers/inc/ux_hcd_ehci.h new file mode 100644 index 0000000..d4a4c49 --- /dev/null +++ b/common/usbx_host_controllers/inc/ux_hcd_ehci.h @@ -0,0 +1,822 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_hcd_ehci.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX host EHCI Controller. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HCD_EHCI_H +#define UX_HCD_EHCI_H + + +/* Possible defined EHCI HCD extentions. */ + +/* Extension for peripheral host mode select (function like). */ +/* #define UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE(hcd_ehci) */ + +/* Extension for embedded TT (UX_TRUE/UX_FALSE). */ +/* #define UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT */ + +/* Extension for phy high speed mode select (function like). */ +/* #define UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, on_off) */ + +/* Define EHCI generic definitions. */ + +#define UX_EHCI_CONTROLLER 2 +#define UX_EHCI_MAX_PAYLOAD 16384 +#define UX_EHCI_FRAME_DELAY 4 +#define UX_EHCI_PAGE_SIZE 4096 +#define UX_EHCI_PAGE_ALIGN 0xfffff000 + + +/* Define EHCI host controller capability registers. */ + +#define EHCI_HCCR_CAP_LENGTH 0x00 +#define EHCI_HCCR_HCS_PARAMS 0x01 +#define EHCI_HCCR_HCC_PARAMS 0x02 +#define EHCI_HCCR_HCSP_PORT_ROUTE 0x03 + + +/* Define EHCI host controller registers. */ + +#define EHCI_HCOR_USB_COMMAND (hcd_ehci -> ux_hcd_ehci_hcor + 0x00) +#define EHCI_HCOR_USB_STATUS (hcd_ehci -> ux_hcd_ehci_hcor + 0x01) +#define EHCI_HCOR_USB_INTERRUPT (hcd_ehci -> ux_hcd_ehci_hcor + 0x02) +#define EHCI_HCOR_FRAME_INDEX (hcd_ehci -> ux_hcd_ehci_hcor + 0x03) +#define EHCI_HCOR_FRAME_LIST_BASE_ADDRESS (hcd_ehci -> ux_hcd_ehci_hcor + 0x05) +#define EHCI_HCOR_ASYNCH_LIST_ADDRESS (hcd_ehci -> ux_hcd_ehci_hcor + 0x06) +#define EHCI_HCOR_CONFIG_FLAG (hcd_ehci -> ux_hcd_ehci_hcor + 0x10) +#define EHCI_HCOR_PORT_SC (hcd_ehci -> ux_hcd_ehci_hcor + 0x11) + + +/* Define EHCI IO control register values. */ + +#define EHCI_HC_IO_RS 0x00000001 +#define EHCI_HC_IO_HCRESET 0x00000002 +#define EHCI_HC_IO_PSE 0x00000010 +#define EHCI_HC_IO_ASE 0x00000020 +#define EHCI_HC_IO_IAAD 0x00000040 +#define EHCI_HC_IO_ITC 0x00010000 +#define EHCI_HC_IO_FRAME_SIZE_1024 0x00000000 +#define EHCI_HC_IO_FRAME_SIZE_512 0x00000004 +#define EHCI_HC_IO_FRAME_SIZE_256 0x00000008 +#define EHCI_HC_IO_FRAME_SIZE_128 0x0000000C +#define EHCI_HC_IO_FRAME_SIZE_64 0x00008000 +#define EHCI_HC_IO_FRAME_SIZE_32 0x00008004 + +/* The number if entries in the periodic tree can be changed to save space IF and only IF the PFLF flag in the HCCPARAMS register + allows it. Setting values less than 1024 in controllers without the ability to change the Frame List Size leads to a EHCI crash. */ + +#ifndef UX_EHCI_FRAME_LIST_ENTRIES +#define UX_EHCI_FRAME_LIST_ENTRIES 1024 +#endif +#define UX_EHCI_FRAME_LIST_MASK EHCI_HC_IO_FRAME_SIZE_1024 + +/* Define EHCI HCOR status register. */ + +#define EHCI_HC_STS_USB_INT 0x00000001 +#define EHCI_HC_STS_USB_ERR_INT 0x00000002 +#define EHCI_HC_STS_PCD 0x00000004 +#define EHCI_HC_STS_FLR 0x00000008 +#define EHCI_HC_STS_HSE 0x00000010 +#define EHCI_HC_STS_IAA 0x00000020 +#define EHCI_HC_STS_HC_HALTED 0x00001000 +#define EHCI_HC_STS_RECLAMATION 0x00002000 +#define EHCI_HC_STS_PSS 0x00004000 +#define EHCI_HC_STS_ASS 0x00008000 + +#define EHCI_HC_INTERRUPT_ENABLE_NORMAL (EHCI_HC_STS_USB_INT|EHCI_HC_STS_USB_ERR_INT|EHCI_HC_STS_PCD|EHCI_HC_STS_HSE|EHCI_HC_STS_IAA) + + +/* Define EHCI HCOR root HUB command/status. */ + +#define EHCI_HC_RH_PPC 0x00000010 +#define EHCI_HC_RH_PSM 0x00000100 +#define EHCI_HC_RH_NPS 0x00000200 +#define EHCI_HC_RH_DT 0x00000400 +#define EHCI_HC_RH_OCPM 0x00000800 +#define EHCI_HC_RH_NOCP 0x00001000 + +#define EHCI_HC_PS_CCS 0x00000001 +#define EHCI_HC_PS_CSC 0x00000002 +#define EHCI_HC_PS_PE 0x00000004 +#define EHCI_HC_PS_PEC 0x00000008 +#define EHCI_HC_PS_OCA 0x00000010 +#define EHCI_HC_PS_OCC 0x00000020 +#define EHCI_HC_PS_FPR 0x00000040 +#define EHCI_HC_PS_SUSPEND 0x00000080 +#define EHCI_HC_PS_PR 0x00000100 +#define EHCI_HC_PS_PP 0x00001000 +#define EHCI_HC_PS_SPEED_MASK 0x00000c00 +#define EHCI_HC_PS_SPEED_LOW 0x00000400 +#define EHCI_HC_PS_PO 0x00002000 +#define EHCI_HC_PS_EMBEDDED_TT_SPEED_MASK 0x0c000000 +#define EHCI_HC_PS_EMBEDDED_TT_SPEED_FULL 0x00000000 +#define EHCI_HC_PS_EMBEDDED_TT_SPEED_LOW 0x04000000 +#define EHCI_HC_PS_EMBEDDED_TT_SPEED_HIGH 0x08000000 + +#define EHCI_HC_RH_POWER_STABLE_DELAY 25 +#define EHCI_HC_RH_RESET_DELAY 50 +#define EHCI_HC_RH_RESET_SETTLE_DELAY 5 + + +/* Define EHCI interrupt status register definitions. */ + +#define EHCI_HC_INT_IE 0x00000001 +#define EHCI_HC_INT_EIE 0x00000002 +#define EHCI_HC_INT_PCIE 0x00000004 +#define EHCI_HC_INT_FLRE 0x00000008 +#define EHCI_HC_INT_HSER 0x00000010 +#define EHCI_HC_INT_IAAE 0x00000020 + + +/* Define EHCI frame interval definition. */ + +#define EHCI_HC_FM_INTERVAL_CLEAR 0x8000ffff +#define EHCI_HC_FM_INTERVAL_SET 0x27780000 + + +/* Define EHCI static definition. */ + +#define UX_EHCI_AVAILABLE_BANDWIDTH 6000 +#define UX_EHCI_STOP 0 +#define UX_EHCI_START 1 +#define UX_EHCI_ROUTE_TO_LOCAL_HC 1 +#define UX_EHCI_INIT_DELAY 1000 +#define UX_EHCI_RESET_RETRY 1000 +#define UX_EHCI_RESET_DELAY 100 +#define UX_EHCI_PORT_RESET_RETRY 10 +#define UX_EHCI_PORT_RESET_DELAY 50 + + +/* Define EHCI initialization values. */ + +#define UX_EHCI_COMMAND_STATUS_RESET 0 +#define UX_EHCI_INIT_RESET_DELAY 10 + + +/* Define EHCI completion code errors. */ + +#define UX_EHCI_NO_ERROR 0x00 +#define UX_EHCI_ERROR_CRC 0x01 +#define UX_EHCI_ERROR_BIT_STUFFING 0x02 +#define UX_EHCI_ERROR_DATA_TOGGLE 0x03 +#define UX_EHCI_ERROR_STALL 0x04 +#define UX_EHCI_ERROR_DEVICE_NOT_RESPONDING 0x05 +#define UX_EHCI_ERROR_PID_FAILURE 0x06 +#define UX_EHCI_ERROR_DATA_OVERRUN 0x08 +#define UX_EHCI_ERROR_DATA_UNDERRUN 0x09 +#define UX_EHCI_ERROR_BUFFER_OVERRUN 0x0c +#define UX_EHCI_ERROR_BUFFER_UNDERRUN 0x0d +#define UX_EHCI_ERROR_NOT_ACCESSED 0x0f +#define UX_EHCI_ERROR_NAK 0x10 +#define UX_EHCI_ERROR_BABBLE 0x11 + +/* EHCI general descriptor type (Link Pointer). */ + +#define UX_EHCI_LP_MASK (0xFFFFFFE0u) /* 32-byte align. */ + +#define UX_EHCI_TYP_MASK (0x3u<<1) +#define UX_EHCI_TYP_ITD (0x0u<<1) +#define UX_EHCI_TYP_QH (0x1u<<1) +#define UX_EHCI_TYP_SITD (0x2u<<1) +#define UX_EHCI_TYP_FSTN (0x3u<<1) + +#define UX_EHCI_T (0x1u<<0) + +/* EHCI general descriptor type (Capabilities). */ + +#define UX_EHCI_ENDPT_MASK (0xFu<<8) +#define UX_EHCI_ENDPT_SHIFT (8) + +#define UX_EHCI_DEVICE_ADDRESS_MASK (0x3Fu<<0) + +#define UX_EHCI_CMASK_MASK (0xFFu<<8) +#define UX_EHCI_CMASK_0 (0x01u<<8) +#define UX_EHCI_CMASK_1 (0x02u<<8) +#define UX_EHCI_CMASK_2 (0x04u<<8) +#define UX_EHCI_CMASK_3 (0x08u<<8) +#define UX_EHCI_CMASK_4 (0x10u<<8) +#define UX_EHCI_CMASK_5 (0x20u<<8) +#define UX_EHCI_CMASK_6 (0x40u<<8) +#define UX_EHCI_CMASK_7 (0x80u<<8) +#define UX_EHCI_CMASK_ISOOUT_ANY (0x00u<<8) +#define UX_EHCI_CMASK_INT_Y0 (0x1Cu<<8) +#define UX_EHCI_CMASK_INT_Y1 (0x31u<<8) +#define UX_EHCI_CMASK_INT_Y2 (0x70u<<8) +#define UX_EHCI_CMASK_INT_Y3 (0xE0u<<8) +#define UX_EHCI_CMASK_INT_Y4 (0xC1u<<8) +#define UX_EHCI_CMASK_INT_Y5 (0x81u<<8) +#define UX_EHCI_CMASK_INT_Y7 (0x07u<<8) +#define UX_EHCI_CMASK_ISOIN_C1 (0x01u<<8) +#define UX_EHCI_CMASK_ISOIN_C2 (0x03u<<8) +#define UX_EHCI_CMASK_ISOIN_C3 (0x07u<<8) +#define UX_EHCI_CMASK_ISOIN_C4 (0x0Fu<<8) +#define UX_EHCI_CMASK_ISOIN_C5 (0x1Fu<<8) +#define UX_EHCI_CMASK_ISOIN_C6 (0x3Fu<<8) + +#define UX_EHCI_SMASK_MASK (0xFFu<<0) +#define UX_EHCI_SMASK_0 (0x01u<<0) +#define UX_EHCI_SMASK_1 (0x02u<<0) +#define UX_EHCI_SMASK_2 (0x04u<<0) +#define UX_EHCI_SMASK_3 (0x08u<<0) +#define UX_EHCI_SMASK_4 (0x10u<<0) +#define UX_EHCI_SMASK_5 (0x20u<<0) +#define UX_EHCI_SMASK_6 (0x40u<<0) +#define UX_EHCI_SMASK_7 (0x80u<<0) +#define UX_EHCI_SMASK_INTERVAL_1 (0xFFu<<0) +#define UX_EHCI_SMASK_INTERVAL_2 (0x55u<<0) +#define UX_EHCI_SMASK_INTERVAL_3 (0x11u<<0) +#define UX_EHCI_SMASK_INTERVAL_4 (0x01u<<0) + + +/* EHCI general descriptor type (Buffer pointer page part). */ + +#define UX_EHCI_BP_MASK (0xFFFFF000u) /* 4K align. */ + +/* Define EHCI pointers. */ + +typedef union UX_EHCI_POINTER_UNION { + ULONG value; + VOID *void_ptr; + UCHAR *u8_ptr; + USHORT *u16_ptr; + ULONG *u32_ptr; +} UX_EHCI_POINTER; + +typedef union UX_EHCI_LINK_POINTER_UNION { + ULONG value; + VOID *void_ptr; + UCHAR *u8_ptr; + USHORT *u16_ptr; + ULONG *u32_ptr; + struct UX_EHCI_ED_STRUCT *qh_ptr; + struct UX_EHCI_ED_STRUCT *ed_ptr; + struct UX_EHCI_TD_STRUCT *td_ptr; + struct UX_EHCI_HSISO_TD_STRUCT *itd_ptr; + struct UX_EHCI_FSISO_TD_STRUCT *sitd_ptr; +} UX_EHCI_LINK_POINTER; + +typedef union UX_EHCI_PERIODIC_LINK_POINTER_UNION { + ULONG value; + VOID *void_ptr; + UCHAR *u8_ptr; + USHORT *u16_ptr; + ULONG *u32_ptr; + struct UX_EHCI_ED_STRUCT *qh_ptr; + struct UX_EHCI_ED_STRUCT *ed_ptr; + struct UX_EHCI_HSISO_TD_STRUCT *itd_ptr; + struct UX_EHCI_FSISO_TD_STRUCT *sitd_ptr; +} UX_EHCI_PERIODIC_LINK_POINTER; + + +/* Define the EHCI structure. */ + +typedef struct UX_HCD_EHCI_STRUCT +{ + + struct UX_HCD_STRUCT + *ux_hcd_ehci_hcd_owner; + ULONG ux_hcd_ehci_hcor; + struct UX_EHCI_ED_STRUCT + **ux_hcd_ehci_frame_list; + ULONG *ux_hcd_ehci_base; + UINT ux_hcd_ehci_nb_root_hubs; + struct UX_EHCI_TD_STRUCT + *ux_hcd_ehci_done_head; + struct UX_EHCI_ED_STRUCT + *ux_hcd_ehci_ed_list; + struct UX_EHCI_TD_STRUCT + *ux_hcd_ehci_td_list; + struct UX_EHCI_FSISO_TD_STRUCT + *ux_hcd_ehci_fsiso_td_list; + struct UX_EHCI_HSISO_TD_STRUCT + *ux_hcd_ehci_hsiso_td_list; + struct UX_EHCI_ED_STRUCT + *ux_hcd_ehci_asynch_head_list; + struct UX_EHCI_ED_STRUCT + *ux_hcd_ehci_asynch_first_list; + struct UX_EHCI_ED_STRUCT + *ux_hcd_ehci_asynch_last_list; + struct UX_EHCI_HSISO_TD_STRUCT + *ux_hcd_ehci_hsiso_scan_list; + struct UX_EHCI_FSISO_TD_STRUCT + *ux_hcd_ehci_fsiso_scan_list; + struct UX_TRANSFER_STRUCT + *ux_hcd_ehci_iso_done_transfer_head; + struct UX_TRANSFER_STRUCT + *ux_hcd_ehci_iso_done_transfer_tail; + struct UX_EHCI_ED_STRUCT + *ux_hcd_ehci_interrupt_ed_list; + TX_MUTEX ux_hcd_ehci_periodic_mutex; + TX_SEMAPHORE ux_hcd_ehci_protect_semaphore; + TX_SEMAPHORE ux_hcd_ehci_doorbell_semaphore; + ULONG ux_hcd_ehci_frame_list_size; + ULONG ux_hcd_ehci_interrupt_count; + ULONG ux_hcd_ehci_embedded_tt; +} UX_HCD_EHCI; + + +/* Define EHCI ED structure. */ + +typedef struct UX_EHCI_ED_STRUCT +{ + + struct UX_EHCI_ED_STRUCT + *ux_ehci_ed_queue_head; + ULONG ux_ehci_ed_cap0; + ULONG ux_ehci_ed_cap1; + struct UX_EHCI_TD_STRUCT + *ux_ehci_ed_current_td; + struct UX_EHCI_TD_STRUCT + *ux_ehci_ed_queue_element; + struct UX_EHCI_TD_STRUCT + *ux_ehci_ed_alternate_td; + ULONG ux_ehci_ed_state; + VOID *ux_ehci_ed_bp0; + VOID *ux_ehci_ed_bp1; + VOID *ux_ehci_ed_bp2; + VOID *ux_ehci_ed_bp3; + VOID *ux_ehci_ed_bp4; + /* 12 DWords, 48 bytes QH for controller end. */ + + ULONG ux_ehci_ed_status; + struct UX_EHCI_ED_STRUCT + *ux_ehci_ed_next_ed; + struct UX_EHCI_ED_STRUCT + *ux_ehci_ed_previous_ed; + struct UX_EHCI_TD_STRUCT + *ux_ehci_ed_first_td; + struct UX_EHCI_TD_STRUCT + *ux_ehci_ed_last_td; + union { + struct { /* For anchor. */ + struct UX_EHCI_ED_STRUCT + *ux_ehci_ed_next_anchor; /* + 1 DWord. */ + USHORT ux_ehci_ed_microframe_load[8]; /* + 4 DWords. */ + UCHAR ux_ehci_ed_microframe_ssplit_count[8]; /* + 2 DWords. */ + }; + struct { /* As interrupt ED. */ + struct UX_EHCI_ED_STRUCT + *ux_ehci_ed_anchor; /* + 1 DWord. */ + struct UX_ENDPOINT_STRUCT + *ux_ehci_ed_endpoint; /* + 1 Dword. */ + }; + struct { /* Space: 7 DWord. */ + ULONG ux_ehci_ed_reserved[7]; + }; + }; + /* 24 DWord aligned. */ +} UX_EHCI_ED; + + +/* Define EHCI ED bitmap. */ + +#define UX_EHCI_QH_TYP_ITD 0 +#define UX_EHCI_QH_TYP_QH 2 +#define UX_EHCI_QH_TYP_SITD 4 +#define UX_EHCI_QH_TYP_FSTN 6 + +#define UX_EHCI_QH_T 1 + +#define UX_EHCI_QH_STATIC 0x80000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_7 0x40000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_6 0x20000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_5 0x10000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_4 0x08000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_3 0x04000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_2 0x02000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_1 0x01000000 +#define UX_EHCI_QH_SSPLIT_SCH_FULL_0 0x00800000 + +#define UX_EHCI_QH_MPS_LOC 16 +#define UX_EHCI_QH_MPS_MASK 0x07ff0000 +#define UX_EHCI_QH_NCR 0xf0000000 +#define UX_EHCI_QH_CEF 0x08000000 +#define UX_EHCI_QH_ED_AD_LOC 8 +#define UX_EHCI_QH_HBPM 0x40000000 +#define UX_EHCI_QH_HBPM_LOC 30 +#define UX_EHCI_QH_HEAD 0x00008000 + +#define UX_EHCI_QH_HIGH_SPEED 0x00002000 +#define UX_EHCI_QH_LOW_SPEED 0x00001000 + +#define UX_EHCI_QH_HUB_ADDR_LOC 16 +#define UX_EHCI_QH_PORT_NUMBER_LOC 23 +#define UX_EHCI_QH_MULT_LOC 30 +#define UX_EHCI_QH_MULT_MASK 0xc0000000 +#define UX_EHCI_QH_C_MASK 0x00001c00 +#define UX_EHCI_QH_IS_MASK 0x00000001 + +#define UX_EHCI_QH_SMASK_MASK 0x000000FFu +#define UX_EHCI_QH_SMASK_0 0x00000001u +#define UX_EHCI_QH_SMASK_1 0x00000002u +#define UX_EHCI_QH_SMASK_2 0x00000004u +#define UX_EHCI_QH_SMASK_3 0x00000008u +#define UX_EHCI_QH_SMASK_4 0x00000010u +#define UX_EHCI_QH_SMASK_5 0x00000020u +#define UX_EHCI_QH_SMASK_6 0x00000040u +#define UX_EHCI_QH_SMASK_7 0x00000080u + +#define UX_EHCI_QH_DTC 0x00004000 +#define UX_EHCI_QH_TOGGLE 0x80000000 +#define UX_EHCI_LINK_ADDRESS_MASK 0xfffffff0 +#define UX_EHCI_TOGGLE_0 0 +#define UX_EHCI_TOGGLE_1 0x80000000 + +/* Define EHCI TD structure. */ + +typedef struct UX_EHCI_TD_STRUCT +{ + + struct UX_EHCI_TD_STRUCT + *ux_ehci_td_link_pointer; + struct UX_EHCI_TD_STRUCT + *ux_ehci_td_alternate_link_pointer; + ULONG ux_ehci_td_control; + VOID *ux_ehci_td_bp0; + VOID *ux_ehci_td_bp1; + VOID *ux_ehci_td_bp2; + VOID *ux_ehci_td_bp3; + VOID *ux_ehci_td_bp4; + /* 8-DWords, 32-bytes qTD for controller. */ + struct UX_TRANSFER_STRUCT + *ux_ehci_td_transfer_request; + struct UX_EHCI_TD_STRUCT + *ux_ehci_td_next_td_transfer_request; + struct UX_EHCI_ED_STRUCT + *ux_ehci_td_ed; + ULONG ux_ehci_td_length; + ULONG ux_ehci_td_status; + ULONG ux_ehci_td_phase; + ULONG ux_ehci_td_reserved_2[2]; + /* 16-DWord aligned. */ +} UX_EHCI_TD; + + +/* Define EHCI TD bitmap. */ + +#define UX_EHCI_TD_T 1 +#define UX_EHCI_TD_LG_LOC 16 +#define UX_EHCI_TD_LG_MASK 0x7fff +#define UX_EHCI_TD_IOC 0x00008000 +#define UX_EHCI_TD_CERR 0x00000c00 + +#define UX_EHCI_TD_PING 1 +#define UX_EHCI_TD_DO_COMPLETE_SPLIT 2 +#define UX_EHCI_TD_MISSED_MICRO_FRAMES 4 +#define UX_EHCI_TD_TRANSACTION_ERROR 8 +#define UX_EHCI_TD_BABBLE_DETECTED 0x10 +#define UX_EHCI_TD_DATA_BUFFER_ERROR 0x20 +#define UX_EHCI_TD_HALTED 0x40 +#define UX_EHCI_TD_ACTIVE 0x80 + +#define UX_EHCI_PID_OUT 0x00000000 +#define UX_EHCI_PID_IN 0x00000100 +#define UX_EHCI_PID_SETUP 0x00000200 +#define UX_EHCI_PID_MASK 0x00000300 + +#define UX_EHCI_TD_SETUP_PHASE 0x00010000 +#define UX_EHCI_TD_DATA_PHASE 0x00020000 +#define UX_EHCI_TD_STATUS_PHASE 0x00040000 + +/* Define EHCI ISOCHRONOUS TD extension structure. */ + +typedef struct UX_EHCI_HSISO_ED_STRUCT +{ + struct UX_ENDPOINT_STRUCT + *ux_ehci_hsiso_ed_endpoint; + struct UX_EHCI_ED_STRUCT + *ux_ehci_hsiso_ed_anchor; + struct UX_TRANSFER_STRUCT + *ux_ehci_hsiso_ed_transfer_head; + struct UX_TRANSFER_STRUCT + *ux_ehci_hsiso_ed_transfer_tail; + struct UX_TRANSFER_STRUCT + *ux_ehci_hsiso_ed_transfer_first_new; + struct UX_EHCI_HSISO_TD_STRUCT + *ux_ehci_hsiso_ed_fr_td[4]; + UCHAR ux_ehci_hsiso_ed_frindex; /* 1st usable micro-frame. */ + UCHAR ux_ehci_hsiso_ed_frinterval; /* Micro-frame interval. */ + UCHAR ux_ehci_hsiso_ed_frinterval_shift; /* Shift for micro-frame interval. */ + UCHAR ux_ehci_hsiso_ed_nb_tds; + USHORT ux_ehci_hsiso_ed_frstart; /* Start micro-frame. */ + USHORT ux_ehci_hsiso_ed_frload; + USHORT ux_ehci_hsiso_ed_fr_hc; /* Micro-frame HC process count. */ + USHORT ux_ehci_hsiso_ed_fr_sw; /* Micro-frame SW load count. */ +} UX_EHCI_HSISO_ED; + +/* Define EHCI ISOCHRONOUS TD structure. */ + +typedef struct UX_EHCI_HSISO_TD_STRUCT +{ + + union UX_EHCI_PERIODIC_LINK_POINTER_UNION + ux_ehci_hsiso_td_next_lp; + ULONG ux_ehci_hsiso_td_control[8]; + VOID *ux_ehci_hsiso_td_bp[7]; + /* 16 DWords, 64-bytes iTD for controller end. */ + UCHAR ux_ehci_hsiso_td_status; + UCHAR ux_ehci_hsiso_td_frload; /* REQ load map. */ + USHORT ux_ehci_hsiso_td_max_trans_size; + union UX_EHCI_PERIODIC_LINK_POINTER_UNION + ux_ehci_hsiso_td_previous_lp; + struct UX_EHCI_HSISO_TD_STRUCT + *ux_ehci_hsiso_td_next_scan_td; + struct UX_EHCI_HSISO_TD_STRUCT + *ux_ehci_hsiso_td_previous_scan_td; + struct UX_TRANSFER_STRUCT + *ux_ehci_hsiso_td_fr_transfer[3]; + struct UX_EHCI_HSISO_ED_STRUCT + *ux_ehci_hsiso_td_ed; + /* 24 DWord aligned. */ +} UX_EHCI_HSISO_TD; + + +/* Next Link Pointer(LP). */ + +#define UX_EHCI_HSISO_LP_MASK UX_EHCI_LP_MASK + +#define UX_EHCI_HSISO_TYP_MASK UX_EHCI_TYP_MASK +#define UX_EHCI_HSISO_TYP_ITD UX_EHCI_TYP_ITD +#define UX_EHCI_HSISO_TYP_QH UX_EHCI_TYP_QH +#define UX_EHCI_HSISO_TYP_SITD UX_EHCI_TYP_SITD +#define UX_EHCI_HSISO_TYP_FSTN UX_EHCI_TYP_FSTN + +#define UX_EHCI_HSISO_T UX_EHCI_T + +/* Transaction Status and Control. */ + +#define UX_EHCI_HSISO_STATUS_MASK 0xF0000000u +#define UX_EHCI_HSISO_STATUS_ACTIVE 0x80000000u +#define UX_EHCI_HSISO_STATUS_DATA_BUFFER_ERR 0x40000000u +#define UX_EHCI_HSISO_STATUS_BABBLE_DETECTED 0x20000000u +#define UX_EHCI_HSISO_STATUS_XACT_ERR 0x10000000u + +#define UX_EHCI_HSISO_XACT_LENGTH_MASK 0x0FFF0000u +#define UX_EHCI_HSISO_XACT_LENGTH_SHIFT 16 +#define UX_EHCI_HSISO_XACT_LENGTH_VALUE_MAX 0xC00 + +#define UX_EHCI_HSISO_IOC 0x00008000u +#define UX_EHCI_HSISO_IOC_SHIFT 15 + +#define UX_EHCI_HSISO_PG_MASK 0x00007000u +#define UX_EHCI_HSISO_PG_SHIFT 12 + +#define UX_EHCI_HSISO_XACT_OFFSET_MASK 0x00000FFFu + +/* Buffer Page Pointer List. */ + +#define UX_EHCI_HSISO_BP_MASK UX_EHCI_BP_MASK + +/* BP0 */ + +#define UX_EHCI_HSISO_ENDPT_MASK UX_EHCI_ENDPT_MASK +#define UX_EHCI_HSISO_ENDPT_SHIFT UX_EHCI_ENDPT_SHIFT + +#define UX_EHCI_HSISO_DEVICE_ADDRESS_MASK UX_EHCI_DEVICE_ADDRESS_MASK + +/* BP1 */ + +#define UX_EHCI_HSISO_DIRECTION (0x1u << 11) +#define UX_EHCI_HSISO_DIRECTION_IN UX_EHCI_HSISO_DIRECTION +#define UX_EHCI_HSISO_DIRECTION_OUT 0 + +#define UX_EHCI_HSISO_MAX_PACKET_SIZE_MASK 0x000007FFu +#define UX_EHCI_HSISO_MAX_PACKET_SIZE_MAX 0x00000400 + +/* BP2 */ + +#define UX_EHCI_HSISO_MULTI_MASK 0x00000003u +#define UX_EHCI_HSISO_MULTI_ONE 1 +#define UX_EHCI_HSISO_MULTI_TWO 2 +#define UX_EHCI_HSISO_MULTI_THREE 3 + +/* Define EHCI FS ISOCHRONOUS TD structure. */ + +typedef struct UX_EHCI_FSISO_TD_STRUCT +{ + union UX_EHCI_PERIODIC_LINK_POINTER_UNION + ux_ehci_fsiso_td_next_lp; + ULONG ux_ehci_fsiso_td_cap0; /* endpoint */ + ULONG ux_ehci_fsiso_td_cap1; /* uFrame schedule */ + ULONG ux_ehci_fsiso_td_state; + VOID *ux_ehci_fsiso_td_bp[2]; + VOID *ux_ehci_fsiso_td_back_pointer; + /* 7 DWords, 28-bytes siTD for controller end. */ + + UCHAR ux_ehci_fsiso_td_status; + UCHAR ux_ehci_fsiso_td_frindex; + UCHAR ux_ehci_fsiso_td_nb_ed_tds; + UCHAR reserved[1]; + struct UX_ENDPOINT_STRUCT + *ux_ehci_fsiso_td_endpoint; + struct UX_TRANSFER_STRUCT + *ux_ehci_fsiso_td_transfer_head; + struct UX_TRANSFER_STRUCT + *ux_ehci_fsiso_td_transfer_tail; + union UX_EHCI_PERIODIC_LINK_POINTER_UNION + ux_ehci_fsiso_td_previous_lp; + struct UX_EHCI_FSISO_TD_STRUCT + *ux_ehci_fsiso_td_next_scan_td; + struct UX_EHCI_FSISO_TD_STRUCT + *ux_ehci_fsiso_td_previous_scan_td; + struct UX_EHCI_ED_STRUCT + *ux_ehci_fsiso_td_anchor; + struct UX_EHCI_TD_STRUCT + *ux_ehci_fsiso_td_next_ed_td; + /* 16-DWord aligned. */ +} UX_EHCI_FSISO_TD; + +/* Next Link Pointer (LP). */ + +#define UX_EHCI_FSISO_LP_MASK UX_EHCI_LP_MASK + +#define UX_EHCI_FSISO_TYP_MASK UX_EHCI_TYP_MASK +#define UX_EHCI_FSISO_TYP_ITD UX_EHCI_TYP_ITD +#define UX_EHCI_FSISO_TYP_QH UX_EHCI_TYP_QH +#define UX_EHCI_FSISO_TYP_SITD UX_EHCI_TYP_SITD +#define UX_EHCI_FSISO_TYP_FSTN UX_EHCI_TYP_FSTN + +#define UX_EHCI_FSISO_T UX_EHCI_T + +/* Endpoint Capabilities/Characteristics. */ + +#define UX_EHCI_FSISO_DIRECTION 0x80000000u +#define UX_EHCI_FSISO_DIRECTION_IN 0x80000000u +#define UX_EHCI_FSISO_DIRECTION_OUT 0x00000000 + +#define UX_EHCI_FSISO_PORT_NUMBER_MASK 0x7F000000u +#define UX_EHCI_FSISO_PORT_NUMBER_SHIFT 24 + +#define UX_EHCI_FSISO_HUB_ADDRESS_MASK 0x003F0000u +#define UX_EHCI_FSISO_HUB_ADDRESS_SHIFT 16 + +#define UX_EHCI_FSISO_ENDPT_MASK UX_EHCI_ENDPT_MASK +#define UX_EHCI_FSISO_ENDPT_SHIFT UX_EHCI_ENDPT_SHIFT + +#define UX_EHCI_FSISO_DEVICE_ADDRESS_MASK UX_EHCI_DEVICE_ADDRESS_MASK + +/* Micro-frame Schedule Control. */ + +#define UX_EHCI_FSISO_UFRAME_CMASK_MASK UX_EHCI_CMASK_MASK +#define UX_EHCI_FSISO_UFRAME_CMASK_0 UX_EHCI_CMASK_0 +#define UX_EHCI_FSISO_UFRAME_CMASK_1 UX_EHCI_CMASK_1 +#define UX_EHCI_FSISO_UFRAME_CMASK_2 UX_EHCI_CMASK_2 +#define UX_EHCI_FSISO_UFRAME_CMASK_3 UX_EHCI_CMASK_3 +#define UX_EHCI_FSISO_UFRAME_CMASK_4 UX_EHCI_CMASK_4 +#define UX_EHCI_FSISO_UFRAME_CMASK_5 UX_EHCI_CMASK_5 +#define UX_EHCI_FSISO_UFRAME_CMASK_6 UX_EHCI_CMASK_6 +#define UX_EHCI_FSISO_UFRAME_CMASK_7 UX_EHCI_CMASK_7 + +#define UX_EHCI_FSISO_UFRAME_SMASK_MASK UX_EHCI_SMASK_MASK +#define UX_EHCI_FSISO_UFRAME_SMASK_0 UX_EHCI_SMASK_0 +#define UX_EHCI_FSISO_UFRAME_SMASK_1 UX_EHCI_SMASK_1 +#define UX_EHCI_FSISO_UFRAME_SMASK_2 UX_EHCI_SMASK_2 +#define UX_EHCI_FSISO_UFRAME_SMASK_3 UX_EHCI_SMASK_3 +#define UX_EHCI_FSISO_UFRAME_SMASK_4 UX_EHCI_SMASK_4 +#define UX_EHCI_FSISO_UFRAME_SMASK_5 UX_EHCI_SMASK_5 +#define UX_EHCI_FSISO_UFRAME_SMASK_6 UX_EHCI_SMASK_6 +#define UX_EHCI_FSISO_UFRAME_SMASK_7 UX_EHCI_SMASK_7 + +/* Transfer State. */ + +/* Transfer Status and Control. */ + +#define UX_EHCI_FSISO_IOC 0x80000000u + +#define UX_EHCI_FSISO_P 0x40000000u + +#define UX_EHCI_FSISO_TOTAL_BYTES_MASK 0x03FF0000u +#define UX_EHCI_FSISO_TOTAL_BYTES_SHIFT 16 +#define UX_EHCI_FSISO_TOTAL_BYTES_MAX_VALUE 1023 + +#define UX_EHCI_FSISO_CPROMASK_MASK 0x0000FF00u +#define UX_EHCI_FSISO_CPROMASK_SHIFT 8 + +#define UX_EHCI_FSISO_STATUS_MASK 0x000000FFu +#define UX_EHCI_FSISO_STATUS_ACTIVE 0x00000080u +#define UX_EHCI_FSISO_STATUS_ERR 0x00000040u +#define UX_EHCI_FSISO_STATUS_DATA_BUFFER_ERR 0x00000020u +#define UX_EHCI_FSISO_STATUS_BABBLE_DETECTED 0x00000010u +#define UX_EHCI_FSISO_STATUS_XACTERR 0x00000008u +#define UX_EHCI_FSISO_STATUS_MISSED_MFRAME 0x00000004u + +#define UX_EHCI_FSISO_STATUS_SPLIT_STATE_MASK 0x00000002u +#define UX_EHCI_FSISO_STATUS_SPLIT_STATE_DO_START 0x00000000u +#define UX_EHCI_FSISO_STATUS_SPLIT_STATE_DO_COMPLETE 0x00000002u + +/* Buffer Page Pointer List. */ + +#define UX_EHCI_FSISO_BP_MASK UX_EHCI_BP_MASK + +/* BP0 */ + +#define UX_EHCI_FSISO_CURRENT_OFFSET_MASK 0x00000FFFu + +/* BP1 */ + +#define UX_EHCI_FSISO_TP_MASK 0x0000000Cu +#define UX_EHCI_FSISO_TP_ALL 0x00000000u +#define UX_EHCI_FSISO_TP_BEGIN 0x00000004u +#define UX_EHCI_FSISO_TP_MID 0x00000008u +#define UX_EHCI_FSISO_TP_END 0x0000000Cu + +#define UX_EHCI_FSISO_TCOUNT_MASK 0x00000007u +#define UX_EHCI_FSISO_TCOUNT_MAX 6 + + +/* Define EHCI function prototypes. */ + +void _ux_hcd_ehci_periodic_descriptor_link(VOID* prev, VOID* prev_next, VOID* next_prev, VOID* next); +UX_EHCI_TD *_ux_hcd_ehci_asynch_td_process(UX_EHCI_ED *ed, UX_EHCI_TD *td); +UX_EHCI_HSISO_TD *_ux_hcd_ehci_hsisochronous_tds_process(UX_HCD_EHCI *hcd_ehci, UX_EHCI_HSISO_TD* itd); +UX_EHCI_FSISO_TD *_ux_hcd_ehci_fsisochronous_tds_process(UX_HCD_EHCI *hcd_ehci, UX_EHCI_FSISO_TD* sitd); +UINT _ux_hcd_ehci_asynchronous_endpoint_create(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ehci_asynchronous_endpoint_destroy(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ehci_controller_disable(UX_HCD_EHCI *hcd_ehci); +VOID _ux_hcd_ehci_done_queue_process(UX_HCD_EHCI *hcd_ehci); +VOID _ux_hcd_ehci_door_bell_wait(UX_HCD_EHCI *hcd_ehci); +UINT _ux_hcd_ehci_ed_clean(UX_EHCI_ED *ed); +UX_EHCI_ED *_ux_hcd_ehci_ed_obtain(UX_HCD_EHCI *hcd_ehci); +UINT _ux_hcd_ehci_endpoint_reset(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ehci_entry(UX_HCD *hcd, UINT function, VOID *parameter); +UINT _ux_hcd_ehci_frame_number_get(UX_HCD_EHCI *hcd_ehci, ULONG *frame_number); +VOID _ux_hcd_ehci_frame_number_set(UX_HCD_EHCI *hcd_ehci, ULONG frame_number); +UX_EHCI_FSISO_TD *_ux_hcd_ehci_fsisochronous_td_obtain(UX_HCD_EHCI *hcd_ehci); +UX_EHCI_HSISO_TD *_ux_hcd_ehci_hsisochronous_td_obtain(UX_HCD_EHCI *hcd_ehci); +UINT _ux_hcd_ehci_initialize(UX_HCD *hcd); +UINT _ux_hcd_ehci_interrupt_endpoint_create(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ehci_interrupt_endpoint_destroy(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint); +VOID _ux_hcd_ehci_interrupt_handler(VOID); +UINT _ux_hcd_ehci_isochronous_endpoint_create(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ehci_isochronous_endpoint_destroy(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint); +UX_EHCI_ED *_ux_hcd_ehci_least_traffic_list_get(UX_HCD_EHCI *hcd_ehci, ULONG microframe_load[8], ULONG microframe_ssplit_count[8]); +UX_EHCI_ED *_ux_hcd_ehci_poll_rate_entry_get(UX_HCD_EHCI *hcd_ehci, UX_EHCI_ED *ed_list, ULONG poll_depth); +VOID _ux_hcd_ehci_next_td_clean(UX_EHCI_TD *td); +UINT _ux_hcd_ehci_periodic_tree_create(UX_HCD_EHCI *hcd_ehci); +UINT _ux_hcd_ehci_port_disable(UX_HCD_EHCI *hcd_ehci, ULONG port_index); +UINT _ux_hcd_ehci_port_reset(UX_HCD_EHCI *hcd_ehci, ULONG port_index); +UINT _ux_hcd_ehci_port_resume(UX_HCD_EHCI *hcd_ehci, UINT port_index); +ULONG _ux_hcd_ehci_port_status_get(UX_HCD_EHCI *hcd_ehci, ULONG port_index); +UINT _ux_hcd_ehci_port_suspend(UX_HCD_EHCI *hcd_ehci, ULONG port_index); +UINT _ux_hcd_ehci_power_down_port(UX_HCD_EHCI *hcd_ehci, ULONG port_index); +UINT _ux_hcd_ehci_power_on_port(UX_HCD_EHCI *hcd_ehci, ULONG port_index); +VOID _ux_hcd_ehci_power_root_hubs(UX_HCD_EHCI *hcd_ehci); +ULONG _ux_hcd_ehci_register_read(UX_HCD_EHCI *hcd_ehci, ULONG ehci_register); +VOID _ux_hcd_ehci_register_write(UX_HCD_EHCI *hcd_ehci, ULONG ehci_register, ULONG value); +UX_EHCI_TD *_ux_hcd_ehci_regular_td_obtain(UX_HCD_EHCI *hcd_ehci); +UINT _ux_hcd_ehci_request_bulk_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ehci_request_control_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ehci_request_interrupt_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ehci_request_isochronous_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ehci_request_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ehci_request_transfer_add(UX_HCD_EHCI *hcd_ehci, UX_EHCI_ED *ed, ULONG phase, ULONG pid, + ULONG toggle, UCHAR * buffer_address, ULONG buffer_length, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ehci_transfer_abort(UX_HCD_EHCI *hcd_ehci,UX_TRANSFER *transfer_request); +VOID _ux_hcd_ehci_transfer_request_process(UX_TRANSFER *transfer_request); + +#define ux_hcd_ehci_initialize _ux_hcd_ehci_initialize +#define ux_hcd_ehci_interrupt_handler _ux_hcd_ehci_interrupt_handler + +#endif + diff --git a/common/usbx_host_controllers/inc/ux_hcd_ohci.h b/common/usbx_host_controllers/inc/ux_hcd_ohci.h new file mode 100644 index 0000000..2463440 --- /dev/null +++ b/common/usbx_host_controllers/inc/ux_hcd_ohci.h @@ -0,0 +1,393 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_hcd_ohci.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX host OHCI Controller. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_HCD_OHCI_H +#define UX_HCD_OHCI_H + + +/* Define generic OHCI constants. */ + +#define UX_OHCI_CONTROLLER 1 +#define UX_OHCI_MAX_PAYLOAD 4096 +#define UX_OHCI_FRAME_DELAY 4 + + +/* Define OHCI HCOR register mapping. */ + +#define OHCI_HC_REVISION 0x00 +#define OHCI_HC_CONTROL 0x01 +#define OHCI_HC_COMMAND_STATUS 0x02 +#define OHCI_HC_INTERRUPT_STATUS 0x03 +#define OHCI_HC_INTERRUPT_ENABLE 0x04 +#define OHCI_HC_INTERRUPT_DISABLE 0x05 +#define OHCI_HC_HCCA 0x06 +#define OHCI_HC_PERIOD_CURRENT_ED 0x07 +#define OHCI_HC_CONTROL_HEAD_ED 0x08 +#define OHCI_HC_CONTROL_CURRENT_ED 0x09 +#define OHCI_HC_BULK_HEAD_ED 0x0a +#define OHCI_HC_BULK_CURRENT_ED 0x0b +#define OHCI_HC_DONE_HEAD 0x0c +#define OHCI_HC_FM_INTERVAL 0x0d +#define OHCI_HC_FM_REMAINING 0x0e +#define OHCI_HC_FM_NUMBER 0x0f +#define OHCI_HC_PERIODIC_START 0x10 +#define OHCI_HC_LS_THRESHOLD 0x11 +#define OHCI_HC_RH_DESCRIPTOR_A 0x12 +#define OHCI_HC_RH_DESCRIPTOR_B 0x13 +#define OHCI_HC_RH_STATUS 0x14 +#define OHCI_HC_RH_PORT_STATUS 0x15 + + +/* Define OHCI control register values. */ + +#define OHCI_HC_CR_CBSR_0 0x00000000 +#define OHCI_HC_CR_CBSR_1 0x00000001 +#define OHCI_HC_CR_CBSR_2 0x00000002 +#define OHCI_HC_CR_CBSR_3 0x00000003 +#define OHCI_HC_CR_PLE 0x00000004 +#define OHCI_HC_CR_IE 0x00000008 +#define OHCI_HC_CR_CLE 0x00000010 +#define OHCI_HC_CR_BLE 0x00000020 +#define OHCI_HC_CR_RESET 0x00000000 +#define OHCI_HC_CR_RESUME 0x00000040 +#define OHCI_HC_CR_OPERATIONAL 0x00000080 +#define OHCI_HC_CR_SUSPEND 0x000000c0 +#define OHCI_HC_CR_IR 0x00000100 +#define OHCI_HC_CR_RWC 0x00000200 +#define OHCI_HC_CR_RWE 0x00000400 + +#define OHCI_HC_CONTROL_VALUE (OHCI_HC_CR_CBSR_3 | OHCI_HC_CR_OPERATIONAL | OHCI_HC_CR_PLE | OHCI_HC_CR_IE | OHCI_HC_CR_CLE | OHCI_HC_CR_BLE) + + +/* Define OHCI HCOR command/status bitmaps. */ + +#define OHCI_HC_CS_HCR 0x00000001 +#define OHCI_HC_CS_CLF 0x00000002 +#define OHCI_HC_CS_BLF 0x00000004 + + +#define OHCI_HC_RH_PSM 0x00000100 +#define OHCI_HC_RH_NPS 0x00000200 +#define OHCI_HC_RH_DT 0x00000400 +#define OHCI_HC_RH_OCPM 0x00000800 +#define OHCI_HC_RH_NOCP 0x00001000 +#define OHCI_HC_RH_POTPGT 24 + +#define OHCI_HC_RS_LPS 0x00000001 +#define OHCI_HC_RS_OCI 0x00000002 +#define OHCI_HC_RS_DRWE 0x00008000 +#define OHCI_HC_RS_LPSC 0x00010000 +#define OHCI_HC_RS_OCIC 0x00020000 +#define OHCI_HC_RS_CRWE 0x80000000 + +#define OHCI_HC_PS_CCS 0x00000001 +#define OHCI_HC_PS_CPE 0x00000001 +#define OHCI_HC_PS_PES 0x00000002 +#define OHCI_HC_PS_PSS 0x00000004 +#define OHCI_HC_PS_POCI 0x00000008 +#define OHCI_HC_PS_PRS 0x00000010 +#define OHCI_HC_PS_PPS 0x00000100 +#define OHCI_HC_PS_LSDA 0x00000200 +#define OHCI_HC_PS_CSC 0x00010000 +#define OHCI_HC_PS_PESC 0x00020000 +#define OHCI_HC_PS_PSSC 0x00040000 +#define OHCI_HC_PS_OCIC 0x00080000 +#define OHCI_HC_PS_PRSC 0x00100000 + + +/* Define OHCI interrupt status register definitions. */ + +#define OHCI_HC_INT_SO 0x00000001 +#define OHCI_HC_INT_WDH 0x00000002 +#define OHCI_HC_INT_SF 0x00000004 +#define OHCI_HC_INT_RD 0x00000008 +#define OHCI_HC_INT_UE 0x00000010 +#define OHCI_HC_INT_FNO 0x00000020 +#define OHCI_HC_INT_RHSC 0x00000040 +#define OHCI_HC_INT_OC 0x40000000 + +#define OHCI_HC_INT_MIE 0x80000000 + + +#define OHCI_HC_INTERRUPT_ENABLE_NORMAL (OHCI_HC_INT_WDH | OHCI_HC_INT_RD | OHCI_HC_INT_UE | OHCI_HC_INT_RHSC | OHCI_HC_INT_MIE) + +#define OHCI_HC_INTERRUPT_DISABLE_ALL (OHCI_HC_INT_SO | \ + OHCI_HC_INT_WDH | \ + OHCI_HC_INT_SF | \ + OHCI_HC_INT_RD | \ + OHCI_HC_INT_UE | \ + OHCI_HC_INT_FNO | \ + OHCI_HC_INT_RHSC | \ + OHCI_HC_INT_OC | \ + OHCI_HC_INT_MIE) + + +/* Define OHCI frame interval definition. */ + +#define OHCI_HC_FM_INTERVAL_CLEAR 0x8000ffff +#define OHCI_HC_FM_INTERVAL_SET 0x27780000 + + +/* Define OHCI static definition. */ + +#define UX_OHCI_AVAILABLE_BANDWIDTH 6000 +#define UX_OHCI_INIT_DELAY 1000 +#define UX_OHCI_RESET_RETRY 1000 +#define UX_OHCI_RESET_DELAY 10 +#define UX_OHCI_PORT_RESET_RETRY 10 +#define UX_OHCI_PORT_RESET_DELAY 10 + + +/* Define OHCI initialization values. */ + +#define UX_OHCI_COMMAND_STATUS_RESET 0 +#define UX_OHCI_INIT_RESET_DELAY 10 +#define UX_OHCI_HC_PERIODIC_START_DEFAULT 0x00003e67 + +/* Define OHCI completion code errors. */ + +#define UX_OHCI_NO_ERROR 0x00 +#define UX_OHCI_ERROR_CRC 0x01 +#define UX_OHCI_ERROR_BIT_STUFFING 0x02 +#define UX_OHCI_ERROR_DATA_TOGGLE 0x03 +#define UX_OHCI_ERROR_STALL 0x04 +#define UX_OHCI_ERROR_DEVICE_NOT_RESPONDING 0x05 +#define UX_OHCI_ERROR_PID_FAILURE 0x06 +#define UX_OHCI_ERROR_PID_UNEXPECTED 0x07 +#define UX_OHCI_ERROR_DATA_OVERRRUN 0x08 +#define UX_OHCI_ERROR_DATA_UNDERRUN 0x09 +#define UX_OHCI_ERROR_BUFFER_OVERRRUN 0x0c +#define UX_OHCI_ERROR_BUFFER_UNDERRUN 0x0d +#define UX_OHCI_NOT_ACCESSED 0x0e + + +/* Define OHCI HCCA structure. */ + +typedef struct UX_HCD_OHCI_HCCA_STRUCT +{ + + struct UX_OHCI_ED_STRUCT + *ux_hcd_ohci_hcca_ed[32]; + USHORT ux_hcd_ohci_hcca_frame_number; + USHORT ux_hcd_ohci_hcca_reserved1; + struct UX_OHCI_TD_STRUCT + *ux_hcd_ohci_hcca_done_head; + UCHAR ux_hcd_ohci_hcca_reserved2[116]; +} UX_HCD_OHCI_HCCA; + + +/* Define OHCI HCD structure. */ + +typedef struct UX_HCD_OHCI_STRUCT +{ + + struct UX_HCD_STRUCT + *ux_hcd_ohci_hcd_owner; + struct UX_HCD_OHCI_HCCA_STRUCT + *ux_hcd_ohci_hcca; + ULONG *ux_hcd_ohci_hcor; + UINT ux_hcd_ohci_nb_root_hubs; + struct UX_OHCI_TD_STRUCT + *ux_hcd_ohci_done_head; + struct UX_OHCI_ED_STRUCT + *ux_hcd_ohci_ed_list; + struct UX_OHCI_TD_STRUCT + *ux_hcd_ohci_td_list; + struct UX_OHCI_ISO_TD_STRUCT + *ux_hcd_ohci_iso_td_list; +} UX_HCD_OHCI; + + +/* Define OHCI ED structure. */ + +typedef struct UX_OHCI_ED_STRUCT +{ + + ULONG ux_ohci_ed_dw0; + struct UX_OHCI_TD_STRUCT + *ux_ohci_ed_tail_td; + struct UX_OHCI_TD_STRUCT + *ux_ohci_ed_head_td; + struct UX_OHCI_ED_STRUCT + *ux_ohci_ed_next_ed; + struct UX_OHCI_ED_STRUCT + *ux_ohci_ed_previous_ed; + ULONG ux_ohci_ed_status; + struct UX_ENDPOINT_STRUCT + *ux_ohci_ed_endpoint; + ULONG ux_ohci_ed_frame; +} UX_OHCI_ED; + + +/* Define OHCI ED bitmap. */ + +#define UX_OHCI_ED_LOW_SPEED 0x00002000 +#define UX_OHCI_ED_SKIP 0x00004000 +#define UX_OHCI_ED_ISOCHRONOUS 0x00008000 +#define UX_OHCI_ED_MPS 0x0000ffff + +#define UX_OHCI_ED_HALTED 0x00000001 +#define UX_OHCI_ED_TOGGLE_CARRY 0x00000002 +#define UX_OHCI_ED_MASK_TD (~0x00000003) + +#define UX_OHCI_ED_OUT 0x800 +#define UX_OHCI_ED_IN 0x1000 + + +/* Define OHCI TD structure. */ + +typedef struct UX_OHCI_TD_STRUCT +{ + ULONG ux_ohci_td_dw0; + UCHAR * ux_ohci_td_cbp; + struct UX_OHCI_TD_STRUCT + *ux_ohci_td_next_td; + UCHAR * ux_ohci_td_be; + ULONG ux_ohci_td_reserved_1[4]; + struct UX_TRANSFER_STRUCT + *ux_ohci_td_transfer_request; + struct UX_OHCI_TD_STRUCT + *ux_ohci_td_next_td_transfer_request; + struct UX_OHCI_ED_STRUCT + *ux_ohci_td_ed; + ULONG ux_ohci_td_length; + ULONG ux_ohci_td_status; + ULONG ux_ohci_td_reserved_2[3]; +} UX_OHCI_TD; + + +/* Define OHCI TD bitmap. */ + +#define UX_OHCI_TD_OUT 0x00080000 +#define UX_OHCI_TD_IN 0x00100000 +#define UX_OHCI_TD_DEFAULT_DW0 0xf0000000 +#define UX_OHCI_TD_DATA0 0x02000000 +#define UX_OHCI_TD_DATA1 0x03000000 +#define UX_OHCI_TD_R 0x00040000 + +#define UX_OHCI_TD_SETUP_PHASE 0x00010000 +#define UX_OHCI_TD_DATA_PHASE 0x00020000 +#define UX_OHCI_TD_STATUS_PHASE 0x00040000 +#define UX_OHCI_TD_CC 28 + + +/* Define OHCI ISOCHRONOUS TD structure. */ + +typedef struct UX_OHCI_ISO_TD_STRUCT +{ + + ULONG ux_ohci_iso_td_dw0; + UCHAR * ux_ohci_iso_td_bp0; + struct UX_OHCI_TD_STRUCT + *ux_ohci_iso_td_next_td; + UCHAR * ux_ohci_iso_td_be; + USHORT ux_ohci_iso_td_offset_psw[8]; + struct UX_TRANSFER_STRUCT + *ux_ohci_iso_td_transfer_request; + struct UX_OHCI_TD_STRUCT + *ux_ohci_iso_td_next_td_transfer_request; + struct UX_OHCI_ED_STRUCT + *ux_ohci_iso_td_ed; + ULONG ux_ohci_iso_td_length; + ULONG ux_ohci_iso_td_status; + ULONG ux_ohci_iso_td_reserved[3]; +} UX_OHCI_ISO_TD; + + +/* Define OHCI ISOCHRONOUS TD bitmap. */ + +#define UX_OHCI_ISO_TD_BASE 0xfffff000 +#define UX_OHCI_ISO_TD_OFFSET 0x00000fff +#define UX_OHCI_ISO_TD_PSW_CC 0x0000e000 +#define UX_OHCI_ISO_TD_FC 24 + + +/* Define OHCI function prototypes. */ + +UINT _ux_hcd_ohci_asynchronous_endpoint_create(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ohci_asynchronous_endpoint_destroy(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ohci_controller_disable(UX_HCD_OHCI *hcd_ohci); +VOID _ux_hcd_ohci_done_queue_process(UX_HCD_OHCI *hcd_ohci); +UX_OHCI_ED *_ux_hcd_ohci_ed_obtain(UX_HCD_OHCI *hcd_ohci); +UINT _ux_hcd_ohci_endpoint_error_clear(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ohci_endpoint_reset(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ohci_entry(UX_HCD *hcd, UINT function, VOID *parameter); +UINT _ux_hcd_ohci_frame_number_get(UX_HCD_OHCI *hcd_ohci, ULONG *frame_number); +VOID _ux_hcd_ohci_frame_number_set(UX_HCD_OHCI *hcd_ohci, ULONG frame_number); +UINT _ux_hcd_ohci_initialize(UX_HCD *hcd); +UINT _ux_hcd_ohci_interrupt_endpoint_create(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint); +VOID _ux_hcd_ohci_interrupt_handler(VOID); +UINT _ux_hcd_ohci_isochronous_endpoint_create(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint); +UX_OHCI_ISO_TD *_ux_hcd_ohci_isochronous_td_obtain(UX_HCD_OHCI *hcd_ohci); +UX_OHCI_ED *_ux_hcd_ohci_least_traffic_list_get(UX_HCD_OHCI *hcd_ohci); +VOID _ux_hcd_ohci_next_td_clean(UX_OHCI_TD *td); +UINT _ux_hcd_ohci_periodic_endpoint_destroy(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint); +UINT _ux_hcd_ohci_periodic_tree_create(UX_HCD_OHCI *hcd_ohci); +UINT _ux_hcd_ohci_port_disable(UX_HCD_OHCI *hcd_ohci, ULONG port_index); +UINT _ux_hcd_ohci_port_enable(UX_HCD_OHCI *hcd_ohci, ULONG port_index); +UINT _ux_hcd_ohci_port_reset(UX_HCD_OHCI *hcd_ohci, ULONG port_index); +UINT _ux_hcd_ohci_port_resume(UX_HCD_OHCI *hcd_ohci, UINT port_index); +ULONG _ux_hcd_ohci_port_status_get(UX_HCD_OHCI *hcd_ohci, ULONG port_index); +UINT _ux_hcd_ohci_port_suspend(UX_HCD_OHCI *hcd_ohci, ULONG port_index); +UINT _ux_hcd_ohci_power_down_port(UX_HCD_OHCI *hcd_ohci, ULONG port_index); +UINT _ux_hcd_ohci_power_on_port(UX_HCD_OHCI *hcd_ohci, ULONG port_index); +VOID _ux_hcd_ohci_power_root_hubs(UX_HCD_OHCI *hcd_ohci); +ULONG _ux_hcd_ohci_register_read(UX_HCD_OHCI *hcd_ohci, ULONG ohci_register); +VOID _ux_hcd_ohci_register_write(UX_HCD_OHCI *hcd_ohci, ULONG ohci_register, ULONG value); +UX_OHCI_TD *_ux_hcd_ohci_regular_td_obtain(UX_HCD_OHCI *hcd_ohci); +UINT _ux_hcd_ohci_request_bulk_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ohci_request_control_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ohci_request_interrupt_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ohci_request_isochronous_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ohci_request_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request); +UINT _ux_hcd_ohci_transfer_abort(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request); +VOID _ux_hcd_ohci_transfer_request_process(UX_TRANSFER *transfer_request); + +#define ux_hcd_ohci_initialize _ux_hcd_ohci_initialize +#define ux_hcd_ohci_interrupt_handler _ux_hcd_ohci_interrupt_handler + +#endif + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_asynch_td_process.c b/common/usbx_host_controllers/src/ux_hcd_ehci_asynch_td_process.c new file mode 100644 index 0000000..b0f1a58 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_asynch_td_process.c @@ -0,0 +1,209 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_asynch_td_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the isochronous, periodic and asynchronous */ +/* lists in search for transfers that occurred in the past */ +/* (micro-)frame. */ +/* */ +/* INPUT */ +/* */ +/* ed Pointer to ED */ +/* td Pointer to TD */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_TD * Pointer to TD */ +/* */ +/* CALLS */ +/* */ +/* (ux_transfer_request_completion_function) Completion function */ +/* _ux_hcd_ehci_ed_clean Clean ED */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_TD *_ux_hcd_ehci_asynch_td_process(UX_EHCI_ED *ed, UX_EHCI_TD *td) +{ + +UX_TRANSFER *transfer_request; +ULONG td_residual_length; +UINT td_error; +UX_EHCI_TD *next_td; +ULONG td_element; +ULONG pid; + + + /* If the TD is still active, we know that the transfer has not taken place yet. In this case, + the TDs following this one do not need to be process and we return a NULL TD. */ + if (td -> ux_ehci_td_control & UX_EHCI_TD_ACTIVE) + return(UX_NULL); + + /* We need the transfer request associated with this TD. */ + transfer_request = td -> ux_ehci_td_transfer_request; + + /* We have a non active TD, meaning the TD was processed. We should explore the error code and + translate it into a generic USBX code. */ + td_error = UX_SUCCESS; + + /* Check if the TD was halted due to a major error. Note that the error status may contain an error + but unless the Halt bit is set, it is not fatal. */ + if (td -> ux_ehci_td_control & UX_EHCI_TD_HALTED) + { + + /* Default to STALL. */ + td_error = UX_TRANSFER_STALLED; + + /* What else could it be ? Buffer underrun/overrun ? */ + if (td -> ux_ehci_td_control & UX_EHCI_TD_DATA_BUFFER_ERROR) + td_error = UX_TRANSFER_ERROR; + + /* Babble ? */ + if (td -> ux_ehci_td_control & UX_EHCI_TD_BABBLE_DETECTED) + td_error = UX_TRANSFER_ERROR; + + /* Timeout, CRD, Bad PID ... ? */ + if (td -> ux_ehci_td_control & UX_EHCI_TD_TRANSACTION_ERROR) + td_error = UX_TRANSFER_NO_ANSWER; + + } + + /* If there is an error, we should terminate this transfer and clean things. */ + if (td_error != UX_SUCCESS) + { + + /* Update the transfer code. */ + transfer_request -> ux_transfer_request_completion_code = td_error; + + /* Clean the link. */ + _ux_hcd_ehci_ed_clean(ed); + + /* Free the TD that was just treated. */ + td -> ux_ehci_td_status = UX_UNUSED; + + /* We may do a call back. */ + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* Notify the application for debugging purposes. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, td_error); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, td_error, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Wake up the semaphore for this request. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* Nothing else to be processed in this queue. */ + return(UX_NULL); + } + + /* Update the length of this transaction if the PID is IN or OUT. */ + pid = td -> ux_ehci_td_control & UX_EHCI_PID_MASK; + td_residual_length = 0; + if ((pid == UX_EHCI_PID_OUT) || (pid == UX_EHCI_PID_IN)) + { + + td_residual_length = (td -> ux_ehci_td_control >> UX_EHCI_TD_LG_LOC) & UX_EHCI_TD_LG_MASK; + transfer_request -> ux_transfer_request_actual_length += td -> ux_ehci_td_length - td_residual_length; + } + + /* We get here when there is no error on the transfer. It may be that this transfer is not the last + one in the link and therefore we process the length received but do not complete the transfer request. */ + if (td -> ux_ehci_td_control & UX_EHCI_TD_IOC) + { + + /* Check if this transaction is complete or a short packet occurred, in both case, complete the + transaction. In the case of a control endpoint in STATUS phase we complete regardless. */ + if ((td_residual_length != 0) || + (transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length) || + (td -> ux_ehci_td_phase & UX_EHCI_TD_STATUS_PHASE)) + { + + /* Update the transfer code. */ + transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS; + + /* Clean the link. */ + _ux_hcd_ehci_ed_clean(ed); + + /* Free the TD that was just treated. */ + td -> ux_ehci_td_status = UX_UNUSED; + + /* We may do a call back. */ + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* Wake up the semaphore for this request. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* Nothing else to be processed in this queue */ + return(UX_NULL); + } + } + + /* Get the next TD attached to this TD. */ + td_element = (ULONG) td -> ux_ehci_td_link_pointer; + td_element &= ~UX_EHCI_TD_T; + next_td = _ux_utility_virtual_address((VOID *) td_element); + + /* Free the TD that was just treated. */ + td -> ux_ehci_td_status = UX_UNUSED; + + /* This TD is now the first TD. */ + ed -> ux_ehci_ed_first_td = next_td; + + /* We get here when we have reached a non completion of a request transfer + we need to return the next TD in the linked list. */ + return(next_td); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_create.c b/common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_create.c new file mode 100644 index 0000000..f7e22d5 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_create.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_asynchronous_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an asynchronous endpoint. The control */ +/* and bulk endpoints fall into this category. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_ed_obtain Obtain EHCI ED */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_asynchronous_endpoint_create(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint) +{ + +UX_DEVICE *device; +UX_EHCI_ED *ed; +UX_EHCI_LINK_POINTER queue_head; + + + /* We need to take into account the nature of the HCD to define the max size + of any transfer in the transfer request. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_maximum_length = UX_EHCI_MAX_PAYLOAD; + + /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint is active + and will be the container for the tds. */ + ed = _ux_hcd_ehci_ed_obtain(hcd_ehci); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *) ed; + + /* Now do the opposite, attach the ED container to the physical ED. */ + ed -> ux_ehci_ed_endpoint = endpoint; + + /* Set the default MPS Capability info in the ED. */ + ed -> ux_ehci_ed_cap0 = endpoint -> ux_endpoint_descriptor.wMaxPacketSize << UX_EHCI_QH_MPS_LOC; + + /* Set the default NAK reload count. */ + ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_NCR; + + /* If the device is not high speed and the endpoint is control, then the CEF bit must be set to on. */ + device = endpoint -> ux_endpoint_device; + if ((device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) && + ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_CONTROL_ENDPOINT)) + ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_CEF; + + /* Set the device address. */ + ed -> ux_ehci_ed_cap0 |= device -> ux_device_address; + + /* Add the endpoint address. */ + ed -> ux_ehci_ed_cap0 |= (endpoint -> ux_endpoint_descriptor.bEndpointAddress & + ~UX_ENDPOINT_DIRECTION) << UX_EHCI_QH_ED_AD_LOC; + + /* Set the High Bandwidth Pipe Multiplier to 1. */ + ed -> ux_ehci_ed_cap1 |= UX_EHCI_QH_HBPM; + + /* Set the device speed for full and low speed devices behind a hub the hub address and the + port index must be stored in the endpoint. */ + switch (device -> ux_device_speed) + { + + case UX_HIGH_SPEED_DEVICE: + + ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_HIGH_SPEED; + break; + + + case UX_LOW_SPEED_DEVICE: + + ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_LOW_SPEED; + break; + + case UX_FULL_SPEED_DEVICE: + + /* The device must be on a hub for this code to execute. We still do a sanity check. */ + if (device -> ux_device_parent != UX_NULL) + { + + /* Store the parent hub device address. */ + ed -> ux_ehci_ed_cap1 |= device -> ux_device_parent -> ux_device_address << UX_EHCI_QH_HUB_ADDR_LOC; + + /* And the port index onto which this device is attached. */ + ed -> ux_ehci_ed_cap1 |= device -> ux_device_port_location << UX_EHCI_QH_PORT_NUMBER_LOC; + } + break; + } + + /* We need to insert this new endpoint into the asynchronous list. All new EDs are inserted at the + end of the list. The current ED will be pointing to the first ED in the list. */ + queue_head.void_ptr = _ux_utility_physical_address(hcd_ehci -> ux_hcd_ehci_asynch_first_list); + queue_head.value |= UX_EHCI_QH_TYP_QH; + ed -> ux_ehci_ed_queue_head = queue_head.ed_ptr; + ed -> ux_ehci_ed_next_ed = hcd_ehci -> ux_hcd_ehci_asynch_first_list; + + /* Now we compute the address and the data type to fill the QH pointer with. */ + queue_head.void_ptr = _ux_utility_physical_address(ed); + queue_head.value |= UX_EHCI_QH_TYP_QH; + hcd_ehci -> ux_hcd_ehci_asynch_last_list -> ux_ehci_ed_queue_head = queue_head.ed_ptr; + ed -> ux_ehci_ed_previous_ed = hcd_ehci -> ux_hcd_ehci_asynch_last_list; + + /* Update the link of the previous ED. */ + ed -> ux_ehci_ed_previous_ed -> ux_ehci_ed_next_ed = ed; + + /* Remember the new last QH. */ + hcd_ehci -> ux_hcd_ehci_asynch_last_list = ed; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_destroy.c b/common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_destroy.c new file mode 100644 index 0000000..308c71e --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_asynchronous_endpoint_destroy.c @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_asynchronous_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy an asynchronous endpoint. The control */ +/* and bulk endpoints fall into this category. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_door_bell_wait Wait for door bell */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_asynchronous_endpoint_destroy(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint) +{ + +UX_EHCI_ED *ed; +UX_EHCI_ED *previous_ed; +UX_EHCI_ED *next_ed; +UX_EHCI_LINK_POINTER queue_head; + + + /* From the endpoint container fetch the EHCI ED descriptor. */ + ed = (UX_EHCI_ED *) endpoint -> ux_endpoint_ed; + + /* Get the previous ED in the list for this ED. */ + previous_ed = ed -> ux_ehci_ed_previous_ed; + + /* Get the next ED in the list for this ED. */ + next_ed = ed -> ux_ehci_ed_next_ed; + + /* Point the previous ED to the ED after the ED to be removed. */ + previous_ed -> ux_ehci_ed_next_ed = next_ed; + + /* Point the next ED previous pointer to the previous ED. */ + next_ed -> ux_ehci_ed_previous_ed = previous_ed; + + /* Now remove the physical link. */ + queue_head.void_ptr = _ux_utility_physical_address(next_ed); + queue_head.value |= UX_EHCI_QH_TYP_QH; + previous_ed -> ux_ehci_ed_queue_head = queue_head.ed_ptr; + + /* If this ED was the last ED, we need to update the HCD last ED value. */ + if (hcd_ehci -> ux_hcd_ehci_asynch_last_list == ed) + hcd_ehci -> ux_hcd_ehci_asynch_last_list = previous_ed; + + /* Arm the doorbell and wait for its completion. */ + _ux_hcd_ehci_door_bell_wait(hcd_ehci); + + /* Now we can safely make the ED free. */ + ed -> ux_ehci_ed_status = UX_UNUSED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_controller_disable.c b/common/usbx_host_controllers/src/ux_hcd_ehci_controller_disable.c new file mode 100644 index 0000000..4d8e648 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_controller_disable.c @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_controller_disable PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will disable the EHCI controller. The controller will */ +/* release all its resources (memory, IO ...). After this, the */ +/* controller will not send SOF any longer. */ +/* */ +/* All transactions should have been completed, all classes should */ +/* have been closed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_controller_disable(UX_HCD_EHCI *hcd_ehci) +{ + +UX_HCD *hcd; +ULONG ehci_register; + + + /* Point to the generic portion of the host controller structure instance. */ + hcd = hcd_ehci -> ux_hcd_ehci_hcd_owner; + + /* Stop the controller. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + ehci_register = EHCI_HC_IO_HCRESET; + ehci_register &= ~EHCI_HC_IO_RS; + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register); + + /* Wait for the Stop signal to be acknowledged by the controller. */ + ehci_register = 0; + while ((ehci_register&EHCI_HC_STS_HC_HALTED) == 0) + { + + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCCR_HCS_PARAMS); + } + + /* Reflect the state of the controller in the main structure. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_done_queue_process.c b/common/usbx_host_controllers/src/ux_hcd_ehci_done_queue_process.c new file mode 100644 index 0000000..722c3dc --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_done_queue_process.c @@ -0,0 +1,167 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_done_queue_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function process the isochronous, periodic and asynchronous */ +/* lists in search for transfers that occurred in the past */ +/* (micro-)frame. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_asynch_td_process Process asynch TD */ +/* _ux_hcd_ehci_hsisochronous_tds_process */ +/* Process high speed */ +/* isochronous TDs */ +/* _ux_hcd_ehci_fsisochronous_tds_process */ +/* Process full speed (split) */ +/* isochronous TDs */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_done_queue_process(UX_HCD_EHCI *hcd_ehci) +{ + +UX_EHCI_TD *td; +UX_EHCI_PERIODIC_LINK_POINTER ed; +UX_EHCI_ED *start_ed; +UX_EHCI_PERIODIC_LINK_POINTER lp; + + +#if UX_MAX_ISO_TD + + /* We scan the active isochronous list first. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + lp.itd_ptr = hcd_ehci -> ux_hcd_ehci_hsiso_scan_list; + while(lp.itd_ptr != UX_NULL) + { + + /* Process the iTD, return next active TD. */ + lp.itd_ptr = _ux_hcd_ehci_hsisochronous_tds_process(hcd_ehci, lp.itd_ptr); + } + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + + /* We scan the split isochronous list then. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + lp.sitd_ptr = hcd_ehci -> ux_hcd_ehci_fsiso_scan_list; + while(lp.sitd_ptr != UX_NULL) + { + + /* Process the iTD, return next active TD. */ + lp.sitd_ptr = _ux_hcd_ehci_fsisochronous_tds_process(hcd_ehci, lp.sitd_ptr); + } + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + +#endif +#endif + + /* We scan the linked interrupt list then. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + ed.ed_ptr = hcd_ehci -> ux_hcd_ehci_interrupt_ed_list; + while(ed.ed_ptr != UX_NULL) + { + + /* Retrieve the fist TD attached to this ED. */ + td = ed.ed_ptr -> ux_ehci_ed_first_td; + + /* Process TD until there is no next available. */ + while (td != UX_NULL) + td = _ux_hcd_ehci_asynch_td_process(ed.ed_ptr, td); + + /* Next ED. */ + ed.ed_ptr = ed.ed_ptr -> ux_ehci_ed_next_ed; + } + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Now we can parse the asynchronous list. The head ED is always empty and + used as an anchor only. */ + start_ed = hcd_ehci -> ux_hcd_ehci_asynch_head_list; + + /* Point to the next ED in the asynchronous tree. */ + ed.ed_ptr = start_ed -> ux_ehci_ed_queue_head; + ed.value &= UX_EHCI_LINK_ADDRESS_MASK; + + /* Obtain the virtual address from the element. */ + ed.void_ptr = _ux_utility_virtual_address(ed.void_ptr); + + /* Now traverse this list until we have found a non anchor endpoint that + has transfers done. */ + while (ed.ed_ptr != start_ed) + { + + /* Retrieve the fist TD attached to this ED. */ + td = ed.ed_ptr -> ux_ehci_ed_first_td; + while (td != UX_NULL) + { + + td = _ux_hcd_ehci_asynch_td_process(ed.ed_ptr, td); + } + + /* Point to the next ED in the asynchronous tree. */ + ed.ed_ptr = ed.ed_ptr -> ux_ehci_ed_queue_head; + ed.value &= UX_EHCI_LINK_ADDRESS_MASK; + + /* Obtain the virtual address from the element. */ + ed.void_ptr = _ux_utility_virtual_address(ed.void_ptr); + } +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_door_bell_wait.c b/common/usbx_host_controllers/src/ux_hcd_ehci_door_bell_wait.c new file mode 100644 index 0000000..39f8b87 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_door_bell_wait.c @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_door_bell_wait PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will raise the doorbell and wait for its */ +/* acknowledgement. This mechanism is used to safely remove a physical */ +/* endpoint from EHCI. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_semaphore_put Release semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_door_bell_wait(UX_HCD_EHCI *hcd_ehci) +{ + +ULONG ehci_register; +UINT status; + + + /* Protect against multiple thread entry to this HCD. */ + status = _ux_utility_semaphore_get(&hcd_ehci -> ux_hcd_ehci_protect_semaphore, UX_WAIT_FOREVER); + if (status != UX_SUCCESS) + return; + + /* Raise the doorbell to the HCD. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + ehci_register |= EHCI_HC_IO_IAAD; + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register); + + /* Wait for the doorbell to be awaken. */ + status = _ux_utility_semaphore_get(&hcd_ehci -> ux_hcd_ehci_doorbell_semaphore, UX_WAIT_FOREVER); + + /* Free the protection semaphore. */ + status = _ux_utility_semaphore_put(&hcd_ehci -> ux_hcd_ehci_protect_semaphore); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_ed_clean.c b/common/usbx_host_controllers/src/ux_hcd_ehci_ed_clean.c new file mode 100644 index 0000000..a60653a --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_ed_clean.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_ed_clean PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cleans the tds attached to a ED in case a transfer */ +/* has to be aborted. */ +/* */ +/* INPUT */ +/* */ +/* ed Pointer to ED */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_ed_clean(UX_EHCI_ED *ed) +{ + +UX_EHCI_TD *td; +UX_EHCI_TD *next_td; + + + /* Get the first pointer to the TD. */ + td = ed -> ux_ehci_ed_queue_element; + td = (UX_EHCI_TD *) ((ULONG) td & ~UX_EHCI_QH_T); + td = _ux_utility_virtual_address(td); + + /* Mark the TD link of the endpoint as terminated. */ + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) UX_EHCI_TD_T; + + /* Free all tds attached to the ED. */ + while (td != UX_NULL) + { + + /* Get the next TD pointed by the current TD. */ + next_td = td -> ux_ehci_td_link_pointer; + next_td = (UX_EHCI_TD *) ((ULONG) next_td & ~UX_EHCI_TD_T); + next_td = _ux_utility_virtual_address(next_td); + + /* Mark the current TD as free. */ + td -> ux_ehci_td_status = UX_UNUSED; + + td = next_td; + } + + /* Reset the first TD. */ + ed -> ux_ehci_ed_first_td = UX_NULL; + + /* Reset the last TD. */ + ed -> ux_ehci_ed_last_td = UX_NULL; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_ed_obtain.c b/common/usbx_host_controllers/src/ux_hcd_ehci_ed_obtain.c new file mode 100644 index 0000000..85d72fb --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_ed_obtain.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_ed_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free ED from the ED list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_ED * Pointer to ED */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_ED *_ux_hcd_ehci_ed_obtain(UX_HCD_EHCI *hcd_ehci) +{ + +UX_EHCI_ED *ed; +ULONG ed_index; + + + /* Start the search from the beginning of the list. */ + ed = hcd_ehci -> ux_hcd_ehci_ed_list; + for (ed_index = 0; ed_index < _ux_system_host -> ux_system_host_max_ed; ed_index++) + { + + /* Check the ED status, a free ED is marked with the UNUSED flag. */ + if (ed -> ux_ehci_ed_status == UX_UNUSED) + { + + /* The ED may have been used, so we reset all fields. */ + _ux_utility_memory_set(ed, 0, sizeof(UX_EHCI_ED)); + + /* This ED is now marked as USED. */ + ed -> ux_ehci_ed_status = UX_USED; + + /* We initialize the type of ED and mark its TD terminator to be safe. */ + ed -> ux_ehci_ed_queue_head = (UX_EHCI_ED *) UX_EHCI_QH_TYP_QH; + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) UX_EHCI_TD_T; + + /* Success, return ED pointer. */ + return(ed); + } + + /* Point to the next ED. */ + ed++; + } + + /* There is no available ED in the ED list. */ + return(UX_NULL); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_endpoint_reset.c b/common/usbx_host_controllers/src/ux_hcd_ehci_endpoint_reset.c new file mode 100644 index 0000000..b69aaf9 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_endpoint_reset.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_endpoint_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reset an endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_endpoint_reset(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint) +{ + +UX_EHCI_ED *ed; +ULONG ed_value; + + + UX_PARAMETER_NOT_USED(hcd_ehci); + + /* From the endpoint container fetch the EHCI ED descriptor. */ + ed = (UX_EHCI_ED*) endpoint -> ux_endpoint_ed; + + /* Reset the data0/data1 toggle bit in the Overlay portion of the ED. */ + ed_value = ed -> ux_ehci_ed_state; + ed_value &= ~UX_EHCI_QH_TOGGLE; + ed -> ux_ehci_ed_state = ed_value; + + /* This operation never fails! */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_entry.c b/common/usbx_host_controllers/src/ux_hcd_ehci_entry.c new file mode 100644 index 0000000..a9e2bf4 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_entry.c @@ -0,0 +1,268 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function dispatch the HCD function internally to the EHCI */ +/* controller. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* function Requested function */ +/* parameter Pointer to parameter(s) */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_asynchronous_endpoint_create Create endpoint */ +/* _ux_hcd_ehci_asynchronous_endpoint_destroy Destroy endpoint */ +/* _ux_hcd_ehci_controller_disable Disable controller */ +/* _ux_hcd_ehci_done_queue_process Process done queue */ +/* _ux_hcd_ehci_endpoint_reset Reset endpoint */ +/* _ux_hcd_ehci_frame_number_get Get frame number */ +/* _ux_hcd_ehci_frame_number_set Set frame number */ +/* _ux_hcd_ehci_interrupt_endpoint_create Endpoint create */ +/* _ux_hcd_ehci_interrupt_endpoint_destroy Endpoint destroy */ +/* _ux_hcd_ehci_isochronous_endpoint_create Endpoint create */ +/* _ux_hcd_ehci_isochronous_endpoint_destroy Endpoint destroy */ +/* _ux_hcd_ehci_port_disable Disable port */ +/* _ux_hcd_ehci_port_reset Reset port */ +/* _ux_hcd_ehci_port_resume Resume port */ +/* _ux_hcd_ehci_port_status_get Get port status */ +/* _ux_hcd_ehci_port_suspend Suspend port */ +/* _ux_hcd_ehci_power_down_port Power down port */ +/* _ux_hcd_ehci_power_on_port Power on port */ +/* _ux_hcd_ehci_request_transfer Request transfer */ +/* _ux_hcd_ehci_transfer_abort Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_entry(UX_HCD *hcd, UINT function, VOID *parameter) +{ + +UINT status; +UX_HCD_EHCI *hcd_ehci; + + + /* Check the status of the controller. */ + if (hcd -> ux_hcd_status == UX_UNUSED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_CONTROLLER_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONTROLLER_UNKNOWN, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONTROLLER_UNKNOWN); + } + + /* Get the pointer to the EHCI HCD. */ + hcd_ehci = (UX_HCD_EHCI *) hcd -> ux_hcd_controller_hardware; + + /* Look at the function and route it. */ + switch(function) + { + + case UX_HCD_DISABLE_CONTROLLER: + + status = _ux_hcd_ehci_controller_disable(hcd_ehci); + break; + + + case UX_HCD_GET_PORT_STATUS: + status = _ux_hcd_ehci_port_status_get(hcd_ehci, (ULONG) parameter); + break; + + + case UX_HCD_ENABLE_PORT: + + status = UX_SUCCESS; + break; + + + case UX_HCD_DISABLE_PORT: + + status = _ux_hcd_ehci_port_disable(hcd_ehci, (ULONG) parameter); + break; + + + case UX_HCD_POWER_ON_PORT: + + status = _ux_hcd_ehci_power_on_port(hcd_ehci, (ULONG) parameter); + break; + + + case UX_HCD_POWER_DOWN_PORT: + + status = _ux_hcd_ehci_power_down_port(hcd_ehci, (ULONG) parameter); + break; + + + case UX_HCD_SUSPEND_PORT: + status = _ux_hcd_ehci_port_suspend(hcd_ehci, (ULONG) parameter); + break; + + + case UX_HCD_RESUME_PORT: + + status = _ux_hcd_ehci_port_resume(hcd_ehci, (UINT) parameter); + break; + + + case UX_HCD_RESET_PORT: + + status = _ux_hcd_ehci_port_reset(hcd_ehci, (ULONG) parameter); + break; + + + case UX_HCD_GET_FRAME_NUMBER: + + status = _ux_hcd_ehci_frame_number_get(hcd_ehci, (ULONG *) parameter); + break; + + + case UX_HCD_SET_FRAME_NUMBER: + + _ux_hcd_ehci_frame_number_set(hcd_ehci, (ULONG) parameter); + status = UX_SUCCESS; + break; + + + case UX_HCD_TRANSFER_REQUEST: + + status = _ux_hcd_ehci_request_transfer(hcd_ehci, (UX_TRANSFER *) parameter); + break; + + + case UX_HCD_TRANSFER_ABORT: + + status = _ux_hcd_ehci_transfer_abort(hcd_ehci, (UX_TRANSFER *) parameter); + break; + + + case UX_HCD_CREATE_ENDPOINT: + + switch ((((UX_ENDPOINT*) parameter) -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + status = _ux_hcd_ehci_asynchronous_endpoint_create(hcd_ehci, (UX_ENDPOINT*) parameter); + break; + + case UX_INTERRUPT_ENDPOINT: + status = _ux_hcd_ehci_interrupt_endpoint_create(hcd_ehci, (UX_ENDPOINT*) parameter); + break; + + case UX_ISOCHRONOUS_ENDPOINT: + status = _ux_hcd_ehci_isochronous_endpoint_create(hcd_ehci, (UX_ENDPOINT*) parameter); + break; + + } + break; + + + case UX_HCD_DESTROY_ENDPOINT: + + switch ((((UX_ENDPOINT*) parameter) -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + status = _ux_hcd_ehci_asynchronous_endpoint_destroy(hcd_ehci, (UX_ENDPOINT*) parameter); + break; + + case UX_INTERRUPT_ENDPOINT: + status = _ux_hcd_ehci_interrupt_endpoint_destroy(hcd_ehci, (UX_ENDPOINT*) parameter); + break; + + case UX_ISOCHRONOUS_ENDPOINT: + status = _ux_hcd_ehci_isochronous_endpoint_destroy(hcd_ehci, (UX_ENDPOINT*) parameter); + break; + + } + break; + + + case UX_HCD_RESET_ENDPOINT: + + status = _ux_hcd_ehci_endpoint_reset(hcd_ehci, (UX_ENDPOINT*) parameter); + break; + + + case UX_HCD_PROCESS_DONE_QUEUE: + + _ux_hcd_ehci_done_queue_process(hcd_ehci); + status = UX_SUCCESS; + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + status = UX_FUNCTION_NOT_SUPPORTED; + break; + } + + /* Return status to caller. */ + return(status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_get.c b/common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_get.c new file mode 100644 index 0000000..a284f77 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_get.c @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_frame_number_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the frame number currently used by the */ +/* controller. This function is mostly used for isochronous purposes. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* frame_number Pointer to frame number */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_frame_number_get(UX_HCD_EHCI *hcd_ehci, ULONG *frame_number) +{ + +ULONG ehci_register; + + + /* Read the micro frame number register. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_FRAME_INDEX); + + /* The register is based on micro frames, so we need to divide the + value by 8 to get to the millisecond frame number. */ + *frame_number = (ehci_register >> 3) & 0x3ff; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_set.c b/common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_set.c new file mode 100644 index 0000000..a80b5ad --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_frame_number_set.c @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_frame_number_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will set the current frame number to the one */ +/* specified. This function is mostly used for isochronous purposes. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* frame_number Frame number */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_frame_number_set(UX_HCD_EHCI *hcd_ehci, ULONG frame_number) +{ + +ULONG ehci_register; + + + /* It is illegal in EHCI to set the frame number while the controller is + running. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + if (ehci_register & EHCI_HC_IO_RS) + return; + + /* The register is based on micro frames, so we need to multiply the + value by 8 to get to the millisecond frame number. */ + ehci_register = frame_number << 3; + + /* Write the frame number, by default the micro frame will be set to 0. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_FRAME_INDEX, ehci_register); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_td_obtain.c b/common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_td_obtain.c new file mode 100644 index 0000000..0bc0ce3 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_td_obtain.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_fsisochronous_td_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free TD from the full speed isochronous */ +/* TD list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_FSISO_TD * Pointer to EHCI FSISO TD */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_FSISO_TD *_ux_hcd_ehci_fsisochronous_td_obtain(UX_HCD_EHCI *hcd_ehci) +{ +#if UX_MAX_ISO_TD == 0 || !defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + + /* This function is not yet supported, return NULL. */ + UX_PARAMETER_NOT_USED(hcd_ehci); + return(UX_NULL); +#else + +UX_EHCI_FSISO_TD *td; +ULONG td_index; + + + /* Start the search from the beginning of the list. */ + td = hcd_ehci -> ux_hcd_ehci_fsiso_td_list; + for (td_index = 0; td_index < _ux_system_host -> ux_system_host_max_iso_td; td_index++) + { + + /* Check the TD status, a free TD is marked with the USED flag. */ + if (td -> ux_ehci_fsiso_td_status == UX_UNUSED) + { + + /* The TD may have been used, so we reset all fields. */ + _ux_utility_memory_set(td, 0, sizeof(UX_EHCI_FSISO_TD)); + + /* This TD is now marked as USED. */ + td -> ux_ehci_fsiso_td_status = UX_USED; + + /* Initialize the link pointer TD fields. */ + td -> ux_ehci_fsiso_td_next_lp.value = UX_EHCI_FSISO_T; + + /* Success, return TD pointer. */ + return(td); + } + + /* Look at next TD. */ + td++; + } + + /* There is no available TD in the TD list. */ + + /* Error, return a null. */ + return(UX_NULL); +#endif +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_tds_process.c b/common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_tds_process.c new file mode 100644 index 0000000..463a985 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_fsisochronous_tds_process.c @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_fsisochronous_tds_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the isochronous, periodic and asynchronous */ +/* lists in search for transfers that occurred in the past */ +/* (micro-)frame. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to HCD EHCI */ +/* prev_itd Pointer to pointer point to */ +/* previous FSISO TD */ +/* itd Pointer to FSISO TD */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_HSISO_TD Pointer to FSISO TD */ +/* */ +/* CALLS */ +/* */ +/* (ux_transfer_request_completion_function) Completion function */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_FSISO_TD* _ux_hcd_ehci_fsisochronous_tds_process( + UX_HCD_EHCI *hcd_ehci, + UX_EHCI_FSISO_TD* itd) +{ + + UX_PARAMETER_NOT_USED(hcd_ehci); + + /* TBD */ + + return(itd -> ux_ehci_fsiso_td_next_scan_td); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_td_obtain.c b/common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_td_obtain.c new file mode 100644 index 0000000..be4d295 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_td_obtain.c @@ -0,0 +1,116 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_hsisochronous_td_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free TD from the high speed isochronous */ +/* TD list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_HSISO_TD * Pointer to EHCI HSISO TD */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_HSISO_TD *_ux_hcd_ehci_hsisochronous_td_obtain(UX_HCD_EHCI *hcd_ehci) +{ +#if UX_MAX_ISO_TD == 0 + + /* This function is not yet supported, return NULL. */ + UX_PARAMETER_NOT_USED(hcd_ehci); + return(UX_NULL); +#else + +UX_EHCI_HSISO_TD *td; +ULONG td_index; + + + /* Start the search from the beginning of the list. */ + td = hcd_ehci -> ux_hcd_ehci_hsiso_td_list; + for (td_index = 0; td_index < _ux_system_host -> ux_system_host_max_iso_td; td_index++) + { + + /* Check the TD status, a free TD is marked with the USED flag. */ + if (td -> ux_ehci_hsiso_td_status == UX_UNUSED) + { + + /* The TD may have been used, so we reset all fields. */ + _ux_utility_memory_set(td, 0, sizeof(UX_EHCI_HSISO_TD)); + + /* This TD is now marked as USED. */ + td -> ux_ehci_hsiso_td_status = UX_USED; + + /* Initialize the link pointer TD fields. */ + td -> ux_ehci_hsiso_td_next_lp.value = UX_EHCI_HSISO_T; + + /* Success, return TD pointer. */ + return(td); + } + + /* Look at next TD. */ + td++; + } + + /* There is no available TD in the TD list. */ + + /* Error, return a null. */ + return(UX_NULL); +#endif +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_tds_process.c b/common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_tds_process.c new file mode 100644 index 0000000..b20af0d --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_hsisochronous_tds_process.c @@ -0,0 +1,443 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_hsisochronous_tds_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the iTDs of an isochronous endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to HCD EHCI */ +/* itd Pointer to HSISO TD */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_HSISO_TD Pointer to HSISO TD */ +/* */ +/* CALLS */ +/* */ +/* (ux_transfer_request_completion_function) */ +/* Transfer Completion function */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_utility_semaphore_put Put semaphore */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_HSISO_TD* _ux_hcd_ehci_hsisochronous_tds_process( + UX_HCD_EHCI *hcd_ehci, + UX_EHCI_HSISO_TD* itd) +{ + +UX_EHCI_HSISO_ED *ed; +UX_EHCI_HSISO_TD *next_scan_td; +ULONG n_fr; +ULONG frindex; +ULONG frindex1; +UX_EHCI_HSISO_TD *fr_td; +UX_EHCI_HSISO_TD *fr_td1; +ULONG control; +ULONG control1; +ULONG trans_bytes; +UX_TRANSFER *transfer; +UX_EHCI_POINTER bp; +ULONG pg; +ULONG pg_addr; +ULONG pg_offset; +ULONG frindex_now; +ULONG frindex_start; +USHORT fr_hc; +USHORT fr_sw; +UINT i; + + + /* Get ED. */ + ed = itd -> ux_ehci_hsiso_td_ed; + + /* Get next scan TD. */ + next_scan_td = itd -> ux_ehci_hsiso_td_next_scan_td; + + /* Check if iTD is skipped. */ + if (ed -> ux_ehci_hsiso_ed_frstart == 0xFF) + return(next_scan_td); + + /* + ** 1. There is requests loaded + ** (0) Micro-frame is active (request loaded) + ** (1) Active is cleared (HC processed) + ** (2) Handle it + ** 2. There is micro-frame free + ** (0) Micro-frame is free + ** (1) Micro-frame not overlap current FRINDEX + ** (2) Link request + ** (3) Build controls to active micro-frame + */ + + /* Get number of frames (8,4,2 or 1). */ + n_fr = 8 >> ed -> ux_ehci_hsiso_ed_frinterval_shift; + + /* Process if there is requests loaded. */ + if (ed -> ux_ehci_hsiso_ed_frload > 0) + { + + /* Process count to target micro frames. */ + fr_hc = ed -> ux_ehci_hsiso_ed_fr_hc << ed -> ux_ehci_hsiso_ed_frinterval_shift; + fr_hc += ed -> ux_ehci_hsiso_ed_frstart; + fr_hc &= 0x7u; + + /* Process done iTDs. */ + for (i = 0; + i < n_fr; /* 8, 4, 2 or 1. */ + i += ed -> ux_ehci_hsiso_ed_frinterval) + { + + /* Get frindex. */ + frindex = fr_hc + i; + frindex &= 7u; + + if ((ed -> ux_ehci_hsiso_ed_frload & (1u << frindex)) == 0) + { + break; + } + + /* Get iTD for the micro-frame. */ + fr_td = ed -> ux_ehci_hsiso_ed_fr_td[frindex >> 1]; + + /* Get control. */ + control = fr_td -> ux_ehci_hsiso_td_control[frindex]; + + /* Check next frame state, if multiple micro-frames loaded. */ + if (n_fr > 1) + { + + /* Micro-frame is active, check next. */ + if (control & UX_EHCI_HSISO_STATUS_ACTIVE) + { + + /* Get next frindex. */ + frindex1 = frindex + ed -> ux_ehci_hsiso_ed_frinterval; + frindex1 &= 7u; + + /* Get next iTD for the micro-frame. */ + fr_td1 = ed -> ux_ehci_hsiso_ed_fr_td[frindex1 >> 1]; + + /* Get control. */ + control1 = fr_td1 -> ux_ehci_hsiso_td_control[frindex1]; + + /* Next micro-frame is also active, break. */ + if (control1 & UX_EHCI_HSISO_STATUS_ACTIVE) + { + break; + } + else + { + + /* Next micro-frame is not loaded, break. */ + if ((ed -> ux_ehci_hsiso_ed_frload & (1u << frindex1)) == 0) + { + break; + } + + /* We are here when micro-frame 1 handled before micro-frame 0, + ** which means we missed one request. + */ + + /* Disable control, discard buffer and handle request. */ + control &= ~(UX_EHCI_HSISO_STATUS_ACTIVE | + UX_EHCI_HSISO_XACT_LENGTH_MASK); + fr_td -> ux_ehci_hsiso_td_control[frindex] = control; + } + + } /* if (control & UX_EHCI_HSISO_STATUS_ACTIVE) */ + } + else + { + + /* If iTD is active, break. */ + if (control & UX_EHCI_HSISO_STATUS_ACTIVE) + { + break; + } + } + + /* Clear load map anyway. */ + fr_td -> ux_ehci_hsiso_td_frload &= ~(1u << frindex); + ed -> ux_ehci_hsiso_ed_frload &= ~(1u << frindex); + + /* Handle the request. */ + transfer = ed -> ux_ehci_hsiso_ed_transfer_head; + + /* Convert error code to completion code. */ + if (control & UX_EHCI_HSISO_STATUS_DATA_BUFFER_ERR) + transfer -> ux_transfer_request_completion_code = UX_TRANSFER_BUFFER_OVERFLOW; + else + { + if (control & UX_EHCI_HSISO_STATUS_MASK) + transfer -> ux_transfer_request_completion_code = UX_TRANSFER_ERROR; + else + transfer -> ux_transfer_request_completion_code = UX_SUCCESS; + } + + /* Get transfer bytes. */ + trans_bytes = control & UX_EHCI_HSISO_XACT_LENGTH_MASK; + trans_bytes >>= UX_EHCI_HSISO_XACT_LENGTH_SHIFT; + + /* Save to actual length. */ + transfer -> ux_transfer_request_actual_length = trans_bytes; + + /* HC processed. */ + ed -> ux_ehci_hsiso_ed_fr_hc ++; + + /* Unlink it from iTD. */ + fr_td -> ux_ehci_hsiso_td_fr_transfer[frindex & 1u] = UX_NULL; + + /* Unlink it from request list head. */ + ed -> ux_ehci_hsiso_ed_transfer_head = + transfer -> ux_transfer_request_next_transfer_request; + + /* If no more requests, also set tail to NULL. */ + if (ed -> ux_ehci_hsiso_ed_transfer_head == UX_NULL) + ed -> ux_ehci_hsiso_ed_transfer_tail = UX_NULL; + + /* Invoke callback. */ + if (transfer -> ux_transfer_request_completion_function) + transfer -> ux_transfer_request_completion_function(transfer); + + /* Put semaphore. */ + _ux_utility_semaphore_put(&transfer -> ux_transfer_request_semaphore); + + } /* for (;i < n_fr;) */ + } + + /* Build request when there is new unloaded requests. */ + if (ed -> ux_ehci_hsiso_ed_transfer_first_new) + { + + /* Get current FRINDEX for SW process. */ + frindex_now = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_FRAME_INDEX); + frindex_now &= 0x7u; + + /* If transfer starts. */ + if (ed -> ux_ehci_hsiso_ed_frstart == 0xFE) + { + + /* 1 micro-frame in iTD. */ + if (ed -> ux_ehci_hsiso_ed_frinterval_shift >= 3) + { + + /* Uses this only micro-frame index. */ + ed -> ux_ehci_hsiso_ed_frstart = ed -> ux_ehci_hsiso_ed_frindex; + } + + /* 8,4,2 micro-frames in each iTD. */ + else + { + + /* Get start index for sw to link requests. */ + if (ed -> ux_ehci_hsiso_ed_frinterval_shift == 0) + + /* Interval 1, just add two. */ + frindex_start = frindex_now + 2; + else + { + + /* Interval 2, 4. */ + /* Scan indexes, to find an index that has 2 in front or more. */ + for (frindex_start = ed -> ux_ehci_hsiso_ed_frindex; + frindex_start < 9; + frindex_start += ed -> ux_ehci_hsiso_ed_frinterval) + { + if (frindex_start - frindex_now >= 2) + break; + } + } + + /* Target to start index calculated and wrap around in one frame. */ + ed -> ux_ehci_hsiso_ed_frstart = frindex_start & 7u; + } + } /* if (ed -> ux_ehci_hsiso_ed_fr_sw == 0xFE) */ + + /* Process count to target micro frames. */ + fr_sw = ed -> ux_ehci_hsiso_ed_fr_sw << ed -> ux_ehci_hsiso_ed_frinterval_shift; + fr_sw += ed -> ux_ehci_hsiso_ed_frstart; + fr_sw &= 0x7u; + + /* Build requests. */ + for (i = 0; + i < n_fr; + i += ed -> ux_ehci_hsiso_ed_frinterval) + { + + /* Get micro-frame index. */ + frindex = i + fr_sw; + frindex &= 7u; + + /* Get iTD. */ + fr_td = ed -> ux_ehci_hsiso_ed_fr_td[frindex >> 1]; + + /* Check load status. */ + if (fr_td -> ux_ehci_hsiso_td_frload & (1u << frindex)) + { + break; + } + + /* Get a transfer request. */ + transfer = ed -> ux_ehci_hsiso_ed_transfer_first_new; + if (transfer == UX_NULL) + { + break; + } + + /* Get control status. */ + control = fr_td -> ux_ehci_hsiso_td_control[frindex]; + + /* If there are multiple micro-frames, check + ** (1) If target micro-frame is already loaded + ** (2) If the micro-frame overlapped current one + */ + if (n_fr) + { + + /* If already loaded(active), break. */ + if (control & UX_EHCI_HSISO_STATUS_ACTIVE) + { + break; + } + + /* If index overlap, break. */ + + /* Check if frindex exceeds same FRINDEX in next frame. */ + if ((frindex == frindex_now) || (i + fr_sw >= frindex_now + 8)) + { + break; + } + } + + /* Now new request is linking. */ + + /* Process count inc. */ + ed -> ux_ehci_hsiso_ed_fr_sw ++; + + /* Sanity check, iTD is not linked. */ + while(fr_td -> ux_ehci_hsiso_td_fr_transfer[frindex & 1u] != UX_NULL); + + /* Link it to iTD. */ + fr_td -> ux_ehci_hsiso_td_fr_transfer[frindex & 1u] = transfer; + + /* Remove it from new free list. */ + ed -> ux_ehci_hsiso_ed_transfer_first_new = + transfer -> ux_transfer_request_next_transfer_request; + + /* Update load map. */ + fr_td -> ux_ehci_hsiso_td_frload |= (1u << frindex); + ed -> ux_ehci_hsiso_ed_frload |= (1u << frindex); + + /* Get transfer size. */ + trans_bytes = transfer -> ux_transfer_request_requested_length; + + /* Limit max transfer size. */ + if (trans_bytes > fr_td -> ux_ehci_hsiso_td_max_trans_size) + trans_bytes = fr_td -> ux_ehci_hsiso_td_max_trans_size; + + /* Build the control. */ + + /* Keep IOC and PG. */ + control &= UX_EHCI_HSISO_IOC | UX_EHCI_HSISO_PG_MASK; + + /* New transfer size. */ + control |= (trans_bytes << UX_EHCI_HSISO_XACT_LENGTH_SHIFT); + + /* Active. */ + control |= (UX_EHCI_HSISO_STATUS_ACTIVE); + + /* Build offset & BPs (5,6/3,4). */ + + /* Get physical buffer address. */ + bp.void_ptr = _ux_utility_physical_address(transfer -> ux_transfer_request_data_pointer); + + /* Get page offset. */ + pg_addr = bp.value & UX_EHCI_PAGE_ALIGN; + pg_offset = bp.value & UX_EHCI_HSISO_XACT_OFFSET_MASK; + + /* Offset in control. */ + control |= pg_offset; + + /* Save BPs. */ + pg = (frindex & 1u) ? 5 : 3; + + /* PG in control. */ + control |= pg << UX_EHCI_HSISO_PG_SHIFT; + + /* Save BPs. */ + + /* First BP. */ + bp.value = pg_addr; + fr_td -> ux_ehci_hsiso_td_bp[pg] = bp.void_ptr; + + /* Next page information. */ + pg ++; + pg_addr += UX_EHCI_PAGE_SIZE; + + /* Next BP. */ + bp.value = pg_addr; + fr_td -> ux_ehci_hsiso_td_bp[pg] = bp.void_ptr; + + /* Save control. */ + UX_DATA_MEMORY_BARRIER + fr_td -> ux_ehci_hsiso_td_control[frindex] = control; + + } /* for(i = 0; i < n_fr; ) */ + + } /* if (ed -> ux_ehci_hsiso_ed_transfer_first_new) */ + + /* Return next iTD in scan list. */ + return(next_scan_td); +} diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_initialize.c b/common/usbx_host_controllers/src/ux_hcd_ehci_initialize.c new file mode 100644 index 0000000..49a1a6f --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_initialize.c @@ -0,0 +1,416 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/* EHCI HCD extention for host mode select. */ +#ifndef UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE + +#if defined(K66) || defined(IMX6UL) || defined(XILINX_ZYNQ) || defined(MIMXRT) +#define UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE(hcd_ehci) do \ +{ \ + _ux_hcd_ehci_register_write(hcd_ehci, (hcd_ehci -> ux_hcd_ehci_hcor + 0x1A), 0x03); \ +} while(0) +#else +#define UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE(hcd_ehci) +#endif + +#endif /* ifndef UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE */ + +#ifndef UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT + +#if defined(IMX25) || defined(IMX6UL) || defined(K66) || defined(LPC3131) || \ + defined(MCF5445X) || defined(MIMXRT) +#define UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT UX_TRUE +#else +#define UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT UX_FALSE +#endif + +#endif /* ifndef UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT */ + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the EHCI controller. It sets the DMA */ +/* areas, programs all the EHCI registers, sets up the ED and TD */ +/* containers, sets the control, bulk and periodic lists. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_periodic_tree_create Create periodic tree */ +/* _ux_hcd_ehci_power_root_hubs Power root HUBs */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_delete Delete memory block */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_semaphore_create Create semaphore */ +/* _ux_utility_semaphore_delete Delete semaphore */ +/* _ux_utility_mutex_create Create mutex */ +/* _ux_utility_mutex_delete Delete mutex */ +/* _ux_utility_set_interrupt_handler Set interrupt handler */ +/* _ux_utility_delay_ms Delay ms */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_initialize(UX_HCD *hcd) +{ + +UX_HCD_EHCI *hcd_ehci; +UX_EHCI_ED *ed; +UX_EHCI_LINK_POINTER lp; +ULONG ehci_register; +ULONG port_index; +UINT status = UX_SUCCESS; + + + /* The controller initialized here is of EHCI type. */ + hcd -> ux_hcd_controller_type = UX_EHCI_CONTROLLER; + + /* Initialize the max bandwidth for periodic endpoints. On EHCI, + the spec says no more than 90% to be allocated for periodic. */ + hcd -> ux_hcd_available_bandwidth = UX_EHCI_AVAILABLE_BANDWIDTH; + + /* Allocate memory for this EHCI HCD instance. */ + hcd_ehci = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HCD_EHCI)); + if (hcd_ehci == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set the pointer to the EHCI HCD. */ + hcd -> ux_hcd_controller_hardware = (VOID *) hcd_ehci; + + /* Save the register memory address. */ + hcd_ehci -> ux_hcd_ehci_base = (ULONG *) hcd -> ux_hcd_io; + + /* Obtain the address of the HCOR registers. This is a byte offset from the + HCOR Cap registers. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCCR_CAP_LENGTH); + hcd_ehci -> ux_hcd_ehci_hcor = (ehci_register & 0xff) >> 2; + + /* Set the generic HCD owner for the EHCI HCD. */ + hcd_ehci -> ux_hcd_ehci_hcd_owner = hcd; + + /* Initialize the function entry for this HCD. */ + hcd -> ux_hcd_entry_function = _ux_hcd_ehci_entry; + + /* Set the state of the controller to HALTED first. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED; + + /* Read the EHCI Controller Command register. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + + /* Isolate the Frame list size. */ + ehci_register = (ehci_register >> 2) & 3; + + /* Frame list size selection. */ + switch (ehci_register) + { + + case 0 : + + /* Frame list size is 1024 entries. */ + hcd_ehci -> ux_hcd_ehci_frame_list_size = 1024; + break; + + case 1 : + + /* Frame list size is 512 entries. */ + hcd_ehci -> ux_hcd_ehci_frame_list_size = 512; + break; + case 2 : + + /* Frame list size is 256 entries. */ + hcd_ehci -> ux_hcd_ehci_frame_list_size = 256; + break; + + default : + + /* Error, Wrong frame size. This should never happen. */ + status = (UX_ERROR); + } + + /* Allocate the EHCI controller frame list. The memory alignment is 4K. + The number of entries may be changeable in some controllers. We get the value in the command register. */ + if (status == UX_SUCCESS) + { + hcd_ehci -> ux_hcd_ehci_frame_list = _ux_utility_memory_allocate(UX_ALIGN_4096, UX_CACHE_SAFE_MEMORY, (hcd_ehci -> ux_hcd_ehci_frame_list_size * 4)); + if (hcd_ehci -> ux_hcd_ehci_frame_list == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } + + /* Allocate the list of eds. All eds are allocated on 32 byte memory boundary. */ + if (status == UX_SUCCESS) + { + hcd_ehci -> ux_hcd_ehci_ed_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_ED) * _ux_system_host -> ux_system_host_max_ed); + if (hcd_ehci -> ux_hcd_ehci_ed_list == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } + + /* Allocate the list of tds. All tds are allocated on 32 byte memory boundary. */ + if (status == UX_SUCCESS) + { + hcd_ehci -> ux_hcd_ehci_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_TD) * _ux_system_host -> ux_system_host_max_td); + if (hcd_ehci -> ux_hcd_ehci_td_list == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } + +#if UX_MAX_ISO_TD == 0 || !defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + hcd_ehci -> ux_hcd_ehci_fsiso_td_list = UX_NULL; +#else + + /* Allocate the list of isochronous Full Speed tds. All tds are allocated on 32 byte + memory boundary. */ + if (status == UX_SUCCESS) + { + hcd_ehci -> ux_hcd_ehci_fsiso_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_FSISO_TD) * _ux_system_host -> ux_system_host_max_iso_td); + if (hcd_ehci -> ux_hcd_ehci_fsiso_td_list == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } +#endif + +#if UX_MAX_ISO_TD == 0 + hcd_ehci -> ux_hcd_ehci_hsiso_td_list = UX_NULL; +#else + + /* Allocate the list of isochronous High Speed tds. All tds are allocated on 32 byte memory boundary. */ + if (status == UX_SUCCESS) + { + hcd_ehci -> ux_hcd_ehci_hsiso_td_list = _ux_utility_memory_allocate(UX_ALIGN_64, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_HSISO_TD) * _ux_system_host -> ux_system_host_max_iso_td); + if (hcd_ehci -> ux_hcd_ehci_hsiso_td_list == UX_NULL) + status = (UX_MEMORY_INSUFFICIENT); + } +#endif + + /* Initialize the periodic tree. */ + if (status == UX_SUCCESS) + status = _ux_hcd_ehci_periodic_tree_create(hcd_ehci); + + if (status == UX_SUCCESS) + { + + /* Since this is a USB 2.0 controller, we can safely hardwire the version. */ + hcd -> ux_hcd_version = 0x200; + + /* The EHCI Controller should not be running. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + ehci_register &= ~EHCI_HC_IO_RS; + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register); + _ux_utility_delay_ms(2); + + /* Perform a global reset to the controller. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + ehci_register |= EHCI_HC_IO_HCRESET; + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register); + + /* Ensure the reset is complete. */ + while (ehci_register & EHCI_HC_IO_HCRESET) + { + + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + } + + /* Enable host mode for hardware peripheral. */ + UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE(hcd_ehci); + + /* Set the Frame List register of the controller. */ + lp.void_ptr = _ux_utility_physical_address(hcd_ehci -> ux_hcd_ehci_frame_list); + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_FRAME_LIST_BASE_ADDRESS, lp.value); + + /* We need one endpoint to be inserted into the Asynchronous list. */ + ed = _ux_hcd_ehci_ed_obtain(hcd_ehci); + if (ed == UX_NULL) + status = (UX_NO_ED_AVAILABLE); + } + + if (status == UX_SUCCESS) + { + + /* Make this ED point to itself. */ + lp.void_ptr = _ux_utility_physical_address(ed); + + /* Store the physical address of this Ed into the asynch list. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_ASYNCH_LIST_ADDRESS, lp.value); + + /* Store LP with QH Typ. */ + lp.value |= UX_EHCI_QH_TYP_QH; + ed -> ux_ehci_ed_queue_head = lp.ed_ptr; + + /* This ED will be the HEAD ED in the asynch list. */ + ed -> ux_ehci_ed_cap0 = UX_EHCI_QH_HEAD; + + /* We keep this ED as being the first, the last and the head ED. */ + hcd_ehci -> ux_hcd_ehci_asynch_head_list = ed; + hcd_ehci -> ux_hcd_ehci_asynch_first_list = ed; + hcd_ehci -> ux_hcd_ehci_asynch_last_list = ed; + + /* Set the EHCI Interrupt threshold default value (1 or 8 per ms) + and the size of the frame list. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, EHCI_HC_IO_ITC); + + /* Get the number of ports on the controller. The number of ports + needs to be reflected both for the generic HCD container and the + local ehci container. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCCR_HCS_PARAMS); + hcd -> ux_hcd_nb_root_hubs = (UINT) (ehci_register & 0xf); + hcd_ehci -> ux_hcd_ehci_nb_root_hubs = (UINT) (ehci_register & 0xf); + + /* The controller transceiver can now send the device connection/extraction + signals to the EHCI controller. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_CONFIG_FLAG, UX_EHCI_ROUTE_TO_LOCAL_HC); + + /* Create mutex for periodic list modification. */ + status = _ux_utility_mutex_create(&hcd_ehci -> ux_hcd_ehci_periodic_mutex, "ehci_periodic_mutex"); + if (status != UX_SUCCESS) + status = (UX_MUTEX_ERROR); + } + + /* We must enable the HCD protection semaphore. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&hcd_ehci -> ux_hcd_ehci_protect_semaphore, "ux_hcd_protect_semaphore", 1); + if (status != UX_SUCCESS) + status = (UX_SEMAPHORE_ERROR); + } + + /* We must enable the HCD doorbell semaphore. */ + if (status == UX_SUCCESS) + { + status = _ux_utility_semaphore_create(&hcd_ehci -> ux_hcd_ehci_doorbell_semaphore, "ux_hcd_doorbell_semaphore", 0); + if (status != UX_SUCCESS) + status = (UX_SEMAPHORE_ERROR); + } + + if (status == UX_SUCCESS) + { + + /* The EHCI Controller can now be Started. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND); + + /* Set the Frame list size and the RUN bit.. */ + ehci_register |= UX_EHCI_FRAME_LIST_MASK + | EHCI_HC_IO_RS + | EHCI_HC_IO_ASE + | EHCI_HC_IO_PSE; + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register); + + /* Regular EHCI with embedded TT. */ + hcd_ehci -> ux_hcd_ehci_embedded_tt = UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT; + + /* All ports must now be powered to pick up device insertion. */ + _ux_hcd_ehci_power_root_hubs(hcd_ehci); + + /* Set the state of the controller to OPERATIONAL. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_OPERATIONAL; + + /* Set the EHCI Interrupt Register. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_INTERRUPT, EHCI_HC_INTERRUPT_ENABLE_NORMAL); + + /* The controller interrupt must have a handler and be active now. */ + _ux_utility_set_interrupt_handler(hcd -> ux_hcd_irq, _ux_hcd_ehci_interrupt_handler); + + + /* Force a enum process if CCS detected. + ** Because CSC may keep zero in this case. + */ + for (port_index = 0, status = 0; port_index < hcd_ehci -> ux_hcd_ehci_nb_root_hubs; port_index ++) + { + + /* Read register. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + + /* Check CCS. */ + if (ehci_register & EHCI_HC_PS_CCS) + { + hcd_ehci -> ux_hcd_ehci_hcd_owner -> ux_hcd_root_hub_signal[port_index]++; + status ++; + } + } + + /* Wakeup enum thread. */ + if (status != 0) + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_enum_semaphore); + + /* Return successful status. */ + return(UX_SUCCESS); + } + + /* Error! Free resources! */ + if (hcd_ehci -> ux_hcd_ehci_frame_list) + _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_frame_list); + if (hcd_ehci -> ux_hcd_ehci_ed_list) + _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_ed_list); + if (hcd_ehci -> ux_hcd_ehci_td_list) + _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_td_list); +#if UX_MAX_ISO_TD && defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (hcd_ehci -> ux_hcd_ehci_fsiso_td_list) + _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_fsiso_td_list); +#endif +#if UX_MAX_ISO_TD + if (hcd_ehci -> ux_hcd_ehci_hsiso_td_list) + _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_hsiso_td_list); +#endif + if (hcd_ehci -> ux_hcd_ehci_periodic_mutex.tx_mutex_id != 0) + _ux_utility_mutex_delete(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + if (hcd_ehci -> ux_hcd_ehci_protect_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&hcd_ehci -> ux_hcd_ehci_protect_semaphore); + if (hcd_ehci -> ux_hcd_ehci_doorbell_semaphore.tx_semaphore_id != 0) + _ux_utility_semaphore_delete(&hcd_ehci -> ux_hcd_ehci_doorbell_semaphore); + _ux_utility_memory_free(hcd_ehci); + + /* Return error status code. */ + return(status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_create.c b/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_create.c new file mode 100644 index 0000000..17166f1 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_create.c @@ -0,0 +1,400 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_interrupt_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an interrupt endpoint. The interrupt */ +/* endpoint has an interval of operation from 1 to 255. In EHCI, the */ +/* hardware assisted interrupt is from 1 to 32. */ +/* */ +/* This routine will match the best interval for the EHCI hardware. */ +/* It will also determine the best node to hook the endpoint based on */ +/* the load that already exists on the horizontal ED chain. */ +/* */ +/* For the ones curious about this coding. The tricky part is to */ +/* understand how the interrupt matrix is constructed. We have used */ +/* eds with the skip bit on to build a frame of anchor eds. Each ED */ +/* creates a node for an appropriate combination of interval frequency */ +/* in the list. */ +/* */ +/* After obtaining a pointer to the list with the lowest traffic, we */ +/* traverse the list from the highest interval until we reach the */ +/* interval required. At that node, we anchor our real ED to the node */ +/* and link the ED that was attached to the node to our ED. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_ed_obtain Obtain an ED */ +/* _ux_hcd_ehci_least_traffic_list_get Get least traffic list */ +/* _ux_hcd_ehci_poll_rate_entry_get Get anchor for poll rate */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_mutex_on Get mutex */ +/* _ux_utility_mutex_off Put mutex */ +/* _ux_hcd_ehci_periodic_descriptor_link Link/unlink descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_interrupt_endpoint_create(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint) +{ + +UX_DEVICE *device; +UX_EHCI_ED *ed; +UX_EHCI_ED *ed_list; +UX_EHCI_ED *ed_anchor; +UINT interval; +UINT poll_depth; +ULONG max_packet_size; +ULONG num_transaction; +ULONG microframe_load[8]; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) +ULONG microframe_ssplit_count[8]; +UINT csplit_count; +ULONG cmask; +#else +#define microframe_ssplit_count UX_NULL +#endif +UX_EHCI_PERIODIC_LINK_POINTER lp; +UINT i; + + + /* Get the pointer to the device. */ + device = endpoint -> ux_endpoint_device; + +#if !defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + + /* Only high speed transfer supported without split transfer. */ + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + return(UX_FUNCTION_NOT_SUPPORTED); +#endif + + /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint is + active and will be the container for the tds. */ + ed = _ux_hcd_ehci_ed_obtain(hcd_ehci); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *) ed; + + /* Now do the opposite, attach the ED container to the physical ED. */ + ed -> ux_ehci_ed_endpoint = endpoint; + + /* Set the default MPS Capability info in the ED. */ + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + ed -> ux_ehci_ed_cap0 = max_packet_size << UX_EHCI_QH_MPS_LOC; + + /* Set the device address. */ + ed -> ux_ehci_ed_cap0 |= device -> ux_device_address; + + /* Add the endpoint address. */ + ed -> ux_ehci_ed_cap0 |= (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION) << UX_EHCI_QH_ED_AD_LOC; + + /* Set the High Bandwidth Pipe Multiplier to number transactions. */ + num_transaction = (endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK) >> UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT; + if (num_transaction < 3) + num_transaction ++; + ed -> ux_ehci_ed_cap1 |= (num_transaction << UX_EHCI_QH_HBPM_LOC); + + /* Set the device speed for full and low speed devices behind a HUB. The HUB address and the + port index must be stored in the endpoint. For low/full speed devices, the C-mask field must be set. */ + switch (device -> ux_device_speed) + { + + case UX_HIGH_SPEED_DEVICE: + ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_HIGH_SPEED; + break; + + case UX_LOW_SPEED_DEVICE: + ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_LOW_SPEED; + + /* Fall through. */ + default: + + /* The device must be on a hub for this code to execute. We still do a sanity check. */ + if (device -> ux_device_parent != UX_NULL) + { + + /* Store the parent hub device address. */ + ed -> ux_ehci_ed_cap1 |= device -> ux_device_parent -> ux_device_address << UX_EHCI_QH_HUB_ADDR_LOC; + + /* And the port index onto which this device is attached. */ + ed -> ux_ehci_ed_cap1 |= device -> ux_device_port_location << UX_EHCI_QH_PORT_NUMBER_LOC; + } + break; + } + + /* Get the interval for the endpoint and match it to a EHCI list. + We match anything that is > 32ms to the 32ms interval layer. + The 32ms list is layer 5, 16ms list is 4 ... the 1ms list is depth 0. */ + interval = endpoint -> ux_endpoint_descriptor.bInterval; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + + /* Convert from ms to 2^i. */ + for (i = 0; i < 16; i ++) + { + if (interval <= (1u << i)) + break; + } + + /* Index 0 for 1ms (interval 4). */ + interval = 4 + i; + } + else +#endif + { + /* High-speed interval is 2^(interval - 1) * 1/2^3. */ + if (interval <= 4) + i = 0; + else + i = interval - 4; + + /* Index 0 for 1ms. */ + } + + /* Match > 32ms to 32ms list. */ + /* Poll depth deeper, interval smaller. */ + if (i > 5) + poll_depth = 0; + else + poll_depth = 5 - i; + + /* Keep interval < 1ms for micro-frame calculation. */ + /* Make it index steps to move. */ + if (interval > 0) + { + interval --; + interval &= 0x3; + } + interval = (1u << interval); /* 1 (1/8ms), 2, 4, 8 (1ms) */ + + /* We are now updating the periodic list. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Get the list index with the least traffic. */ + ed_list = _ux_hcd_ehci_least_traffic_list_get(hcd_ehci, microframe_load, microframe_ssplit_count); + + /* Now we need to scan the list of eds from the lowest load entry until we reach the + appropriate interval node. The depth index is the interval EHCI value and the + 1st entry is pointed by the ED list entry. */ + ed_anchor = _ux_hcd_ehci_poll_rate_entry_get(hcd_ehci, ed_list, poll_depth); + + /* Save anchor pointer for interrupt ED. */ + ed -> ux_ehci_ed_anchor = ed_anchor; + + /* Calculate packet size with num transactions. */ + max_packet_size *= num_transaction; + + /* Go through the transaction loads for start + index of micro-frame. */ + for (i = 0; i < interval; i ++) + { + + /* Skip if load too much. */ + if (microframe_load[i] + max_packet_size > UX_MAX_BYTES_PER_MICROFRAME_HS) + continue; + +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + + /* Skip Y6 since host must not use it. */ + if (i == 6) + continue; + + /* Skip if start split count over 16 split. */ + if (microframe_ssplit_count[i] >= 16) + continue; + } +#endif + + /* Use the load. */ + break; + } + + /* Sanity check, bandwidth checked before endpoint creation so there should + not be error but we check it any way. */ + if (i >= interval) + { + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + ed -> ux_ehci_ed_status = UX_UNUSED; + return(UX_NO_BANDWIDTH_AVAILABLE); + } + + /* Now start microframe index is calculated, build masks. */ + + /* It's interval is larger than 1ms, use any of micro-frame. */ + if (interval >= 8) + { + + /* Interrupt schedule. */ + ed -> ux_ehci_ed_cap1 |= (UX_EHCI_SMASK_0 << i); + +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + /* For split transfer, complete split should be scheduled. */ + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + + /* Interrupt IN/OUT: + must schedule a complete-split transaction in each of the two + microframes following the first microframe in which the + full/low speed transaction is budgeted. An additional + complete-split must also be scheduled in the third following + microframe unless the full/low speed transaction was budgeted + to start in Y6. */ + + if (i == 5) + { + + /* Budgeted in Y6, Follow two (C7, C0). */ + cmask = UX_EHCI_CMASK_INT_Y5; + csplit_count = 2; + } + else + { + + /* Follow three. */ + cmask = UX_EHCI_CMASK_INT_Y0 << i; + if (i > 3) + { + cmask |= cmask >> 8; + cmask &= UX_EHCI_CMASK_MASK; + } + csplit_count = 3; + } + + /* Reserve count for SSplit (Max 16). */ + ed_anchor -> ux_ehci_ed_microframe_ssplit_count[i] ++; + + /* Reserve packet bytes for microframe load. */ + if (endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) + { + + /* Reserve load for CSplit. */ + ed_anchor -> ux_ehci_ed_microframe_load[(i + 2)&7] += max_packet_size; + ed_anchor -> ux_ehci_ed_microframe_load[(i + 3)&7] += max_packet_size; + + /* Need additional CSplit. */ + if (csplit_count > 2) + ed_anchor -> ux_ehci_ed_microframe_load[(i + 4)&7] += max_packet_size; + } + else + { + + /* Reserve load for SSplit. */ + ed_anchor -> ux_ehci_ed_microframe_load[i] += max_packet_size; + } + + /* Update schedule masks. */ + ed -> ux_ehci_ed_cap1 |= cmask; + } + else +#endif + { + /* Update anchor micro-frame load. */ + ed_anchor -> ux_ehci_ed_microframe_load[i] += max_packet_size; + } + } + else + { + + /* It must be high speed high bandwidth one. */ + switch(interval) + { + case 1: + ed -> ux_ehci_ed_cap1 |= UX_EHCI_SMASK_INTERVAL_1; + break; + case 2: + ed -> ux_ehci_ed_cap1 |= UX_EHCI_SMASK_INTERVAL_2 << i; + break; + default: /* 4, interval 3, 1/2ms */ + ed -> ux_ehci_ed_cap1 |= UX_EHCI_SMASK_INTERVAL_3 << i; + break; + } + + /* Update anchor micro-frame loads. */ + for (; i < 8; i += interval) + ed_anchor -> ux_ehci_ed_microframe_load[i] += max_packet_size; + } + + /* We found the node entry of the ED pointer that will be the anchor for this interrupt + endpoint. Now we attach this endpoint to the anchor and rebuild the chain. */ + + /* Physical LP, with Typ QH, clear T. */ + lp.void_ptr = _ux_utility_physical_address(ed); + lp.value |= UX_EHCI_TYP_QH; + + /* Save previous LP: to anchor. */ + ed -> ux_ehci_ed_previous_ed = ed_anchor; + + /* Link the QH at next to anchor. */ + _ux_hcd_ehci_periodic_descriptor_link(ed_anchor, lp.void_ptr, ed, ed_anchor -> ux_ehci_ed_queue_head); + + /* Insert ED to interrupt scan list for fast done queue scan. */ + ed -> ux_ehci_ed_next_ed = hcd_ehci -> ux_hcd_ehci_interrupt_ed_list; + hcd_ehci -> ux_hcd_ehci_interrupt_ed_list = ed; + + /* Release the periodic list. */ + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Return successful completion. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_destroy.c b/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_destroy.c new file mode 100644 index 0000000..fbcc385 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_endpoint_destroy.c @@ -0,0 +1,195 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_interrupt_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy an interrupt endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_door_bell_wait Setup doorbell wait */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_mutex_on Get mutex */ +/* _ux_utility_mutex_off Put mutex */ +/* _ux_hcd_ehci_periodic_descriptor_link Link/unlink descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_interrupt_endpoint_destroy(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint) +{ + +UX_EHCI_ED *ed; +UX_EHCI_ED *prev_ed; +ULONG frindex; +ULONG max_packet_size; + + + /* From the endpoint container fetch the EHCI ED descriptor. */ + ed = (UX_EHCI_ED *) endpoint -> ux_endpoint_ed; + + /* Access to periodic list. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Unlink the periodic item. */ + _ux_hcd_ehci_periodic_descriptor_link(ed -> ux_ehci_ed_previous_ed, + UX_NULL, UX_NULL, ed -> ux_ehci_ed_queue_head); + + /* Update the interrupt ED scan list. */ + prev_ed = hcd_ehci -> ux_hcd_ehci_interrupt_ed_list; + + /* Check if ED in head of scan list. */ + if (prev_ed == ed) + + /* Point head to the next ED. */ + hcd_ehci -> ux_hcd_ehci_interrupt_ed_list = ed -> ux_ehci_ed_next_ed; + else + { + + /* Try to find previous ED in scan list. */ + /* It's at head now. */ + while(prev_ed -> ux_ehci_ed_next_ed) + { + + /* The expected ED is found. */ + if (prev_ed -> ux_ehci_ed_next_ed == ed) + { + + /* Point next to next of the ED. */ + prev_ed -> ux_ehci_ed_next_ed = ed -> ux_ehci_ed_next_ed; + break; + } + + /* Try next ED. */ + prev_ed = prev_ed -> ux_ehci_ed_next_ed; + } + } + + /* Update micro-frame loads of anchor. */ + + /* Calculate max packet size. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + else +#endif + { + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK; + max_packet_size >>= UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT; + if (max_packet_size < 3) + max_packet_size ++; + max_packet_size *= endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + } + + /* Update according to ED S-Mask. */ + for(frindex = 0; frindex < 8; frindex ++) + { + + /* Check schedule mask. */ + if ((ed -> ux_ehci_ed_cap1 & (UX_EHCI_SMASK_0 << frindex))) + { + +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + + /* Start split check. */ + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + + /* Decrement the start split count. */ + ed -> ux_ehci_ed_anchor -> ux_ehci_ed_microframe_ssplit_count[frindex] --; + + /* Check next endpoint if it's IN (load in C-Mask). */ + if (endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) + continue; + } +#endif + /* Decrement the microframe load. */ + ed -> ux_ehci_ed_anchor -> ux_ehci_ed_microframe_load[frindex] -= max_packet_size; + + /* S-Mask found, no C-Mask at the same time, skip C-Mask check. */ + continue; + } + +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + + /* Complete split interrupt IN check in C-Mask. */ + if ((endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) && + (endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) && + (ed -> ux_ehci_ed_cap1 & (UX_EHCI_CMASK_0 << frindex))) + { + + /* Decrement the microframe load. */ + ed -> ux_ehci_ed_anchor -> ux_ehci_ed_microframe_load[frindex] -= max_packet_size; + } +#endif + } + + /* Release periodic list. */ + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Arm the doorbell and wait for its completion. */ + _ux_hcd_ehci_door_bell_wait(hcd_ehci); + + /* Now we can safely make the ED free. */ + ed -> ux_ehci_ed_status = UX_UNUSED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_handler.c b/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_handler.c new file mode 100644 index 0000000..e304a7b --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_interrupt_handler.c @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_interrupt_handler PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the interrupt handler for the EHCI interrupts. */ +/* Normally an interrupt occurs from the controller when there is */ +/* either a EOF signal and there has been transfers within the frame */ +/* or when there is a change on one of the downstream ports, a */ +/* doorbell signal or an unrecoverable error. */ +/* */ +/* All we need to do in the ISR is scan the controllers to find out */ +/* which one has issued a IRQ. If there is work to do for this */ +/* controller we need to wake up the corresponding thread to take care */ +/* of the job. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_controller_disable Disable controller */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_interrupt_handler(VOID) +{ + +UINT hcd_index; +UX_HCD *hcd; +UX_HCD_EHCI *hcd_ehci; +ULONG ehci_register; +ULONG ehci_register_port_status; +ULONG root_hub_thread_wakeup = 0; +ULONG port_index; + + + /* We need to parse the controller driver table to find all controllers that registered + as EHCI. */ + for (hcd_index = 0; hcd_index < _ux_system_host -> ux_system_host_registered_hcd; hcd_index++) + { + + /* Check type of controller. */ + if (_ux_system_host -> ux_system_host_hcd_array[hcd_index].ux_hcd_controller_type == UX_EHCI_CONTROLLER) + { + + /* Get the pointers to the generic HCD and EHCI specific areas. */ + hcd = &_ux_system_host -> ux_system_host_hcd_array[hcd_index]; + hcd_ehci = (UX_HCD_EHCI *) hcd -> ux_hcd_controller_hardware; + + /* Check if the controller is operational, if not, skip it. */ + if (hcd -> ux_hcd_status == UX_HCD_STATUS_OPERATIONAL) + { + + /* For debugging purposes, increment the interrupt count. */ + hcd_ehci -> ux_hcd_ehci_interrupt_count++; + + /* We get the current interrupt status for this controller. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_STATUS); + + /* We acknowledge the interrupts for this controller so that it + can continue to work. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_STATUS, ehci_register); + + /* Examine the source of interrupts. */ + if ((ehci_register & EHCI_HC_STS_USB_INT) || (ehci_register & EHCI_HC_STS_USB_ERR_INT)) + { + + /* We have some transactions done in the past frame/micro-frame. + The controller thread needs to wake up and process them. */ + hcd -> ux_hcd_thread_signal++; + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_hcd_semaphore); + } + + if (ehci_register & EHCI_HC_STS_HSE) + { + + /* The controller has issued a Host System Error which is fatal. + The controller will be reset now, and we wake up the HCD thread. */ + _ux_hcd_ehci_controller_disable(hcd_ehci); + hcd -> ux_hcd_thread_signal++; + hcd -> ux_hcd_status = UX_HCD_STATUS_DEAD; + hcd -> ux_hcd_thread_signal++; + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_hcd_semaphore); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_INTERRUPT, UX_SYSTEM_CONTEXT_HCD, UX_CONTROLLER_DEAD); + + } + + if (ehci_register & EHCI_HC_STS_PCD) + { + + /* The controller has issued a Root hub status change signal. Scan all ports. */ + for (port_index = 0; port_index < hcd_ehci -> ux_hcd_ehci_nb_root_hubs; port_index++) + { + + /* Read the port status. */ + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + + /* Check for Connect Status Change signal. */ + if (ehci_register_port_status & EHCI_HC_PS_CSC) + { + /* Something happened on this port. Signal it to the root hub thread. */ + hcd -> ux_hcd_root_hub_signal[port_index]++; + + /* Memorize wake up signal. */ + root_hub_thread_wakeup ++; + + } + + } + + /* We only wake up the root hub thread if there has been device insertion/extraction. */ + if (root_hub_thread_wakeup != 0) + + /* The controller has issued a Root hub status change signal. + We need to resume the thread in charge of the USB topology. */ + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_enum_semaphore); + } + + if (ehci_register & EHCI_HC_STS_IAA) + { + + /* The controller has issued a Door Bell status change signal. + We need to resume the thread who raised the doorbell. */ + _ux_utility_semaphore_put(&hcd_ehci -> ux_hcd_ehci_doorbell_semaphore); + } + } + } + } +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_create.c b/common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_create.c new file mode 100644 index 0000000..0b1ae42 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_create.c @@ -0,0 +1,546 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_isochronous_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an isochronous endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_allocate Allocate memory */ +/* _ux_utility_memory_free Free memory */ +/* _ux_hcd_ehci_hsisochronous_td_obtain Obtain a TD */ +/* _ux_hcd_ehci_least_traffic_list_get Get least traffic list */ +/* _ux_hcd_ehci_poll_rate_entry_get Get anchor for poll rate */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_mutex_on Get mutex */ +/* _ux_utility_mutex_off Put mutex */ +/* _ux_hcd_ehci_periodic_descriptor_link Link/unlink descriptor */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_isochronous_endpoint_create(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint) +{ +#if UX_MAX_ISO_TD == 0 + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Not supported, return error. */ + return(UX_FUNCTION_NOT_SUPPORTED); +#else + +UX_DEVICE *device; +UX_EHCI_HSISO_ED *ed; +UX_EHCI_PERIODIC_LINK_POINTER itd; +UX_EHCI_ED *ed_list; +UX_EHCI_ED *ed_anchor; +UX_EHCI_PERIODIC_LINK_POINTER lp; +UX_EHCI_POINTER bp; +UCHAR interval; +UCHAR interval_shift; +UINT poll_depth; +ULONG microframe_load[8]; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) +ULONG microframe_ssplit_count[8]; +ULONG mask; +UINT split_count; +ULONG split_last_size; +#else +#define microframe_ssplit_count UX_NULL +#endif +ULONG microframe_i; +ULONG endpt; +ULONG device_address; +ULONG max_packet_size; +ULONG max_trans_size; +ULONG mult; +ULONG io; +UINT i; +UINT status; + + + /* Get the pointer to the device. */ + device = endpoint -> ux_endpoint_device; + + /* Get the interval value from endpoint descriptor. */ + interval = endpoint -> ux_endpoint_descriptor.bInterval; + + /* For ISO, interval 1 ~ 16, means 2^(n-1). */ + if (interval == 0) + interval = 1; + if (interval > 16) + interval = 16; + + /* Interval shift is base 0. */ + interval_shift = interval - 1; + + /* Keep interval as number of micro-frames. */ + interval = (1u << interval_shift); + + /* Get max packet size. */ + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + + /* Get number transactions per micro-frame. */ + mult = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK; + mult >>= UX_MAX_NUMBER_OF_TRANSACTIONS_SHIFT; + if (mult < 3) + mult ++; + + /* Get max transfer size. */ + max_trans_size = max_packet_size * mult; + + /* Get the Endpt, Device Address, I/O, Maximum Packet Size, Mult. */ + endpt = (endpoint -> ux_endpoint_descriptor.bEndpointAddress << UX_EHCI_HSISO_ENDPT_SHIFT) & UX_EHCI_HSISO_ENDPT_MASK; + device_address = device -> ux_device_address & UX_EHCI_HSISO_DEVICE_ADDRESS_MASK; + io = (endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) ? UX_EHCI_HSISO_DIRECTION_IN : UX_EHCI_HSISO_DIRECTION_OUT; + + /* Only high speed transfer supported without split transfer. */ + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { +#if !defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + return(UX_FUNCTION_NOT_SUPPORTED); +#else + + /* 1 ~ N siTDs ... */ + /* OUT: only start-splits, no complete splits. */ + /* IN : at most one start-split and one to N complete-splits. */ + + /* TBD. */ +#endif + } + else + { + + /* Allocate memory for ED. */ + ed = (UX_EHCI_HSISO_ED *)_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_EHCI_HSISO_ED)); + if (ed == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Obtain iTDs for this new endpoint. + ** For noncontinuous request address and simplify calculation, allocate + ** one iTD for two microframes. + ** - interval 1 (0): 8 micro-frames, 4 iTD + ** - interval 2 (1): 4 micro-frames, 2 iTD + ** - interval 4 (2): 2 micro-frames, 1 iTD + ** - interval >=8 (3): 1 micro-frame , 1 iTD + ** Two micro-frames in iTD uses BP[3,4] and BP[5,6] to avoid merging of + ** page buffer and iTD settings. + */ + + /* Get number of iTDs should be allocated. */ + if (interval > 2) + ed -> ux_ehci_hsiso_ed_nb_tds = 1; + else + ed -> ux_ehci_hsiso_ed_nb_tds = 4 >> interval_shift; + + /* Obtain iTDs. */ + status = UX_SUCCESS; + for (i = 0; i < ed -> ux_ehci_hsiso_ed_nb_tds; i ++) + { + + /* Get a new free iTD. */ + itd.itd_ptr = _ux_hcd_ehci_hsisochronous_td_obtain(hcd_ehci); + if (itd.itd_ptr == UX_NULL) + { + status = UX_NO_TD_AVAILABLE; + break; + } + + /* Link to ED. */ + itd.itd_ptr -> ux_ehci_hsiso_td_ed = ed; + + /* Save max transfer size. */ + itd.itd_ptr -> ux_ehci_hsiso_td_max_trans_size = max_trans_size; + + /* Save the iTD for the micro-frame(s). */ + ed -> ux_ehci_hsiso_ed_fr_td[i] = itd.itd_ptr; + } + + /* If there is error, free allocated resources. */ + if (status != UX_SUCCESS) + { + for (i = 0; i < ed -> ux_ehci_hsiso_ed_nb_tds; i ++) + ed -> ux_ehci_hsiso_ed_fr_td[i] -> ux_ehci_hsiso_td_status = UX_UNUSED; + _ux_utility_memory_free(ed); + } + + /* Save information not related to periodic things. */ + + /* Save endpoint. */ + ed -> ux_ehci_hsiso_ed_endpoint = endpoint; + + /* Save interval. */ + ed -> ux_ehci_hsiso_ed_frinterval = interval; + ed -> ux_ehci_hsiso_ed_frinterval_shift = interval_shift; + + /* Disable iTDs for now. */ + ed -> ux_ehci_hsiso_ed_frstart = 0xFF; + } + + /* Attach the first iTD as the endpoint container. */ + endpoint -> ux_endpoint_ed = ed -> ux_ehci_hsiso_ed_fr_td[0]; + + /* Match the interval for the endpoint to a EHCI list. + We match anything that is > 32ms to the 32ms interval layer. + The 32ms list is layer 0, 16ms list is 1 ... the 1ms list is depth 5. */ + + /* Match > 32ms to 32ms list. */ + /* Poll depth deeper, interval smaller. */ + if (interval < 4) + poll_depth = 5; + else if (interval > 8) + poll_depth = 0; + else + poll_depth = 8 - interval; + + /* Keep only interval < 1ms for micro-frame calculation. */ + interval_shift &= 0x3; + interval &= 0x7; + + /* Fill the iTDs/siTDs contents that are not related to periodic list. + Initialize the fields to be ready for ZLPs if OUT. + But a zero buffer to underrun IN? */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + + /* TBD. */ + } + else +#endif + { + + /* Prepare things not related to periodic things. */ + + for (i = 0; i < ed -> ux_ehci_hsiso_ed_nb_tds; i ++) + { + + /* Get iTD. */ + itd.itd_ptr = ed -> ux_ehci_hsiso_ed_fr_td[i]; + + /* Build next link pointer, if not last one.*/ + if (i < ed -> ux_ehci_hsiso_ed_nb_tds - 1) + { + lp.void_ptr = _ux_utility_physical_address(ed -> ux_ehci_hsiso_ed_fr_td[i + 1]); + itd.itd_ptr -> ux_ehci_hsiso_td_next_lp = lp; + } + + /* Build previous pointer, if not first one. */ + if (i > 0) + { + itd.itd_ptr -> ux_ehci_hsiso_td_previous_lp.itd_ptr = + ed -> ux_ehci_hsiso_ed_fr_td[i - 1]; + } + + /* Save Device Address and Endpt @ BP0. */ + bp.value = device_address | endpt; + itd.itd_ptr -> ux_ehci_hsiso_td_bp[0] = bp.void_ptr; + + /* Save I/O and max packet size @ BP1. */ + bp.value = io | max_packet_size; + itd.itd_ptr -> ux_ehci_hsiso_td_bp[1] = bp.void_ptr; + + /* Save Mult @ BP2. */ + bp.value = mult; + itd.itd_ptr -> ux_ehci_hsiso_td_bp[2] = bp.void_ptr; + } + + } + + /* Lock the periodic list to update. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Get the list index with the least traffic. */ + ed_list = _ux_hcd_ehci_least_traffic_list_get(hcd_ehci, microframe_load, microframe_ssplit_count); + + /* Now we need to scan the list of EDs from the lowest load entry until we reach the + appropriate interval node. The depth index is the interval EHCI value and the + 1st entry is pointed by the ED list entry. */ + ed_anchor = _ux_hcd_ehci_poll_rate_entry_get(hcd_ehci, ed_list, poll_depth); + + /* Calculate packet size with num transactions. */ + max_packet_size *= mult; + + /* Go through the transaction loads for for start + index of micro-frame. */ + for (microframe_i = 0; microframe_i < interval; microframe_i ++) + { + + /* Skip if load too much. */ + if (microframe_load[microframe_i] + max_packet_size > UX_MAX_BYTES_PER_MICROFRAME_HS) + continue; + +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + + /* Skip Y6 since host must not use it. */ + if (i == 6) + continue; + + /* Skip if start split count over 16 split. */ + if (microframe_ssplit_count[i] >= 16) + continue; + } +#endif + + /* Use the load. */ + break; + } + + /* Sanity check, bandwidth checked before endpoint creation so there should + not be error but we check it any way. */ + if (microframe_i >= interval) + { + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + for (i = 0; i < ed -> ux_ehci_hsiso_ed_nb_tds; i ++) + ed -> ux_ehci_hsiso_ed_fr_td[i] -> ux_ehci_hsiso_td_status = UX_UNUSED; + _ux_utility_memory_free(ed); + return(UX_NO_BANDWIDTH_AVAILABLE); + } + + /* Now start microframe index is calculated, things related periodic list. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + /* OUT: each microframe budgeted, 188 (or the remaining data size) data byte. + never complete-split. + IN : complete-split must be scheduled for each following microframe. + L - the last microframe in which a complete-split is scheduled. + L < Y6, schedule additional complete-splits in microframe L+1 and L+2. + L == Y6, schedule one complete-split in microframe Y7, + schedule one complete-split in microframe Y0 of the next frame, + unless the full speed transaction was budgeted to start in microframe Y0. + L == Y7, schedule one complete-split in microframe Y0 of the next frame, + unless the full speed transaction was budgeted to start in microframe Y0. + */ + + /* Save anchor pointer. */ + itd.sitd_ptr -> ux_ehci_fsiso_td_anchor = ed_anchor; + + /* No back pointer by default. */ + lp.value = UX_EHCI_T; + + /* OUT or IN? */ + if (io == 0) + { + + /* Multiple start split based on max packet size, no complete split. */ + split_count = (max_packet_size + 187) / 188; + split_last_size = max_packet_size % 188; + + mask = (UX_EHCI_SMASK_0 << split_count) - UX_EHCI_SMASK_0; + mask <<= microframe_i; + if (microframe_i + split_count > 8) + { + mask |= mask >> 8; + mask &= UX_EHCI_SMASK_MASK; + + /* Need back pointer. */ + lp = itd; + lp.void_ptr = _ux_utility_physical_address(lp.void_ptr); + } + + /* Save settings. */ + itd.sitd_ptr -> ux_ehci_fsiso_td_cap1 = mask; + itd.sitd_ptr -> ux_ehci_fsiso_td_back_pointer = lp.void_ptr; + + /* Update anchor micro-frame loads and start splits. */ + for (i = 0; i < 8; i ++) + { + if ((mask & (UX_EHCI_SMASK_0 << i)) == 0) + continue; + + /* Add to load. */ + if (split_last_size && + i == ((microframe_i + split_count - 1) & 7)) + { + ed_anchor -> ux_ehci_ed_microframe_load[i] += split_last_size; + } + else + ed_anchor -> ux_ehci_ed_microframe_load[i] += 188; + + /* Increment SSplit count. */ + ed_anchor -> ux_ehci_ed_microframe_ssplit_count[i] ++; + } + } + else + { + + /* Single start split. */ + itd.sitd_ptr -> ux_ehci_fsiso_td_cap1 = UX_EHCI_SMASK_0 << microframe_i; + + /* Multiple complete split, start +2, based on max packet size. */ + split_count = (max_packet_size + 187) / 188; + + /* Adding extra 2 at end. */ + split_count += 2; + mask = (UX_EHCI_CMASK_0 << split_count) - UX_EHCI_CMASK_0; + mask <<= microframe_i + 2; + if (microframe_i + 2 + split_count > 8) + { + mask |= mask >> 8; + mask &= UX_EHCI_CMASK_MASK; + + /* Need back pointer. */ + lp = itd; + lp.void_ptr = _ux_utility_physical_address(lp.void_ptr); + } + + /* If Y0 has budget, clear complete mask of it. */ + if (microframe_i == 7) + { + if (mask & UX_EHCI_CMASK_0) + { + mask &= ~UX_EHCI_CMASK_0; + split_count --; + } + } + + /* Save settings. */ + itd.sitd_ptr -> ux_ehci_fsiso_td_cap1 |= mask; + itd.sitd_ptr -> ux_ehci_fsiso_td_back_pointer = lp.void_ptr; + + /* Update anchor micro-frame loads and complete splits. */ + for (i = 0; i < 8; i ++) + { + if ((mask & (UX_EHCI_CMASK_0 << i)) == 0) + continue; + + /* Add to load. */ + ed_anchor -> ux_ehci_ed_microframe_load[i] += 188; + } + + /* Increment SSplit count. */ + ed_anchor -> ux_ehci_ed_microframe_ssplit_count[i] ++; + } + + } + else +#endif + { + + /* Save index base of allocated micro-frame. */ + ed -> ux_ehci_hsiso_ed_frindex = microframe_i; + + /* Save anchor pointer. */ + ed -> ux_ehci_hsiso_ed_anchor = ed_anchor; + + /* Update micro-frames. */ + for (i = microframe_i; i < 8; i += interval) + { + + /* Update anchor micro-frame loads. */ + ed_anchor -> ux_ehci_ed_microframe_load[i] += max_packet_size; + + /* Initialize control with PG -> BP (3, 5). */ + itd.itd_ptr = ed -> ux_ehci_hsiso_ed_fr_td[i >> 1]; + + /* Buffer in page 3,4 or 5,6 to avoid merging settings. */ + if (i & 1u) + itd.itd_ptr -> ux_ehci_hsiso_td_control[i] = UX_EHCI_HSISO_IOC | + (5 << UX_EHCI_HSISO_PG_SHIFT); + else + itd.itd_ptr -> ux_ehci_hsiso_td_control[i] = UX_EHCI_HSISO_IOC | + (3 << UX_EHCI_HSISO_PG_SHIFT); + } + } + + /* Link iTDs to periodic list. */ + + /* Physical LP for anchor (Typ iTD, 0). */ + lp.void_ptr = _ux_utility_physical_address(ed -> ux_ehci_hsiso_ed_fr_td[0]); + + /* Link to periodic list. */ + ed -> ux_ehci_hsiso_ed_fr_td[0] -> ux_ehci_hsiso_td_previous_lp.ed_ptr = ed_anchor; + _ux_hcd_ehci_periodic_descriptor_link(ed_anchor, lp.void_ptr, + ed -> ux_ehci_hsiso_ed_fr_td[ed -> ux_ehci_hsiso_ed_nb_tds - 1], + ed_anchor -> ux_ehci_ed_queue_head); + + /* Simply insert all iTD[0]/siTD[0] to head of scan list. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + itd.sitd_ptr -> ux_ehci_fsiso_td_next_scan_td = hcd_ehci -> ux_hcd_ehci_fsiso_scan_list; + hcd_ehci -> ux_hcd_ehci_fsiso_scan_list = itd.sitd_ptr; + } + else +#endif + { + itd.itd_ptr = ed -> ux_ehci_hsiso_ed_fr_td[0]; + itd.itd_ptr -> ux_ehci_hsiso_td_next_scan_td = + hcd_ehci -> ux_hcd_ehci_hsiso_scan_list; + hcd_ehci -> ux_hcd_ehci_hsiso_scan_list = itd.itd_ptr; + if (itd.itd_ptr -> ux_ehci_hsiso_td_next_scan_td) + itd.itd_ptr -> ux_ehci_hsiso_td_next_scan_td -> ux_ehci_hsiso_td_previous_scan_td = + itd.itd_ptr; + } + + /* Release the periodic table. */ + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Return successful completion. */ + return(UX_SUCCESS); +#endif +} diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_destroy.c b/common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_destroy.c new file mode 100644 index 0000000..819f522 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_isochronous_endpoint_destroy.c @@ -0,0 +1,275 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_isochronous_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy an isochronous endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* _ux_utility_mutex_on Get mutex */ +/* _ux_utility_mutex_off Put mutex */ +/* _ux_hcd_ehci_periodic_descriptor_link Link/unlink descriptor */ +/* _ux_utility_memory_free Free memory */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_isochronous_endpoint_destroy(UX_HCD_EHCI *hcd_ehci, UX_ENDPOINT *endpoint) +{ +#if UX_MAX_ISO_TD == 0 + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Not supported, return error. */ + return(UX_FUNCTION_NOT_SUPPORTED); +#else + +UX_EHCI_HSISO_ED *ed; +UX_EHCI_PERIODIC_LINK_POINTER ed_td; +UX_EHCI_PERIODIC_LINK_POINTER lp; +ULONG max_packet_size; +UINT frindex; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) +ULONG split_count; +ULONG last_frindex; +ULONG last_size; +#endif + + + /* Get ED iTD/siTD. */ + ed_td.void_ptr = endpoint -> ux_endpoint_ed; + + /* Access to periodic list. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Check active iTD/siTD and unlink it. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + /* TBD. */ + } + else +#endif + { + + /* Get ED. */ + ed = ed_td.itd_ptr -> ux_ehci_hsiso_td_ed; + + /* Unlink from periodic list. */ + if (ed_td.itd_ptr -> ux_ehci_hsiso_td_previous_lp.void_ptr != UX_NULL) + { + + /* Get next LP of last iTD. */ + lp = ed -> ux_ehci_hsiso_ed_fr_td[ed -> ux_ehci_hsiso_ed_nb_tds - 1] + -> ux_ehci_hsiso_td_next_lp; + _ux_hcd_ehci_periodic_descriptor_link( + ed_td.itd_ptr -> ux_ehci_hsiso_td_previous_lp.void_ptr, + UX_NULL, UX_NULL, + lp.void_ptr); + } + } + + /* Unlink iTD/siTD from the scan list. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + + /* Get list head. */ + lp.sitd_ptr = hcd_ehci -> ux_hcd_ehci_fsiso_scan_list; + + /* Check if unlink from head. */ + if (lp.sitd_ptr == ed_td.sitd_ptr) + + /* Unlink from list head. */ + hcd_ehci -> ux_hcd_ehci_fsiso_scan_list = ed_td.sitd_ptr -> ux_ehci_fsiso_td_next_scan_td; + else + { + + /* Scan items in list. */ + while(lp.sitd_ptr) + { + + /* Not the previous item, just try next. */ + if (lp.sitd_ptr -> ux_ehci_fsiso_td_next_scan_td != ed_td.sitd_ptr) + { + lp.sitd_ptr = lp.sitd_ptr -> ux_ehci_fsiso_td_next_scan_td; + continue; + } + + /* Found it, unlink and break. */ + lp.sitd_ptr -> ux_ehci_fsiso_td_next_scan_td = ed_td.sitd_ptr -> ux_ehci_fsiso_td_next_scan_td; + break; + } + } + } + else +#endif + { + + /* Get head. */ + lp.itd_ptr = hcd_ehci -> ux_hcd_ehci_hsiso_scan_list; + + /* Check if iTD is in head. */ + if (lp.itd_ptr == ed_td.itd_ptr) + + /* Unlink from list head. */ + hcd_ehci -> ux_hcd_ehci_hsiso_scan_list = lp.itd_ptr -> ux_ehci_hsiso_td_next_scan_td; + else + { + + /* Not in head, there must be previous. */ + + /* Link it's previous to next. */ + lp.itd_ptr -> ux_ehci_hsiso_td_previous_scan_td -> ux_ehci_hsiso_td_next_scan_td = + lp.itd_ptr -> ux_ehci_hsiso_td_next_scan_td; + + /* Link it's next to previous. */ + if (lp.itd_ptr -> ux_ehci_hsiso_td_next_scan_td) + lp.itd_ptr -> ux_ehci_hsiso_td_next_scan_td -> ux_ehci_hsiso_td_previous_scan_td = + lp.itd_ptr -> ux_ehci_hsiso_td_previous_scan_td; + } + } + + /* Update micro-frame loads of anchor. */ + + /* Calculate max packet size. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_PACKET_SIZE_MASK; + + /* Update according to siTD S-Mask. */ + max_packet_size = endpoint -> ux_endpoint_descriptor.wMaxPacketSize & UX_MAX_NUMBER_OF_TRANSACTIONS_MASK; + + /* Update according to mask. */ + + for (frindex = 0; frindex < 8; frindex ++) + { + + /* Update start split related. */ + if (ed_td.sitd_ptr -> ux_ehci_fsiso_td_cap1 & (UX_EHCI_SMASK_0 << frindex)) + { + + /* Update start split count. */ + ed_td.sitd_ptr -> ux_ehci_fsiso_td_anchor -> ux_ehci_ed_microframe_ssplit_count[frindex] --; + + /* Update load for OUT. */ + if ((endpoint -> ux_endpoint_descriptor.bEndpointAddress & UX_ENDPOINT_DIRECTION) == 0) + { + split_count = (max_packet_size + 187) / 188; + last_frindex = ((ed_td.sitd_ptr -> ux_ehci_fsiso_td_frindex + split_count - 1) & 7); + last_size = max_packet_size % 188; + if (last_size == 0 && + frindex == last_frindex) + ed_td.sitd_ptr -> ux_ehci_fsiso_td_anchor -> ux_ehci_ed_microframe_load[frindex] -= last_size; + else + ed_td.sitd_ptr -> ux_ehci_fsiso_td_anchor -> ux_ehci_ed_microframe_load[frindex] -= 188; + } + + } + + /* Update complete split related (IN only). */ + if (ed_td.sitd_ptr -> ux_ehci_fsiso_td_cap1 & (UX_EHCI_CMASK_0 << frindex)) + ed_td.sitd_ptr -> ux_ehci_fsiso_td_anchor -> ux_ehci_ed_microframe_load[frindex] -= 188; + } + } + else +#endif + { + + /* Get max transfer size. */ + max_packet_size = ed_td.itd_ptr -> ux_ehci_hsiso_td_max_trans_size; + + /* Update according to mask. */ + for (frindex = ed -> ux_ehci_hsiso_ed_frindex; + frindex < 8; + frindex += ed -> ux_ehci_hsiso_ed_frinterval) + { + + /* Decrement the microframes scheduled. */ + ed -> ux_ehci_hsiso_ed_anchor -> ux_ehci_ed_microframe_load[frindex] -= max_packet_size; + } + } + + /* Release periodic list. */ + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Now we can safely make the iTD/siTDs free. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + ed_td.sitd_ptr -> ux_ehci_fsiso_td_status = UX_UNUSED; + } + else +#endif + { + for (frindex = 0; frindex < ed -> ux_ehci_hsiso_ed_nb_tds; frindex ++) + ed -> ux_ehci_hsiso_ed_fr_td[frindex] -> ux_ehci_hsiso_td_status = UX_UNUSED; + _ux_utility_memory_free(ed); + } + + return(UX_SUCCESS); +#endif +} diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_least_traffic_list_get.c b/common/usbx_host_controllers/src/ux_hcd_ehci_least_traffic_list_get.c new file mode 100644 index 0000000..5d4d06c --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_least_traffic_list_get.c @@ -0,0 +1,165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_least_traffic_list_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function return a pointer to the first ED in the periodic tree */ +/* that has the least traffic registered. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* microframe_load Pointer to an array for 8 */ +/* micro-frame loads */ +/* microframe_ssplit_count Pointer to an array for 8 */ +/* micro-frame start split count */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_ED * Pointer to ED */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_ED *_ux_hcd_ehci_least_traffic_list_get(UX_HCD_EHCI *hcd_ehci, + ULONG microframe_load[8], ULONG microframe_ssplit_count[8]) +{ + +UX_EHCI_ED *min_bandwidth_ed; +UX_EHCI_ED *ed; +UX_EHCI_PERIODIC_LINK_POINTER anchor; +UINT list_index; +UINT frindex; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) +UINT bfindex; +#endif +ULONG min_bandwidth_used; +ULONG bandwidth_used; + + +#if !defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + UX_PARAMETER_NOT_USED(microframe_ssplit_count); +#endif + + /* Set the min bandwidth used to a arbitrary maximum value. */ + min_bandwidth_used = 0xffffffff; + + /* The first ED is the list candidate for now. */ + min_bandwidth_ed = *hcd_ehci -> ux_hcd_ehci_frame_list; + + /* All list will be scanned. */ + for (list_index = 0; list_index < 32; list_index++) + { + + /* Reset the bandwidth for this list. */ + bandwidth_used = 0; + + /* Get the ED of the beginning of the list we parse now. */ + /* Obtain the ED address only. */ + /* Obtain the virtual address from the element. */ + anchor.ed_ptr = *(hcd_ehci -> ux_hcd_ehci_frame_list + list_index); + anchor.value &= UX_EHCI_LINK_ADDRESS_MASK; + anchor.void_ptr = _ux_utility_virtual_address(anchor.void_ptr); + + /* Summary micro-frames loads of anchors. */ + /* Reset microframe load table. */ + for (frindex = 0; frindex < 8; frindex ++) + { + microframe_load[frindex] = 0; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + microframe_ssplit_count[frindex] = 0; +#endif + } + + /* Scan static anchors in the list. */ + ed = anchor.ed_ptr; + while(ed -> ux_ehci_ed_next_anchor != UX_NULL) + { + for (frindex = 0; frindex < 8; frindex ++) + { + microframe_load[frindex] += ed -> ux_ehci_ed_microframe_load[frindex]; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + microframe_ssplit_count[frindex] += ed -> ux_ehci_ed_microframe_ssplit_count[frindex]; +#endif + } + + /* Next static anchor. */ + ed = ed -> ux_ehci_ed_next_anchor; + } + + /* Summarize bandwidth from micro-frames. */ + for (frindex = 0; frindex < 8; frindex ++) + bandwidth_used += microframe_load[frindex]; + + /* We have processed a list, check the bandwidth used by this list. If this bandwidth is + the minimum, we memorize the ED. */ + if (bandwidth_used < min_bandwidth_used) + { + + /* We have found a better list with a lower used bandwidth, + memorize the bandwidth for this list. */ + min_bandwidth_used = bandwidth_used; + + /* Memorize the ED for this list. */ + min_bandwidth_ed = anchor.ed_ptr; + + /* To optimize time, if bandwidth is 0, we just break the loop. */ + if (min_bandwidth_used == 0) + break; + } + } + + /* Return the ED list with the lowest bandwidth. */ + return(min_bandwidth_ed); +} diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_next_td_clean.c b/common/usbx_host_controllers/src/ux_hcd_ehci_next_td_clean.c new file mode 100644 index 0000000..a027df4 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_next_td_clean.c @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_next_td_clean PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cleans all the tds attached to a ED. The end of the */ +/* TD chain is pointed by the tail TD. */ +/* */ +/* INPUT */ +/* */ +/* TD Pointer to TD */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_next_td_clean(UX_EHCI_TD *td) +{ + + UX_PARAMETER_NOT_USED(td); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_periodic_descriptor_link.c b/common/usbx_host_controllers/src/ux_hcd_ehci_periodic_descriptor_link.c new file mode 100644 index 0000000..bbc7404 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_periodic_descriptor_link.c @@ -0,0 +1,167 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_periodic_descriptor_link PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function link/unlink the iTD/siTD/QH descriptor in periodic */ +/* lists for Host Controller (HC) to scan for transfers in the */ +/* (micro-)frames. */ +/* */ +/* If link_desc is not NULL: */ +/* prev_desc -> physical_next = link_lp */ +/* link_desc -> physical_next = next_desc */ +/* next_desc -> virtual_previous = link_desc */ +/* Resulting sequence prev - link_lp ... link_desc - next_desc. */ +/* */ +/* If link_desc is not NULL: */ +/* prev_desc -> physical_next = link_lp */ +/* next_desc -> virtual_previous = link_desc */ +/* Resulting unlink of things between prev_desc and next_desc. */ +/* */ +/* Note previous LP for linking item is not updated by this function. */ +/* */ +/* INPUT */ +/* */ +/* prev Link Pointer to previous item */ +/* (virtual memory address) */ +/* prev_next Physical link pointer data */ +/* including address, Typ and T. */ +/* If it's NULL the item between */ +/* previous and next is unlinked */ +/* next_prev Link Pointer to item to link */ +/* (virtual memory address) */ +/* next Link pointer to next item */ +/* (physical address, Typ & T) */ +/* */ +/* OUTPUT */ +/* */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +void _ux_hcd_ehci_periodic_descriptor_link( + VOID* prev, + VOID* prev_next, /* for prev -> next_lp. */ + VOID* next_prev, /* for next -> prev_lp (virtual). */ + VOID* next_desc) +{ + +UX_EHCI_PERIODIC_LINK_POINTER virtual_lp; +UX_EHCI_PERIODIC_LINK_POINTER prev_virtual_lp; +UX_EHCI_PERIODIC_LINK_POINTER link_virtual_lp; +UX_EHCI_PERIODIC_LINK_POINTER link_physical_lp; +UX_EHCI_PERIODIC_LINK_POINTER next_physical_lp; + + + /* Pointers. */ + prev_virtual_lp.void_ptr = prev; + link_physical_lp.void_ptr = prev_next; + link_virtual_lp.void_ptr = next_prev; + next_physical_lp.void_ptr = next_desc; + + /* Check link/unlink. */ + if (prev_next && next_prev) + { + + /* Link, fill next LP for linked item first. */ + link_virtual_lp.itd_ptr -> ux_ehci_hsiso_td_next_lp = next_physical_lp; + } + else + { + + /* Unlink, use info from previous and next item. */ + link_virtual_lp = prev_virtual_lp; + link_physical_lp = next_physical_lp; + } + + /* Update LP of previous item for HC to access. */ + prev_virtual_lp.itd_ptr -> ux_ehci_hsiso_td_next_lp = link_physical_lp; + + /* If next item is valid, update its previous LP. */ + if ((next_physical_lp.value & (UX_EHCI_LINK_ADDRESS_MASK | UX_EHCI_T)) != UX_EHCI_T) + { + + /* Get virtual address of next item. */ + virtual_lp.value = next_physical_lp.value & UX_EHCI_LINK_ADDRESS_MASK; + virtual_lp.void_ptr = _ux_utility_virtual_address(virtual_lp.void_ptr); + + /* Update previous LP, except static anchor. */ + switch(next_physical_lp.value & UX_EHCI_TYP_MASK) + { + case UX_EHCI_TYP_ITD: + virtual_lp.itd_ptr -> ux_ehci_hsiso_td_previous_lp = link_virtual_lp; + break; +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + case UX_EHCI_TYP_SITD: + virtual_lp.sitd_ptr -> ux_ehci_fsiso_td_previous_lp = link_virtual_lp; + break; +#endif + default: /* QH. */ + if ((virtual_lp.ed_ptr -> ux_ehci_ed_status & UX_EHCI_QH_STATIC) == 0) + virtual_lp.ed_ptr -> ux_ehci_ed_previous_ed = link_virtual_lp.ed_ptr; + break; + } + } +} + +/* +anchor, next + head, tail ==> anchor, head ... tail, next + head -> prev = anchor (Vir) + anchor -> next = head (Phy) + tail -> next = next (Phy) + next -> prev = tail (Vir) + +anchor, head ... tail, next ==> anchor, next + anchor -> next = next (Phy) + next -> prev = anchor (Vir) + +*/ diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_periodic_tree_create.c b/common/usbx_host_controllers/src/ux_hcd_ehci_periodic_tree_create.c new file mode 100644 index 0000000..c0d14b9 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_periodic_tree_create.c @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_periodic_tree_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the periodic static tree for the interrupt */ +/* and isochronous eds. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_ed_obtain Obtain an ED */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_periodic_tree_create(UX_HCD_EHCI *hcd_ehci) +{ + +UX_EHCI_ED *ed; +UX_EHCI_ED *previous_ed; +UINT list_index; +UINT list_entries; +UINT current_list_entry; +UX_EHCI_ED *ed_list[32]; +UX_EHCI_ED *ed_start_list[32]; +UX_EHCI_PERIODIC_LINK_POINTER lp; + + /* Start with the 1st list - it has 32 entries. */ + list_entries = 32; + + /* Create each list one by one starting from the 32ms list. */ + for (list_index = 0; list_index < 6; list_index++) + { + + for (current_list_entry = 0; current_list_entry < list_entries; current_list_entry++) + { + + /* In each list, insert an static QH as the anchor. There should not + be any errors when obtaining a new ED, still we do a sanity check. */ + ed = _ux_hcd_ehci_ed_obtain(hcd_ehci); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Mark the anchor as being a QH pointer and a terminator. */ + ed -> ux_ehci_ed_queue_head = (UX_EHCI_ED *) (UX_EHCI_QH_TYP_QH | UX_EHCI_QH_T); + + /* The Queue element has the terminator bit on. */ + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) UX_EHCI_TD_T; + + /* The Alternate TD has the terminator bit on. */ + ed -> ux_ehci_ed_alternate_td = (UX_EHCI_TD *) UX_EHCI_TD_T; + + /* This endpoint is an anchor. */ + ed -> ux_ehci_ed_status |= UX_EHCI_QH_STATIC; + + /* Either we hook this new ED to the start list for further processing + or we hook it to the 2 successive entries in the previous list. */ + if (list_index == 0) + { + + ed_start_list[current_list_entry] = ed; + } + else + { + + /* We need to update the previous ED with the link to this new ED. Since + this is a tree structure, this operation is done twice to the 2 previous + eds in the previous list. */ + lp.void_ptr = _ux_utility_physical_address(ed); + lp.value |= UX_EHCI_QH_TYP_QH; + previous_ed = ed_list[current_list_entry * 2]; + previous_ed -> ux_ehci_ed_queue_head = lp.ed_ptr; + previous_ed -> ux_ehci_ed_next_ed = ed; + previous_ed -> ux_ehci_ed_next_anchor = ed; + previous_ed = ed_list[(current_list_entry * 2) + 1]; + previous_ed -> ux_ehci_ed_queue_head = lp.ed_ptr; + previous_ed -> ux_ehci_ed_next_ed = ed; + previous_ed -> ux_ehci_ed_next_anchor = ed; + } + + /* Memorize this ED in the local list. We do this operation now, otherwise + we would erase the previous list eds. */ + ed_list[current_list_entry] = ed; + } + + /* Shift the number of entries in the next list by 1 (i.e. divide by 2). */ + list_entries = list_entries>>1; + } + + /* Check the value of the ehci frame list entries. If 0, it was not initialized by the controller init function. */ + if (hcd_ehci -> ux_hcd_ehci_frame_list_size == 0) + + /* Value not initialized. Use default. */ + hcd_ehci -> ux_hcd_ehci_frame_list_size = UX_EHCI_FRAME_LIST_ENTRIES; + + /* The tree has been completed but the entries in the EHCI frame list are in the wrong order. + We need to swap each entry according to the EHCI specified entry order list so that we + have a fair interval frequency for each periodic ED. The primary eds are fetched from the + start list, translated into physical addresses and stored into the frame List. */ + for (current_list_entry = 0; current_list_entry < 32; current_list_entry++) + { + + ed = ed_start_list[_ux_system_host_hcd_periodic_tree_entries[current_list_entry]]; + *(hcd_ehci -> ux_hcd_ehci_frame_list+current_list_entry) = (UX_EHCI_ED *) _ux_utility_physical_address(ed); + } + + /* We still haven't set the type of each queue head in the list itself. Do that now. */ + for (current_list_entry = 0; current_list_entry < 32; current_list_entry++) + { + + lp.ed_ptr = hcd_ehci -> ux_hcd_ehci_frame_list[current_list_entry]; + lp.value |= UX_EHCI_QH_TYP_QH; + hcd_ehci -> ux_hcd_ehci_frame_list[current_list_entry] = lp.ed_ptr; + } + + /* Now the first 32 entries in the frame list have to be duplicated to fill the other entries in the frame list. + If the list is set to 32 entries, nothing is done here. */ + for (current_list_entry = 32; current_list_entry < hcd_ehci -> ux_hcd_ehci_frame_list_size; current_list_entry++) + hcd_ehci -> ux_hcd_ehci_frame_list[current_list_entry] = hcd_ehci -> ux_hcd_ehci_frame_list[current_list_entry & 0x1f]; + + /* Now each traffic list from entry consumes 1/32 periodic bandwidth. + * The poll entry layers depth low to high: + * 32ms, 16ms, 8ms, 4ms, 2ms, 1ms -- micro-frames + */ + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_poll_rate_entry_get.c b/common/usbx_host_controllers/src/ux_hcd_ehci_poll_rate_entry_get.c new file mode 100644 index 0000000..f47fba2 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_poll_rate_entry_get.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_poll_rate_entry_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function return a pointer to the first ED in the periodic tree */ +/* that start specific poll rate. */ +/* Note that when poll rate is longer, poll depth is smaller and */ +/* endpoint period interval is larger. */ +/* PollInterval Depth */ +/* 1 M */ +/* 2 M-1 */ +/* 4 M-2 */ +/* 8 M-3 */ +/* ... ... */ +/* N 0 */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* ed_list Pointer to ED list to scan */ +/* poll_depth Poll depth expected */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_ED * Pointer to ED */ +/* */ +/* CALLS */ +/* */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_ED *_ux_hcd_ehci_poll_rate_entry_get(UX_HCD_EHCI *hcd_ehci, + UX_EHCI_ED *ed_list, ULONG poll_depth) +{ + + + UX_PARAMETER_NOT_USED(hcd_ehci); + + /* Scan the list of ED/iTD/siTDs from the poll rate lowest/interval longest + entry until appropriate poll rate node. + The depth index is the poll rate EHCI value and the first entry (anchor) + is pointed. */ + + /* Obtain next link pointer including Typ and T. */ + while(poll_depth --) + { + if (ed_list -> ux_ehci_ed_next_anchor == UX_NULL) + break; + ed_list = ed_list -> ux_ehci_ed_next_anchor; + } + + /* Return the list entry. */ + return(ed_list); +} diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_port_disable.c b/common/usbx_host_controllers/src/ux_hcd_ehci_port_disable.c new file mode 100644 index 0000000..e833f52 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_port_disable.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_port_disable PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will disable a specific port attached to the root */ +/* HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* port_index Port index to disable */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_port_disable(UX_HCD_EHCI *hcd_ehci, ULONG port_index) +{ + +ULONG ehci_register_port_status; + + + /* Check to see if this port is valid on this controller. */ + if (hcd_ehci -> ux_hcd_ehci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* Read the port status for this port. */ + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + + /* Disable the port (CPE field). */ + ehci_register_port_status &= ~EHCI_HC_PS_PE; + + /* Write the status back. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, ehci_register_port_status); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_port_reset.c b/common/usbx_host_controllers/src/ux_hcd_ehci_port_reset.c new file mode 100644 index 0000000..ae4ae87 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_port_reset.c @@ -0,0 +1,240 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/* EHCI HCD extention for host mode select. */ +#ifndef UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET + +#if defined(K66) + +#define UX_EHCI_USBPHY_CTRL_K66 0x400A2000 +#define UX_EHCI_USBPHY_CTRL_SET_BIT1 ((*(volatile ULONG *)(UX_EHCI_USBPHY_CTRL_K66 + 0x34)) = 0x02) +#define UX_EHCI_USBPHY_CTRL_CLEAR_BIT1 ((*(volatile ULONG *)(UX_EHCI_USBPHY_CTRL_K66 + 0x38)) = 0x02) + +#define UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, on_off) do \ +{ \ + if (on_off) \ + UX_EHCI_USBPHY_CTRL_SET_BIT1; \ + else \ + UX_EHCI_USBPHY_CTRL_CLEAR_BIT1; \ +} while(0) + +#elif defined(IMX6UL) || defined(MIMXRT) + +#if defined(IMX6UL) +#define UX_EHCI_USBPHY1 (0x020C9000) +#define UX_EHCI_USBPHY2 (0x020CA000) +#define UX_EHCI_BASE1 (0x02184100) +#define UX_EHCI_BASE2 (0x02184300) +#elif defined(MIMXRT) +#define UX_EHCI_USBPHY1 (0x400D9000u) +#define UX_EHCI_USBPHY2 (0x400DA000u) +#define UX_EHCI_BASE1 (0x402E0000u) +#define UX_EHCI_BASE2 (0x402E0200u) +#endif + +#define UX_EHCI_USBPHY_CTRL_SET_BIT1(base) ((*(volatile ULONG *) ( base + 0x34)) = 0x02) +#define UX_EHCI_USBPHY_CTRL_CLEAR_BIT1(base) ((*(volatile ULONG *) ( base + 0x38)) = 0x02) + +#define UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, on_off) do \ +{ \ + ULONG base; \ + if ((ULONG)hcd_ehci -> ux_hcd_ehci_base == UX_EHCI_BASE1) \ + base = (UX_EHCI_USBPHY1); \ + else \ + base = (UX_EHCI_USBPHY1); \ + if (on_off) \ + UX_EHCI_USBPHY_CTRL_SET_BIT1(base); \ + else \ + UX_EHCI_USBPHY_CTRL_CLEAR_BIT1(base); \ +} while(0) + +#else +#define UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, on_off) +#endif + +#endif /* ifndef UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET */ + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_port_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reset a specific port attached to the root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* port_index Port index to reset */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* _ux_utility_delay_ms Delay */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_port_reset(UX_HCD_EHCI *hcd_ehci, ULONG port_index) +{ + +ULONG ehci_register_port_status; +INT i; + + + /* Check to see if this port is valid on this controller. */ + if (hcd_ehci -> ux_hcd_ehci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* Ensure that the downstream port has a device attached. It is unnatural to perform + a port reset if there is no device. */ + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + + /* Check Device Connection Status. */ + if ((ehci_register_port_status & EHCI_HC_PS_CCS) == 0) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_NO_DEVICE_CONNECTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_DEVICE_CONNECTED, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_NO_DEVICE_CONNECTED); + } + + /* Check for the speed of the device. If low speed, we need to send the connection signal + to the companion chip. Unless we are on a controller with a built-in TT. */ + if ((ehci_register_port_status & EHCI_HC_PS_SPEED_MASK) != EHCI_HC_PS_SPEED_LOW || (hcd_ehci -> ux_hcd_ehci_embedded_tt == UX_TRUE)) + { + + for (i = 0; ; i ++) + { + + /* Before reset, phy does not know the speed. */ + UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, UX_FALSE); + + /* The device may be high speed or full speed, we try to reset the port for some time + and see if the port is enabled by the host controller. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, (ehci_register_port_status | EHCI_HC_PS_PR)); + + /* Wait until the port has been reset. */ + _ux_utility_delay_ms(EHCI_HC_RH_RESET_DELAY ); + + /* Now turn off the port reset. */ + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + if (ehci_register_port_status & EHCI_HC_PS_PR) + { + + ehci_register_port_status &= ~EHCI_HC_PS_PR; + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, ehci_register_port_status); + + /* According to the USB 2.0 spec, the controller may take 2ms to reset the port bit from 1 to 0 after + we write a 0. Wait until the port reset bit has been turned off completely. */ + _ux_utility_delay_ms(EHCI_HC_RH_RESET_SETTLE_DELAY); + } + + /* Now check port speed. */ + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + if (hcd_ehci -> ux_hcd_ehci_embedded_tt && + (ehci_register_port_status & EHCI_HC_PS_EMBEDDED_TT_SPEED_MASK) == EHCI_HC_PS_EMBEDDED_TT_SPEED_HIGH) + break; + + /* Seems we need to set the Port to a Suspend state before forcing the reset. Otherwise some devices fail the + HS detection handshake. */ + if (i == 0) + { + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, (ehci_register_port_status | EHCI_HC_PS_SUSPEND)); + _ux_utility_delay_ms(UX_HIGH_SPEED_DETECTION_HANDSHAKE_SUSPEND_WAIT); + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, (ehci_register_port_status & ~EHCI_HC_PS_SUSPEND)); + } + else + break; + } + + /* Now we can read the port enable bit. */ + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + if ((ehci_register_port_status & EHCI_HC_PS_PE) != 0) + { + + /* After reset, adjust phy speed. */ + UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, UX_TRUE); + return(UX_SUCCESS); + } + } + + /* We come here when the device is either low speed or full speed. In this case, we release + the ownership of the port to the companion chip. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, (ehci_register_port_status | EHCI_HC_PS_PO)); + + /* Delay. */ + _ux_utility_delay_ms(EHCI_HC_RH_RESET_DELAY); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_RESET_FAILED, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_RESET_FAILED); + + /* When the root HUB sees an error message, it will give up on this device and the companion chip root HUB + will pick up the insertion signal again and reawake the root HUB driver. */ + return(UX_PORT_RESET_FAILED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_port_resume.c b/common/usbx_host_controllers/src/ux_hcd_ehci_port_resume.c new file mode 100644 index 0000000..25e33e7 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_port_resume.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_port_resume PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will resume a specific port attached to the root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* port_index Port index to resume */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_port_resume(UX_HCD_EHCI *hcd_ehci, UINT port_index) +{ + + UX_PARAMETER_NOT_USED(hcd_ehci); + UX_PARAMETER_NOT_USED(port_index); + + /* Not supported, return to caller. */ + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_port_status_get.c b/common/usbx_host_controllers/src/ux_hcd_ehci_port_status_get.c new file mode 100644 index 0000000..1ecb182 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_port_status_get.c @@ -0,0 +1,252 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/* EHCI HCD extention for host mode select. */ +#ifndef UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET + +#if defined(K66) + +#define UX_EHCI_USBPHY_CTRL_K66 0x400A2000 +#define UX_EHCI_USBPHY_CTRL_SET_BIT1 ((*(volatile ULONG *)(UX_EHCI_USBPHY_CTRL_K66 + 0x34)) = 0x02) +#define UX_EHCI_USBPHY_CTRL_CLEAR_BIT1 ((*(volatile ULONG *)(UX_EHCI_USBPHY_CTRL_K66 + 0x38)) = 0x02) + +#define UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, on_off) do \ +{ \ + if (on_off) \ + UX_EHCI_USBPHY_CTRL_SET_BIT1; \ + else \ + UX_EHCI_USBPHY_CTRL_CLEAR_BIT1; \ +} while(0) + +#elif defined(IMX6UL) || defined(MIMXRT) + +#if defined(IMX6UL) +#define UX_EHCI_USBPHY1 (0x020C9000) +#define UX_EHCI_USBPHY2 (0x020CA000) +#define UX_EHCI_BASE1 (0x02184100) +#define UX_EHCI_BASE2 (0x02184300) +#elif defined(MIMXRT) +#define UX_EHCI_USBPHY1 (0x400D9000u) +#define UX_EHCI_USBPHY2 (0x400DA000u) +#define UX_EHCI_BASE1 (0x402E0000u) +#define UX_EHCI_BASE2 (0x402E0200u) +#endif + +#define UX_EHCI_USBPHY_CTRL_SET_BIT1(base) ((*(volatile ULONG *) ( base + 0x34)) = 0x02) +#define UX_EHCI_USBPHY_CTRL_CLEAR_BIT1(base) ((*(volatile ULONG *) ( base + 0x38)) = 0x02) + +#define UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, on_off) do \ +{ \ + ULONG base; \ + if ((ULONG)hcd_ehci -> ux_hcd_ehci_base == UX_EHCI_BASE1) \ + base = (UX_EHCI_USBPHY1); \ + else \ + base = (UX_EHCI_USBPHY1); \ + if (on_off) \ + UX_EHCI_USBPHY_CTRL_SET_BIT1(base); \ + else \ + UX_EHCI_USBPHY_CTRL_CLEAR_BIT1(base); \ +} while(0) + +#else +#define UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, on_off) +#endif + +#endif /* ifndef UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET */ + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_port_status_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the status for each port attached to the */ +/* root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* port_index Port index to get status for */ +/* */ +/* OUTPUT */ +/* */ +/* Port Status */ +/* */ +/* Status of the root hub port with the following format: */ +/* */ +/* bit 0 device connection status */ +/* if 0 : no device connected */ +/* if 1 : device connected to the port */ +/* bit 1 port enable status */ +/* if 0 : port disabled */ +/* if 1 : port enabled */ +/* bit 2 port suspend status */ +/* if 0 : port is not suspended */ +/* if 1 : port is suspended */ +/* bit 3 port overcurrent status */ +/* if 0 : port has no overcurrent condition */ +/* if 1 : port has overcurrent condition */ +/* bit 4 port reset status */ +/* if 0 : port is not in reset */ +/* if 1 : port is in reset */ +/* bit 5 port power status */ +/* if 0 : port power is off */ +/* if 1 : port power is on */ +/* bit 6-7 device attached speed */ +/* if 00 : low speed device attached */ +/* if 01 : full speed device attached */ +/* if 10 : high speed device attached */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_hcd_ehci_port_status_get(UX_HCD_EHCI *hcd_ehci, ULONG port_index) +{ + +ULONG ehci_register_port_status; +ULONG port_status; + + + /* Check to see if this port is valid on this controller. */ + if (hcd_ehci -> ux_hcd_ehci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* The port is valid, build the status mask for this port. This function + returns a controller agnostic bit field. */ + port_status = 0; + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + + /* Device Connection Status. */ + if (ehci_register_port_status & EHCI_HC_PS_CCS) + port_status |= UX_PS_CCS; + else + { + + /* When disconnected PHY does not know speed. */ + UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, UX_FALSE); + } + + /* Port Enable Status. */ + if (ehci_register_port_status & EHCI_HC_PS_PE) + port_status |= UX_PS_PES; + + /* Port Suspend Status. */ + if (ehci_register_port_status & EHCI_HC_PS_SUSPEND) + { + port_status |= UX_PS_PSS; + + /* When suspend put PHY in normal to avoid wrong disconnect status. */ + UX_HCD_EHCI_EXT_USBPHY_HIGHSPEED_MODE_SET(hcd_ehci, UX_FALSE); + } + + /* Port Overcurrent Status. */ + if (ehci_register_port_status & EHCI_HC_PS_OCC) + port_status |= UX_PS_POCI; + + /* Port Reset Status. */ + if (ehci_register_port_status & EHCI_HC_PS_PR) + port_status |= UX_PS_PRS; + + /* Port Power Status. */ + if (ehci_register_port_status & EHCI_HC_PS_PP) + port_status |= UX_PS_PPS; + + /* Port Device Attached speed. This field is valid only if the CCS bit is active. + Only EHCI high speed devices are meaningful in a regular EHCI controller. + In embedded EHCI with built-in TTs some bits reflect the true speed of + the device behind the TT. */ + if (ehci_register_port_status & EHCI_HC_PS_CCS) + { + /* Check for EHCI with embedded TT. */ + if (hcd_ehci -> ux_hcd_ehci_embedded_tt == UX_TRUE) + { + + /* Isolate speed from the non EHCI compliant POTSC bits. */ + switch (ehci_register_port_status & EHCI_HC_PS_EMBEDDED_TT_SPEED_MASK) + { + + case EHCI_HC_PS_EMBEDDED_TT_SPEED_FULL : + + /* Full speed. */ + port_status |= UX_PS_DS_FS; + break; + + case EHCI_HC_PS_EMBEDDED_TT_SPEED_LOW : + + /* Low speed. */ + port_status |= UX_PS_DS_LS; + break; + + case EHCI_HC_PS_EMBEDDED_TT_SPEED_HIGH : + + /* High speed. */ + port_status |= UX_PS_DS_HS; + break; + + } + } + else + + /* No embedded TT. Fall back to default HS. */ + port_status |= UX_PS_DS_HS; + } + + /* Return port status. */ + return(port_status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_port_suspend.c b/common/usbx_host_controllers/src/ux_hcd_ehci_port_suspend.c new file mode 100644 index 0000000..224c8ea --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_port_suspend.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_port_suspend PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will suspend a specific port attached to the root */ +/* HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* port_index Port index to suspend */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_port_suspend(UX_HCD_EHCI *hcd_ehci, ULONG port_index) +{ +ULONG ehci_register_port_status; + + /* Check to see if this port is valid on this controller. */ + if (hcd_ehci -> ux_hcd_ehci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* Ensure that the downstream port has a device attached. If not, ignore the request. */ + ehci_register_port_status = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + + /* Check Device Connection Status. */ + if ((ehci_register_port_status & EHCI_HC_PS_CCS) == 0) + + /* Nothing on the downstream port. */ + return(UX_NO_DEVICE_CONNECTED); + + /* Set Suspend. */ + ehci_register_port_status |= EHCI_HC_PS_SUSPEND; + + /* Update the port status. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, ehci_register_port_status); + + /* Not supported, return error. */ + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_power_down_port.c b/common/usbx_host_controllers/src/ux_hcd_ehci_power_down_port.c new file mode 100644 index 0000000..60e3b52 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_power_down_port.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_power_down_port PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will power down a specific port attached to the root */ +/* HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* port_index Port index to power down */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_power_down_port(UX_HCD_EHCI *hcd_ehci, ULONG port_index) +{ + + UX_PARAMETER_NOT_USED(hcd_ehci); + UX_PARAMETER_NOT_USED(port_index); + + /* Not supported, return error. */ + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_power_on_port.c b/common/usbx_host_controllers/src/ux_hcd_ehci_power_on_port.c new file mode 100644 index 0000000..2c3c70b --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_power_on_port.c @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_power_on_port PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will power a specific port attached to the root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* port_index Port index to power */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_power_on_port(UX_HCD_EHCI *hcd_ehci, ULONG port_index) +{ + + UX_PARAMETER_NOT_USED(hcd_ehci); + UX_PARAMETER_NOT_USED(port_index); + + /* Not supported, return error code. */ + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_power_root_hubs.c b/common/usbx_host_controllers/src/ux_hcd_ehci_power_root_hubs.c new file mode 100644 index 0000000..0499c7a --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_power_root_hubs.c @@ -0,0 +1,105 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_power_root_hubs PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function powers individually or in gang mode the root HUBs */ +/* attached to the EHCI controller. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_register_read Read EHCI register */ +/* _ux_hcd_ehci_register_write Write EHCI register */ +/* _ux_utility_delay_ms Delay */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_power_root_hubs(UX_HCD_EHCI *hcd_ehci) +{ + +ULONG ehci_register; +UINT port_index; + + + /* Read the control PPC field. If the PPC field is set, the controller has + implemented port power. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCCR_HCS_PARAMS); + + if (ehci_register & EHCI_HC_RH_PPC) + { + + /* We have power management in this controller. Apply power to each port. */ + for (port_index = 0; port_index < hcd_ehci -> ux_hcd_ehci_nb_root_hubs; port_index++) + { + + /* Read register first to preserve existing settings. */ + ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index); + + /* Apply power to a port. */ + _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_PORT_SC + port_index, ehci_register | EHCI_HC_PS_PP); + } + } + + /* The EHCI needs some time for the power to be stable. */ + _ux_utility_delay_ms(EHCI_HC_RH_POWER_STABLE_DELAY); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_register_read.c b/common/usbx_host_controllers/src/ux_hcd_ehci_register_read.c new file mode 100644 index 0000000..1a78a98 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_register_read.c @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_register_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a register from the EHCI memory mapped */ +/* registers. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* ehci_register EHCI register to read */ +/* */ +/* OUTPUT */ +/* */ +/* EHCI Register Value */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_hcd_ehci_register_read(UX_HCD_EHCI *hcd_ehci, ULONG ehci_register) +{ + + /* Return value of EHCI register. */ + return(*(hcd_ehci -> ux_hcd_ehci_base + ehci_register)); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_register_write.c b/common/usbx_host_controllers/src/ux_hcd_ehci_register_write.c new file mode 100644 index 0000000..9b6ccb4 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_register_write.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_register_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a register to the EHCI space. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* ehci_register EHCI register to write */ +/* value Value to write */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_register_write(UX_HCD_EHCI *hcd_ehci, ULONG ehci_register, ULONG value) +{ + + /* Write to the specified EHCI register. */ + *(hcd_ehci -> ux_hcd_ehci_base + ehci_register) = value; + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_regular_td_obtain.c b/common/usbx_host_controllers/src/ux_hcd_ehci_regular_td_obtain.c new file mode 100644 index 0000000..705f66b --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_regular_td_obtain.c @@ -0,0 +1,122 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_regular_td_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free TD from the regular TD list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* UX_EHCI_TD * Pointer to TD */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* _ux_utility_mutex_on Get protection mutex */ +/* _ux_utility_mutex_off Release protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_EHCI_TD *_ux_hcd_ehci_regular_td_obtain(UX_HCD_EHCI *hcd_ehci) +{ + +UX_EHCI_TD *td; +ULONG td_index; +ULONG td_element; + + + /* Get the mutex as this is a critical section. */ + _ux_utility_mutex_on(&_ux_system -> ux_system_mutex); + + /* Start the search from the beginning of the list. */ + td = hcd_ehci -> ux_hcd_ehci_td_list; + for (td_index = 0; td_index < _ux_system_host -> ux_system_host_max_td; td_index++) + { + + /* Check the TD status, a free TD is marked with the USED flag. */ + if (td -> ux_ehci_td_status == UX_UNUSED) + { + + /* The TD may have been used, so we reset all fields. */ + _ux_utility_memory_set(td, 0, sizeof(UX_EHCI_TD)); + + /* This TD is now marked as USED. */ + td -> ux_ehci_td_status = UX_USED; + + /* Initialize the link pointer and alternate TD fields. */ + td_element = UX_EHCI_TD_T; + td -> ux_ehci_td_link_pointer = (UX_EHCI_TD *) td_element; + td -> ux_ehci_td_alternate_link_pointer = (UX_EHCI_TD *) td_element; + + /* Release the protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Success, return TD pointer. */ + return(td); + } + + /* Look at next TD. */ + td++; + } + + /* There is no available TD in the TD list. */ + + /* Release protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Error, return a null. */ + return(UX_NULL); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_request_bulk_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ehci_request_bulk_transfer.c new file mode 100644 index 0000000..9e62567 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_request_bulk_transfer.c @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_request_bulk_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a bulk transfer request. A bulk transfer */ +/* can be larger than the size of the EHCI buffer so it may be */ +/* required to chain multiple tds to accommodate this request. A bulk */ +/* transfer is non blocking, so we return before the request is */ +/* completed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_request_transfer_add Add transfer to ED */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_request_bulk_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_EHCI_ED *ed; +ULONG transfer_request_payload_length; +ULONG bulk_packet_payload_length; +UCHAR * data_pointer; +ULONG pid; +ULONG td_component; +UINT status; +ULONG zlp_flag; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* The overlay parameters should be reset now. */ + ed -> ux_ehci_ed_current_td = UX_NULL; + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *)UX_EHCI_TD_T; + ed -> ux_ehci_ed_alternate_td = (UX_EHCI_TD *)UX_EHCI_TD_T; + ed -> ux_ehci_ed_state &= UX_EHCI_QH_TOGGLE; + ed -> ux_ehci_ed_bp0 = UX_NULL; + ed -> ux_ehci_ed_bp0 = UX_NULL; + ed -> ux_ehci_ed_bp1 = UX_NULL; + ed -> ux_ehci_ed_bp2 = UX_NULL; + ed -> ux_ehci_ed_bp3 = UX_NULL; + ed -> ux_ehci_ed_bp4 = UX_NULL; + + /* It may take more than one TD if the transfer_request length is more than the + maximum length for an EHCI TD (this is irrelevant of the MaxPacketSize value + in the endpoint descriptor). EHCI data payload has a maximum size of 16K. */ + transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length; + data_pointer = transfer_request -> ux_transfer_request_data_pointer; + + /* Check for ZLP condition. */ + if (transfer_request_payload_length == 0) + + /* We have a zlp condition. */ + zlp_flag = UX_TRUE; + else + + /* We do not have a zlp. */ + zlp_flag = UX_FALSE; + + /* Build all necessary TDs. */ + while ((transfer_request_payload_length != 0) || zlp_flag == UX_TRUE) + { + + /* Reset ZLP now. */ + zlp_flag = UX_FALSE; + + /* Check if we are exceeding the max payload. */ + if (transfer_request_payload_length > UX_EHCI_MAX_PAYLOAD) + bulk_packet_payload_length = UX_EHCI_MAX_PAYLOAD; + else + bulk_packet_payload_length = transfer_request_payload_length; + + /* Add this transfer request to the ED. */ + if ((transfer_request -> ux_transfer_request_type&UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + pid = UX_EHCI_PID_IN; + else + pid = UX_EHCI_PID_OUT; + + status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, 0, pid, 0, + data_pointer, bulk_packet_payload_length, transfer_request); + + if (status != UX_SUCCESS) + return(status); + + /* Adjust the data payload length and the data payload pointer. */ + transfer_request_payload_length -= bulk_packet_payload_length; + data_pointer += bulk_packet_payload_length; + } + + /* Set the IOC bit in the last TD. */ + ed -> ux_ehci_ed_last_td -> ux_ehci_td_control |= UX_EHCI_TD_IOC; + + /* Ensure the IOC bit is set before activating the TD. This is necessary + for some processors that perform writes out of order as an optimization. */ + UX_DATA_MEMORY_BARRIER + + /* Activate the first TD linked to the ED. */ + td_component = (ULONG) ed -> ux_ehci_ed_queue_element; + td_component &= ~UX_EHCI_TD_T; + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) td_component; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_request_control_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ehci_request_control_transfer.c new file mode 100644 index 0000000..bdc53da --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_request_control_transfer.c @@ -0,0 +1,234 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_request_control_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a control transfer from a transfer request. */ +/* The USB control transfer is in 3 phases (setup, data, status). */ +/* This function will chain all phases of the control sequence before */ +/* setting the EHCI endpoint as a candidate for transfer. */ +/* */ +/* The max aggregated size of a data payload in EHCI is 16K. We are */ +/* assuming that this size will be sufficient to contain the control */ +/* packet. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_ed_clean Clean TDs */ +/* _ux_hcd_ehci_request_transfer_add Add transfer to ED */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_short_put Write a 16-bit value */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_request_control_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request) +{ + +UX_DEVICE *device; +UX_ENDPOINT *endpoint; +UCHAR * setup_request; +UX_EHCI_ED *ed; +ULONG td_component; +UINT status; +UINT pid; + + + /* Get the pointer to the Endpoint and to the device. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + device = endpoint -> ux_endpoint_device; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Build the SETUP packet (phase 1 of the control transfer). */ + setup_request = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SETUP_SIZE); + if (setup_request == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + *setup_request = transfer_request -> ux_transfer_request_function; + *(setup_request + UX_SETUP_REQUEST_TYPE) = transfer_request -> ux_transfer_request_type; + *(setup_request + UX_SETUP_REQUEST) = transfer_request -> ux_transfer_request_function; + _ux_utility_short_put(setup_request + UX_SETUP_VALUE, transfer_request -> ux_transfer_request_value); + _ux_utility_short_put(setup_request + UX_SETUP_INDEX, transfer_request -> ux_transfer_request_index); + _ux_utility_short_put(setup_request + UX_SETUP_LENGTH, (USHORT) transfer_request -> ux_transfer_request_requested_length); + + /* Reset the last TD pointer since it is the first time we hook a transaction. */ + ed -> ux_ehci_ed_last_td = UX_NULL; + + /* Set the ED address and MPS values since they may have changed from 0 to x + The ED direction will be set from the TD. */ + ed -> ux_ehci_ed_cap0 |= (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION) << UX_EHCI_QH_ED_AD_LOC; + + /* Set the endpoint address (this should have changed after address setting). */ + ed -> ux_ehci_ed_cap0 |= device -> ux_device_address; + + /* Set the default MPS Capability info in the ED. */ + ed -> ux_ehci_ed_cap0 &= ~UX_EHCI_QH_MPS_MASK; + ed -> ux_ehci_ed_cap0 |= endpoint -> ux_endpoint_descriptor.wMaxPacketSize << UX_EHCI_QH_MPS_LOC; + + /* On Control transfers, the toggle is set in the TD, not the QH. */ + ed -> ux_ehci_ed_cap0 |= UX_EHCI_QH_DTC; + + /* The overlay parameters should be reset now. */ + ed -> ux_ehci_ed_current_td = UX_NULL; + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) UX_EHCI_TD_T; + ed -> ux_ehci_ed_alternate_td = (UX_EHCI_TD *) UX_EHCI_TD_T; + ed -> ux_ehci_ed_state &= UX_EHCI_QH_TOGGLE; + ed -> ux_ehci_ed_bp0 = UX_NULL; + ed -> ux_ehci_ed_bp1 = UX_NULL; + ed -> ux_ehci_ed_bp2 = UX_NULL; + ed -> ux_ehci_ed_bp3 = UX_NULL; + ed -> ux_ehci_ed_bp4 = UX_NULL; + + /* Build and hook the setup phase to the ED. */ + status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, UX_EHCI_TD_SETUP_PHASE, UX_EHCI_PID_SETUP, UX_EHCI_TOGGLE_0, + setup_request, UX_SETUP_SIZE, transfer_request); + if (status != UX_SUCCESS) + { + + /* We need to clean the tds attached if any. */ + _ux_hcd_ehci_ed_clean(ed); + return(status); + } + + /* Test if data phase required, if so decide the PID to use and build/hook it to the ED. */ + if (transfer_request -> ux_transfer_request_requested_length != 0) + { + + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + pid = UX_EHCI_PID_IN; + else + + pid = UX_EHCI_PID_OUT; + + status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, UX_EHCI_TD_DATA_PHASE, pid, UX_EHCI_TOGGLE_1, + transfer_request -> ux_transfer_request_data_pointer, + transfer_request -> ux_transfer_request_requested_length, + transfer_request); + if (status != UX_SUCCESS) + { + + /* We need to clean the tds attached if any. */ + _ux_hcd_ehci_ed_clean(ed); + return(status); + } + } + + /* Program the status phase. the PID is the opposite of the data phase. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + pid = UX_EHCI_PID_OUT; + else + pid = UX_EHCI_PID_IN; + + status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, UX_EHCI_TD_STATUS_PHASE, pid, + UX_EHCI_TOGGLE_1, UX_NULL, 0, transfer_request); + + if (status != UX_SUCCESS) + { + + /* We need to clean the tds attached if any. */ + _ux_hcd_ehci_ed_clean(ed); + return(status); + } + + /* Set the IOC bit in the last TD. */ + ed -> ux_ehci_ed_last_td -> ux_ehci_td_control |= UX_EHCI_TD_IOC; + + /* Ensure the IOC bit is set before activating the TD. This is necessary + for some processors that perform writes out of order as an optimization. */ + UX_DATA_MEMORY_BARRIER + + /* Activate the first TD linked to the ED. */ + td_component = (ULONG) ed -> ux_ehci_ed_queue_element; + td_component &= ~UX_EHCI_TD_T; + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) td_component; + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* There was an error, return to the caller. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + + /* Free the resources. */ + _ux_utility_memory_free(setup_request); + + /* Return completion status. */ + return(transfer_request -> ux_transfer_request_completion_code); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_request_interrupt_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ehci_request_interrupt_transfer.c new file mode 100644 index 0000000..740104f --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_request_interrupt_transfer.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_request_interrupt_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs an interrupt transfer request. An interrupt */ +/* transfer can only be as large as the MaxpacketField in the */ +/* endpoint descriptor. This was verified at the upper layer and does */ +/* not need to be reverified here. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_request_transfer_add Add transfer to ED */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_request_interrupt_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_EHCI_ED *ed; +ULONG pid; +ULONG td_component; +UINT status; + + + /* Get the pointer to the endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* The overlay parameters should be reset now. */ + ed -> ux_ehci_ed_current_td = UX_NULL; + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) UX_EHCI_TD_T; + ed -> ux_ehci_ed_alternate_td = (UX_EHCI_TD *) UX_EHCI_QH_T; + ed -> ux_ehci_ed_state &= UX_EHCI_QH_TOGGLE; + ed -> ux_ehci_ed_bp0 = UX_NULL; + ed -> ux_ehci_ed_bp1 = UX_NULL; + ed -> ux_ehci_ed_bp2 = UX_NULL; + ed -> ux_ehci_ed_bp3 = UX_NULL; + ed -> ux_ehci_ed_bp4 = UX_NULL; + + /* Get the correct PID for this transfer. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + pid = UX_EHCI_PID_IN; + else + pid = UX_EHCI_PID_OUT; + + /* Add this transfer request to the ED. */ + status = _ux_hcd_ehci_request_transfer_add(hcd_ehci, ed, 0, pid, 0, + transfer_request -> ux_transfer_request_data_pointer, + transfer_request -> ux_transfer_request_requested_length, + transfer_request); + + /* Ensure we got the TD allocated properly. */ + if (status == UX_SUCCESS) + { + + /* Set the IOC bit in the last TD. */ + ed -> ux_ehci_ed_last_td -> ux_ehci_td_control |= UX_EHCI_TD_IOC; + + /* Ensure the IOC bit is set before activating the TD. This is necessary + for some processors that perform writes out of order as an optimization. */ + UX_DATA_MEMORY_BARRIER + + /* Activate the first TD linked to the ED. */ + td_component = (ULONG) ed -> ux_ehci_ed_queue_element; + td_component &= ~UX_EHCI_TD_T; + ed -> ux_ehci_ed_queue_element = (UX_EHCI_TD *) td_component; + + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_request_isochronous_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ehci_request_isochronous_transfer.c new file mode 100644 index 0000000..938cc5c --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_request_isochronous_transfer.c @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_request_isochronous_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs an isochronous transfer request (list). */ +/* */ +/* Note: the request max length is endpoint max packet size, multiple */ +/* endpoint max number of transactions. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* transfer_request Pointer to transfer request. */ +/* If next transfer request is */ +/* valid the whole request list */ +/* is added, until next transfer */ +/* request being NULL. If next */ +/* next transfer request is not */ +/* valid (being NULL) single */ +/* transfer request is added. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_mutex_on Get mutex */ +/* _ux_utility_mutex_off Put mutex */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_request_isochronous_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request) +{ +#if UX_MAX_ISO_TD == 0 + + UX_PARAMETER_NOT_USED(hcd_ehci); + UX_PARAMETER_NOT_USED(transfer_request); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Not supported yet - return error. */ + return(UX_FUNCTION_NOT_SUPPORTED); +#else + +UX_ENDPOINT *endpoint; +UX_EHCI_PERIODIC_LINK_POINTER lp; +UX_EHCI_HSISO_ED *ied; +UX_TRANSFER **head; +UX_TRANSFER **tail; +UX_TRANSFER **first_new; +UX_TRANSFER *request_list; +UCHAR start = UX_FALSE; + + + /* Get the pointer to the endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical iTD/siTD attached to this endpoint. */ + lp.ed_ptr = endpoint -> ux_endpoint_ed; + + /* Lock the periodic list to update. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Append the request to iTD/siTD request list tail. */ +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + head = &lp.sitd_ptr -> ux_ehci_fsiso_td_transfer_head; + else +#endif + { + + /* Get pointer of ED. */ + ied = lp.itd_ptr -> ux_ehci_hsiso_td_ed; + + /* Get pointer locations. */ + head = &ied -> ux_ehci_hsiso_ed_transfer_head; + tail = &ied -> ux_ehci_hsiso_ed_transfer_tail; + first_new = &ied -> ux_ehci_hsiso_ed_transfer_first_new; + + /* If there is no transfer, start. */ + if (ied -> ux_ehci_hsiso_ed_frstart == 0xFF) + { + ied -> ux_ehci_hsiso_ed_frstart = 0xFE; + start = UX_TRUE; + } + } + + request_list = (*head); + if (request_list == UX_NULL) + { + + /* Link to head. */ + (*head) = transfer_request; + (*tail) = transfer_request; + (*first_new) = transfer_request; + } + else + { + + /* Link to tail of the list. */ + (*tail) -> ux_transfer_request_next_transfer_request = transfer_request; + + /* In case there is nothing to load, set new ones. */ + if (*first_new == UX_NULL) + *first_new = transfer_request; + } + + /* Move tail until it's real last one. */ + while((*tail) -> ux_transfer_request_next_transfer_request != UX_NULL) + (*tail) = ((*tail) -> ux_transfer_request_next_transfer_request); + + /* Release the periodic table. */ + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + + /* Simulate iTD/siTD done to start - HCD signal. */ + if (start) + { + hcd_ehci -> ux_hcd_ehci_hcd_owner -> ux_hcd_thread_signal ++; + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_hcd_semaphore); + } + + /* Return completion status. */ + return(UX_SUCCESS); +#endif +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer.c new file mode 100644 index 0000000..11aa77d --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer.c @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_request_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the handler for all the transactions on the USB. */ +/* The transfer request passed as parameter contains the endpoint and */ +/* the device descriptors in addition to the type of transaction de */ +/* be executed. */ +/* */ +/* This function routes the transfer_request to according to the type */ +/* of transfer to be executed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_request_control_transfer Start control transfer */ +/* _ux_hcd_ehci_request_bulk_transfer Start bulk transfer */ +/* _ux_hcd_ehci_request_interrupt_transfer Start interrupt transfer */ +/* _ux_hcd_ehci_request_isochronous_transfer Start iso transfer */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_request_transfer(UX_HCD_EHCI *hcd_ehci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UINT status; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* We reset the actual length field of the transfer request as a safety measure. */ + transfer_request -> ux_transfer_request_actual_length = 0; + + /* Isolate the endpoint type and route the transfer request. */ + switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + + status = _ux_hcd_ehci_request_control_transfer(hcd_ehci, transfer_request); + break; + + + case UX_BULK_ENDPOINT: + + status = _ux_hcd_ehci_request_bulk_transfer(hcd_ehci, transfer_request); + break; + + + case UX_INTERRUPT_ENDPOINT: + + status = _ux_hcd_ehci_request_interrupt_transfer(hcd_ehci, transfer_request); + break; + + + case UX_ISOCHRONOUS_ENDPOINT: + + status = _ux_hcd_ehci_request_isochronous_transfer(hcd_ehci, transfer_request); + break; + + } + + /* Note that it is physically impossible to have a wrong endpoint type here + so no error checking. */ + return(status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer_add.c b/common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer_add.c new file mode 100644 index 0000000..599fdc5 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_request_transfer_add.c @@ -0,0 +1,156 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_request_transfer_add PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function adds a component of a transfer to an existing ED. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* ed Pointer to the ED */ +/* phase Phase (for control transfers) */ +/* pid PID to be used with this */ +/* request (SETUP,IN,OUT) */ +/* toggle Toggle value 0 or 1 */ +/* buffer_address Buffer address for transfer */ +/* buffer_length Buffer length */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ehci_regular_td_obtain Obtain regular TD */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_request_transfer_add(UX_HCD_EHCI *hcd_ehci, UX_EHCI_ED *ed, ULONG phase, ULONG pid, + ULONG toggle, UCHAR * buffer_address, ULONG buffer_length, UX_TRANSFER *transfer_request) +{ + +UX_EHCI_TD *last_td; +UX_EHCI_TD *td; +UX_EHCI_LINK_POINTER lp; +UX_EHCI_POINTER bp; + + + /* Obtain a TD for this transaction. */ + td = _ux_hcd_ehci_regular_td_obtain(hcd_ehci); + if (td == UX_NULL) + return(UX_NO_TD_AVAILABLE); + + /* Store the transfer request associated with this TD. */ + td -> ux_ehci_td_transfer_request = transfer_request; + + /* Store the ED associated with this TD. */ + td -> ux_ehci_td_ed = ed; + + /* Mark the TD with the phase. */ + td -> ux_ehci_td_phase |= phase; + + /* Set the PID in the control DWORD of the TD. */ + td -> ux_ehci_td_control = pid; + + /* Set the buffer address if there is a data payload. */ + bp.void_ptr = _ux_utility_physical_address(buffer_address); + td -> ux_ehci_td_bp0 = bp.void_ptr; /* with offset. */ + + /* Fill in the next pages addresses if required. */ + bp.value &= UX_EHCI_PAGE_ALIGN; + td -> ux_ehci_td_bp1 = bp.u8_ptr + UX_EHCI_PAGE_SIZE; + td -> ux_ehci_td_bp2 = bp.u8_ptr + UX_EHCI_PAGE_SIZE * 2; + td -> ux_ehci_td_bp3 = bp.u8_ptr + UX_EHCI_PAGE_SIZE * 3; + td -> ux_ehci_td_bp4 = bp.u8_ptr + UX_EHCI_PAGE_SIZE * 4; + + /* Set the length of the data transfer. We keep its original value. */ + td -> ux_ehci_td_control |= buffer_length << UX_EHCI_TD_LG_LOC; + td -> ux_ehci_td_length = buffer_length; + + /* Add the completion trigger, the default error count, the active bit. */ + td -> ux_ehci_td_control |= UX_EHCI_TD_CERR | UX_EHCI_TD_ACTIVE; + + /* Add the toggle value. This value is only used for control transfers. */ + td -> ux_ehci_td_control |= toggle; + + /* Recall the last TD hooked to the ED. If the value is NULL, this will be the + first TD and should be hooked to the ED itself.. */ + last_td = ed -> ux_ehci_ed_last_td; + + /* Do we hook this TD to the ED? */ + if (last_td == UX_NULL) + { + + /* The TD is hooked to the ED. We set the T bit so that the controller will + not transfer this TD on hook up but when all the TDs have been hooked. + We memorize this TD as the first TD as well. */ + ed -> ux_ehci_ed_first_td = td; + lp.void_ptr = _ux_utility_physical_address(td); + lp.value |= UX_EHCI_TD_T; + ed -> ux_ehci_ed_queue_element = lp.td_ptr; + } + else + { + + /* The TD is hooked to the end of the linked TDs. */ + last_td -> ux_ehci_td_link_pointer = _ux_utility_physical_address(td); + } + + /* Memorize the last TD hooked. */ + ed -> ux_ehci_ed_last_td = td; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_transfer_abort.c b/common/usbx_host_controllers/src/ux_hcd_ehci_transfer_abort.c new file mode 100644 index 0000000..92d3619 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_transfer_abort.c @@ -0,0 +1,181 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_transfer_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will abort transactions attached to a transfer */ +/* request. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ehci Pointer to EHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_virtual_address Get virtual address */ +/* _ux_utility_mutex_on Get mutex */ +/* _ux_utility_mutex_off Put mutex */ +/* _ux_utility_delay_ms Delay miliseconds */ +/* _ux_hcd_ehci_ed_clean Clean TDs on ED */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ehci_transfer_abort(UX_HCD_EHCI *hcd_ehci,UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_EHCI_HSISO_ED *ied; +UX_EHCI_PERIODIC_LINK_POINTER lp; +UX_TRANSFER **list_head; +UX_TRANSFER *transfer; +ULONG max_load_count; +ULONG remain_count; + + UX_PARAMETER_NOT_USED(hcd_ehci); + + + /* Get the pointer to the endpoint associated with the transfer request*/ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* From the endpoint container, get the address of the physical endpoint. */ + lp.void_ptr = endpoint -> ux_endpoint_ed; + + /* Check if this physical endpoint has been initialized properly! */ + if (lp.void_ptr == UX_NULL) + { + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* Check endpoint type. */ + if ((endpoint -> ux_endpoint_descriptor.bmAttributes & UX_MASK_ENDPOINT_TYPE) == UX_ISOCHRONOUS_ENDPOINT) + { + + /* Lock the periodic table. */ + _ux_utility_mutex_on(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + +#if defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE) + if (endpoint -> ux_endpoint_device -> ux_device_speed != UX_HIGH_SPEED_DEVICE) + { + list_head = &lp.sitd_ptr -> ux_ehci_fsiso_td_transfer_head; + } + else +#endif + { + /* Get ED for the iTD(s). */ + ied = lp.itd_ptr -> ux_ehci_hsiso_td_ed; + + /* Get list head for further process. */ + list_head = &ied -> ux_ehci_hsiso_ed_transfer_head; + + /* Max load count (in 1 ms): 8, 4, 2, 1 ... */ + max_load_count = 8 >> ied -> ux_ehci_hsiso_ed_frinterval_shift; + } + + /* The whole list is aborted. */ + if ((transfer_request == 0) || + (transfer_request == &endpoint -> ux_endpoint_transfer_request) || + (*list_head) == transfer_request) + { + *list_head = UX_NULL; + remain_count = 0; + } + else + { + + /* At least one request remains. */ + remain_count = 1; + + /* Remove it and transfers after it. */ + transfer = (*list_head) -> ux_transfer_request_next_transfer_request; + while(transfer) + { + + /* If next is transfer we expect, remove from it. */ + if (transfer -> ux_transfer_request_next_transfer_request == transfer_request) + { + + /* Point to NULL to remove all things after it. */ + transfer -> ux_transfer_request_next_transfer_request = UX_NULL; + break; + } + + /* Next transfer. */ + transfer = transfer -> ux_transfer_request_next_transfer_request; + + /* Remain plus one. */ + remain_count ++; + } + } + + /* Wait a while so background transfer completed. */ + if (remain_count < max_load_count) + _ux_utility_delay_ms(1); + + /* Release the periodic table. */ + _ux_utility_mutex_off(&hcd_ehci -> ux_hcd_ehci_periodic_mutex); + } + else + + /* Clean the TDs attached to the ED. */ + _ux_hcd_ehci_ed_clean(lp.ed_ptr); + + /* Return successful completion. */ + return(UX_SUCCESS); +} diff --git a/common/usbx_host_controllers/src/ux_hcd_ehci_transfer_request_process.c b/common/usbx_host_controllers/src/ux_hcd_ehci_transfer_request_process.c new file mode 100644 index 0000000..8c1e2df --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ehci_transfer_request_process.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** EHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ehci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ehci_transfer_request_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function process the transfer that was completed either */ +/* successfully because of a partial transmission or because of an */ +/* error. The transfer descriptor tells us what to do with it, either */ +/* put a semaphore to the caller or invoke a completion routine. If a */ +/* completion routine is specified, the routine is called and no */ +/* semaphore is put. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_transfer_request_completion_function) Transfer complete function*/ +/* _ux_utility_semaphore_put Put producer semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* EHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ehci_transfer_request_process(UX_TRANSFER *transfer_request) +{ + + /* Check if there is a function for the transfer completion. */ + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + + /* Yes, so we call it. */ + transfer_request -> ux_transfer_request_completion_function(transfer_request); + else + + /* There is a semaphore so send the signal to the class. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_create.c b/common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_create.c new file mode 100644 index 0000000..36127c4 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_create.c @@ -0,0 +1,167 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_asynchronous_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an asynchronous endpoint. The control */ +/* and bulk endpoints fall into this category. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI HCD */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_ed_obtain Obtain a new ED */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_asynchronous_endpoint_create(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint) +{ + +UX_DEVICE *device; +UX_OHCI_ED *ed; +UX_OHCI_ED *head_ed; +UX_OHCI_TD *td; +ULONG ohci_register; + + + /* We need to take into account the nature of the HCD to define the max size + of any transfer in the transfer request. */ + endpoint -> ux_endpoint_transfer_request.ux_transfer_request_maximum_length = UX_OHCI_MAX_PAYLOAD; + + /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint is active and + will be the container for the TDs. */ + ed = _ux_hcd_ohci_ed_obtain(hcd_ohci); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Obtain a dummy TD for terminating the ED transfer chain. */ + td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + if (td == UX_NULL) + { + + ed -> ux_ohci_ed_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *) ed; + + /* Now do the opposite, attach the ED container to the physical ED. */ + ed -> ux_ohci_ed_endpoint = endpoint; + + /* Program the ED for subsequent transfers. We need to set the following things: + 1) Address of the device + 2) endpoint number + 3) speed + 4) format of TD + 5) maximum packet size */ + device = endpoint -> ux_endpoint_device; + ed -> ux_ohci_ed_dw0 = device -> ux_device_address | + ((ULONG) (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION)) << 7 | + ((ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize) << 16; + + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_LOW_SPEED; + + /* Hook the TD to both the tail and head of the ED. */ + ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(td); + ed -> ux_ohci_ed_head_td = _ux_utility_physical_address(td); + + /* We now need to get the type of transfer (control or bulk) to hook this ED to the appropriate list. + We also enable the appropriate list. */ + switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + + head_ed = (UX_OHCI_ED *) _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_CONTROL_HEAD_ED); + ed -> ux_ohci_ed_next_ed = head_ed; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL_HEAD_ED, (ULONG) ed); + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_CONTROL); + ohci_register |= OHCI_HC_CR_CLE; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, ohci_register); + break; + + + case UX_BULK_ENDPOINT: + + head_ed = (UX_OHCI_ED *) _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_BULK_HEAD_ED); + ed -> ux_ohci_ed_next_ed = head_ed; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_BULK_HEAD_ED, (ULONG) ed); + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_CONTROL); + ohci_register |= OHCI_HC_CR_BLE; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, ohci_register); + break; + + default: + + head_ed = UX_NULL; + } + + /* Build the back chaining pointer. The previous head ED needs to know about the + inserted ED. */ + if (head_ed != UX_NULL) + head_ed -> ux_ohci_ed_previous_ed = ed; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_destroy.c b/common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_destroy.c new file mode 100644 index 0000000..fb2cee4 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_asynchronous_endpoint_destroy.c @@ -0,0 +1,201 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" +#include "ux_hcd_ohci.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_asynchronous_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy an asynchronous endpoint. The control */ +/* and bulk endpoints fall into this category. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI HCD */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* _ux_utility_virtual_address Get virtual address */ +/* _ux_utility_delay_ms Delay ms */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_asynchronous_endpoint_destroy(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint) +{ + +UX_OHCI_ED *ed; +UX_OHCI_ED *previous_ed; +UX_OHCI_ED *next_ed; +UX_OHCI_TD *tail_td; +UX_OHCI_TD *head_td; +ULONG value_td; +ULONG ohci_register; + + + /* From the endpoint container fetch the OHCI ED descriptor. */ + ed = (UX_OHCI_ED*) endpoint -> ux_endpoint_ed; + + /* Check if this physical endpoint has been initialized properly! */ + if (ed == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* The endpoint may be active. If so, set the skip bit. */ + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_SKIP; + + /* Wait for the controller to finish the current frame processing. */ + _ux_utility_delay_ms(1); + + /* Get the previous ED in the list for this ED. */ + previous_ed = ed -> ux_ohci_ed_previous_ed; + + /* Get the next ED in the list for this ED. */ + next_ed = ed -> ux_ohci_ed_next_ed; + + /* If the previous ED is NULL, we are at trying to remove the head ED. */ + if (previous_ed == UX_NULL) + { + + switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + + /* Check if the next control endpoint is NULL. */ + if (next_ed == UX_NULL) + { + + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_CONTROL); + ohci_register &= ~OHCI_HC_CR_CLE; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, ohci_register); + } + + /* Store the new endpoint in the Control list. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL_HEAD_ED, (ULONG) next_ed); + break; + + + case UX_BULK_ENDPOINT: + + /* Check if the next bulk endpoint is NULL. */ + if (next_ed == UX_NULL) + { + + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_CONTROL); + ohci_register &= ~OHCI_HC_CR_BLE; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, ohci_register); + } + + /* Store the new endpoint in the Bulk list */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_BULK_HEAD_ED, (ULONG) next_ed); + break; + } + } + else + { + + /* The previous ED points now to the ED after the ED we are removing. */ + previous_ed -> ux_ohci_ed_next_ed = next_ed; + } + + /* Update the previous ED pointer in the next ED if exists. */ + if (next_ed != UX_NULL) + next_ed -> ux_ohci_ed_previous_ed = previous_ed; + + /* We use the tail TD as a pointer to the Dummy TD. */ + tail_td = (UX_OHCI_TD *) _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + + /* Ensure that the potential Halt bit is removed in the head ED. */ + value_td = (ULONG) _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td) & UX_OHCI_ED_MASK_TD; + head_td = (UX_OHCI_TD *) _ux_utility_physical_address((VOID *) value_td); + ed -> ux_ohci_ed_head_td = head_td; + + /* Remove all the tds from this ED and leave the head and tail pointing + to the dummy TD. */ + tail_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + + /* Free all tds attached to the ED */ + while (head_td != tail_td) + { + + /* Update the head TD with the next TD. */ + ed -> ux_ohci_ed_head_td = head_td -> ux_ohci_td_next_td; + + /* Mark the current head TD as free. */ + head_td -> ux_ohci_td_status = UX_UNUSED; + + /* Now the new head TD is the next TD in the chain. */ + head_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td); + } + + /* We need to free the dummy TD that was attached to the ED. */ + tail_td -> ux_ohci_td_status = UX_UNUSED; + + + /* Now we can safely make the ED free. */ + ed -> ux_ohci_ed_status = UX_UNUSED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_controller_disable.c b/common/usbx_host_controllers/src/ux_hcd_ohci_controller_disable.c new file mode 100644 index 0000000..0edbc33 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_controller_disable.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_controller_disable PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will disable the OHCI controller. The controller */ +/* will release all its resources (memory, IO ...). After this, the */ +/* controller will not send SOF any longer. All transactions should */ +/* have been completed, all classes should have been closed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI HCD */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_controller_disable(UX_HCD_OHCI *hcd_ohci) +{ + +UX_HCD *hcd; +ULONG ohci_register; + + + /* Point to the generic portion of the host controller structure instance. */ + hcd = hcd_ohci -> ux_hcd_ohci_hcd_owner; + + /* Set the controller to disabled state. */ + ohci_register = OHCI_HC_CR_RESET; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, ohci_register); + + /* Reflect the state of the controller in the main structure. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_done_queue_process.c b/common/usbx_host_controllers/src/ux_hcd_ohci_done_queue_process.c new file mode 100644 index 0000000..a104172 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_done_queue_process.c @@ -0,0 +1,340 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_done_queue_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function process the done queue that was posted by the */ +/* controller during the last interrupt. The bad news is that the */ +/* list of the TDs in the queue is in the opposite order of their */ +/* actual completion. This FIFO made the OHCI design easier but the */ +/* software has to work harder! */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI HCD */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_transfer_request_completion_function) */ +/* Transfer completion function */ +/* _ux_hcd_ohci_endpoint_error_clear Clear endpoint error */ +/* _ux_hcd_ohci_endpoint_reset Reset endpoint */ +/* _ux_hcd_ohci_frame_number_get Get frame number */ +/* _ux_hcd_ohci_next_td_clean Clean next TD */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* _ux_utility_semaphore_put Put producer semaphore */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ohci_done_queue_process(UX_HCD_OHCI *hcd_ohci) +{ + +UX_ENDPOINT *endpoint; +UX_OHCI_TD *td; +UX_OHCI_ISO_TD *iso_td; +UX_OHCI_TD *previous_td; +UX_OHCI_TD *next_td; +UINT td_error_code; +UX_TRANSFER *transfer_request; +ULONG ohci_register_interrupt; +ULONG transaction_length; +ULONG current_frame; + + + /* Get the first entry of the done queue. It may be NULL which means there is nothing to do! + The LSB of the TD pointer may be set by the OHCI controller to indicate that the Interrupt + status of the controller should be read. The TDs in the list are using physical addresses. + They need to be translated into virtual addresses. */ + next_td = _ux_utility_virtual_address((VOID *) ((ULONG) hcd_ohci -> ux_hcd_ohci_done_head & 0xfffffff0)); + + /* Reset the last TD in the chain. */ + previous_td = UX_NULL; + td = UX_NULL; + + /* The TD we have now is the last in the FIFO, re-traverse the chain to get the TDs in the + chronological order. */ + while (next_td != UX_NULL) + { + + td = next_td; + next_td = _ux_utility_virtual_address(td -> ux_ohci_td_next_td); + td -> ux_ohci_td_next_td = previous_td; + previous_td = td; + } + + /* Process each TD in their chronological order now. The TD pointer now has the first TD in the + list, all values are in virtual addresses. */ + while (td != UX_NULL) + { + + /* Get the pointer to the transfer request attached with this TD. */ + transfer_request = td -> ux_ohci_td_transfer_request; + + /* Get the endpoint associated with the transfer request */ + endpoint = transfer_request -> ux_transfer_request_endpoint; + + /* Retrieve the error code for this transaction. There are 3 types of errors: + transmission, sequence, system. */ + td_error_code = td -> ux_ohci_td_dw0 >> UX_OHCI_TD_CC; + + /* The handling of the isoch TD is slightly different. */ + switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + case UX_INTERRUPT_ENDPOINT: + + switch (td_error_code) + { + + case UX_OHCI_NO_ERROR: + + /* No error on the transmission of this TD. Update the length of the transfer request. */ + transfer_request -> ux_transfer_request_actual_length += td -> ux_ohci_td_length; + + /* Check at the phase of the transfer, if this is a SETUP or DATA phases for a control + endpoint, we wait for the setup phase or any phase for other types of endpoints. */ + if ((td -> ux_ohci_td_status & UX_OHCI_TD_SETUP_PHASE) || (td -> ux_ohci_td_status & UX_OHCI_TD_DATA_PHASE)) + break; + + /* Either this is a non control endpoint or it is the status phase and we are done. */ + if (transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length) + { + + transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS; + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + } + break; + + + case UX_OHCI_ERROR_DATA_UNDERRUN: + + /* No error on the transmission of this TD but all data is not accounted for. This is typically + a short packet and OHCI report it as an error. This allows for the ED to be halted and further + attached TDs to be stopped. In this case, compute the correct received\sent length and + process the transfer request. */ + transfer_request -> ux_transfer_request_actual_length += td -> ux_ohci_td_length - ((ULONG) td -> ux_ohci_td_be - + (ULONG) td -> ux_ohci_td_cbp) - 1; + + /* Check at the phase of the transfer, if this is a SETUP or DATA phases for a control endpoint, + we wait for the setup phase or any phase for other types of endpoints. */ + if ((td -> ux_ohci_td_status & UX_OHCI_TD_SETUP_PHASE) || (td -> ux_ohci_td_status & UX_OHCI_TD_DATA_PHASE)) + break; + + /* We need to reset the error bit in the ED. */ + _ux_hcd_ohci_endpoint_error_clear(hcd_ohci, endpoint); + + /* Either this is a non control endpoint or it is the status phase and we are done */ + transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS; + _ux_hcd_ohci_next_td_clean(td); + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + break; + + + case UX_OHCI_ERROR_STALL: + + /* A stall condition happens when the device refuses the requested command or when a + parameter in the command is wrong. We retire the transfer_request and mark the error. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_STALLED; + _ux_hcd_ohci_next_td_clean(td); + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_STALLED, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We need to reset the error bit in the ED. */ + _ux_hcd_ohci_endpoint_reset(hcd_ohci, endpoint); + break; + + + case UX_OHCI_ERROR_DEVICE_NOT_RESPONDING: + + /* A stall condition happens when the device does not respond to the request. This mostly + happens at the first GET_DESCRIPTOR after the port is enabled. This error has to be + picked up by the enumeration module to reset the port and retry the command. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_NO_ANSWER; + _ux_hcd_ohci_next_td_clean(td); + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_NO_ANSWER, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We need to reset the error bit in the ED */ + _ux_hcd_ohci_endpoint_reset(hcd_ohci, endpoint); + break; + + + default: + + /* Any other errors default to this section. The command has been repeated 3 times + and there is still a problem. The endpoint probably should be reset. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_ERROR; + _ux_hcd_ohci_next_td_clean(td); + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_ERROR, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* We need to reset the error bit in the ED. */ + _ux_hcd_ohci_endpoint_reset(hcd_ohci, endpoint); + break; + } + break; + + + case UX_ISOCHRONOUS_ENDPOINT: + + /* The length of the transfer is in the PSW. */ + iso_td = (UX_OHCI_ISO_TD *) td; + + switch (td_error_code) + { + + case UX_OHCI_NO_ERROR: + + /* No error on the transmission of this TD. All data is accounted for. Check for the + last TD in the transfer request. If last, process the transfer request. The method + to calculate the length of the transaction is different between a IN and OUT + transactions. For a OUT, if the PSW is 0, then all data was transmitted. For an IN + the PSW indicates the number of bytes received. */ + transaction_length = iso_td -> ux_ohci_iso_td_offset_psw[0] & UX_OHCI_ISO_TD_OFFSET; + + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + { + + transfer_request -> ux_transfer_request_actual_length += transaction_length; + } + else + { + + if (transaction_length == 0) + transfer_request -> ux_transfer_request_actual_length += iso_td -> ux_ohci_iso_td_length; + else + transfer_request -> ux_transfer_request_actual_length += transaction_length; + } + + /* Check if the transfer request is complete or if this is an IN transaction and the length received + is less than the max packet size. */ + if ((transfer_request -> ux_transfer_request_actual_length == transfer_request -> ux_transfer_request_requested_length) || + (((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) && + (transaction_length < transfer_request -> ux_transfer_request_packet_length))) + { + + transfer_request -> ux_transfer_request_completion_code = UX_SUCCESS; + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + } + break; + + + case UX_OHCI_ERROR_DATA_OVERRRUN: + + /* In this case, we have missed the frame for the isoch transfer. */ + _ux_hcd_ohci_frame_number_get(hcd_ohci, ¤t_frame); + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_MISSED_FRAME; + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_MISSED_FRAME, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + break; + + + default: + + /* Some other error happened, in isoch transfer, there is not much we can do. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_ERROR; + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + transfer_request -> ux_transfer_request_completion_function(transfer_request); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_ERROR, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + break; + } + } + + /* Free the TD that was just treated. */ + td -> ux_ohci_td_status = UX_UNUSED; + + /* And continue the TD loop. */ + td = td -> ux_ohci_td_next_td; + } + + /* The OHCI controller is now ready to receive the next done queue. We need to + reawake the OHCI controller on the WDH signal. */ + ohci_register_interrupt = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_INTERRUPT_ENABLE); + ohci_register_interrupt |= OHCI_HC_INT_WDH; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_ENABLE, ohci_register_interrupt); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_ed_obtain.c b/common/usbx_host_controllers/src/ux_hcd_ohci_ed_obtain.c new file mode 100644 index 0000000..1f8c660 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_ed_obtain.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_ed_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free ED from the ED list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI HCD */ +/* */ +/* OUTPUT */ +/* */ +/* UX_OHCI_ED * Pointer to ED */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_OHCI_ED *_ux_hcd_ohci_ed_obtain(UX_HCD_OHCI *hcd_ohci) +{ + +UX_OHCI_ED *ed; +ULONG ed_index; + + + /* Start the search from the beginning of the list. */ + ed = hcd_ohci -> ux_hcd_ohci_ed_list; + for (ed_index = 0; ed_index < _ux_system_host -> ux_system_host_max_ed; ed_index++) + { + + /* Check the ED status, a free ED is marked with the UNUSED flag. */ + if (ed -> ux_ohci_ed_status == UX_UNUSED) + { + + /* The ED may have been used, so we reset all fields. */ + _ux_utility_memory_set(ed, 0, sizeof(UX_OHCI_ED)); + + /* This ED is now marked as USED. */ + ed -> ux_ohci_ed_status = UX_USED; + + /* Success, return the ED pointer. */ + return(ed); + } + + /* Point to the next ED. */ + ed++; + } + + /* There is no available ED in the ED list, return a NULL. */ + return(UX_NULL); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_error_clear.c b/common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_error_clear.c new file mode 100644 index 0000000..81ab6cd --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_error_clear.c @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_endpoint_error_clear PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will clear the error bit in a OHCI endpoint. This */ +/* command, unlike the reset command does not clear the toggle bit. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI HCD */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_endpoint_error_clear(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint) +{ + +UX_OHCI_ED *ed; +ULONG td_value; + + + /* From the endpoint container fetch the OHCI ED descriptor. */ + ed = (UX_OHCI_ED *) endpoint -> ux_endpoint_ed; + + /* Reset the Error bit in the Head TD. */ + td_value = (ULONG) ed -> ux_ohci_ed_head_td; + td_value &= ~UX_OHCI_ED_HALTED; + ed -> ux_ohci_ed_head_td = (UX_OHCI_TD *) td_value; + + /* This operation never fails! */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_reset.c b/common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_reset.c new file mode 100644 index 0000000..110242e --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_endpoint_reset.c @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_endpoint_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reset an endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI HCD */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_endpoint_reset(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint) +{ + +UX_OHCI_ED *ed; +ULONG td_value; + + + /* From the endpoint container fetch the OHCI ED descriptor. */ + ed = (UX_OHCI_ED *) endpoint -> ux_endpoint_ed; + + /* Reset the data0/data1 toggle bit in the Head TD. */ + td_value = (ULONG) ed -> ux_ohci_ed_head_td; + td_value &= UX_OHCI_ED_MASK_TD; + ed -> ux_ohci_ed_head_td = (UX_OHCI_TD *) td_value; + + /* This operation never fails! */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_entry.c b/common/usbx_host_controllers/src/ux_hcd_ohci_entry.c new file mode 100644 index 0000000..31ef6c0 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_entry.c @@ -0,0 +1,274 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function dispatches the HCD function internally to the OHCI */ +/* controller driver routines. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* function Function for driver to perform*/ +/* parameter Pointer to parameter(s) */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_asynchronous_endpoint_create Create async endpoint */ +/* _ux_hcd_ohci_asynchronous_endpoint_destroy Destroy async endpoint */ +/* _ux_hcd_ohci_controller_disable Disable controller */ +/* _ux_hcd_ohci_done_queue_process Process done queue */ +/* _ux_hcd_ohci_endpoint_reset Reset endpoint */ +/* _ux_hcd_ohci_frame_number_get Get frame number */ +/* _ux_hcd_ohci_frame_number_set Set frame number */ +/* _ux_hcd_ohci_interrupt_endpoint_create Create interrupt endpoint*/ +/* _ux_hcd_ohci_isochronous_endpoint_create Create isoch endpoint */ +/* _ux_hcd_ohci_periodic_endpoint_destroy Destroy periodic endpoint*/ +/* _ux_hcd_ohci_port_enable Enable port */ +/* _ux_hcd_ohci_port_disable Disable port */ +/* _ux_hcd_ohci_port_reset Reset port */ +/* _ux_hcd_ohci_port_resume Resume port */ +/* _ux_hcd_ohci_port_status_get Get port status */ +/* _ux_hcd_ohci_port_suspend Suspend port */ +/* _ux_hcd_ohci_power_down_port Power down port */ +/* _ux_hcd_ohci_power_on_port Power on port */ +/* _ux_hcd_ohci_request_transfer Request transfer */ +/* _ux_hcd_ohci_transfer_abort Abort transfer */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_entry(UX_HCD *hcd, UINT function, VOID *parameter) +{ + +UINT status; +UX_HCD_OHCI *hcd_ohci; + + + /* Check the status of the controller. */ + if (hcd -> ux_hcd_status == UX_UNUSED) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_CONTROLLER_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONTROLLER_UNKNOWN, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONTROLLER_UNKNOWN); + } + + /* Get the pointer to the OHCI HCD. */ + hcd_ohci = (UX_HCD_OHCI *) hcd -> ux_hcd_controller_hardware; + + /* look at the function and route it. */ + switch(function) + { + + case UX_HCD_DISABLE_CONTROLLER: + + status = _ux_hcd_ohci_controller_disable(hcd_ohci); + break; + + + case UX_HCD_GET_PORT_STATUS: + + status = _ux_hcd_ohci_port_status_get(hcd_ohci, (ULONG) parameter); + break; + + + case UX_HCD_ENABLE_PORT: + + status = _ux_hcd_ohci_port_enable(hcd_ohci, (ULONG) parameter); + break; + + + case UX_HCD_DISABLE_PORT: + + status = _ux_hcd_ohci_port_disable(hcd_ohci, (ULONG) parameter); + break; + + + case UX_HCD_POWER_ON_PORT: + + status = _ux_hcd_ohci_power_on_port(hcd_ohci, (ULONG) parameter); + break; + + + case UX_HCD_POWER_DOWN_PORT: + + status = _ux_hcd_ohci_power_down_port(hcd_ohci, (ULONG) parameter); + break; + + + case UX_HCD_SUSPEND_PORT: + + status = _ux_hcd_ohci_port_suspend(hcd_ohci, (ULONG) parameter); + break; + + + case UX_HCD_RESUME_PORT: + + status = _ux_hcd_ohci_port_resume(hcd_ohci, (UINT) parameter); + break; + + + case UX_HCD_RESET_PORT: + + status = _ux_hcd_ohci_port_reset(hcd_ohci, (ULONG) parameter); + break; + + + case UX_HCD_GET_FRAME_NUMBER: + + status = _ux_hcd_ohci_frame_number_get(hcd_ohci, (ULONG *) parameter); + break; + + + case UX_HCD_SET_FRAME_NUMBER: + + _ux_hcd_ohci_frame_number_set(hcd_ohci, (ULONG) parameter); + status = UX_SUCCESS; + break; + + + case UX_HCD_TRANSFER_REQUEST: + + status = _ux_hcd_ohci_request_transfer(hcd_ohci, (UX_TRANSFER *) parameter); + break; + + + case UX_HCD_TRANSFER_ABORT: + + status = _ux_hcd_ohci_transfer_abort(hcd_ohci, (UX_TRANSFER *) parameter); + break; + + + case UX_HCD_CREATE_ENDPOINT: + + switch ((((UX_ENDPOINT*) parameter) -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + + status = _ux_hcd_ohci_asynchronous_endpoint_create(hcd_ohci, (UX_ENDPOINT*) parameter); + break; + + + case UX_INTERRUPT_ENDPOINT: + + status = _ux_hcd_ohci_interrupt_endpoint_create(hcd_ohci, (UX_ENDPOINT*) parameter); + break; + + + case UX_ISOCHRONOUS_ENDPOINT: + + status = _ux_hcd_ohci_isochronous_endpoint_create(hcd_ohci, (UX_ENDPOINT*) parameter); + break; + + } + break; + + + case UX_HCD_DESTROY_ENDPOINT: + + switch ((((UX_ENDPOINT*) parameter) -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + case UX_BULK_ENDPOINT: + + status = _ux_hcd_ohci_asynchronous_endpoint_destroy(hcd_ohci, (UX_ENDPOINT*) parameter); + break; + + case UX_INTERRUPT_ENDPOINT: + case UX_ISOCHRONOUS_ENDPOINT: + + status = _ux_hcd_ohci_periodic_endpoint_destroy(hcd_ohci, (UX_ENDPOINT*) parameter); + break; + + } + break; + + + case UX_HCD_RESET_ENDPOINT: + + status = _ux_hcd_ohci_endpoint_reset(hcd_ohci, (UX_ENDPOINT*) parameter); + break; + + + case UX_HCD_PROCESS_DONE_QUEUE: + + _ux_hcd_ohci_done_queue_process(hcd_ohci); + status = UX_SUCCESS; + break; + + + default: + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_FUNCTION_NOT_SUPPORTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_FUNCTION_NOT_SUPPORTED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* Set status to not supported. */ + status = UX_FUNCTION_NOT_SUPPORTED; + } + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_get.c b/common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_get.c new file mode 100644 index 0000000..8eb44ca --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_get.c @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_frame_number_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the frame number currently used by the */ +/* controller. This function is mostly used for isochronous purposes. */ +/* It is easier to read the frame number off the HCCA structure */ +/* because this can be done without going back to the controller. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* frame_number Pointer to frame number */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_frame_number_get(UX_HCD_OHCI *hcd_ohci, ULONG *frame_number) +{ + + /* Pickup the frame number. */ + *frame_number = (ULONG) hcd_ohci -> ux_hcd_ohci_hcca -> ux_hcd_ohci_hcca_frame_number; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_set.c b/common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_set.c new file mode 100644 index 0000000..96d90c2 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_frame_number_set.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_frame_number_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will set the current frame number to the one */ +/* specified. This function is mostly used for isochronous purposes. */ +/* Here we need to write to the host controller which in turn will */ +/* update the HCCA at the end of the frame. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* frame_number Frame number to set */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ohci_frame_number_set(UX_HCD_OHCI *hcd_ohci, ULONG frame_number) +{ + + /* Write to OHCI register. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_FM_NUMBER, frame_number & 0xffff); + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_initialize.c b/common/usbx_host_controllers/src/ux_hcd_ohci_initialize.c new file mode 100644 index 0000000..509e424 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_initialize.c @@ -0,0 +1,224 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the OHCI controller. It sets the dma */ +/* areas, programs all the OHCI registers, setup the ED and TD */ +/* containers, sets the control, and builds the periodic lists. */ +/* */ +/* INPUT */ +/* */ +/* HCD Pointer to HCD */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_periodic_tree_create Create OHCI periodic tree */ +/* _ux_hcd_ohci_power_root_hubs Power root HUBs */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_mutex_on Get mutex protection */ +/* _ux_utility_mutex_off Release mutex protection */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_set_interrupt_handler Setup interrupt handler */ +/* */ +/* CALLED BY */ +/* */ +/* Host Stack */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_initialize(UX_HCD *hcd) +{ + +UX_HCD_OHCI *hcd_ohci; +ULONG ohci_register; +UINT index_loop; +UINT status; + + + /* The controller initialized here is of OHCI type. */ + hcd -> ux_hcd_controller_type = UX_OHCI_CONTROLLER; + + /* Initialize the max bandwidth for periodic endpoints. On OHCI, the spec says no + more than 90% to be allocated for periodic. */ + hcd -> ux_hcd_available_bandwidth = UX_OHCI_AVAILABLE_BANDWIDTH; + + /* Allocate memory for this OHCI HCD instance. */ + hcd_ohci = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HCD_OHCI)); + if (hcd_ohci == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Set the pointer to the OHCI HCD. */ + hcd -> ux_hcd_controller_hardware = (VOID *) hcd_ohci; + + /* Save the HCOR address. */ + hcd_ohci -> ux_hcd_ohci_hcor = (ULONG *) hcd -> ux_hcd_io; + + /* Set the generic HCD owner for the OHCI HCD. */ + hcd_ohci -> ux_hcd_ohci_hcd_owner = hcd; + + /* Initialize the function collector for this HCD. */ + hcd -> ux_hcd_entry_function = _ux_hcd_ohci_entry; + + /* Set the state of the controller to HALTED first. */ + hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED; + + /* get an DMA safe address for the HCCA. This block of memory is to be aligned + on 256 bytes. */ + hcd_ohci -> ux_hcd_ohci_hcca = _ux_utility_memory_allocate(UX_ALIGN_256, UX_CACHE_SAFE_MEMORY, sizeof(UX_HCD_OHCI_HCCA)); + if (hcd_ohci -> ux_hcd_ohci_hcca == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Allocate the list of eds. All eds are allocated on 16 byte memory boundary. */ + hcd_ohci -> ux_hcd_ohci_ed_list = _ux_utility_memory_allocate(UX_ALIGN_16, UX_CACHE_SAFE_MEMORY, sizeof(UX_OHCI_ED) * _ux_system_host -> ux_system_host_max_ed); + if (hcd_ohci -> ux_hcd_ohci_ed_list == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Allocate the list of tds. All tds are allocated on 32 byte memory boundary. */ + hcd_ohci -> ux_hcd_ohci_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_OHCI_TD) * _ux_system_host -> ux_system_host_max_td); + if (hcd_ohci -> ux_hcd_ohci_td_list == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Allocate the list of isochronous tds. All tds are allocated on 32 byte memory boundary. */ + hcd_ohci -> ux_hcd_ohci_iso_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_OHCI_ISO_TD) * _ux_system_host -> ux_system_host_max_iso_td); + if (hcd_ohci -> ux_hcd_ohci_td_list == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + /* Initialize the periodic tree. */ + status = _ux_hcd_ohci_periodic_tree_create(hcd_ohci); + if (status != UX_SUCCESS) + return(status); + + /* Read the OHCI controller version, it is either USB 1.0 or 1.1. This is important for + filtering INT out endpoints on a 1.0 OHCI. */ + hcd -> ux_hcd_version = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_REVISION); + + /* Set the state of the OHCI controller to reset in the control register. + This is not compulsory but some controllers demand to start in this state. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, 0); + + /* The following is time critical. If we get interrupted here, the controller will go in + suspend mode. Get the protection mutex. */ + _ux_utility_mutex_on(&_ux_system -> ux_system_mutex); + + /* Send the reset command to the controller. The controller should ack + this command within 10us. We try this several time and check for timeout. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, OHCI_HC_CS_HCR); + + for (index_loop = 0; index_loop < UX_OHCI_RESET_RETRY; index_loop++) + { + + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS); + if ((ohci_register & OHCI_HC_CS_HCR) == 0) + break; + } + + /* Check if the controller is reset properly. */ + if ((ohci_register & OHCI_HC_CS_HCR) != 0) + { + + /* Release the thread protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_CONTROLLER_INIT_FAILED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONTROLLER_INIT_FAILED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_CONTROLLER_INIT_FAILED); + } + + /* Set the HCCA pointer to the HCOR. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_HCCA, (ULONG) _ux_utility_physical_address(hcd_ohci -> ux_hcd_ohci_hcca)); + + /* For now and until we have control and bulk ED, reset the control and bulk head registers. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL_HEAD_ED, 0); + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL_CURRENT_ED, 0); + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_BULK_HEAD_ED, 0); + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_BULK_CURRENT_ED, 0); + + /* Turn on the OHCI controller functional registers we will use after this operation, + the controller is operational. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, OHCI_HC_CONTROL_VALUE); + hcd -> ux_hcd_status = UX_HCD_STATUS_OPERATIONAL; + + /* We can safely release the mutex protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Set the controller interval. */ + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_FM_INTERVAL) & OHCI_HC_FM_INTERVAL_CLEAR; + ohci_register |= OHCI_HC_FM_INTERVAL_SET; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_FM_INTERVAL, ohci_register); + + /* Set the default Periodic Start value. In some controller, a reset will set the default value + but in some controller this value has to be set manually (like the LPC2468. ) */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_PERIODIC_START, UX_OHCI_HC_PERIODIC_START_DEFAULT); + + /* Reset all the OHCI interrupts and re-enable only the ones we will use. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_DISABLE, OHCI_HC_INTERRUPT_DISABLE_ALL); + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_ENABLE, OHCI_HC_INTERRUPT_ENABLE_NORMAL); + + /* Get the number of ports on the controller. The number of ports needs to be reflected both + for the generic HCD container and the local OHCI container. */ + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_DESCRIPTOR_A); + hcd -> ux_hcd_nb_root_hubs = (UINT) (ohci_register & 0xff); + hcd_ohci -> ux_hcd_ohci_nb_root_hubs = (UINT) (ohci_register & 0xff); + + /* All ports must now be powered to pick up device insertion. */ + _ux_hcd_ohci_power_root_hubs(hcd_ohci); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_endpoint_create.c b/common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_endpoint_create.c new file mode 100644 index 0000000..5ecf246 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_endpoint_create.c @@ -0,0 +1,210 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_interrupt_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will create an interrupt endpoint. The interrupt */ +/* endpoint has an interval of operation from 1 to 255. In OHCI, the */ +/* hardware assisted interrupt is from 1 to 32. */ +/* */ +/* This routine will match the best interval for the OHCI hardware. */ +/* It will also determine the best node to hook the endpoint based on */ +/* the load that already exists on the horizontal ED chain. */ +/* */ +/* For the ones curious about this coding. The tricky part is to */ +/* understand how the interrupt matrix is constructed. We have used */ +/* eds with the skip bit on to build a frame of anchor eds. Each ED */ +/* creates a node for an appropriate combination of interval */ +/* frequency in the list. */ +/* */ +/* After obtaining a pointer to the list with the lowest traffic, we */ +/* traverse the list from the highest interval until we reach the */ +/* interval required. At that node, we anchor our real ED to the node */ +/* and link the ED that was attached to the node to our ED. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_ed_obtain Obtain OHCI ED */ +/* _ux_hcd_ohci_least_traffic_list_get Get least traffic list */ +/* _ux_hcd_ohci_regular_td_obtain Obtain OHCI regular TD */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_interrupt_endpoint_create(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint) +{ + +UX_DEVICE *device; +UX_OHCI_ED *ed; +UX_OHCI_ED *ed_list; +UX_OHCI_ED *next_ed; +UX_OHCI_TD *td; +UINT interval; +UINT interval_index; +UINT interval_ohci; + + + /* Obtain a ED for this new endpoint. This ED will live as long as the endpoint + is active and will be the container for the tds. */ + ed = _ux_hcd_ohci_ed_obtain(hcd_ohci); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Obtain a dummy TD for terminating the ED transfer chain. */ + td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + if (td == UX_NULL) + { + + ed -> ux_ohci_ed_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *) ed; + + /* Program the ED for subsequent transfers we need to set the following things: + + 1) Address of the device + 2) endpoint number + 3) speed + 4) format of TD + 5) maximum packet size */ + device = endpoint -> ux_endpoint_device; + ed -> ux_ohci_ed_dw0 = device -> ux_device_address | + ((ULONG) (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION)) << 7 | + ((ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize) << 16; + + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_LOW_SPEED; + + /* Hook the TD to both the tail and head of the ED. */ + ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(td); + ed -> ux_ohci_ed_head_td = _ux_utility_physical_address(td); + + /* Get the list index with the least traffic. */ + ed_list = _ux_hcd_ohci_least_traffic_list_get(hcd_ohci); + + /* Get the interval for the endpoint and match it to a OHCI list. We match anything that + is > 32ms to the 32ms interval list. The 32ms list is list 0, 16ms list is 1 ... + the 1ms list is number 5. */ + interval = endpoint -> ux_endpoint_descriptor.bInterval; + interval_index = 0x10; + interval_ohci = 1; + + /* Do a sanity check if the frequency is 0. That should not happen, so treat it as 1. */ + if (interval == 0) + { + + interval = 1; + } + + /* If the frequency is beyond the OHCI framework, make it the maximum of 32. */ + if (interval >= 32) + { + + interval_ohci = 0; + } + else + { + + /* We parse the interval from the high bits. This gives us the first power of 2 entry in the tree. */ + while (interval_index != 0) + { + + /* When we find the first bit of the interval the current value of interval_ohci is set to the the list index. */ + if (interval & interval_index) + break; + + /* Go down the tree one entry. */ + interval_ohci++; + + /* And shift the bit of the device interval to check. */ + interval_index = interval_index >> 1; + } + } + + /* Now we need to scan the list of eds from the lowest load entry until we reach the + appropriate interval node. The depth index is the interval OHCI value and the 1st + entry is pointed by the ED list entry. */ + while (interval_ohci--) + { + + ed_list = _ux_utility_virtual_address(ed_list -> ux_ohci_ed_next_ed); + while (!(ed_list -> ux_ohci_ed_dw0 & UX_OHCI_ED_SKIP)) + ed_list = _ux_utility_virtual_address(ed_list -> ux_ohci_ed_next_ed); + } + + /* We found the node entry of the ED pointer that will be the anchor for this interrupt + endpoint. Now we attach this endpoint to the anchor and rebuild the chain. */ + next_ed = ed_list -> ux_ohci_ed_next_ed; + + /* Check for end of tree which happens for devices with interval of 1. In this case + there might not be a next_ed. */ + if (next_ed != UX_NULL) + next_ed -> ux_ohci_ed_previous_ed = ed; + ed -> ux_ohci_ed_next_ed = _ux_utility_physical_address(next_ed); + ed -> ux_ohci_ed_previous_ed = ed_list; + ed_list -> ux_ohci_ed_next_ed = _ux_utility_physical_address(ed); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_handler.c b/common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_handler.c new file mode 100644 index 0000000..c1877ed --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_interrupt_handler.c @@ -0,0 +1,179 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_interrupt_handler PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the interrupt handler for the OHCI interrupts. */ +/* Normally an interrupt occurs from the controller when there is */ +/* either a EOF signal and there has been transfers within the frame */ +/* or when there is a change on one of the downstream ports. */ +/* */ +/* All we need to do in the ISR is scan the controllers to find out */ +/* which one has issued a IRQ. If there is work to do for this */ +/* controller we need to wake up the corresponding thread to take */ +/* care of the job. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* _ux_utility_semaphore_put Put semaphore */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Interrupt Handler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ohci_interrupt_handler(VOID) +{ + +UINT hcd_index; +UX_HCD *hcd; +UX_HCD_OHCI *hcd_ohci; +ULONG ohci_register = 0; +ULONG ohci_register_port_status; +ULONG root_hub_thread_wakeup = 0; +ULONG port_index; + + + /* We need to parse the controller driver table to find all controllers that + registered as OHCI. */ + for (hcd_index = 0; hcd_index < _ux_system_host -> ux_system_host_registered_hcd; hcd_index++) + { + + /* Check type of controller. */ + if (_ux_system_host -> ux_system_host_hcd_array[hcd_index].ux_hcd_controller_type == UX_OHCI_CONTROLLER) + { + + /* Get the pointers to the generic HCD and OHCI specific areas. */ + hcd = &_ux_system_host -> ux_system_host_hcd_array[hcd_index]; + hcd_ohci = (UX_HCD_OHCI *) hcd -> ux_hcd_controller_hardware; + + /* Check if the controller is operational, if not, skip it. */ + if (hcd -> ux_hcd_status == UX_HCD_STATUS_OPERATIONAL) + { + + /* We get the current interrupt status for this controller. */ + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_INTERRUPT_STATUS); + + /* Examine the source of interrupts. */ + if (ohci_register & OHCI_HC_INT_WDH) + { + + /* We have some transferred EDs in the done queue. The controller thread needs + to wake up and process them. */ + hcd_ohci -> ux_hcd_ohci_done_head = hcd_ohci -> ux_hcd_ohci_hcca -> ux_hcd_ohci_hcca_done_head; + hcd_ohci -> ux_hcd_ohci_hcca -> ux_hcd_ohci_hcca_done_head = UX_NULL; + hcd -> ux_hcd_thread_signal++; + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_hcd_semaphore); + + /* Since we have delayed the processing of the done queue to a thread. + We need to ensure the host controller will not overwrite the done + queue pointer. So we disable the WDH bit in the interrupt status + before we acknowledge the IRQ register. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_DISABLE, OHCI_HC_INT_WDH); + } + + if (ohci_register & OHCI_HC_INT_UE) + { + + /* The controller has issued a Unrecoverable Error signal. The controller will + be reset now, and we wake up the HCD thread. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, OHCI_HC_CS_HCR); + hcd -> ux_hcd_thread_signal++; + hcd -> ux_hcd_status = UX_HCD_STATUS_DEAD; + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_hcd_semaphore); + } + + if (ohci_register & OHCI_HC_INT_RHSC) + { + + /* The controller has issued a Root HUB status change signal. There may be one or more events + that caused this status change. Only device insertion/extraction are monitored here. */ + for (port_index = 0; port_index < hcd_ohci -> ux_hcd_ohci_nb_root_hubs; port_index++) + { + + /* Read the port status. */ + ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index); + + /* Check for Connect Status Change signal. */ + if (ohci_register_port_status & OHCI_HC_PS_CSC) + { + /* Something happened on this port. Signal it to the root hub thread. */ + hcd -> ux_hcd_root_hub_signal[port_index]++; + + /* Memorize wake up signal. */ + root_hub_thread_wakeup ++; + } + + /* Clear the root hub interrupt signal. We do not turn off Port Reset status change here. + This will be done when the root hub requests a Port Reset. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index, (OHCI_HC_PS_CSC | OHCI_HC_PS_PESC | OHCI_HC_PS_PSSC | OHCI_HC_PS_OCIC )); + } + + /* We only wake up the root hub thread if there has been device insertion/extraction. */ + if (root_hub_thread_wakeup != 0) + _ux_utility_semaphore_put(&_ux_system_host -> ux_system_host_enum_semaphore); + } + } + + /* We have processed the interrupts for this controller, acknowledge them + so that the controller can continue to work. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_STATUS, ohci_register); + } + } +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_endpoint_create.c b/common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_endpoint_create.c new file mode 100644 index 0000000..988be0e --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_endpoint_create.c @@ -0,0 +1,133 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_isochronous_endpoint_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates an isochronous endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_ed_obtain Obtain an OHCI ED */ +/* _ux_hcd_ohci_isochronous_td_obtain Obtain an OHCI TD */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_isochronous_endpoint_create(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint) +{ + +UX_HCD_OHCI_HCCA *ohci_hcca; +UX_DEVICE *device; +UX_OHCI_ED *ed; +UX_OHCI_ED *ed_list; +UX_OHCI_ISO_TD *td; + + + /* Get the pointer to the HCCA. */ + ohci_hcca = hcd_ohci -> ux_hcd_ohci_hcca; + + /* Obtain a ED for this new endpoint. This ED will live as long as + the endpoint is active and will be the container for the tds. */ + ed = _ux_hcd_ohci_ed_obtain(hcd_ohci); + if(ed==UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Obtain a dummy isoch TD for terminating the ED transfer chain. */ + td = _ux_hcd_ohci_isochronous_td_obtain(hcd_ohci); + if (td == UX_NULL) + { + ed -> ux_ohci_ed_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the ED to the endpoint container. */ + endpoint -> ux_endpoint_ed = (VOID *) ed; + + /* Program the ED for subsequent transfers we need to set the following things: + + 1) Address of the device + 2) endpoint number + 3) speed (always full speed for iso) + 4) format of TD + 5) maximum packet size */ + + device = endpoint -> ux_endpoint_device; + ed -> ux_ohci_ed_dw0 = device -> ux_device_address | UX_OHCI_ED_ISOCHRONOUS | + ((ULONG) (endpoint -> ux_endpoint_descriptor.bEndpointAddress & ~UX_ENDPOINT_DIRECTION)) << 7 | + ((ULONG) (endpoint -> ux_endpoint_descriptor.wMaxPacketSize)) << 16; + + /* Hook the TD to both the tail and head of the ED. */ + ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(td); + ed -> ux_ohci_ed_head_td = _ux_utility_physical_address(td); + + /* Attach the ED to the 1ms interrupt tree. We scan the interrupt tree until the last entry. */ + ed_list = _ux_utility_virtual_address(ohci_hcca -> ux_hcd_ohci_hcca_ed[0]); + while (ed_list -> ux_ohci_ed_next_ed != UX_NULL) + ed_list = _ux_utility_virtual_address(ed_list -> ux_ohci_ed_next_ed); + + /* Now ed_list, points to the last ED, which is in the 1ms entry. */ + ed -> ux_ohci_ed_previous_ed = ed_list; + ed_list -> ux_ohci_ed_next_ed = _ux_utility_physical_address(ed); + + /* Return success. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_td_obtain.c b/common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_td_obtain.c new file mode 100644 index 0000000..dfb0690 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_isochronous_td_obtain.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_isochronous_td_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free TD from the isochronous TD list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI */ +/* */ +/* OUTPUT */ +/* */ +/* UX_OHCI_ISO_TD * Pointer to OHCI TD */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_OHCI_ISO_TD *_ux_hcd_ohci_isochronous_td_obtain(UX_HCD_OHCI *hcd_ohci) +{ + +UX_OHCI_ISO_TD *td; +ULONG td_index; + + + /* Start the search from the beginning of the list. */ + td = hcd_ohci -> ux_hcd_ohci_iso_td_list; + for (td_index = 0; td_index < _ux_system_host -> ux_system_host_max_iso_td; td_index++) + { + + /* Check the TD status, a free TD is marked with the UNUSED flag. */ + if (td -> ux_ohci_iso_td_status == UX_UNUSED) + { + + /* The TD may have been used, so we reset all fields. */ + _ux_utility_memory_set(td, 0, sizeof(UX_OHCI_ISO_TD)); + + /* This TD is now marked as USED. */ + td -> ux_ohci_iso_td_status = UX_USED; + + /* Success, return the TD pointer. */ + return(td); + } + + /* Move to next TD. */ + td++; + } + + /* There is no available TD in the TD list. */ + return(UX_NULL); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_least_traffic_list_get.c b/common/usbx_host_controllers/src/ux_hcd_ohci_least_traffic_list_get.c new file mode 100644 index 0000000..1ee868a --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_least_traffic_list_get.c @@ -0,0 +1,133 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_least_traffic_list_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function return a pointer to the first ED in the periodic */ +/* tree that has the least traffic registered. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI */ +/* */ +/* OUTPUT */ +/* */ +/* UX_OHCI_ED * Pointer to OHCI ED */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_OHCI_ED *_ux_hcd_ohci_least_traffic_list_get(UX_HCD_OHCI *hcd_ohci) +{ + +UX_HCD_OHCI_HCCA *ohci_hcca; +UX_OHCI_ED *min_bandwidth_ed; +UX_OHCI_ED *begin_ed; +UX_OHCI_ED *ed; +UINT list_index; +ULONG min_bandwidth_used; +ULONG bandwidth_used; + + + /* Get the pointer to the HCCA. */ + ohci_hcca = hcd_ohci -> ux_hcd_ohci_hcca; + + /* Set the min bandwidth used to a arbitrary maximum value. */ + min_bandwidth_used = 0xffffffff; + + /* The first ED is the list candidate for now. */ + min_bandwidth_ed = _ux_utility_virtual_address(ohci_hcca -> ux_hcd_ohci_hcca_ed[0]); + + /* All list will be scanned. */ + for (list_index = 0; list_index < 32; list_index++) + { + + /* Reset the bandwidth for this list. */ + bandwidth_used = 0; + + /* Get the ED of the beginning of the list we parse now. */ + ed = _ux_utility_virtual_address(ohci_hcca -> ux_hcd_ohci_hcca_ed[list_index]); + + /* We keep track of the first ED for the current list. */ + begin_ed = ed; + + /* Parse the eds in the list. */ + while (ed -> ux_ohci_ed_next_ed != UX_NULL) + { + + /* Add to the bandwidth used the max packet size pointed by this ED. */ + bandwidth_used += (ed -> ux_ohci_ed_dw0 >> 16) & UX_OHCI_ED_MPS; + + /* Next ED. */ + ed = _ux_utility_virtual_address(ed -> ux_ohci_ed_next_ed); + } + + /* We have processed a list, check the bandwidth used by this list. + If this bandwidth is the minimum, we memorize the ED. */ + if (bandwidth_used < min_bandwidth_used) + { + + /* We have found a better list with a lower used bandwidth, + memorize the bandwidth for this list. */ + min_bandwidth_used = bandwidth_used; + + /* Memorize the begin ED for this list. */ + min_bandwidth_ed = begin_ed; + } + } + + /* Return the ED list with the lowest bandwidth. */ + return(min_bandwidth_ed); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_next_td_clean.c b/common/usbx_host_controllers/src/ux_hcd_ohci_next_td_clean.c new file mode 100644 index 0000000..dd185c0 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_next_td_clean.c @@ -0,0 +1,118 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_next_td_clean PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function cleans all the tds attached to a ED. The end of the */ +/* TD chain is pointed by the tail TD. */ +/* */ +/* INPUT */ +/* */ +/* td Pointer to OHCI TD */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ohci_next_td_clean(UX_OHCI_TD *td) +{ + +UX_OHCI_ED *ed; +UX_OHCI_TD *head_td; +UX_OHCI_TD *tail_td; +ULONG value_td; +ULONG value_carry; + + + /* Obtain the pointer to the ED from the TD. */ + ed = td -> ux_ohci_td_ed; + + /* Ensure that the potential Carry bit is maintained in the head ED. */ + value_carry = (ULONG) _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td) & UX_OHCI_ED_TOGGLE_CARRY; + + /* Ensure that the potential Halt bit is removed in the head ED. */ + value_td = (ULONG) _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td) & UX_OHCI_ED_MASK_TD; + head_td = (UX_OHCI_TD *) _ux_utility_physical_address((VOID *) value_td); + + /* Remove all the tds from this ED and leave the head and tail pointing + to the dummy TD. */ + tail_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + + /* Free all tds attached to the ED. */ + while (head_td != tail_td) + { + + /* Mark the current head_td as free. */ + head_td -> ux_ohci_td_status = UX_UNUSED; + + /* Update the head TD with the next TD. */ + ed -> ux_ohci_ed_head_td = head_td -> ux_ohci_td_next_td; + + /* Now the new head_td is the next TD in the chain. */ + head_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td); + } + + /* Restore the value carry for next transfers. */ + value_td = (ULONG) ed -> ux_ohci_ed_head_td; + value_td |= value_carry; + ed -> ux_ohci_ed_head_td = (UX_OHCI_TD *) value_td; + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_periodic_endpoint_destroy.c b/common/usbx_host_controllers/src/ux_hcd_ohci_periodic_endpoint_destroy.c new file mode 100644 index 0000000..f99fe04 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_periodic_endpoint_destroy.c @@ -0,0 +1,155 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_host_stack.h" +#include "ux_hcd_ohci.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_periodic_endpoint_destroy PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will destroy an interrupt or isochronous endpoint. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* endpoint Pointer to endpoint */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_delay_ms Delay ms */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_periodic_endpoint_destroy(UX_HCD_OHCI *hcd_ohci, UX_ENDPOINT *endpoint) +{ + +UX_OHCI_ED *ed; +UX_OHCI_ED *previous_ed; +UX_OHCI_ED *next_ed; +UX_OHCI_TD *tail_td; +UX_OHCI_TD *head_td; +ULONG value_td; + + + /* From the endpoint container fetch the OHCI ED descriptor. */ + ed = (UX_OHCI_ED*) endpoint -> ux_endpoint_ed; + + /* Check if this physical endpoint has been initialized properly! */ + if (ed == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* The endpoint may be active. If so, set the skip bit. */ + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_SKIP; + + /* Wait for the controller to finish the current frame processing. */ + _ux_utility_delay_ms(1); + + /* Get the previous ED in the list for this ED. */ + previous_ed = ed -> ux_ohci_ed_previous_ed; + + /* Get the next ED in the list for this ED. */ + next_ed = ed -> ux_ohci_ed_next_ed; + + /* The previous ED points now to the ED after the ED we are removing. */ + previous_ed -> ux_ohci_ed_next_ed = next_ed; + + /* There may not be any next endpoint. But if there is one, link it + to the previous ED. */ + if (next_ed != UX_NULL) + + /* Update the previous ED pointer in the next ED. */ + next_ed -> ux_ohci_ed_previous_ed = previous_ed; + + /* We use the tail TD as a pointer to the Dummy TD. */ + tail_td = (UX_OHCI_TD *) _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + + /* Ensure that the potential Halt bit is removed in the head ED. */ + value_td = (ULONG) _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td) & UX_OHCI_ED_MASK_TD; + head_td = (UX_OHCI_TD *) _ux_utility_physical_address((VOID *) value_td); + ed -> ux_ohci_ed_head_td = head_td; + + /* Remove all the tds from this ED and leave the head and tail pointing + to the dummy TD. */ + tail_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + + /* Free all tds attached to the ED */ + while (head_td != tail_td) + { + + /* Update the head TD with the next TD. */ + ed -> ux_ohci_ed_head_td = head_td -> ux_ohci_td_next_td; + + /* Mark the current head TD as free. */ + head_td -> ux_ohci_td_status = UX_UNUSED; + + /* Now the new head TD is the next TD in the chain. */ + head_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td); + } + + /* We need to free the dummy TD that was attached to the ED. */ + tail_td -> ux_ohci_td_status = UX_UNUSED; + + /* Now we can safely make the ED free. */ + ed -> ux_ohci_ed_status = UX_UNUSED; + + /* Return success. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_periodic_tree_create.c b/common/usbx_host_controllers/src/ux_hcd_ohci_periodic_tree_create.c new file mode 100644 index 0000000..b193ac0 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_periodic_tree_create.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_periodic_tree_create PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the periodic static tree for the interrupt */ +/* and isochronous eds. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_ed_obtain Obtain an ED */ +/* _ux_utility_physical_address Get physical address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_periodic_tree_create(UX_HCD_OHCI *hcd_ohci) +{ + +UX_HCD_OHCI_HCCA *ohci_hcca; +UX_OHCI_ED *ed; +UINT list_index; +UINT list_entries; +UINT current_list_entry; +UX_OHCI_ED *ed_list[32]; +UX_OHCI_ED *ed_start_list[32]; + + + /* We need the pointer to the HCCA. It contains the pointer to the first + 32 periodic entries. */ + ohci_hcca = hcd_ohci -> ux_hcd_ohci_hcca; + + /* Start with the 1st list - it has 32 entries. */ + list_entries = 32; + + /* Create each list one by one starting from the 32ms list. */ + for (list_index = 0; list_index < 6; list_index++) + { + + for (current_list_entry = 0; current_list_entry < list_entries;current_list_entry++) + { + + /* In each list, insert an static ED as the anchor. There should not + be any errors when obtaining a new ED, still we do a sanity check. */ + ed = _ux_hcd_ohci_ed_obtain(hcd_ohci); + if (ed == UX_NULL) + return(UX_NO_ED_AVAILABLE); + + /* Mark this anchor ED as static by putting it as SKIPPED, the OHCI + controller will not look into its tail and head list and will simply + jump to the next ED. */ + ed -> ux_ohci_ed_dw0 = UX_OHCI_ED_SKIP; + + /* Either we hook this new ED to the start list for further processing + or we hook it to the 2 successive entries in the previous list. */ + if (list_index == 0) + { + + ed_start_list[current_list_entry] = ed; + } + else + { + + ed_list[current_list_entry * 2] -> ux_ohci_ed_next_ed = _ux_utility_physical_address(ed); + ed_list[(current_list_entry * 2) + 1] -> ux_ohci_ed_next_ed = _ux_utility_physical_address(ed); + } + + /* Memorize this ED in the local list. We do this operation now, otherwise + we would erase the previous list eds. */ + ed_list[current_list_entry] = ed; + } + + /* Shift the number of entries in the next list by 1 (i.e. divide by 2). */ + list_entries = list_entries >> 1; + } + + /* The tree has been completed but the entries in the HCCA are in the wrong order. + We need to swap each entry according to the OHCI specified entry order list + so that we have a fair interval frequency for each periodic ED. The primary eds + are fetched from the start list, translated into physical addresses and stored + into the HCCA. */ + for (current_list_entry = 0; current_list_entry < 32; current_list_entry++) + { + + ed = ed_start_list[_ux_system_host_hcd_periodic_tree_entries[current_list_entry]]; + ohci_hcca -> ux_hcd_ohci_hcca_ed[current_list_entry] = _ux_utility_physical_address(ed); + } + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_port_disable.c b/common/usbx_host_controllers/src/ux_hcd_ohci_port_disable.c new file mode 100644 index 0000000..35f4bdb --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_port_disable.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_port_disable PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will disable a specific port attached to the root */ +/* HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read OHCI register read */ +/* _ux_hcd_ohci_register_write OHCI register write */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_port_disable(UX_HCD_OHCI *hcd_ohci, ULONG port_index) +{ + +ULONG ohci_register_port_status; + + + /* Check to see if this port is valid on this controller. */ + if (hcd_ohci -> ux_hcd_ohci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* Read the port status for this port. */ + ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index); + + /* Disable the port (CPE field). */ + ohci_register_port_status |= OHCI_HC_PS_CPE; + + /* Write the status back. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index, ohci_register_port_status); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_port_enable.c b/common/usbx_host_controllers/src/ux_hcd_ohci_port_enable.c new file mode 100644 index 0000000..377d572 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_port_enable.c @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_port_enable PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will enable a specific port attached to the root */ +/* HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read OHCI register read */ +/* _ux_hcd_ohci_register_write OHCI register write */ +/* _ux_utility_delay_ms Delay */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_port_enable(UX_HCD_OHCI *hcd_ohci, ULONG port_index) +{ + +ULONG ohci_register_port_status; + + + /* Check to see if this port is valid on this controller. */ + if (hcd_ohci -> ux_hcd_ohci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* Ensure that the downstream port has a device attached. It is unnatural + to perform a port enable if there is no device. */ + ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index); + + /* Check Device Connection Status. */ + if ((ohci_register_port_status & OHCI_HC_PS_CCS) == 0) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_NO_DEVICE_CONNECTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_DEVICE_CONNECTED, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_NO_DEVICE_CONNECTED); + } + + /* Wait 50ms before we issue the command, otherwise some device do not answer! */ + _ux_utility_delay_ms(UX_PORT_ENABLE_WAIT); + + /* Write the command PES to this port. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index, OHCI_HC_PS_PES); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_port_reset.c b/common/usbx_host_controllers/src/ux_hcd_ohci_port_reset.c new file mode 100644 index 0000000..ab1cab9 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_port_reset.c @@ -0,0 +1,144 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_port_reset PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will reset a specific port attached to the root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read OHCI register read */ +/* _ux_hcd_ohci_register_write OHCI register write */ +/* _ux_utility_delay_ms Delay */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_port_reset(UX_HCD_OHCI *hcd_ohci, ULONG port_index) +{ + +ULONG ohci_register_port_status; +UINT index_loop; + + + /* Check to see if this port is valid on this controller. */ + if (hcd_ohci -> ux_hcd_ohci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* Ensure that the downstream port has a device attached. It is unnatural + to perform a port reset if there is no device. */ + ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index); + + /* Check Device Connection Status. */ + if ((ohci_register_port_status & OHCI_HC_PS_CCS) == 0) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_NO_DEVICE_CONNECTED); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_NO_DEVICE_CONNECTED, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_NO_DEVICE_CONNECTED); + } + + /* Now we can safely issue a RESET command to this port. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index, OHCI_HC_PS_PRS); + + /* Normally, a port reset lasts for around 10ms. When the reset is completed + the controller will set the PRSC bit. We need to invert this bit by rewriting + a 1 to the PRSC field. */ + for (index_loop = 0; index_loop < UX_OHCI_PORT_RESET_RETRY; index_loop++) + { + + /* Perform the necessary delay to let the port reset properly. */ + _ux_utility_delay_ms(UX_OHCI_PORT_RESET_DELAY); + + /* Read the root HUB status change register. */ + ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index); + + /* Reset completed? */ + if (ohci_register_port_status & OHCI_HC_PS_PRSC) + { + + /* The reset phase is complete, rewrite that bit to clear it and + return to the root HUB driver. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index, OHCI_HC_PS_PRSC); + + /* Return successful completion. */ + return(UX_SUCCESS); + } + } + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_RESET_FAILED, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + /* The reset failed! Inform the root HUB driver. This should not really happen in a 1.x controller. */ + return(UX_PORT_RESET_FAILED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_port_resume.c b/common/usbx_host_controllers/src/ux_hcd_ohci_port_resume.c new file mode 100644 index 0000000..36a6d98 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_port_resume.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_port_resume PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will resume a specific port attached to the root */ +/* HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_port_resume(UX_HCD_OHCI *hcd_ohci, UINT port_index) +{ + + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_port_status_get.c b/common/usbx_host_controllers/src/ux_hcd_ohci_port_status_get.c new file mode 100644 index 0000000..5a935d5 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_port_status_get.c @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_port_status_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will return the status for each port attached to the */ +/* root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* port_status */ +/* */ +/* Status of the root HUB port with the following format: */ +/* */ +/* bit 0 device connection status */ +/* if 0 : no device connected */ +/* if 1 : device connected to the port */ +/* bit 1 port enable status */ +/* if 0 : port disabled */ +/* if 1 : port enabled */ +/* bit 2 port suspend status */ +/* if 0 : port is not suspended */ +/* if 1 : port is suspended */ +/* bit 3 port overcurrent status */ +/* if 0 : port has no overcurrent condition */ +/* if 1 : port has overcurrent condition */ +/* bit 4 port reset status */ +/* if 0 : port is not in reset */ +/* if 1 : port is in reset */ +/* bit 5 port power status */ +/* if 0 : port power is off */ +/* if 1 : port power is on */ +/* bit 6-7 device attached speed */ +/* if 00 : low speed device attached */ +/* if 01 : full speed device attached */ +/* if 10 : high speed device attached */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_hcd_ohci_port_status_get(UX_HCD_OHCI *hcd_ohci, ULONG port_index) +{ + +ULONG ohci_register_port_status; +ULONG port_status; + + + /* Check to see if this port is valid on this controller */ + if (hcd_ohci -> ux_hcd_ohci_nb_root_hubs < port_index) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_PORT_INDEX_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_PORT_INDEX_UNKNOWN, port_index, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_PORT_INDEX_UNKNOWN); + } + + /* The port is valid, build the status mask for this port. This function + returns a controller agnostic bit field. */ + port_status = 0; + ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index); + + /* Device Connection Status. */ + if (ohci_register_port_status & OHCI_HC_PS_CCS) + port_status |= UX_PS_CCS; + + /* Port Enable Status */ + if (ohci_register_port_status & OHCI_HC_PS_PES) + port_status |= UX_PS_PES; + + /* Port Suspend Status */ + if (ohci_register_port_status & OHCI_HC_PS_PSS) + port_status |= UX_PS_PSS; + + /* Port Overcurrent Status */ + if (ohci_register_port_status & OHCI_HC_PS_POCI) + port_status |= UX_PS_POCI; + + /* Port Reset Status */ + if (ohci_register_port_status & OHCI_HC_PS_PRS) + port_status |= UX_PS_PRS; + + /* Port Power Status */ + if (ohci_register_port_status & OHCI_HC_PS_PPS) + port_status |= UX_PS_PPS; + + /* Port Device Attached speed. This field is valid only if the CCS bit is active. + On OHCI, only low speed or full speed are available. */ + if (ohci_register_port_status & OHCI_HC_PS_CCS) + { + + if (ohci_register_port_status & OHCI_HC_PS_LSDA) + port_status |= UX_PS_DS_LS; + else + port_status |= UX_PS_DS_FS; + } + + /* Return port status. */ + return(port_status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_port_suspend.c b/common/usbx_host_controllers/src/ux_hcd_ohci_port_suspend.c new file mode 100644 index 0000000..b51c494 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_port_suspend.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_port_suspend PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will suspend a specific port attached to the root */ +/* HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_port_suspend(UX_HCD_OHCI *hcd_ohci, ULONG port_index) +{ + + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_power_down_port.c b/common/usbx_host_controllers/src/ux_hcd_ohci_power_down_port.c new file mode 100644 index 0000000..b52158c --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_power_down_port.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_power_down_port PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will power down a specific port attached to the */ +/* root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_power_down_port(UX_HCD_OHCI *hcd_ohci, ULONG port_index) +{ + + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_power_on_port.c b/common/usbx_host_controllers/src/ux_hcd_ohci_power_on_port.c new file mode 100644 index 0000000..40c4f81 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_power_on_port.c @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_power_on_port PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will power a specific port attached to the root HUB. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* port_index Port index */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_power_on_port(UX_HCD_OHCI *hcd_ohci, ULONG port_index) +{ + + return(UX_FUNCTION_NOT_SUPPORTED); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_power_root_hubs.c b/common/usbx_host_controllers/src/ux_hcd_ohci_power_root_hubs.c new file mode 100644 index 0000000..a85cbf8 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_power_root_hubs.c @@ -0,0 +1,148 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_power_root_hubs PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function powers individually or in gang mode the root HUBs */ +/* attached to the OHCI controller. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read OHCI register read */ +/* _ux_hcd_ohci_register_write OHCI register write */ +/* _ux_utility_delay_ms Delay */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ohci_power_root_hubs(UX_HCD_OHCI *hcd_ohci) +{ + +ULONG ohci_register_a; +ULONG ohci_register_b; +ULONG ohci_register_port_status; +UINT port_index; + + + /* Read the RH descriptor A. This will tell us if ports are always powered or not. */ + ohci_register_a = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_DESCRIPTOR_A); + if (ohci_register_a & OHCI_HC_RH_NPS) + return; + + /* Read the RH descriptor B. It will give us the characteristics of the root HUB. */ + ohci_register_b = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_DESCRIPTOR_B); + + /* The ports must be power switched. There are 3 possibilities: + + 1) individual + 2) gang mode + 3) a combination of both + + The logic is as follows: + + If the PSM bit is not set, gang mode is forced and we use the global power (LPSC) command. + If PSM is set, each port is powered individually. + + BUT we also need to look into the PPCM field to check if there is any ports + that may still want to be powered by the global power command. If the bit for a port in + the mask is set, the power is applied by the local port command in the RH port status (PPS). */ + if (ohci_register_a & OHCI_HC_RH_PSM) + { + + /* Check the PPCM field to see if some existing ports need to be powered by the LPSC command. */ + for (port_index = 0; port_index < hcd_ohci -> ux_hcd_ohci_nb_root_hubs; port_index++) + { + + if ((ohci_register_b & (0x20000 << port_index)) == 0) + { + + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_STATUS, OHCI_HC_RS_LPSC); + break; + } + } + + /* Ports have to be powered individually. This is done for each of the ports whose bit mask is + set in the PPCM field. */ + for (port_index = 0; port_index < hcd_ohci -> ux_hcd_ohci_nb_root_hubs; port_index++) + { + + if ((ohci_register_b & (0x20000 << port_index)) != 0) + { + + ohci_register_port_status = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index); + + ohci_register_port_status |= OHCI_HC_PS_PPS; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_PORT_STATUS + port_index, ohci_register_port_status); + } + } + } + else + { + + /* Ports have to be powered all at the same time. */ + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_RH_STATUS, OHCI_HC_RS_LPSC); + } + + /* Wait for the power to be stable. the RH descriptor contains the value POTPGT. We multiply this value by 2 + and this is the number of milliseconds to wait for power to set. */ + _ux_utility_delay_ms(ohci_register_a >> (OHCI_HC_RH_POTPGT - 1)); + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_register_read.c b/common/usbx_host_controllers/src/ux_hcd_ohci_register_read.c new file mode 100644 index 0000000..23ae571 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_register_read.c @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_register_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a register from the OHCI HCOR. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* ohci_register OHCI register to write to */ +/* */ +/* OUTPUT */ +/* */ +/* Value Read */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +ULONG _ux_hcd_ohci_register_read(UX_HCD_OHCI *hcd_ohci, ULONG ohci_register) +{ + + /* Return the value. */ + return(*(hcd_ohci -> ux_hcd_ohci_hcor + ohci_register)); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_register_write.c b/common/usbx_host_controllers/src/ux_hcd_ohci_register_write.c new file mode 100644 index 0000000..be3f026 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_register_write.c @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_register_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a register to the OHCI HCOR. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* ohci_register OHCI register to write to */ +/* value Value to write */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ohci_register_write(UX_HCD_OHCI *hcd_ohci, ULONG ohci_register, ULONG value) +{ + + /* Write to the register. */ + *(hcd_ohci -> ux_hcd_ohci_hcor + ohci_register) = value; + + /* Return to caller. */ + return; +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_regular_td_obtain.c b/common/usbx_host_controllers/src/ux_hcd_ohci_regular_td_obtain.c new file mode 100644 index 0000000..8f9b275 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_regular_td_obtain.c @@ -0,0 +1,117 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_regular_td_obtain PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function obtains a free TD from the regular TD list. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* */ +/* OUTPUT */ +/* */ +/* *UX_OHCI_TD Regular TD pointer */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_memory_set Set memory block */ +/* _ux_utility_mutex_on Get protection mutex */ +/* _ux_utility_mutex_off Release protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UX_OHCI_TD *_ux_hcd_ohci_regular_td_obtain(UX_HCD_OHCI *hcd_ohci) +{ + +UX_OHCI_TD *td; +ULONG td_index; + + + /* Set the Mutex as this is a critical section. */ + _ux_utility_mutex_on(&_ux_system -> ux_system_mutex); + + /* Start the search from the beginning of the regular TD list. */ + td = hcd_ohci -> ux_hcd_ohci_td_list; + + for (td_index = 0; td_index < _ux_system_host -> ux_system_host_max_td; td_index++) + { + + /* Check the TD status, a free TD is marked with the UNUSED flag. */ + if (td -> ux_ohci_td_status == UX_UNUSED) + { + + /* The TD may have been used, so we reset all fields. */ + _ux_utility_memory_set(td, 0, sizeof(UX_OHCI_TD)); + + /* This TD is now marked as USED. */ + td -> ux_ohci_td_status = UX_USED; + + /* Release the protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Return TD pointer - success! */ + return(td); + } + + /* Move to the next TD. */ + td++; + } + + /* There is no available TD in the TD list. */ + + /* Release protection. */ + _ux_utility_mutex_off(&_ux_system -> ux_system_mutex); + + /* Return NULL to caller. */ + return(UX_NULL); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_request_bulk_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ohci_request_bulk_transfer.c new file mode 100644 index 0000000..d65c88f --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_request_bulk_transfer.c @@ -0,0 +1,242 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_request_bulk_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a bulk transfer request. A bulk transfer */ +/* can be larger than the size of the OHCI buffer so it may be */ +/* required to chain multiple tds to accommodate this transfer */ +/* request. A bulk transfer is non blocking, so we return before the */ +/* transfer request is completed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* _ux_hcd_ohci_regular_td_obtain Get regular TD */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_request_bulk_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_OHCI_TD *data_td; +UX_OHCI_TD *start_data_td; +UX_OHCI_TD *next_data_td; +UX_OHCI_TD *previous_td; +UX_OHCI_TD *tail_td; +UX_OHCI_ED *ed; +ULONG transfer_request_payload_length; +ULONG bulk_packet_payload_length; +UCHAR * data_pointer; +ULONG ohci_register; +ULONG zlp_flag; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Use the TD pointer by ed -> tail for the first TD of this transfer + and chain from this one on. */ + data_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + previous_td = data_td; + + /* Reset the first obtained data TD in case there is a TD shortage while building the list of tds. */ + start_data_td = 0; + + /* It may take more than one TD if the transfer_request length is more than the + maximum length for a OHCI TD (this is irrelevant of the MaxPacketSize value in + the endpoint descriptor). OHCI data payload has a maximum size of 4K. */ + transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length; + data_pointer = transfer_request -> ux_transfer_request_data_pointer; + + /* Check for ZLP condition. */ + if (transfer_request_payload_length == 0) + + /* We have a zlp condition. */ + zlp_flag = UX_TRUE; + else + + /* We do not have a zlp. */ + zlp_flag = UX_FALSE; + + /* Build all necessary TDs. */ + while ((transfer_request_payload_length != 0) || zlp_flag == UX_TRUE) + { + + /* Reset ZLP now. */ + zlp_flag = UX_FALSE; + + /* Check if we are exceeding the max payload. */ + if (transfer_request_payload_length > UX_OHCI_MAX_PAYLOAD) + + bulk_packet_payload_length = UX_OHCI_MAX_PAYLOAD; + else + + bulk_packet_payload_length = transfer_request_payload_length; + + /* IN transfer ? */ + if ((transfer_request -> ux_transfer_request_type&UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_IN | UX_OHCI_TD_DEFAULT_DW0; + else + + data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_OUT | UX_OHCI_TD_DEFAULT_DW0; + + /* Store the beginning of the buffer address in the TD. */ + data_td -> ux_ohci_td_cbp = _ux_utility_physical_address(data_pointer); + + /* Store the end buffer address in the TD. */ + data_td -> ux_ohci_td_be = data_td -> ux_ohci_td_cbp + bulk_packet_payload_length - 1; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_ohci_td_length = bulk_packet_payload_length; + + /* Attach the endpoint and transfer request to the TD. */ + data_td -> ux_ohci_td_transfer_request = transfer_request; + data_td -> ux_ohci_td_ed = ed; + + /* Adjust the data payload length and the data payload pointer. */ + transfer_request_payload_length -= bulk_packet_payload_length; + data_pointer += bulk_packet_payload_length; + + /* Check if there will be another transaction. */ + if (transfer_request_payload_length != 0) + { + + /* Get a new TD to hook this payload. */ + data_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + + if (data_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while(data_td) + { + + next_data_td = _ux_utility_virtual_address(data_td -> ux_ohci_td_next_td); + data_td -> ux_ohci_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* the first obtained TD in the chain has to be remembered. */ + if (start_data_td == UX_NULL) + start_data_td = data_td; + + /* Attach this new TD to the previous one. */ + previous_td -> ux_ohci_td_next_td = _ux_utility_physical_address(data_td); + previous_td -> ux_ohci_td_next_td_transfer_request = data_td; + previous_td = data_td; + } + } + + /* At this stage, the Head and Tail in the ED are still the same and the OHCI controller + will skip this ED until we have hooked the new tail TD. */ + tail_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + if (tail_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while(data_td) + { + + next_data_td = _ux_utility_virtual_address(data_td -> ux_ohci_td_next_td); + data_td -> ux_ohci_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the tail TD to the last data TD. */ + data_td -> ux_ohci_td_next_td = _ux_utility_physical_address(tail_td); + + /* Store the new tail TD. */ + ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(tail_td); + + /* Now, we must tell the OHCI controller that there is something in the + bulk queue. */ + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS); + ohci_register |= OHCI_HC_CS_BLF; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, ohci_register); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_request_control_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ohci_request_control_transfer.c new file mode 100644 index 0000000..a7900b9 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_request_control_transfer.c @@ -0,0 +1,273 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_request_control_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a control transfer from a transfer request. */ +/* The USB control transfer is in 3 phases (setup, data, status). */ +/* This function will chain all phases of the control sequence before */ +/* setting the OHCI endpoint as a candidate for transfer. */ +/* */ +/* The maximum aggregated size of a data payload in OHCI is 4K. We */ +/* are assuming that this size will be sufficient to contain the */ +/* control packet. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_register_read Read OHCI register */ +/* _ux_hcd_ohci_register_write Write OHCI register */ +/* _ux_hcd_ohci_regular_td_obtain Get regular TD */ +/* _ux_host_stack_transfer_request_abort Abort transfer request */ +/* _ux_utility_memory_allocate Allocate memory block */ +/* _ux_utility_memory_free Release memory block */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_semaphore_get Get semaphore */ +/* _ux_utility_short_put Write 16-bit value */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_request_control_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request) +{ + +UX_DEVICE *device; +UX_ENDPOINT *endpoint; +UCHAR * setup_request; +UX_OHCI_ED *ed; +UX_OHCI_TD *setup_td; +UX_OHCI_TD *chain_td; +UX_OHCI_TD *data_td; +UX_OHCI_TD *tail_td; +UX_OHCI_TD *status_td; +ULONG ohci_register; +UINT status; + + + /* Get the pointer to the Endpoint and the Device. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + device = endpoint -> ux_endpoint_device; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Build the SETUP packet (phase 1 of the control transfer). */ + setup_request = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, UX_SETUP_SIZE); + if (setup_request == UX_NULL) + return(UX_MEMORY_INSUFFICIENT); + + *setup_request = transfer_request -> ux_transfer_request_function; + *(setup_request + UX_SETUP_REQUEST_TYPE) = transfer_request -> ux_transfer_request_type; + *(setup_request + UX_SETUP_REQUEST) = transfer_request -> ux_transfer_request_function; + _ux_utility_short_put(setup_request + UX_SETUP_VALUE, transfer_request -> ux_transfer_request_value); + _ux_utility_short_put(setup_request + UX_SETUP_INDEX, transfer_request -> ux_transfer_request_index); + _ux_utility_short_put(setup_request + UX_SETUP_LENGTH, (USHORT) transfer_request -> ux_transfer_request_requested_length); + + /* Set the ED address and MPS values since they may have changed. + The ED direction will be set from the TD. */ + ed -> ux_ohci_ed_dw0 = device -> ux_device_address | ((ULONG) endpoint -> ux_endpoint_descriptor.bEndpointAddress << 7) | + ((ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize << 16); + + /* Refresh the speed. */ + if (device -> ux_device_speed == UX_LOW_SPEED_DEVICE) + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_LOW_SPEED; + + /* Use the TD pointer by ed -> tail for our setup TD and chain from this one on. */ + setup_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + setup_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_DATA0 | UX_OHCI_TD_R; + setup_td -> ux_ohci_td_cbp = _ux_utility_physical_address(setup_request); + setup_td -> ux_ohci_td_be = setup_td -> ux_ohci_td_cbp + UX_SETUP_SIZE - 1; + chain_td = setup_td; + + /* Attach the endpoint and transfer request to the TD. */ + setup_td -> ux_ohci_td_transfer_request = transfer_request; + setup_td -> ux_ohci_td_ed = ed; + + /* Mark the TD with the SETUP phase. */ + setup_td -> ux_ohci_td_status |= UX_OHCI_TD_SETUP_PHASE; + + /* Check if there is a data phase, if not jump to status phase. */ + data_td = UX_NULL; + if (transfer_request -> ux_transfer_request_requested_length != 0) + { + + data_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + if (data_td == UX_NULL) + { + + _ux_utility_memory_free(setup_request); + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the endpoint and transfer request to the TD. */ + data_td -> ux_ohci_td_transfer_request = transfer_request; + data_td -> ux_ohci_td_ed = ed; + + /* Mark the TD with the DATA phase. */ + data_td -> ux_ohci_td_status |= UX_OHCI_TD_DATA_PHASE; + + /* Program the control bits of the TD. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_IN | UX_OHCI_TD_DATA1 | UX_OHCI_TD_R; + else + + data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_OUT | UX_OHCI_TD_DATA1 | UX_OHCI_TD_R; + + /* Attach the CBP and BE values to the TD. */ + data_td -> ux_ohci_td_cbp = _ux_utility_physical_address(transfer_request -> ux_transfer_request_data_pointer); + data_td -> ux_ohci_td_be = data_td -> ux_ohci_td_cbp + transfer_request -> ux_transfer_request_requested_length - 1; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_ohci_td_length = transfer_request -> ux_transfer_request_requested_length; + + /* Chain the TD. */ + chain_td -> ux_ohci_td_next_td = _ux_utility_physical_address(data_td); + chain_td = data_td; + } + + /* Now, program the status phase. */ + status_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + if (status_td == UX_NULL) + { + + _ux_utility_memory_free(setup_request); + if (data_td != UX_NULL) + data_td -> ux_ohci_td_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the endpoint and transfer request to the TD. */ + status_td -> ux_ohci_td_transfer_request = transfer_request; + status_td -> ux_ohci_td_ed = ed; + + /* Mark the TD with the STATUS phase. */ + status_td -> ux_ohci_td_status |= UX_OHCI_TD_STATUS_PHASE; + + /* The direction of the status phase is IN if data phase is OUT and + vice versa. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + status_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_OUT | UX_OHCI_TD_DATA1; + else + + status_td -> ux_ohci_td_dw0 = UX_OHCI_TD_DEFAULT_DW0 | UX_OHCI_TD_IN | UX_OHCI_TD_DATA1; + + /* No data payload for the status phase. */ + status_td -> ux_ohci_td_cbp = 0; + status_td -> ux_ohci_td_be = 0; + + /* Hook the status phase to the previous TD. */ + chain_td -> ux_ohci_td_next_td = _ux_utility_physical_address(status_td); + + /* Since we have consumed out tail TD for the setup packet, we must get another + one and hook it to the ED's tail. */ + tail_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + if (tail_td == UX_NULL) + { + + _ux_utility_memory_free(setup_request); + if (data_td != UX_NULL) + data_td -> ux_ohci_td_status = UX_UNUSED; + status_td -> ux_ohci_td_status = UX_UNUSED; + return(UX_NO_TD_AVAILABLE); + } + + /* Hook the new TD to the status TD. */ + status_td -> ux_ohci_td_next_td = _ux_utility_physical_address(tail_td); + + /* At this stage, the Head and Tail in the ED are still the same and + the OHCI controller will skip this ED until we have hooked the new + tail TD. */ + ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(tail_td); + + /* Now, we must tell the OHCI controller that there is something in the + control queue. */ + ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS); + ohci_register |= OHCI_HC_CS_CLF; + _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, ohci_register); + + /* Wait for the completion of the transfer request. */ + status = _ux_utility_semaphore_get(&transfer_request -> ux_transfer_request_semaphore, MS_TO_TICK(UX_CONTROL_TRANSFER_TIMEOUT)); + + /* If the semaphore did not succeed we probably have a time out. */ + if (status != UX_SUCCESS) + { + + /* All transfers pending need to abort. There may have been a partial transfer. */ + _ux_host_stack_transfer_request_abort(transfer_request); + + /* There was an error, return to the caller. */ + transfer_request -> ux_transfer_request_completion_code = UX_TRANSFER_TIMEOUT; + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_TRANSFER_TIMEOUT); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_TRANSFER_TIMEOUT, transfer_request, 0, 0, UX_TRACE_ERRORS, 0, 0) + + } + + /* Free the resources. */ + _ux_utility_memory_free(setup_request); + + /* Return the completion status. */ + return(transfer_request -> ux_transfer_request_completion_code); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_request_interupt_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ohci_request_interupt_transfer.c new file mode 100644 index 0000000..a981267 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_request_interupt_transfer.c @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_request_interrupt_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs an interrupt transfer request. An interrupt */ +/* transfer can only be as large as the MaxpacketField in the */ +/* endpoint descriptor. This was verified at a higher layer and does */ +/* not need to be reverified here. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_regular_td_obtain Get regular TD */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_request_interrupt_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_OHCI_ED *ed; +UX_OHCI_TD *data_td; +UX_OHCI_TD *tail_td; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Use the TD pointer by ed -> tail for the first TD of this transfer + and chain from this one on. */ + data_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + + /* Set the direction of the transfer. In USB 1.0, the direction of the Interrupt pipe could + only be HOST to DEVICE. In 1.1 bidirectional interrupt endpoints can be allowed. The + direction was checked when the endpoint was initialized. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_IN | UX_OHCI_TD_DEFAULT_DW0; + else + + data_td -> ux_ohci_td_dw0 = UX_OHCI_TD_OUT | UX_OHCI_TD_DEFAULT_DW0; + + /* Store the beginning of the buffer address in the TD. */ + data_td -> ux_ohci_td_cbp = _ux_utility_physical_address(transfer_request -> ux_transfer_request_data_pointer); + + /* Store the end buffer address in the TD. */ + data_td -> ux_ohci_td_be = data_td -> ux_ohci_td_cbp + transfer_request -> ux_transfer_request_requested_length - 1; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_ohci_td_length = transfer_request -> ux_transfer_request_requested_length; + + /* Attach the endpoint and transfer_request to the TD. */ + data_td -> ux_ohci_td_transfer_request = transfer_request; + data_td -> ux_ohci_td_ed = ed; + + /* At this stage, the Head and Tail in the ED are still the same and the OHCI controller + will skip this ED until we have hooked the new tail TD. */ + tail_td = _ux_hcd_ohci_regular_td_obtain(hcd_ohci); + if (tail_td == UX_NULL) + return(UX_NO_TD_AVAILABLE); + + /* Attach the tail TD to the last data TD. */ + data_td -> ux_ohci_td_next_td = _ux_utility_physical_address(tail_td); + + /* Store the new tail TD. */ + ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(tail_td); + + /* There is no need to wake up the ohci controller on this transfer + since periodic transactions will be picked up when the interrupt + tree is scanned. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_request_isochronous_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ohci_request_isochronous_transfer.c new file mode 100644 index 0000000..9000e0c --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_request_isochronous_transfer.c @@ -0,0 +1,246 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_request_isochronous_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs an isochronous transfer request. This */ +/* function does not support multiple packets per TD as there can be */ +/* issues with packets crossing 4K pages. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_isochronous_td_obtain Get isochronous TD */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_request_isochronous_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_OHCI_ISO_TD *data_td; +UX_OHCI_ISO_TD *start_data_td; +UX_OHCI_ISO_TD *next_data_td; +UX_OHCI_ISO_TD *previous_td; +UX_OHCI_ISO_TD *tail_td; +UX_OHCI_ED *ed; +ULONG transfer_request_payload_length; +ULONG isoch_packet_payload_length; +UCHAR * data_pointer; +ULONG current_frame_number; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* Now get the physical ED attached to this endpoint. */ + ed = endpoint -> ux_endpoint_ed; + + /* Reset the MPS value of the ED. */ + ed -> ux_ohci_ed_dw0 &= UX_OHCI_ED_MPS; + + /* If the transfer request specifies a max packet length other than the endpoint + size, we force the transfer request value into the endpoint. */ + if (transfer_request -> ux_transfer_request_packet_length == 0) + transfer_request -> ux_transfer_request_packet_length = (ULONG) endpoint -> ux_endpoint_descriptor.wMaxPacketSize; + + /* Set the packet length in the ED. */ + ed -> ux_ohci_ed_dw0 |= transfer_request -> ux_transfer_request_packet_length << 16; + isoch_packet_payload_length = transfer_request -> ux_transfer_request_packet_length; + + /* Use the TD pointer by ed -> tail for the first TD of this transfer + and chain from this one on. */ + data_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + previous_td = data_td; + + /* Reset the first obtained data TD in case there is a TD shortage while building the list of TDs. */ + start_data_td = UX_NULL; + + /* Calculate the frame number to be used to send this payload. If there are no current transfers, + we take the current frame number and add a safety value (2-5) to it. If here is pending transactions, + we use the frame number stored in the transfer request. */ + if (ed -> ux_ohci_ed_tail_td == ed -> ux_ohci_ed_head_td) + { + + current_frame_number = hcd_ohci -> ux_hcd_ohci_hcca -> ux_hcd_ohci_hcca_frame_number + UX_OHCI_FRAME_DELAY; + ed -> ux_ohci_ed_frame = current_frame_number; + } + else + current_frame_number = ed -> ux_ohci_ed_frame; + + /* Load the start buffer address and URB length to split the URB in multiple TD transfer. */ + transfer_request_payload_length = transfer_request -> ux_transfer_request_requested_length; + data_pointer = transfer_request -> ux_transfer_request_data_pointer; + + while (transfer_request_payload_length != 0) + { + + /* Set the default CC value. Default is 1 packet per TD frame. */ + data_td -> ux_ohci_iso_td_dw0 = UX_OHCI_TD_DEFAULT_DW0; + + /* Set the direction. In Iso, this is done at the ED level. */ + if ((transfer_request -> ux_transfer_request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) + + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_IN; + else + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_OUT; + + /* Set the frame number. */ + data_td -> ux_ohci_iso_td_dw0 |= (USHORT) current_frame_number; + + /* The buffer address is divided into a page and an offset. */ + data_td -> ux_ohci_iso_td_bp0 = _ux_utility_physical_address((VOID *)((ULONG) data_pointer & UX_OHCI_ISO_TD_BASE)); + data_td -> ux_ohci_iso_td_offset_psw[0] = (USHORT)((ULONG) _ux_utility_physical_address(data_pointer) & UX_OHCI_ISO_TD_OFFSET); + + /* Set the condition code for the current packet. */ + data_td -> ux_ohci_iso_td_offset_psw[0] |= (USHORT) UX_OHCI_ISO_TD_PSW_CC; + + /* Program the end address of the buffer. */ + data_td -> ux_ohci_iso_td_be = ((UCHAR *) _ux_utility_physical_address(data_pointer)) + isoch_packet_payload_length - 1; + + /* Update the length of the transfer for this TD. */ + data_td -> ux_ohci_iso_td_length = isoch_packet_payload_length; + + /* Attach the endpoint and transfer request to the TD. */ + data_td -> ux_ohci_iso_td_transfer_request = transfer_request; + data_td -> ux_ohci_iso_td_ed = ed; + + /* Adjust the data payload length and the data payload pointer. */ + transfer_request_payload_length -= isoch_packet_payload_length; + data_pointer += isoch_packet_payload_length; + + /* Prepare the next frame for the next TD in advance. */ + current_frame_number++; + + /* Check if there will be another transaction. */ + if (transfer_request_payload_length != 0) + { + + /* Get a new TD to hook this payload. */ + data_td = _ux_hcd_ohci_isochronous_td_obtain(hcd_ohci); + if (data_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while(data_td) + { + + next_data_td = _ux_utility_virtual_address(data_td -> ux_ohci_iso_td_next_td); + data_td -> ux_ohci_iso_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* the first obtained TD in the chain has to be remembered. */ + if (start_data_td == UX_NULL) + start_data_td = data_td; + + /* Attach this new TD to the previous one. */ + previous_td -> ux_ohci_iso_td_next_td = _ux_utility_physical_address(data_td); + previous_td = data_td; + } + } + + /* Memorize the next frame number for this ED. */ + ed -> ux_ohci_ed_frame = current_frame_number; + + /* At this stage, the Head and Tail in the ED are still the same and + the OHCI controller will skip this ED until we have hooked the new + tail TD. */ + tail_td = _ux_hcd_ohci_isochronous_td_obtain(hcd_ohci); + if (tail_td == UX_NULL) + { + + /* If there was already a TD chain in progress, free it. */ + if (start_data_td != UX_NULL) + { + + data_td = start_data_td; + while (data_td) + { + + next_data_td = _ux_utility_virtual_address(data_td -> ux_ohci_iso_td_next_td); + data_td -> ux_ohci_iso_td_status = UX_UNUSED; + data_td = next_data_td; + } + } + + return(UX_NO_TD_AVAILABLE); + } + + /* Attach the tail TD to the last data TD. */ + data_td -> ux_ohci_iso_td_next_td = _ux_utility_physical_address(tail_td); + + /* Adjust the ED tail pointer, the controller can now start this transfer + at the chosen frame number. */ + ed -> ux_ohci_ed_tail_td = _ux_utility_physical_address(tail_td); + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_request_transfer.c b/common/usbx_host_controllers/src/ux_hcd_ohci_request_transfer.c new file mode 100644 index 0000000..a962860 --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_request_transfer.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_request_transfer PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the handler for all the transactions on the USB. */ +/* The transfer request passed as parameter contains the endpoint and */ +/* the device descriptors in addition to the type of transaction to */ +/* be executed. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_hcd_ohci_request_bulk_transfer Start bulk transfer */ +/* _ux_hcd_ohci_request_control_transfer Start control transfer */ +/* _ux_hcd_ohci_request_interrupt_transfer Start interrupt transfer */ +/* _ux_hcd_ohci_request_isochronous_transfer Start isochronous transfer*/ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_request_transfer(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UINT status; + + + /* Get the pointer to the Endpoint. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* We reset the actual length field of the transfer request as a safety measure. */ + transfer_request -> ux_transfer_request_actual_length = 0; + + /* Isolate the endpoint type and route the transfer request. */ + switch ((endpoint -> ux_endpoint_descriptor.bmAttributes) & UX_MASK_ENDPOINT_TYPE) + { + + case UX_CONTROL_ENDPOINT: + + /* Control transfer. */ + status = _ux_hcd_ohci_request_control_transfer(hcd_ohci, transfer_request); + break; + + + case UX_BULK_ENDPOINT: + + /* Bulk transfer. */ + status = _ux_hcd_ohci_request_bulk_transfer(hcd_ohci, transfer_request); + break; + + + case UX_INTERRUPT_ENDPOINT: + + /* Interrupt transfer. */ + status = _ux_hcd_ohci_request_interrupt_transfer(hcd_ohci, transfer_request); + break; + + + case UX_ISOCHRONOUS_ENDPOINT: + + /* Isochronous transfer. */ + status = _ux_hcd_ohci_request_isochronous_transfer(hcd_ohci, transfer_request); + break; + + } + + /* Note that it is physically impossible to have a wrong endpoint type here + so no error checking! */ + + /* Return completion status. */ + return(status); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_transfer_abort.c b/common/usbx_host_controllers/src/ux_hcd_ohci_transfer_abort.c new file mode 100644 index 0000000..61c7a9e --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_transfer_abort.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_transfer_abort PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function will abort transactions attached to a transfer */ +/* request. */ +/* */ +/* INPUT */ +/* */ +/* hcd_ohci Pointer to OHCI controller */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* _ux_utility_delay_ms Delay */ +/* _ux_utility_physical_address Get physical address */ +/* _ux_utility_virtual_address Get virtual address */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_hcd_ohci_transfer_abort(UX_HCD_OHCI *hcd_ohci, UX_TRANSFER *transfer_request) +{ + +UX_ENDPOINT *endpoint; +UX_OHCI_ED *ed; +UX_OHCI_TD *head_td; +UX_OHCI_TD *tail_td; +ULONG value_td; +ULONG value_carry; + + + /* Get the pointer to the endpoint associated with the transfer request. */ + endpoint = (UX_ENDPOINT *) transfer_request -> ux_transfer_request_endpoint; + + /* From the endpoint container, get the address of the physical endpoint. */ + ed = (UX_OHCI_ED *) endpoint -> ux_endpoint_ed; + + /* Check if this physical endpoint has been initialized properly! */ + if (ed == UX_NULL) + { + + /* Error trap. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_ENDPOINT_HANDLE_UNKNOWN); + + /* If trace is enabled, insert this event into the trace buffer. */ + UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_ENDPOINT_HANDLE_UNKNOWN, endpoint, 0, 0, UX_TRACE_ERRORS, 0, 0) + + return(UX_ENDPOINT_HANDLE_UNKNOWN); + } + + /* The endpoint may be active. If so, set the skip bit. */ + ed -> ux_ohci_ed_dw0 |= UX_OHCI_ED_SKIP; + + /* Wait for the controller to finish the current frame processing. */ + _ux_utility_delay_ms(1); + + /* Ensure that the potential Carry bit is maintained in the head ED. */ + value_carry = (ULONG) _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td) & UX_OHCI_ED_TOGGLE_CARRY; + + /* Ensure that the potential Halt bit is removed in the head ED. */ + value_td = (ULONG) _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td) & UX_OHCI_ED_MASK_TD; + head_td = (UX_OHCI_TD *) _ux_utility_physical_address((VOID *) value_td); + ed -> ux_ohci_ed_head_td = head_td; + + /* Remove all the tds from this ED and leave the head and tail pointing + to the dummy TD. */ + tail_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_tail_td); + + /* Free all tds attached to the ED */ + while (head_td != tail_td) + { + + /* Update the head TD with the next TD. */ + ed -> ux_ohci_ed_head_td = head_td -> ux_ohci_td_next_td; + + /* Mark the current head TD as free. */ + head_td -> ux_ohci_td_status = UX_UNUSED; + + /* Now the new head TD is the next TD in the chain. */ + head_td = _ux_utility_virtual_address(ed -> ux_ohci_ed_head_td); + } + + /* Restore the value carry for next transfers. */ + value_td = (ULONG) ed -> ux_ohci_ed_head_td; + value_td |= value_carry; + ed -> ux_ohci_ed_head_td = (UX_OHCI_TD *) value_td; + + /* Remove the reset bit in the ED. */ + ed -> ux_ohci_ed_dw0 &= ~UX_OHCI_ED_SKIP; + + /* Return successful completion. */ + return(UX_SUCCESS); +} + diff --git a/common/usbx_host_controllers/src/ux_hcd_ohci_transfer_request_process.c b/common/usbx_host_controllers/src/ux_hcd_ohci_transfer_request_process.c new file mode 100644 index 0000000..ae7bb7a --- /dev/null +++ b/common/usbx_host_controllers/src/ux_hcd_ohci_transfer_request_process.c @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** OHCI Controller Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#define UX_SOURCE_CODE + +#include "ux_api.h" +#include "ux_hcd_ohci.h" +#include "ux_host_stack.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_hcd_ohci_transfer_request_process PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function process the transfer request that was completed */ +/* either with success or because of a partial transmission or */ +/* because of an error. The transfer request descriptor tells us what */ +/* to do with it, either put a semaphore to the caller or invoke a */ +/* completion routine. If a completion routine is specified, the */ +/* routine is called and no semaphore is put. */ +/* */ +/* INPUT */ +/* */ +/* transfer_request Pointer to transfer request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (ux_transfer_request_completion_function) Completion function */ +/* _ux_utility_semaphore_put Signal transfer complete */ +/* */ +/* CALLED BY */ +/* */ +/* OHCI Controller Driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_hcd_ohci_transfer_request_process(UX_TRANSFER *transfer_request) +{ + + /* Check if there is a function for the transfer completion. */ + if (transfer_request -> ux_transfer_request_completion_function != UX_NULL) + + /* Yes, so we call it! */ + transfer_request -> ux_transfer_request_completion_function(transfer_request); + else + + /* There is a semaphore so send the signal to the class. */ + _ux_utility_semaphore_put(&transfer_request -> ux_transfer_request_semaphore); + + /* Return to caller. */ + return; +} diff --git a/common/usbx_network/CMakeLists.txt b/common/usbx_network/CMakeLists.txt new file mode 100644 index 0000000..94b0a92 --- /dev/null +++ b/common/usbx_network/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/ux_network_driver.c + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/common/usbx_network/inc/ux_network_driver.h b/common/usbx_network/inc/ux_network_driver.h new file mode 100644 index 0000000..1e35c1d --- /dev/null +++ b/common/usbx_network/inc/ux_network_driver.h @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** USBX Network Driver for NETX 5.3 and above */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* ux_network_driver.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains all the header and extern functions used by the */ +/* USBX Network driver. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_NETWORK_DRIVER_H +#define UX_NETWORK_DRIVER_H + +#include "tx_api.h" +#include "nx_api.h" +#define USB_NETWORK_DEVICE_MAX_INSTANCES 8 +#define USB_NETWORK_DRIVER_SUCCESS UX_SUCCESS +#define USB_NETWORK_DRIVER_FAILURE UX_ERROR + +#define USB_NETWORK_DEVICE_MAC_HEADER_SIZE 14 +#define NX_ETHERNET_SIZE 14 +#define NX_ETHERNET_ARP 0x0806 +#define NX_ETHERNET_RARP 0x0835 +#define NX_ETHERNET_IP 0x0800 +#define NX_ETHERNET_IPV6 0x08DD +#define NX_ETHERNET_MTU 1514 + +typedef struct USB_NETWORK_DEVICE_STRUCT +{ + + /* ip_instance is populated by NetX, as part of the interface attachment. */ + NX_IP *ux_network_device_ip_instance; + + /* interface_ptr is populated by NetX, as part of the interface attachment. */ + NX_INTERFACE *ux_network_device_interface_ptr; + + /* Define synchronization objecs for deactivation. Note that these are only + used if the activation/deactivation functions are not called under interrupt. */ + UCHAR ux_network_device_activated_by_thread; + TX_MUTEX ux_network_device_deactivate_mutex; + TX_SEMAPHORE ux_network_device_deactivate_semaphore; + UCHAR ux_network_device_deactivate_thread_waiting; + UINT ux_network_device_num_threads_inside; + + /* usb_instance is populated by USB instance activation. */ + VOID *ux_network_device_usb_instance_ptr; + + /* The write_function is populated by USB instance activation. */ + UINT (*ux_network_device_write_function)(VOID *ux_instance, NX_PACKET *packet_ptr); + + USHORT ux_network_device_usb_link_up; + USHORT ux_network_device_link_status; + + ULONG ux_network_physical_address_msw; + ULONG ux_network_physical_address_lsw; + + +} USB_NETWORK_DEVICE_TYPE; + + +UINT _ux_network_driver_init(VOID); + +UINT _ux_network_driver_activate(VOID *ux_instance, UINT(*ux_network_device_write_function)(VOID *, NX_PACKET *), + VOID **ux_network_handle, ULONG physical_address_msw, ULONG physical_address_lsw); + +UINT _ux_network_driver_deactivate(VOID *ux_instance, VOID *ux_network_handle); + + +VOID _ux_network_driver_entry(NX_IP_DRIVER *nx_ip_driver); + +VOID _ux_network_driver_link_up(VOID *ux_network_handle); +VOID _ux_network_driver_link_down(VOID *ux_network_handle); + +VOID _ux_network_driver_packet_received(VOID *ux_network_handle, NX_PACKET *packet_ptr); +#endif diff --git a/common/usbx_network/src/ux_network_driver.c b/common/usbx_network/src/ux_network_driver.c new file mode 100644 index 0000000..ae22d83 --- /dev/null +++ b/common/usbx_network/src/ux_network_driver.c @@ -0,0 +1,937 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** USBX Network Driver for NETX 5.3 and above. */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include "tx_api.h" +#include "tx_thread.h" +#include "nx_api.h" +#include "ux_api.h" + +#include "ux_network_driver.h" + +static UINT usb_network_driver_initialized; + +static USB_NETWORK_DEVICE_TYPE usb_network_devices[USB_NETWORK_DEVICE_MAX_INSTANCES]; + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_network_init PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by the application to initialize the */ +/* USBX portion of the network driver. */ +/* */ +/* INPUT */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_network_driver_init(VOID) +{ + +UINT status = NX_SUCCESS; + + /* Check if driver is already initialized. */ + if (usb_network_driver_initialized == 0) + { + + /* Driver is not initialized yet. */ + usb_network_driver_initialized = 1; + + /* Reset the network device memory array. */ + _ux_utility_memory_set(&usb_network_devices[0], 0, sizeof(usb_network_devices)); + } + + return(status); +} + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_network_driver_activate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* The USB network driver activate function is called as the USB instance */ +/* is created. This API takes a pointer to the instance, and returns a */ +/* ux_network_handle back to the instance. Everytime the instance receives*/ +/* a network packet, it should call ux_network_driver_packet_received with*/ +/* ux_network_handle. */ +/* */ +/* INPUT */ +/* */ +/* ux_instance Instance of the USBX network class */ +/* ux_network_device_write_function Address of the function to write a */ +/* packet when sent by the application */ +/* ux_network_handle Address where to store the network */ +/* handle */ +/* */ +/* physical_address_msw Most significant word of network ad */ +/* */ +/* physical_address_lsw Least significant word of network ad */ +/* */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Result */ +/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +UINT _ux_network_driver_activate(VOID *ux_instance, UINT(*ux_network_device_write_function)(VOID *, NX_PACKET *), + VOID **ux_network_handle, ULONG physical_address_msw, ULONG physical_address_lsw) +{ + +TX_INTERRUPT_SAVE_AREA + +UINT i; + + /* Critical section. */ + TX_DISABLE + + /* Find an available entry in the usb_network_devices table. */ + for (i = 0; i < USB_NETWORK_DEVICE_MAX_INSTANCES; i++) + { + + /* If the ptr to instance is NULL, we have a free entry. */ + if (usb_network_devices[i].ux_network_device_usb_instance_ptr == NX_NULL) + { + + /* Add the instance of the USBX class driver to the network device. */ + usb_network_devices[i].ux_network_device_usb_instance_ptr = ux_instance; + + break; + } + } + + /* Unprotect the critical section. */ + TX_RESTORE + + /* Did we reach the max number of instance ? */ + if (i == USB_NETWORK_DEVICE_MAX_INSTANCES) + { + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT); + + /* Return error. */ + return(USB_NETWORK_DRIVER_FAILURE); + } + + /* Store the write function. */ + usb_network_devices[i].ux_network_device_write_function = ux_network_device_write_function; + + /* Store the physical address of the network interface. */ + usb_network_devices[i].ux_network_physical_address_msw = physical_address_msw; + usb_network_devices[i].ux_network_physical_address_lsw = physical_address_lsw; + + /* Are we not under interrupt? */ + if (TX_THREAD_GET_SYSTEM_STATE() == 0) + { + + /* Note that we were activated by a thread. */ + usb_network_devices[i].ux_network_device_activated_by_thread = UX_TRUE; + + /* Create deactivation sync objects. */ + _ux_utility_mutex_create(&usb_network_devices[i].ux_network_device_deactivate_mutex, "usb network device mutex"); + _ux_utility_semaphore_create(&usb_network_devices[i].ux_network_device_deactivate_semaphore, "usb network device semaphore", 0); + } + + /* Is there an interface at the NETX level ? */ + if (usb_network_devices[i].ux_network_device_interface_ptr) + { + + /* Store the physical address at the NETX level. */ + usb_network_devices[i].ux_network_device_interface_ptr -> nx_interface_physical_address_msw = physical_address_msw; + usb_network_devices[i].ux_network_device_interface_ptr -> nx_interface_physical_address_lsw = physical_address_lsw; + + /* Is the link UP ? */ + if (usb_network_devices[i].ux_network_device_interface_ptr -> nx_interface_link_up == NX_TRUE) + + /* Yes, store its state. */ + usb_network_devices[i].ux_network_device_link_status = NX_TRUE; + + } + else + + /* Link not yet up. */ + usb_network_devices[i].ux_network_device_link_status = NX_FALSE; + + + /* Is there a network handle associated ? */ + if (ux_network_handle) + + /* Yes, the application wants to know its address. */ + *ux_network_handle = (VOID*)&usb_network_devices[i]; + + /* The operation was successful. */ + return(USB_NETWORK_DRIVER_SUCCESS); +} + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_network_driver_deactivate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* The USB network driver activate function is called as the USB instance */ +/* is created. This API takes a pointer to the instance, and returns a */ +/* ux_network_handle back to the instance. Everytime the instance receives*/ +/* a network packet, it should call ux_network_driver_packet_received with*/ +/* ux_network_handle. */ +/* */ +/* INPUT */ +/* */ +/* ux_instance Instance of the USBX network class */ +/* ux_network_device_write_function Address of the function to write a */ +/* packet when sent by the application */ +/* ux_network_handle Address where to store the network */ +/* handle */ +/* */ +/* physical_address_msw Most significant word of network ad */ +/* */ +/* physical_address_lsw Least significant word of network ad */ +/* */ +/* */ +/* */ +/* OUTPUT */ +/* */ +/* Result */ +/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _ux_network_driver_deactivate(VOID *ux_instance, VOID *ux_network_handle) +{ + +TX_INTERRUPT_SAVE_AREA + +USB_NETWORK_DEVICE_TYPE *usb_network_device; + + UX_PARAMETER_NOT_USED(ux_instance); + + /* Check if the handle exists. */ + if(ux_network_handle == NX_NULL) + return(USB_NETWORK_DRIVER_FAILURE); + + /* Cast the network handle properly. */ + usb_network_device = (USB_NETWORK_DEVICE_TYPE*) ux_network_handle; + + /* Critical section. */ + TX_DISABLE + + /* The link is down. */ + _ux_network_driver_link_down(ux_network_handle); + + /* Unprotect the critical section. */ + TX_RESTORE + + /* Are the sync objects valid? */ + if (usb_network_device -> ux_network_device_activated_by_thread) + { + + /* Get mutex. */ + _ux_utility_mutex_on(&usb_network_device -> ux_network_device_deactivate_mutex); + + /* Any threads in instance? */ + if (usb_network_device -> ux_network_device_num_threads_inside != 0) + { + + /* Signal that we're waiting. */ + usb_network_device -> ux_network_device_deactivate_thread_waiting = UX_TRUE; + + /* Release mutex. */ + _ux_utility_mutex_off(&usb_network_device -> ux_network_device_deactivate_mutex); + + /* Wait for last thread inside to resume us. */ + _ux_utility_semaphore_get(&usb_network_device -> ux_network_device_deactivate_semaphore, UX_WAIT_FOREVER); + + /* We're done waiting. */ + usb_network_device -> ux_network_device_deactivate_thread_waiting = UX_FALSE; + } + else + { + + /* Release mutex. */ + _ux_utility_mutex_off(&usb_network_device -> ux_network_device_deactivate_mutex); + } + + /* Delete sync objects. */ + _ux_utility_mutex_delete(&usb_network_device -> ux_network_device_deactivate_mutex); + _ux_utility_semaphore_delete(&usb_network_device -> ux_network_device_deactivate_semaphore); + + /* Reset for next activation. */ + usb_network_device -> ux_network_device_activated_by_thread = UX_FALSE; + } + + /* All threads are outside of the instance, and can't re-enter because the + link flag has been set to down. Now we can clean up. */ + + /* Reset the instance pointer. */ + usb_network_device -> ux_network_device_usb_instance_ptr = NX_NULL; + + /* And the write function ptr. */ + usb_network_device -> ux_network_device_write_function = NX_NULL; + + /* The operation was successful. */ + return(USB_NETWORK_DRIVER_SUCCESS); + +} + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_network_driver_entry PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by NETX. This is the dispatcher to all the */ +/* NETX function to the driver layer. */ +/* */ +/* INPUT */ +/* */ +/* nx_ip_driver Pointer to the NX_IP driver instance */ +/* */ +/* OUTPUT */ +/* */ +/* Result */ +/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* NETX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +VOID _ux_network_driver_entry(NX_IP_DRIVER *nx_ip_driver) +{ + +TX_INTERRUPT_SAVE_AREA +NX_IP *nx_ip; +NX_PACKET *packet_ptr; +ULONG *ethernet_frame_ptr; +NX_INTERFACE *nx_interface_ptr; +USB_NETWORK_DEVICE_TYPE *usb_network_device_ptr; +UINT i; + + /* Get the pointer to the NX_IP instance. */ + nx_ip = nx_ip_driver -> nx_ip_driver_ptr; + + /* Set the default status return. */ + nx_ip_driver -> nx_ip_driver_status = NX_NOT_SUCCESSFUL; + + /* Get the pointer to the interface in local variable. */ + nx_interface_ptr = nx_ip_driver -> nx_ip_driver_interface; + + /* Is this the ATTACH command? */ + if (nx_ip_driver -> nx_ip_driver_command == NX_LINK_INTERFACE_ATTACH) + { + + /* Critical section. */ + TX_DISABLE + + /* Find an available entry in the usb_network_devices table. */ + for (i = 0; i < USB_NETWORK_DEVICE_MAX_INSTANCES; i++) + { + + /* If the interface pointer is NULL, it is free. */ + if (usb_network_devices[i].ux_network_device_interface_ptr == NX_NULL) + break; + + } + + /* Check if we have ran out of instances. */ + if (i == USB_NETWORK_DEVICE_MAX_INSTANCES) + + /* No more instances, set the error code. */ + nx_ip_driver -> nx_ip_driver_status = NX_NO_MORE_ENTRIES; + + else + { + + /* Save the IP address in the network instance. */ + usb_network_devices[i].ux_network_device_ip_instance = nx_ip; + + /* Save pointer to interface. */ + usb_network_devices[i].ux_network_device_interface_ptr = nx_interface_ptr; + + /* Set the USB class instance in the additional link. This will be used by the USB class driver. */ + nx_interface_ptr -> nx_interface_additional_link_info = (VOID *) &usb_network_devices[i]; + + /* The operation was successful. */ + nx_ip_driver -> nx_ip_driver_status = NX_SUCCESS; + } + + /* Unprotect the critical section. */ + TX_RESTORE + } + else + { + + /* Get the usb instance. */ + usb_network_device_ptr = (USB_NETWORK_DEVICE_TYPE *) nx_interface_ptr -> nx_interface_additional_link_info; + + /* Identify command. */ + switch(nx_ip_driver -> nx_ip_driver_command) + { + + case NX_LINK_INITIALIZE: + + /* INIT command, set the interface parameters. */ + nx_interface_ptr -> nx_interface_valid = NX_TRUE; + nx_interface_ptr -> nx_interface_address_mapping_needed = NX_TRUE; + nx_interface_ptr -> nx_interface_ip_mtu_size = NX_ETHERNET_MTU - NX_ETHERNET_SIZE; + + /* Set the link to down for now. */ + nx_interface_ptr -> nx_interface_link_up = NX_FALSE; + + /* Check if instance exists. */ + if (usb_network_device_ptr -> ux_network_device_usb_instance_ptr) + { + + /* Store the physical address in the nx interface. */ + nx_interface_ptr -> nx_interface_physical_address_msw = usb_network_device_ptr -> ux_network_physical_address_msw; + nx_interface_ptr -> nx_interface_physical_address_lsw = usb_network_device_ptr -> ux_network_physical_address_lsw; + + } + else + { + + /* Reset the physical address. */ + nx_interface_ptr -> nx_interface_physical_address_msw = 0; + nx_interface_ptr -> nx_interface_physical_address_lsw = 0; + } + + /* Operation is successful. */ + nx_ip_driver -> nx_ip_driver_status = NX_SUCCESS; + break; + + + + + case NX_LINK_ENABLE: + + /* Set the link state to UP. */ + nx_interface_ptr -> nx_interface_link_up = NX_TRUE; + + /* Reflect link state in network device. */ + if (usb_network_device_ptr -> ux_network_device_usb_link_up == NX_TRUE) + usb_network_device_ptr -> ux_network_device_link_status = NX_TRUE; + else + usb_network_device_ptr -> ux_network_device_link_status = NX_FALSE; + + nx_ip_driver -> nx_ip_driver_status = NX_SUCCESS; + break; + + + case NX_LINK_DISABLE: + + /* Set the link down. */ + nx_interface_ptr -> nx_interface_link_up = NX_FALSE; + usb_network_device_ptr -> ux_network_device_link_status = NX_FALSE; + nx_ip_driver -> nx_ip_driver_status = NX_SUCCESS; + break; + + + case NX_LINK_PACKET_SEND: + case NX_LINK_PACKET_BROADCAST: + case NX_LINK_ARP_SEND: + case NX_LINK_ARP_RESPONSE_SEND: + case NX_LINK_RARP_SEND: + + /* Place the ethernet frame at the front of the packet. */ + packet_ptr = nx_ip_driver -> nx_ip_driver_packet; + + /* Are the sync objects valid? */ + if (usb_network_device_ptr -> ux_network_device_activated_by_thread == UX_TRUE) + + /* Get mutex for checking link state and setting our state. */ + _ux_utility_mutex_on(&usb_network_device_ptr -> ux_network_device_deactivate_mutex); + + /* Do not send a packet if the link is not enabled. */ + if (usb_network_device_ptr -> ux_network_device_link_status == NX_TRUE) + { + + /* Increment number of threads inside this instance. */ + usb_network_device_ptr -> ux_network_device_num_threads_inside++; + + /* Are the sync objects valid? */ + if (usb_network_device_ptr -> ux_network_device_activated_by_thread == UX_TRUE) + + /* Release mutex. */ + _ux_utility_mutex_off(&usb_network_device_ptr -> ux_network_device_deactivate_mutex); + + /* Adjust the prepend pointer. */ + packet_ptr -> nx_packet_prepend_ptr = packet_ptr -> nx_packet_prepend_ptr - NX_ETHERNET_SIZE; + + /* Adjust the packet length. */ + packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length + NX_ETHERNET_SIZE; + + /* Setup the ethernet frame pointer to build the ethernet frame. Back up another 2 bytes to get 32-bit word alignment. */ + ethernet_frame_ptr = (ULONG*)(packet_ptr -> nx_packet_prepend_ptr - 2); + + /* Build the ethernet frame. */ + *ethernet_frame_ptr = nx_ip_driver -> nx_ip_driver_physical_address_msw; + *(ethernet_frame_ptr + 1) = nx_ip_driver -> nx_ip_driver_physical_address_lsw; + *(ethernet_frame_ptr + 2) = (nx_interface_ptr -> nx_interface_physical_address_msw << 16) | + (nx_interface_ptr -> nx_interface_physical_address_lsw >> 16); + *(ethernet_frame_ptr + 3) = (nx_interface_ptr -> nx_interface_physical_address_lsw << 16); + + if ((nx_ip_driver -> nx_ip_driver_command == NX_LINK_ARP_SEND)|| + (nx_ip_driver -> nx_ip_driver_command == NX_LINK_ARP_RESPONSE_SEND)) + { + *(ethernet_frame_ptr+3) |= NX_ETHERNET_ARP; + } + else if (nx_ip_driver -> nx_ip_driver_command == NX_LINK_RARP_SEND) + { + *(ethernet_frame_ptr+3) |= NX_ETHERNET_RARP; + } + else + { +#ifdef FEATURE_NX_IPV6 + if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4) +#endif /* FEATURE_NX_IPV6 */ + *(ethernet_frame_ptr+3) |= NX_ETHERNET_IP; +#ifdef FEATURE_NX_IPV6 + else if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6) + *(ethernet_frame_ptr+3) |= NX_ETHERNET_IPV6; + else + { + /* Unknown IP version */ + /* free the packet that we will not send */ + nx_packet_release(packet_ptr); + nx_ip_driver -> nx_ip_driver_status = NX_NOT_SUCCESSFUL; + break; + } +#endif /* FEATURE_NX_IPV6 */ + } + + /* Endian swapping if NX_LITTLE_ENDIAN is defined. */ + NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr)); + NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr+1)); + NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr+2)); + NX_CHANGE_ULONG_ENDIAN(*(ethernet_frame_ptr+3)); + + /* Write the packet or queue it. */ + nx_ip_driver -> nx_ip_driver_status = + usb_network_device_ptr -> ux_network_device_write_function(usb_network_device_ptr -> ux_network_device_usb_instance_ptr, + packet_ptr); + + /* Are the sync objects valid? */ + if (usb_network_device_ptr -> ux_network_device_activated_by_thread == UX_TRUE) + { + + /* Get mutex. */ + _ux_utility_mutex_on(&usb_network_device_ptr -> ux_network_device_deactivate_mutex); + + /* Decrement number of threads in instance. */ + usb_network_device_ptr -> ux_network_device_num_threads_inside--; + + /* No more threads in the instance? */ + if (usb_network_device_ptr -> ux_network_device_num_threads_inside == 0) + { + + /* Release mutex. */ + _ux_utility_mutex_off(&usb_network_device_ptr -> ux_network_device_deactivate_mutex); + + /* Anyone waiting for us to exit? */ + if (usb_network_device_ptr -> ux_network_device_deactivate_thread_waiting == UX_TRUE) + + /* Resume deactivate thread waiting. */ + _ux_utility_semaphore_put(&usb_network_device_ptr -> ux_network_device_deactivate_semaphore); + } + else + { + + /* Release mutex. */ + _ux_utility_mutex_off(&usb_network_device_ptr -> ux_network_device_deactivate_mutex); + } + } + } + else + { + + /* Are the sync objects valid? */ + if (usb_network_device_ptr -> ux_network_device_activated_by_thread == UX_TRUE) + + /* Release mutex. */ + _ux_utility_mutex_off(&usb_network_device_ptr -> ux_network_device_deactivate_mutex); + + /* Link down, throw away packet. */ + nx_packet_transmit_release(packet_ptr); + nx_ip_driver -> nx_ip_driver_status = NX_SUCCESS; + + /* Report error to application. */ + _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_CDC_ECM_LINK_STATE_DOWN_ERROR); + } + + break; + + case NX_LINK_UNINITIALIZE: + + usb_network_driver_initialized = 0; + + break; + + case NX_LINK_MULTICAST_JOIN: + case NX_LINK_MULTICAST_LEAVE: + case NX_LINK_GET_STATUS: + case NX_LINK_GET_ERROR_COUNT: + case NX_LINK_GET_RX_COUNT: + case NX_LINK_GET_TX_COUNT: + case NX_LINK_GET_ALLOC_ERRORS: + case NX_LINK_GET_SPEED: + case NX_LINK_GET_DUPLEX_TYPE: + case NX_LINK_USER_COMMAND : + default: + + /* Invalid driver request. */ + nx_ip_driver -> nx_ip_driver_status = NX_UNHANDLED_COMMAND; + + /* Return the link status in the supplied return pointer. */ + if(nx_ip_driver -> nx_ip_driver_return_ptr) + *(nx_ip_driver -> nx_ip_driver_return_ptr) = (ULONG)0; + break; + } + } + + /* We are done here. */ + return; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_network_driver_packet_received PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by USBX when a packet has been receiver over */ +/* the USB. */ +/* */ +/* INPUT */ +/* */ +/* ux_network_handle Handle of the USB network instance */ +/* packet_ptr Pointer to packet received */ +/* */ +/* OUTPUT */ +/* */ +/* Result */ +/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +VOID _ux_network_driver_packet_received(VOID *ux_network_handle, NX_PACKET *packet_ptr) +{ + +USB_NETWORK_DEVICE_TYPE *usb_network_device_ptr = (USB_NETWORK_DEVICE_TYPE*) ux_network_handle; + +ULONG packet_type; +NX_IP *nx_ip; + + /* Check the state of the Link. */ + if (usb_network_device_ptr -> ux_network_device_link_status != NX_TRUE) + { + + /* Link down, throw away packet. */ + nx_packet_release(packet_ptr); + return; + + } + + /* Pickup the packet header to determine where the packet needs to be + sent. */ + packet_type = _ux_utility_short_get_big_endian(packet_ptr -> nx_packet_prepend_ptr + 12); + + /* Storing in into the packet the interface. */ + packet_ptr -> nx_packet_ip_interface = usb_network_device_ptr -> ux_network_device_interface_ptr; + + /* Get the IP instance. */ + nx_ip = usb_network_device_ptr -> ux_network_device_ip_instance; + + /* Route the incoming packet according to its ethernet type. */ + switch (packet_type) + { + case NX_ETHERNET_IP : + + /* Note: The length reported by some Ethernet hardware includes + bytes after the packet as well as the Ethernet header. In some + cases, the actual packet length after the Ethernet header should + be derived from the length in the IP header (lower 16 bits of + the first 32-bit word). */ + + /* Clean off the Ethernet header. */ + packet_ptr -> nx_packet_prepend_ptr = packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE; + + /* Adjust the packet length. */ + packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE; + + /* Route to the ip receive function. */ + _nx_ip_packet_deferred_receive(nx_ip, packet_ptr); + + break; + + case NX_ETHERNET_ARP : + + /* Clean off the Ethernet header. */ + packet_ptr -> nx_packet_prepend_ptr = packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE; + + /* Adjust the packet length. */ + packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE; + + /* Route to the ARP receive function. */ + _nx_arp_packet_deferred_receive(nx_ip, packet_ptr); + + break; + + case NX_ETHERNET_RARP : + + /* Clean off the Ethernet header. */ + packet_ptr -> nx_packet_prepend_ptr = + packet_ptr -> nx_packet_prepend_ptr + NX_ETHERNET_SIZE; + + /* Adjust the packet length. */ + packet_ptr -> nx_packet_length = + packet_ptr -> nx_packet_length - NX_ETHERNET_SIZE; + + /* Route to the RARP receive function. */ + _nx_rarp_packet_deferred_receive(nx_ip, packet_ptr); + + break; + + + default : + + /* Invalid ethernet header... release the packet. */ + nx_packet_release(packet_ptr); + + } +} + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_network_driver_link_up PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by USBX when the line link is up */ +/* */ +/* INPUT */ +/* */ +/* ux_network_handle Handle of the USB network instance */ +/* */ +/* OUTPUT */ +/* */ +/* Result */ +/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +VOID _ux_network_driver_link_up(VOID *ux_network_handle) +{ + +USB_NETWORK_DEVICE_TYPE *usb_network_device_ptr = (USB_NETWORK_DEVICE_TYPE*)ux_network_handle; + + /* The USB side of the link is UP. */ + usb_network_device_ptr -> ux_network_device_usb_link_up = NX_TRUE; + + /* Check if there is an existing interface. */ + if (usb_network_device_ptr -> ux_network_device_interface_ptr) + { + + /* Set link status. */ + if (usb_network_device_ptr -> ux_network_device_interface_ptr -> nx_interface_link_up) + usb_network_device_ptr -> ux_network_device_link_status = NX_TRUE; + else + usb_network_device_ptr -> ux_network_device_link_status = NX_FALSE; + } + +} + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _ux_network_driver_link_down PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is called by USBX when the link is down. */ +/* */ +/* INPUT */ +/* */ +/* ux_network_handle Handle of the USB network instance */ +/* */ +/* OUTPUT */ +/* */ +/* Result */ +/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* USBX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _ux_network_driver_link_down(VOID *ux_network_handle) +{ + +USB_NETWORK_DEVICE_TYPE *usb_network_device_ptr = (USB_NETWORK_DEVICE_TYPE*)ux_network_handle; + + /* Set the USB link status. */ + usb_network_device_ptr -> ux_network_device_usb_link_up = NX_FALSE; + + /* Set the link status. */ + usb_network_device_ptr -> ux_network_device_link_status = NX_FALSE; + +} diff --git a/docs/USBX_Device_Stack_User_Guide.docx b/docs/USBX_Device_Stack_User_Guide.docx new file mode 100644 index 0000000000000000000000000000000000000000..6d9f66a453feacc79fdcf395e6ab5274e7c41e2a GIT binary patch literal 568349 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≀MsxQc2c;nsXIq^qpzvk)7$84>SW)%BVRt#n!5bQe%wiIL(iML>PEVFuU2}l zdPdw}m1WBMHOuk|_T1hD>BX#fL8RHOVf?I}rdRcCs(P0F>DjRbRd47-wH>qV7QU^t zdi}_B59vl|pVhAB6||a<$3I>w5if=)p@ZEg^YKubCK=CKW(mtKvh1d4h+pX-hvdP9 z@*Wb1g-4}4-N+KC5rfEG+`UjXQprUeVAHn@nKlT|MaY1p#L60rD=kysz`Ew|az4VD zCKoGx=JyTY;}o` zsR?1#{^zvsvg$LjeV7@8mrO^1-ps=D}@kQv2iI+o8N0ZT*vdw6!OrvZL z)gHXqtS{)@TTS!@y|S`&QDU-s9YI18S_bMOVlp-~+mGAS{74b;2@Vm64q+asY&Kx1xl|7|!bidc8ZYI(YlHke3D>3? z5T1coQ!kWi=ouE#&T6{_EORma(_7#DIk~sLJ8$%lSkeDV%4rA(FR)Wma#waGi^P1) z>Zk0`h7tE`M^olz&SGfT&f>dd#yZg>IMxARwQu;r4y-eM0r`^x&@>&5jJKefu8n+0 zCZafUl1kNG-7s^Eoc6qht|qBa1J=sggi}4(3R!d57(xwbD{J3V54R%nT3k4&>rv(R z@?ik*$qw#Gb4D9A!m_|58!zSOsx_#W-aV0+#FmfS-915?rW&YqknMxJCuQZ&@*9h7 zh9+~s4au15`9&+6$TgI2$TuZsOE8XX1TY;7K#n|x5iV>3>rbIqCUBhA(v--U2-6E( z+w3X6#VNDQ;{%9 zFpGI60I+7FNvu3ZKOphDyys_u-WfrV&s#yn>$3z3AOP%L8IEpmV>su2{9}8}(&gmW zAgjK91aKbeWKQFONL@sHihMeB)`fY(WMi7 z;~{zEXH2H^$x70rG|2`NM2H%?t)E}kf>dDSIDt*zg=d`ZDsbXb5VGh`ftCyhbHdjm zO1TWGciEZk*?p9WO^jt*Q(h#*LYI8IWbrC?jNde)u}v~x*6MOV-_cYYfz94nPO&lJ zodU1YZwc&X)>(BUxDUiF;-dhds#y$pxXY{@D_9xM*{}=vPBN@#V9(OEF0vj94XlQ2 zci?PTZ<*f&H8@q`@6qI8%rwWM;A=^H(7z?k1RNGp&33Q&qg1M<+a$j3TUslX(}hJ! zzR_rb&g3KDka4X_AMK1schd>Tj_`u=Ev3~JiqgSo1ZOHxrlib&6+gm@@11G7`YEht z@gw%Y^soFSivukrct*F78JJ$%%TeBt<4Nv8GGr%~x3E|>3$O(vHrxrU^EalOXk;Dy zcIgGz;V0*P@cV~P|GIt`T)p}7_WJGRo441WKL&xX5LJTPR7=wh&1<_hqx;;}u>1#m zFHqvE_tyC&l*aW!X7L2z<#t)4TJVumFC6&O)M$y%ZfAcFKQ)L!?NW4Mj35QS{*rI#F9YwAd=w_IA@V)` z@Y_zD9DfE~sx)XX^U}R_Ji7@=mcHfV@1kyLAjL1gO4whWAV(#}4)(WggG0?;Z*khR zIO=z#y;RiiDq=rX_LG0Ye=q#R>Prb`L7}OkAZX=8w=?968U{G(dpS|nh>^{NpPC_v zS0yw5U6UcIX(8M6ytdD$?}@qsf&Bu0p(6FA4VIp&v(@}eK+<9|cE>7;Vo6j}7d8pFqKFN6~4u395 zPR-F&&EEwIsQ34~@7LK&A73l__}t$sWW@$jzMmGFp%LO8S{!<2={&ROQYler3-?gi zf&9SJaQ~4L`YLvI$XNF7f43!yy8p{2sowQ}uCD(fTDW?*|4`ihT-<)sbWGnicWCJ8 zJ-Ksn`|(O@pC|jRgzs@2Q+K zCk|l|C!GkU@mGNOJ~Mc|Jx*ZBWwP#4<)E43rONb+mjqeC($$&VeSNY{I7(0E1Qu)% z%FE$N5exIl$=n0%obx=ui6##q4bd2|9!rP2j%LckeD;u}F`x%#i879}1@I(;c>Hsk zJSx#mGUuO9qUroL8o-Gxvog%(OAbpqONR47rhtgPRFf@8kJI_&CWfQIX36~S zUims50j8aRzmz+y%fY@zeXN#AQC7WSl4S8rnBnLq760a!2je)J!E@=tfzP+F*iB!t z2=~xndg5_g(Y}5uh_9KxE7!r=6|MyIQg;51AUR`C>i)#v<5Y<$a}4>BZ$D zP@LhAsdmcD`6Ke%2hkMJOAAk-3{5Y>IdXHx{Gyssj?sKg#je{nu44;y$F6GVh_qb; za!|TEX*!>9h})V&pQwKM7AyA*ZWhk@iE8ZSN(tA^(9(O!|L`k!^Es;c9pS9^_}RzJ5^o`-=ZP{P#S~ zK8^0~v&-p_tHNg=yxf4JkueYBA-ZF@t?Sr~IGUyEXa|JIEo04|GV5^MfBi)S3!8!j zpl&yFWqsbZ{?&VjBOO|=?vA{x30xUdk37{4Oylrn1HBGzVr=-&g%c4}1%gNPO8GsR z4cQR~G-}8lNa!P&Q_Zbh`mZ3FYpa+c+kgEmL1i^`%|L72vG1w81C=%Q>nr|8g~-hN zU*;HU>m!=v*g~p`55Ei3Q+OkV^)(b8$J4v)ewD-`j+3FZ8|neVK){~T2d4CEx#m2A z3=)#IQ>Et${I7*Xwd|fjkHVmTW9gn)`TXV6<<*;&vqu!&l3-xXFau(_hsVwBDV85N z!qxrqz!B+FMoix_RHyZ${mxa{n0koqKm>ih_kRiaDv1?j}mboKCUUgzO27`Eq|e%Sj9 zNAedwisG0?S3#3J+{YzG=oo#7(lu9smljKz6GOwAhxOi7=z^=@GdIA4pE|=9(iv67 z@62J)nX917!ORm9BJB3AdDa?p3@0f zniZkMPz$D9@p{=the4Nf7zpwm>N7x*eowsf#RQit>fHqxHs#!3fixNruR zKaq@0(R@P)DQF4J*q_;+4Ju=5mg))N=O%WlCbt7s&gj% z_@ab&7Kyc7vT>S4(_u6lDzlhsBhXjywcBJ&?J8VZ%4zOLJCSM!&< z)ZAqF1E?XoOlzCS53UIE7QWA70bfGF=6LuxqMrdlqd`X3GZ5?iQu!^Ou}(mAL4Qxi z^9fbzNP${U?Im<1E*|J5(=?f7qxs||Yx+u)>EC~k({cPm`3S^F?}08}GI@xmKVB-I zc(=6j-jOi~x?v(qZ9SZA!5|m`QL&0!DHsx0y4`KgfY?{N%`08i<$GYqqNdwi48Hyz z@VaZd&DXw%t@lc|xh+JC3xU7VZT?PFC#TBi`NIR{Vd|~i{3v#wFMJ^0Yf`b5b?xG~ zJj~)HqxlkPzGUV^T4h1axBM_5Cv}RqRYhMvyMKrV!dQuJM&nWT1GIR`DL{S7aW;Aw zFH6U%D>=^YSr>9Jy#sZ^I-pq{Dw_oBk1jS4QtC3AEsD>9iUr@N(54EH59%d&d%1BTK}`(>Brw8b!D;u@ z+#?09)3`8jTR$MF1rJg)J=G1Z^TM}Th8)onsb=Buzf{uEbdO%akj%4f>)xYRFeF*FP2>9^LXfl9zqyqj|Hr2dp$PLm1K7lz zJ`HbtA5E!gsvd1nmzESro^_W*+O_6V6`HhoiRE|eDl-fms4c|u#AAu zxCB9?ZZ0)1TgZmw`_6vb^}v$W()%B*~G!o1j_lExraSv>xs+|H6o5s(%Dj%LYtOqI=WHu@INWVD9vV}k9l?P8%8 zv%0o`{rG`Wj8)_Fot=kql(E9I^dTOMZgcYon-?OyfiL$) ztv6~1)&(z>5p%~Lk~AINj9DX4e#Fd*uS{L->2Ta9jCN*8zJ5JZ?&N7PFN>#o(!!2A z`}pz$ujpO6UX&yw2o2z4k^K}b&QBsKMjax8EJhw|lOEJDBU!GDFG{TcYyGB0U|`ek zWWlfPS=jJk46Tf8E22X0VRhXfN9ldcmcCaky<$13V(D40ce9SpcsD<&_m#%uTkyhC z8XJs%D6{xK^U*A3MUni(zd-R& z?mP>mwar zC_K9h{uW*MI8CD&YxCkQzSXdTJ$~^x=D|^pV(obGAeyJSFIGtZQdxM6{){1i%SanJj-h%+ zE1j(D{guaK-}LoRW_wKVRL$cH+$Lo26bCStR5V>@{?khn}Bze(J3B zvw@N*fRugP4XghiGB9;g^q1r zR?+qALv+_Soz_n~E3@@^oR@8y$R-4}%_>`{(p`9@{s}<$kFz98K<7!Yfrd$FAtVE5 zx{awSvq4IOhCvLu&1QM21Qv3XBiOZ78~{NM$HXm+ehobv?gYYpZBVHt)y^99glY%9 zjnw9PcrHATjb80`$cNc%KNxdg$&!aiFMSi?w$4FMs9(KN0u2Rl&c7yB$xohvE3e$ zVFDo$HOp9#mHn5oVVs_$yO^|HW_SwNu~E9nR=+T%I=@VgP4ACcp<@1G@|rT zC+k5~H*n1M&rdmw#*h&5%jv^BQ$9w3x{_7;(;8^wVaE(rnda4#3OiCm%fqOZ%tv9I z^-Z>bMLx|$_NH~wZzf;GVsEYe3(t!9JAfrh^3T)*Zwy(lMyQNaVW%caPQ;;~UJpBD zdh5rjnQG8h0jrO8%#T*%$R)Wox^^*5FiAbkg3tt>$k1B)vhlVCYt!PA||S{HNE z95=H@hty7eN6`c=^7W7Fs`+W-73*dLZ~Oe!^McRi@)E8g7@MqNM}!QftPjSHdKiR( zY0D%YpC~&Xlo8kS#S8Vjs@7#NHn9aUwZpQ7iENB?S7w{LXN$Jkf+5;QS)^pMXqp1h z`C~+5+hp=Ej-kAwnPK1`{*cAcwz8$fAtW#vaPv9XQq46q?CLW9d2dU1<<(8E5Oy{A z^y$N=E?PJ>M84%q;)$d{0P5bC#-!_bA>o&EctqwP{U2$gh_yRY41FMT-ocv*}5VSVO-zJs$ z>aVP`XCc$p+CE3Mg^?KnQUlF;GI_D!djvjUjYL#ujx8_rJ+tj1igT~Faj!m1_Us+v>0_S;U^Hd7|8a7*9zilJa4H7hPon`2rA(biaxlV@be*J}E za4(OGi@7~jsLxM*DFBOt=9W>afVLP7V#!kxOx4po&3I0{X*!PKd8qAr)o06__1223 zN9CW#>8iWoKUca5Huu==T=I8Naeq-Q6_l|v*syiYRiCEPZ7{8NVA2S#ZP3YQZD%fT zg>R$8|Cs|+#n(Ts#V#O6o=6_znZQ4YsJ)pFpiT02K9)2&Dl&XK)DL@d>A~Z>4K^lp zHDs$W^u6aOuI$nM!+vbrYRR(BHGNd;+9oj@?P?!Yr6;F+Po+IG??PJpo=SVB=t9ct z!{En$T}Y`+7ZR?V>{-0j+=Galdk}G+m#&TDOk$|^`FA0GK1>%9cEixqtizu9aUNMC z&G$`A*g+gmB6YY;7m`jK7u!+}!AYxU3D$gr1Z_Jm-RL)}j|f=eq@q>9CRGX2*85K0 zJG5#HT*bZrq3cwc<6vw{h*>WwxCpAc#K}_HGTk)U76&A6q4!y(eTxkv*dYoRa z&l`KWl;3lRpz?R&9eGV156ap57ijV;15Xt=TaaNF^215kz-W+^UPv%8@>~)Uxt=8I zdonM4m5KQkStgcaTdGj)(3R@jRt=pWowqWw@|CWzZM|2+3M@I?6!SYcqSPo&=ajq~ zTplsctz-w1KkuC1f@|-c^ZE0&_u>6}=gPnK-a1!rg6iw`pI(VI*L#agc9Nb9pnn}; z%*o?aY`!e@0iDMzz8%K{p;7Lm>0J!sxgwe}0*EnEeakv*O+>lbPUc)oIT`$@ITC%QGM#6tV_-idoez~wLv^riA2wgyH=33j zP3f;PYDpt5vDMbMQoBW(hH$SH5`i_J#*Z~b;Qpip4KVgJY+*n>>qvN2KThHquI053 zqjfS}ySdS~rh%5eSK&@%I7=qL)zrD>ohB^*zVa!J7BXi#xQ9A#STp#H{_$7-8QaQH z__P0jEd;_q-Qdsvrhn!~C~xlF?P6!Hp=(0iOCLkM?&_zy0IuT?-m@{+bdeR>;ReS* zwwVYU23w#5_KE1KkL?4ryp=6~x0{Jtm2ak^7s_3B1$TcR3$%9bwdE!32kiU~4a+nv z85h3q8Ig1Xc5k7@#AYy@2PC(n*;oq8uP3)Gxlh*`i;?AG%Wer~;_%kY(jCh)dTX|$ zH9M9St5~l^cKZ6QbLGDauD@RW>*~YrR~h> zlUtVDS8ygqo?wcyEE?P&)QIVhAKJdXp_-vHMhuz6bxo;LK6@k99V50Y6vUw|7IIA6 z@twYDzh#TH8%D#yEf%&_(+IIl=~ho}S#r0G(V&G+pxO2gPUI>%3TrQ+v$jjTW77Eq^Y310h5BP4DQ?srZR;waCNu)G<_q zH1kX#(k0TE@;DE=ubF139<~JfOe*$6O#Qmn@whj_y$Ja$Oy(K)^|RVuWfzPEY$8i{ zTR)_sC*Ka_YuK%qA){)#hT*hcXmYk>Y%o|lLB#Wp?N0Q}USRe^p+Oj~O*GeQyHvlY ztvqd?8k&M+6Vy@B6l9vGG#A;fDYZq=EDEdDk()$;=sfi3MPcnx-4g#*xvqv{M`y&Q z&M_J91YsMimf73gwcZJxA$Ez?Td>o5Cx^xm!LH+2n%8zq!6q{IIV~w6Bqp&u+l2uf zEvF?nGTl)F95Tp}y+^&m+z^bi7jwhtabOW zMHC^kIc!kWOYXYt)A%_?W7EeBAz6FG{sA7f$CYw&{W;>TemdF z2W0sq)>nyGD+2VxiUvcr{u0`l_=e?ih4b7qxhg`o@1Uceta!+D{x;YvhUu%WVZbam z&Hc0uWgaz7#5r`w%<~D!I3#XGKZ99gi?$JNrKNnuHhGG*mgVTH`ufFJ@2&Gm`GpS) z?59kMf*=#ACTeBtP+Z}??<A_p z)U?Qd{_^Ro_eJ^TXBk>zOLH}>?HADaSXBvg^?F)1ZV?TV>Fwxl&ZEyJoyC?8X@QOp zqp_NYc{?6@_Yn+4{Pu-~1~*hyP4jPdWT>Ml6@V|4RT~m@e+0@;@b$&_q({$+rt;US z?7xOrzb=t;$JcdXvAR3*29BgLT!|l#F3#7}`Q&ERco!G!RC!)GbT(uY%jZ@u@@ z_{-fN&M{Kdv31Q3^`1Hhq|R?2E`8+}SaF6~B7>ht2+}?5xNVT7K!fVE_YN^pNIcIn zIt0qlKw!x8-nu`O&v1E$XPn!rZL!FMZZJ^w?QI%v!S0vV2xH|>`H{e?@45Mmo z^6x@n598kw8#XCqd{cS@$LIodyJ$3BZ;01;E)COY+YfGd&Eb65CN|y1w=FMls{@f= zg2TLZ53jO`@f*e;Q3E?oGKIm4Q|6rx7!KPK08w!SE2sC#d^}X{qi->MJE8(af6MM? zaWrI)xmk9)f$-D;L0(AcN9+d%vOLf*UDiAGgC|yqO_j85f={h9KPMBx4{@r*-yZWz!nf{X`mSvDBj>{W>*DSa zg8-%1p|Fo~Sv;AKqbvqh92drLJBsOJ;hXpIEWS-<@PG%{S(|1GDE9@5A^?kyW-;6D z1uh;?-Udz^K=gu@uw2P2*rTR6~ZpmGx;vAH83}b=!Nw z4X+x<(>p3vbOETqzW@nK<}0O=XFtmlB|%?iX*|CDc|qxEqKu>29cZ3=s+PX~asA%; zLnP9#ocF=?yWr~0m$x#yMpsdQTnCPZIa0 zG`r+h>ooci59MeI2@?pc2 zxRCLNIUeECUkZsH8O^8pR_Nf?dw{!&QeLl@wxLDI>?}b!+MtE$I36(>z|+&E#Uc~b z3K5)g21w*X^Xz6N^Cdw7tgF~E9T~6x#2aoL$PWNZf9(_A$){Y{B}4!Bov*&86U*gRHA z(jH`%Y3r8hJ2I8`>zAO3y~UNY1Kg{f=8i%N`eZ+#a+_(;!(j)Tx*a&W%L)$`U?277PR}YX(Uie9}SfgFp@=jqou0Dv}o%hZc?`;Pyi0;!j1k8Wz zRmu)31z^C<5?vq5d7E{sw$+r-rC}zh^%EbDBI(HyR=Mrxu%4 zb*K}l$%M7GW7v#bD+?~JbZK=DmpW*KA>hltswEv)(_6>NJJICVffC3AniAVWwtC>Q zzB2(db!-npF&X)DHHYk7;vU#Scpg+!rIG8ap5pAT*E|Cj#@7#$CQ)H=_sQ%fAkl0 zK-8DE^OE$%F6|(hPU8X8>q0fFjCHX!OC^pBgi=rZqY=Lmc5}H&k0eq0LR&WiBGW|M z)Az*mT_6nC7jzzEkT?A24miVI!|%DK15AI=u}rShar8~QZPP8ZrGa=zycyv%qdFfL2fjlfNzvJ5t)CBdm_6UoDjcq1KeTm~Md6G2Sia%1bnMD}%2(KY45Vv* zDaH1jz_OYV8n#E!S_|snxrPZs7QxuaUe*drv^&;`#ss)>qB!d>7~byQ=+Gy>m}X^~vN1Ty?yn6KdX z9;t&#EhEnwHaK*jwP9(*_w5~=*r&k-t6J=n%`?Wt-VO5a=oN! z8ioGgIAxaQSXyrm_m=jeoCEZX)=BRNW7o&T@15N?$$gb%N<2;GclQccUl+}!hky8O zix+R@QednbW~gt-f_BEGKm_Za8|+XA)Vmb2F2$F(p906f4nBSQ@M#B~b7%{LQK%8( z_9~}k3loN&v@oh`xLPO$IMS0_mfT)6g_9!(vpCA&z&suq^8+C_8yoni4xG%;jlc-C zBNS@>&>q2JixTWZ!8jo|z`ligayOH^e&KWj>qJ)p%(P#)PRYqv85k!i;V=!| zHIdW-3q6gGM&qZ0=F`|BeSEp;@qFaQ)#Lf}cs{#J*^YQVDz-3jq>k)5upR2bT(%;( z9G;I9m*z>^I@xVIDYM!cwq13^FeF#9KQoT4IoO>$})|L1_^Jtv1Zg5_Q`H}@;$wSPK@MT3->3lGV)AV*emJGx+*AIv#1=q3_ zrJqL1P7MgLLcjHd_3G+8F$JPht!oEbr6ak1;Wiy#u?<_*!+EhEYx3sx&|QpVOXx17 z>V~BueJ$J<8*SkpVrm$hqyoZKzK1nvufmF1>+WH~wgM|g1P{QEtzgA=I8lAyqteg+ z4D^-{H{T+%qT$Y6oK*-_hw#SwP3kWR!&i4n^kUUr!nt6rio9uSMu4gy1iOiRqkJF1 zY#bku?oFKWmc99G20M+rh@<4o_LW-6*POp_w=~hiyj8w5b=9%g?NAbq?uj2OWn$tW zU6U$uIfv8Qw?QS;F!2K2mho%1Inu7o1}SwV2k{^oGGD8HUKMv!j&d~Mz?SDoRiiah z?g*n_L(hggA9uqEteGjVQz}eSVic>DiETKFJG}T1R%*j5pQgpybJB+&fuz%Wa7M53 zr|VuS`TXLOU#xqqj{tmOm&XEj`C$eu1t;j4*bD=ilzD5mGan<2XpL>~3|B*zOy=?| ztonA)fGE^ZXn0abWo}{D_n3YCI8E#afiDM0v9lZ6G0?yJB+UTrFS*I0(X!JX)`s!V=F=$@K?)A#-rt3U>d{P$49t?59Wx0?#hznJo!@;HCgF1WFwc~a zkpOvMz`ozFKI{WOzzB`od^2=2+l{%?F!>;9f8-Ra~DJD&d#&SK~ zYTI`>8F`9eUJ_)|P2aLzUFrm~lghH|5d$G9aKB!Lk2^J?V~tM8epvfNz(BCI?CYA{s{w9o!6f3Q5J!n4O9ev}SddB4QL#?AoTB z8K8>Afa54~9i4mCZU)C}g(*rIR#q!>y`u55wU_M<=G&4G+iG4l6llnKbC5qiSo2+H zqz~5c^UHB}X6R0(AID_B*_(&D&Fog1w3YU1$Gw6vNKoiq-}ZG4<}yi`Yp9wJ5E|(7 zA-FghPaZorITDdf9S_CUmbJON)PyU7 zL!KoOvr&Vi0uCtUSU|2@)SkLdMxA4YY%}`JVDUTOx8oQ|&o;4rrDvP8G$G%<`ugG1$9K!|-t`|EG-gHENT9C0?0RS1g=(D(N^%IKNti(H52~O=M(5#dIPmI8|uH*6qxGw0R61Icl)5; zsIJ|s_UP#_%x|Y^-l(}!zNaGDHx^bfOf!}gxyFupCy)?A;rYM{n{!8iTKaJcY=B2c z^-z$LFqM((J+f^c>Imn^yLLUBUe&52+Vs*hcT4+r`w(q#<+3)bZn0Kb)!Cg7tUO|g zFt8-1z&u-dsX^h0Dgm68`^h4;0AUBEF79RGDC7xt9)usw88u`+Q6wUeahoLUfjrT( zx^I(Wn+-&H^ppBke)(olo&~1)RprL7CJ`s* zuKWIYGQAwBubJY9W}8<3wYbPfgQxGq%Jta?=9b~;eX1hN`Cq>kf0;6Lx?h#{zZ>`O z^gq9;-|87fV?%<=o#myEAz)=CyQPl_n8P$G4_rQ!kq{NYXq72G+I%P@rG!Qx+_6Jg z9LiWZ{%gY#Z3a`ZKI|?}yr&f6*apL}eF)ZO$nxz5*ih+jsrTn|MB8Tbw1F{K%t<4d zEeZh3TA=MIfJ89iyY$s7fc1m4h-+AlcH*YY6q0N4NJg0uo>-mn?8R*>?md{Nr6TBG z#b>prGD68j#*%}>T3;HHkAe^-n7FPv>!4*Mq=bdIL)8?F67Tf{Jqhgjs4&WegwQos z;v``sX@*Jcye5vvZ8yMe7u%+RlU2(q?bhl6Bsd|=BoMu9k0V=O z55U(0@bv(WHgu=bj}xf}V2MC(w;n(pa@i&x#1N!Of_+i1t??jZF{IR&KAVlwF;x5z zB?188Oaj+D*5%~tmruL|Uw3!ILHi6Haw`j%-;c)1=*}AQj7W3AA@%4vkl-&r|rvn(_)vmf=}F{83p zXdo&jt$#ErG;mikH%4RSFo$<59czA^}vONCXezqI*KMmn1+b-Dy7NT}7(9!+Nc zF&ONs!@XcJ+68XBfa_V9KuSf5LW5U&>HA9>umlt5nCBff@}~HCWLh#psT7&jsSd}b zC1V*07PecScQW#{$~q0 zcFsM`rnQ}577oUqm`h0D0_O=HQl1w@AZ~Zk_jy7^Rg*W5)^fUOZN72uVi#j zEW`qN9P7f%osVr0264!p*J%v_A?cR%X)VjU8nz>bbq4;|hu%<`&u{7jO&n_{D%vdb zmF41+;BB5Ej+licy$iX+;)&DMNboqqyDTy*o#b0BrVxpE-KSTJucSY2UKj_0 zSP3la&|boZcFH7@QR}16VEa)+jIK#4o3;GvSxTb_1tro7St7k(1oME$` zU$_Q9fKUu~Ow#TYuHkWzGiQqqhbv#QMQAl+Zmt3PznVRx@X7GJ_|5~^CCP>(C{sI zsud3SgzA9sDw|YR^Nxmz zbVPvmqT5q+jVrK$mdU3=SFQVveZS@7>p3!h$Ag;BcJC%N%Oo2GNItJ}lh})z70p%ggq}8z~ z&%_k&^4$c--gnQ3%G|MnL4Cbcp221zLX#*Wk@HeN=e7_@Q!cRd!Rm1rB5jxcqfrtk zG4f{$wY(FBCC?DXPSTC#l|vWpqdL*OUbL^+7}@k41A$*J+E;EQ*MCPVV@=V%t%d#! zoo20QUkQ^neQH#+uRkddAYX}J>dNbt*#EjqCEma864`u-&V;qDMnI4>L!nlflioZ# ze1X7hF4Cm~>McwpLmVL3GU@3wHY`JlQpTtg%4cOLwzjOzUF#;L0U@K1P-){Sk6PaV za01gzx|WeI4S2lLfSbJ09s~IH)wXfUAmY*H+A&)m``f3B%t7mH>gypYm3v2>SMUwf zlYxY!P&cakCUmE&9<7odLIg*V753t|)n>SidFrOLdU>UY``4OowM=#zQt3^y-_ozk zOrRgWrkH&iU5}nGCAMA7HnjhHZ>Sb=y7t{*Q0~UgD?9#p$cH*PGR}38$3hc0@g$;r~Jj5oEx|xqJ5ce%7Pg)h19vqcE~6fan##=0RG--K{o(Bav_!+C(W{ z+_vJbEW$N@VX;Ji4!xF!G7;Dm33M4mc~HaZj1Ul!u#5CsLJ3N<)HOxuXkoNHy_S*$ zq13tVpCr9D46(GU2IbTYcnDi1f-87p_B1gl2%Ol16ZbjXtX!E6#Fi>-e&1^v(LOfC9 z2e|8P{=nE-;B~-R z7E@%C3OO!ZGLms9XuH{~$;gA(Yu;5P@d1Z1khX6tWL(wCMuvNjL45m4$6Ftg-^8`} zn0@}FNAEAQmslOoo-iy>;+oFmd@4YQ1r0;jDW0R5D-9L5$9Dq;BS<0}zGSJfHNCIs za%%<)N+BcaI$5&~saZ6&$r`NaVK83>&HrBMer4rjn2VMQ;OH${C)Q4A*YMuO5kp_E z(%EjI0p}J--`;Q$uuL9CPlLO~5!x#O%+fj97HUYdh|A1*(J}|o48f8{7`d)rcr^3S z99pVYJ86_z8OWoW2*Q#uiBs1)WN{o>BQ<-T_+}GuNp3cetUP4tkg@rUHhBL2?Sq-O ztJAr!(fMZ0QjZr{(-}%A7oc5peHy!?Fbz|TXBfQGHy}-srbOA#&^g6oFTuPNY$*b> z)YLMzG(@K4!OF)N6{Qu5CqENgiXqF=WEU1-9a}n6ukrg_=U`?dikPY2pqnkN}ori?Ppn@he8b_jgM;aOqmQJl8|GaDRZVM)FnwjL#HPH;`-AK z%N>n%b@Qg`Q};p7VrIZ3EvhMnS%!~x^wGJZXH@{aaldi18W_Jd^Q<+pk~E=|AAG0T zdJA&P9d+UK(j7C(%9cCW>^$*JcTl{mr7AAlbw@nkUOtEIR3;^f!}y@5CEK+OqyTe3 z&W2KFj|C=x=wmKisFkpY3(|mb*1$3 z@^?%|b{)^U4FhY~3F~;)azDfLsR^Fd99r=y+Y!rb|BZzowRjUW`o>Dl1jn!s~0K{Ufw zOy#3OStyEd7D?B%`jdv|HUrG=eBXiLxtsma9wT@L#idyik(fIVD4owl7IMO*^W%$; zW-XhcgJH%~WrjL$FGE5kOR*mO_Uz^J>$uv8t}3ieDBW4z-9<$o{h;Y6&<3U&8R_D? z`fH=_-*pLHy@@#-{0vlg`s+_oSNlV?G@LxD*gE*G{KA{yVyZvZPb~1fvwFObkAwbg zv7S^{?(*CIU0yt}Kdo<$h7=b~lB02;Dd?D*Q_m9uiIu zT6D**WhkQ%aN>Gck7g~KgQc+y5vQiFE-mKC*SFnZx047a@bqEAu1D*6T5DMi7$+bV z)cIsR$($aEA*ASR*2E=7`wxxLo>-^?O+cmsD4Y1{z;WKoDF=+BqJb;rfLYzg1i)El z(E(&T#jy%0b}Yp@Qw|tmfr+p}n4BfRaW-!@BVH>uq_*pOPEBn^%59}3lj7(#yD+?6 zElwQ4lmV-B>Y1uztBSZ?C%Rk27|2v|hHQ2Ty;`_d3yp@-sglM(?frcyVIT|nj>>AC z@u5T?MGdS-dESTatE0PKQTkJHdHLzGi=K@F6+x$o4FJ|NNo*cedMqhB>9I6B6zybg zdT}=r_uLcR_xd_>|7s~~KM5YkqnqB%;Ab!#O?CENUoJE7d69$vWw+PTzv}2k5R)(_ z@Zi|&msa5r$xv`p*>e$Z;y;%r&s%%#3~*r&*i{Z}g9A^xLK!(S*n-biR6Bj^Mw1;)_BT9bMIG1FZe3 zvV!^jXsm3pgO#n`qy9V|jqYdPE1$WPu51>GNfn8mG%HF6+cm7Ht!yPz%x9eyWvZ5K z`i>cnbyn0?Ce_Mm0wpVIhAGrPny{k!!~Rq$LjPaOOXcxvvCWqZ zqM=@mSQJA7k?W!hhvz6=%tyFdOxKLQh3#XpAQC~FT5}SKp5czeiad6J}K#aesB4j67Yb=3VX3l{PwvJqGaYtFQcha(Vsv%VlW?ukSUW z`p0Oj7HP^JUr!$M{O+~Igg}BzLa6hzgmA*Rr1rX34g66AOE-8ZjePs{!>5n$*2i*? z55G+xnhq%f96f-rOi01MJ^W^a}tl9g)$C$!*5S&y?NcgYp9VZk~D!Yep(xi ztR2NtEq}2xl2YId$9w2y5kZ!t*0CY3W+7q8gs7&diqC2UQQNu7OPf5_NJ+>0ul}Oq zr=3|wruZ=($1B;F6gjohW2cZ zeu#Z-2qL9SWoVPCJ!*1_K|)xObnmjR+K8ip*3@Mm!XoVujUxA{}GY&=r$8c)jd66INNH@SMmPz_0c`VdTVJ^s^t(L~+8**6R% z_It0di%~p|M}PG13`V4EP4R>ZQg$8?1^22EvkuIjY#Kj#dVCy>)sWqc)OU3XB+wNv z^g=T*3(d*msgI+{q<_;N^rt`d*gPn2qf2Bw4)STP8Z>Wu?c4L-?L+azH^G%zMpSOl zul%l5rmyiYvyg5Q^u`+1YZ_>Gd=?`@WRiEV!UK%}mtpigTpE-FMXnCGeV)Dh+$(E4 z*wCa2;(#m3blv2IluH~%=>bh%xk*|L^a+B7#iZj5HBAi5egj70*_Ls$fVX6v8@tWQ zbLVI1ebxnqm4lugc&ROqdHTXESk3qt3eK??S)It%65CEB_rW|h7~MZhaA6h$jL{)O z;nNY^BG22#Tw6HzW+H=OOa%k30r6)w*Bnc3d9Lj%I2?va0$PSeoQU97Q`oL2Sn>RZ zPAtM%7#mEKmrwl`gX7t>kCZS%JR(*L_p_H$ZQ8x1sWf|6Gid?DGYnfkQ0bJV)y~8! zk`d348^e_Gh*-+&c zZi>ik@1NKh8_c6DV{yA&iPOLfvnVEj0N2sY9!liKc9x|af3q#+2$+zt zliM;crIaIzqjCp!rIaHmkC$1#o07_tpIgdNGDP7nq?~OTDl>=IhuvMt8J0$A7>71n zb7x$Js>WW_c5}~Zs4d2Vnpa~%jCi)F0R$~qBMWg#IiWTi^1kwrWyG<6X+(kyG@{Ky zQ>nzURk<85Zd-9b^Mv}nJr2P{t#Wsr|JWIi10x2Qv|=iGkF#3?mmgPpn!ky^eY*T# z*B2kJzDjw)FT}?$OZr!Nn|@cbmEhuC2UzJZUe<{f{`dU*X#7*3o$_>l-@olE@wc$v zx9@bD@3vXr+xfHh!xteU&O#eVjQ3}@{!E20FzjUbf>6j4nb>UjdvV)}`{QUDK0*a;5QjM54=45SH7ss5JByh~?FCA-gUZLH5x zNOdiAOH#^c!2S zyuX%TKYX~p`Yc2i5MVQxNrpMLs>$zVu_6n@M%~8T|KI;7_-pWgZ6=&xFpNxP{gakL zeeW<0ah#$({6!JkMIqwMgx9;fT!kSVt~j3dtfp3)`RsoD-{qxM&|3Da3vDdDM^-qD zyYj%^(emk&l@93UZ&_v^zWJ^*Gl%a%C(=D-#IR6+=O%l)H;yOQp}oku-LfqkL}3+< z0zy%gu|1IU&+{J;7f_Mo58$ad5S>1I9e_Up)IT9u{2LViuKtW{3QQ1&SWM6!7$YzH zL(4wDE#I(o0<+-EPnWzfS6^hQE|`662{vad;4CYhQV7bBI+t zWX|11^S6{?PHe&)vz`g7v1_rOsjO;lt)f{hl~D=J`1+Mhm#3hTJqoQPDrh3vK6@uG zBDiHyjFP?faeZ)mP<_|O>S)&Rea~n9UvOY+nByxt;Dg$(YWGVCY6*@a9?^q)5g#@+ z51Y!68Ly>#i%kKO7zR}TBvv6a6fBGIKCw>Gm5;EdyBkI=S*VcP1z zZ|HK!1jN$y*~i`J@-cKxV-bgUkGdjF2cycJ4z@etqkV95kT@}47i#CiMEfD@Zp)uh zY^2S3yX6&~@S2mmQKb zvoIo+eFd{%!i^C#kjE)haeIr>=~~zhNE|~%TvuoHVY?6859Y0W*zO(tQN#9&p`YKU zKEF-Wu7<|X=@&DnZG(2gqlhx+jA|dWmqB|}>~GWr?H0meOc0syHih%D0@(lepBAHz zd4|F`b3$>ijQYU`c*BHGWdfL0wU`s^T^OoGAJ5vZ$#IsGV^{rv_ZRQpm-PdvjrGEu zh?EQ0yJjy!78~jZEDoHqt{*_-2u6UOyc9Of_b6og(C+5F^FYFbwXsYc`T7CJ>p>@$ zp}u~=$*A^Afe=EFl31`kVto%GJgfs*t{<=j9$e#)W2+spFpM%8L7)0?nfmA%DVPLE zXc8c0(xWQ1**>O)j8Mu@mJur%b)VR~;@Ia1$emM|t$vd4;P zU!P#kfcc=--hN@xsEx}Di+?`#2K{^eyhVg*7X?rzVHUAHs!leSxRbo!cbL<{;B5d|We|*T?j)i$n$V@XFXDB(i zll5WK-XXKIqCXhp1kp@O+3(mt`@N-%tVW)SkR~zNW3h6&MSnO-QzY9|HqRdR?BQoM z)w7444_S9x?u-8T3GR#j_*s1zZ*9pRW-&vOBzx?K`;dHNNWP)y&kCf-MmiutB1SJN zY)=t`JU!1kN5{}dAGV*TSNCE2QT5Un{qgg={ixj+{dq}q+BRsXoDd0^uhiL6&|WS2 zvmCTr2nR74g=wk(I1)u8sn2M98=#r7IBnu>Ux#lk=_9=(okf3P!>+1 zcZg8QVRoKt_+!`uwV=J4j{C!JGvc6})z?|F6F+>=+o-9X2ztvBBlC($KKebJQXW0} z{nf(H!<2H0rE5R`a;<6lbD9Sr=&1pKzXpFFjqie6_1<28s4eXDArCZA=&AQD4=%D_ zbr2i_9Neo3{q@R1USUKNEQptvQ+b7f`R~`j{Iw!4H8%O5!RX%nu_5ETVRAvtu>h_s zXTES@p>4t%W{_Pxq9C?GSpR+P<%MUfP3MyxMR7TD~8FvIR(X7_`I zufdnFixoJ1?|iBq?vJ(T>nId zCy9)7%6}V&ii|5oR&Zb-G7Z<>qt2z5(Iy!cp$su$4K}j|r&e56hN0x?9z5ogb)`6t zVj5B1IqymxxzekA-0uy7k5Auk@-0hKWam(kk^=~7nHu-o{qzo;1NJ$KIhj zl*QaxE`CA_QG{_uMdmjlok_w9l7|T4Fm?m$GA&vaDq$G3nudFMc~Uhj{l~##&wmp1 z#(6M#dVCy>r}%wON1Cd)kUO*0xG5R*1jd0vWIiZ^-Cdwk4Q=y#{*SQUkH z-M?FGnW@=1?)?ljcxM47un;5xQ4GXB$L@JSHk*!j|D~pul49@nb~L_I!TsRp8_QT8 z%OoRNn61pFR(g*i4QH^5{s}PH1q{ST1YY33_rlBh{btE z1;-eDt5imql7z*eZ9JrIe4_Kg_68A00;UYQ_5qDrwn1#Q-_b+;<-sg(2L0ROM339v zFc|0me99+NwSK+r4F;@dhK;O7Fpyidd31)Lp;2ugqV<;p_+e!OB zyUk729naC{UMiGlyNZZLx9B~XT#nsopS&aZDAxUEBnt1L& zKMBZ46am*!TdNYdC>R-4J9RfwmSs4a2E$wc$v`&0t>~|wCOSj&AD8*$>3beb`rn4V z!Q{2FOu>)Q)8NizO$R@fMN`83`1Qkw;QncNs~_~nLYQH~B~OfX>6kD9h!{)vDd4@e zW!s_YHSx)cVAY(t`1mCVj0bp{*m!js#Zppnp4&njW@4HFkWs&mZ#D6!7F9YZF{$;N zd@%YE^d~05>5rA+(6Nao2CFz{#sVv8&w7(Z8=BF*VGqyM_s248xDs6)vaicWXCC_P zZ@*rp|5&!1-58cF^zUj*yU{GAmg8J&<8V|7w6#W-Dp*+0GR#(Ls?<~6?X_`L8}mPV z-ya9LWzvMO1VC`6+n@DaMqxtJENl~sv;VLB*mm*ltN*5E?thl8nz-&vtQVTwWIoc^n<+NAtF z>ws2u{pH8eL@)8;S$}4?zSs#dKOehXD6j5oUBAD0_x}3ovk;lpx!GYNMTm))=&>X^ z=uM`V8uOrn$q0fu43v;m+V`3E% z2`*CL9BbhvxrZdT0l2J)-wDYQlDbYncv~#NRmV%c%F#s`Yw|MrIcgc zxkIXI2&q@qa;VG+;`g&$aQ@Gy-e974F|WRIF0QU#2lI4}*%)2Y;~&24Zlk7lqU`Rn zJblsK!>MFPkCFM&n*@`m+uM9Hxqlj1GZs>kqRdHRrM&W4e-uL+GhhT(3l?~v_cXHt zm0hb^%^N-7Q!~6(#@m->$!mZXrM< z3L(bDK-PXI(%!lH9m+zC8Kc|2qnEjr?~pJGF;rdO_8l`Si|>ROMPUd{x63|lNukxG zD9%DGf6mMDVMJC1_zoXn9BrG>^f`Oj(OXu zFj+piwN(lWTd7OotS&R_hY>+Upd@;9Tw2O!3}byeNhT$wnQQ*_va+V5yPtX)bfMzG z;k2xw(a06sh^;E9FB&U0Lpf`dnTD99-ulInH! z-Ml}2P)b%_FP|}ksp;!~PRG65X@2)QP|Mu=(O@w8q4AWx;O0rAEhqWZT$!;*3fx;S z80J5M-ax%**i&Jdx@)Sw^~OTE!UV>#4iYF#2I4HN#brKy8V`ftdV?pM)f7oF3B-qUncs?E*d!sJqZ6x$Q?Tkihs&F}ay8vQ;~E`Sqr zmtnJAj?~vo7Rn@z^k0jMd^C9aKCE1yeNd#jM(@+!@NWLsZ^d6G&!fk`(*Aej{+<5k zH}zXRqiC#J5tf;xrH>(C<%C%Jn1HbI$Vh27!|p{m0_PeXsImEXOoOCrDsNYFj&Y^cddN(%2X9`8irO3 zrR_torohX$n>1^szokwq+d95iVby0H(wVO57GLS*XC_wu{#g8N>fK!G)SA-GLJ3u? zlR^L6@VjzGvvFByaZ|rk+rd32ptco@ZLJ#HVFa^90U$JMjrJ6PfS7@3ro~sQ}D;@L7axkVe ziou})>5s<>K zSyZPbBdNrP&F;2CI!7o{MF0fRI5!Gw%Ynx&Ta5 z$TN=vKC%ngiUZaO`Hd?pJ8uvQ$VKA(Yz7B0XGjSHshg^5<&{Ij86zD))xa6I!VGn0 zyavw5wtYvhf@|Q6TOp8oU9%2nERmL`Py4|czvaud>=*B^LJ!MrvARnuV9)BMm4T~X zGJUPrB!+1$iF94e>+o2{clop^tN1h?>-GL(Gwu(|-IovmiBhANFMUl55KS_{bPvxW zvC#z5h={-x6B{+?Afyoz5hplp7nkdx^;_h*;hP)R1|5SmhRTT|Wc#*4XREr}#Cz;9 zhHqb)@mf69m`58JBa#%x61NKwX>Vf4Feb5S=UJFE4P>-OA3{K4iL6e#J}L-_5|JW| zSMtgbgd8MD?>)KDv5uh+b=2}OnoNU#KIKZUb>gZfj46sqml(sb!If;dvj}+JSZD>n zF$8jTPGAmFkcKAKo{u)y=OLSreRZ_UD=m(8edCp;FiiN$>I@So(~Ov)zY~pbswy8< zeE4gTyR0hAU9KF^ZEu<{A8Gbu+0!9HVv?HfSif*tQ5rUPFArH6bn8~Gdc`?R_PT`0 zHt~)Qdb00mFVMwL6J-E2AyloYpVROa0dAP-DTbIjFT?I&8dJ5nrtS}pKAK-{4iLtI z2m-p#41h?@+C*v^YJ2ftX8^)QT>PAgIfn^fEPLhx9jqO=)y$0&8FJ@DOXu2w5l$G7 z&n9J1h3-x)V~#3|h2#5#lB zWr&4|u!5|#f#tIZv7MvB9ALthQ#BPUQ#&|&PF`(ix{dpVFeb{**^m^x@@$Jz_EVdx zqI4H6j1bCL2(>W1lAJWgcyFHwam0C+?nt0E<)_Oo z`ZR)NLL*@ViWOvroI~u+3~A38k_<4{`<7m}8|ZdhWGljW ziiMG)m+f(6doE05g8u zHcni;ax6f}Zz~yOrce9v+a^~0ww0z;FRk>ndTDco|M@{qbqffQ#Az10c3+3*xAEfG zb8W=dO2h`E`{e?Z7N(4VEXCYt+)K}Yfe0clGS~U9tu1Ra3EHTDzY1G))lG}OAD0-y zBAoKrbxAAFYukc#Z~oxzuHn^J{yw?9{`}>#h z)52?~N)Phkx9Nk`NtszlL6jVp588=ATW5$Ai(VU}$U>HcscTJhZWJ+%W5#iZu;E(- zX5qM+aT-C4Lq<~@mvGz@6-Wj+Zuyj%u5ud3Y9XFaruuY)+aiK3W7YK__I@;8j$+?f zC;=uQk|aE62}V1y1C)X^v!eBEZW)vWMZ6P}^K-AHG}*8rLP63bV>UW})VZmKUPTqs z_42S=oTk!TJZh6vtSqXCXd>u&i(o@==w@cDc{cNGP5YbCes6y6F*l&hvM^0tAH;Q%8JjSZ zB9dp5v1v*+q@j#sly=a)da_~bWW$g^NeSN(@^*(`gbjJblTX|AQ!Dprccv_Zn-EP}C3tz@qx?J9{=vSAo;nn`q;hZ!y1(h=04OLZv( zxhgUSl*I5XPC5MqkUeh^D_ap~R_my9&jMn=LzsGU&<+)Bm-xnKke)S^iiI*Wm=Kf@ z*8m1D!6p(cnWw?J1j~&ntAm4*lndhAhk8lwC&>-_TUL@x(-0@bHKM{xvWX;1I&mXL zF@jkvB(~4b;0eMsXC2G-a+2GxD;vm+iSQir1M;ho2^| z_Cd)L9;JdgKc!{Y93U9@IWQk8bH_%YmyH;iK!ic(W%0cj_YmVICV}0V+7vPxy9RBY zSx^>cKu9iqCg-?8S?lz9q=IjpXz8_=yR6+9*{KQVoGXVBAb_1RnLaikzI8JF3X^>K zJEpbkWcn2z`SN$PzpWwDZzJFv+rCbwFM0l^PkWN-^;?!^)uIJe_PfC>)?X^MzwXlJ zLuTU{RIwI#8mE!4D*oPN1|ObIUx;v$_r|vmhO5vEZk3oO8l^q@F${Xc$_Ar8rKvZx zqSXV;Nt_VZOVCr?o(a=b3QSz*qqerJ%{{tCwE8qrL7&xWI7j6_VGxTra!#M~w0i5q z@S44C9aYuLf9x@eZ(r#s@I&&OxK<-4E@i?+@R#{(Z1~h_9V-FKTzd@%HVVrqOtSQB zY*aG_HAPsr-6qL!Tq{LFC`8)s$-^CEP%i}_4{Eh_ldDfB`5?cY&KP7R$GWFtw+h?O z*nwb}=XWNWS&DJWTb4qdWhUA@*m+~2Kp18qO}G|8$0jzDae@FosL5$_VmCillEnX? zy>H!Z+(;LE6=HrIB-u_+-HV(-dQoH*(?%RS<4Yzpi-jS}bXyx)G9JqH7vci3P1 zdgn>Dsz}KaMam_Kl4!dE%*ZW@#o~9Z?~aAwP9DUAGB*}PpF!kqPt2_uE4hF;mo?{B znK>kX8QS#>11~g82)f#^)Y8F;bwMOmVvFhCx}gGS7;_1m&Rsc}tXQ|2lwu-SnEKI` z2dgH<1Qu?oQZ*^4`kEj(b46}lO-iB=Cgiy@plfXpW_fSPo#o)OP>7gNmq>xn!7p)J zg_NlT&W6luL-^{zYDiTdP8L*SIL>pLtwPl3xmR;|@ePOp+zI$KDpM*z%wf3OP@AtZ-Hdv2M3zwo z2v9gC1{1HsG2~uliXp(MkWD-GU!->s#6nVexDMJy8e)kAW<^5o1wv4W7!lPpQeRmv zDn>D83HN1Gt3}R>ED%B=#fmufP4u|k4Q^jefsku<&7Ma3IaMQI$|D&9pJ%^P13<+P zNTnaBd{_;zhiU|-h}vyd(F;k{6=6iMYD6v+SN2{aVCowxN(5G>?wf*mqi|L_nW^2IJdobX5%3C~2Xpl>TD0U)813K*IS;rSgjT zOBSrv#KcsZa%L%^Nxs^DjlfwBUYll9fIPVAOghuN8=B$$1q`YW=kh?03%3w~?wT27&#r=FuN4C^vjeGq1VJXV>6U zpWha+xd9=r(;`Bo0136*4Bl4qsX2HRfO086VnReoC|uIM8as8AU$=T^K!qrc*sD1Y ztyg|Tp71gU9UexuJk3QwFeo`u&*fpiZ~-xA%rp&o zdk!Da|B=K{DuU?LQwr*u8!ulMSI!$r&m7&w?uVv0C zOX7%t=Vqg~X6hS7jE54sg!6qBjNSQ$LZsbuMp+ceFp5pY{Y{cVV+1gd^7hUdrEwhL z6uJ0+Ji1-ceW)A(UHngrQsG#ovzS zQ}D!4b0{mWw24u6=f0`Uh^_%}n#vHty=Fb^?&VkIN4uA`@|c_YuiZM7Ur5S-2n8qM zZb7jsd#Jgx)+nVMN|*Tt>o_T2Sy2-YVIrs>mivq_I(%8{-SEpoip~A7$c%B3ZwgQ* zv5WJ|BY8JSu89x_!Q`%EyE=Cs9{Rm*?pZ0~a%0zuHHZ^{upiBOKpy^b*Xw8BOoo$A zf1MOmh7u)hy_v_{fiU-djro0p1`Nip(_)%TOmuafb zuFjACsa!-ciXnnA^b;9dh<9^LnkK6jnE$~6?Jia#2ZDw%^=NM)+Oz)H#d`xInL>^l z8D!{DxChRi7vWBx92Ihbu|FjjTd#%^C7}cgcq_1-XVhf)kk4j1pA1KxPgxK@-Woqn zZ%|Vz5!`3@!_ikq9{@o#MQP)+)I9bZ!2SaIzLcg4raZ>g@zI9*nLX3TtZs98_;xOm zc8Kjwdi~zy>s8JPS6$6QYTP zBx`0Wb#cWD=jMd-eRk996z70c$hD)hBm`pWl*U4V3ZK66*xog^+uziAlgK%({vPHc|Q{RTzbCEKsio@+)i5`%Cj2 zhhYd(y4|Sl<(zA;EE`akWwxt8#@;z`4^7xPOHdT0J_}O#-c_wIF$Q215wwKp^r#Eo*)d(c95C(4^!7W4ZLbm1i8%a4tPztwgc)CCI z8qlrV(ru@w?SDJK7)!(=7By1ERb1KopdNFItr*m^Vt@_;dH1ZPK|QvOR^2_eA=lEN z9$b(^^*t6UOM`lfR$KO+HX78^xgIVJi>sQdQ-Z1GUz!$&R|dlLu|(3Q zr=0D7OiyBZMfZcwQeu?@KM|mXmI|9Ln0tu@!Hl9fgrI2~WaZ9wS1-QaY#s^$#`umT~7@Zq`! zJc-AB5nwTMq&qZ~+Q!j&Wp?ILX+$YyqE*w*s`tr5lt3OeEk6z>`PZ95LR5qzrd*se z>&X7bvBbidVwg6a_}%U)B#Y(pgDktTEyTNZM?N#J#NPQdv}AUZm8DhpbjIUhx7V4N zr}nh@tDmY5_%^5Py#oOOK?K2#5Ak`C^BuK%AZ*K;+Lt=>gsTX|z_yoZwIBg$oZ<*K z?Tz*`Ph5lrW&lg^$_LJr&Pt8IUYQPofmzOg|2iwiNNAZoO&hfHs=jo36VqAe)()k2TF_C|f=`*RvFMUoUiESu(*#Dw_r%4Ta7QSz2QbFOq zEKe#Z&LX8r1!!*NZ6_5(Kw-|G;nDU?Do7#)a{u&mwCa1wZYDk2J);1VIE@*0IVO7o za6Blqct!z^VkIf_=x!3-Th1uR_0BS`801)C=`uy8ea8I4PZopZuep;nO(PLTO~>#K zcJg&^XG~~9g@oI=TV99fiNv82DZ)Di#j3zWW9^J_tYC~{pDef%-}(%VQ7jESJe2B1 z&aTS?oZA^MIp2+!-hmq+1vC+jyI(w>8^{^#1lNydw&*y^r;J`_*&w*dCRx{nQigdD zM*qYyJroK;qR@$@6Uh-anM)qkdq8#j>v%WKnrL4wFC`)*7;G0GSY!`t78YNv!55VH z_7qcG*4OgR$(y|r*X3`sOrERw^8D&dovRC{Qd!CnM9dF@n37Wslt7{|Pkocc!=5j9 zU49A?Axa1ramytY)Yz9xXfCeIp}CDNH|Kl4tUGCnhbk77y70=gKrF6UowW>Cm!gE~ z#D2T-axuWl0s@n2wOkBP1i9?9)+q*f9Q4XZl6lAo$RN!X^yI_xPxJ358dbf2l>_Qk ziT_2=zv|L-LU>hb2*z3h2^Uw|mYCdsnZV_sSAZ{140DhU7)P;0R<`ezuwRDDjmIet zIotzuR&I8u`Cn_)5Rfo}N$eEWZIoEX!yT0szy#4aNnBhnZF`{=Sg*PB%gRcFJ*WRy z3IEL&cGfu?EnlssHd?A$vu%LI58pV=V^T>3aO@H;IGpUN-Z}2_B1A_Qfa+#uOo&m$&BDa}B-Y_h}xp)gH%%msxvbhCoh+1E)dB zNg6R3y+oM`xRL^@*KC%onHx(Ra1{%Ss58c`;hu+f$0aMf7uZQaSCBs5?c3L#4t15uD;8X6O5 zjh!tJ3)R3}#krKZ|?{GeQp{w+>@t%NOW8d&53F(OW}}tOVSOm8V zf?J@AQTC~4Gjz)?)@mfy_oai=z+#mM#!4|z9<_uCPZhPrnj1p9!0+6K)AM z!1JW!aoz_+115MF^#+e|XRgk_ROr?vEgD4x3RogbMkJM?vbfy}P!)`TBhIx=<>GiQ zJ+W{>ZO0vD{l2gCr4{pw>xA*b`|fq`WE;yS#XAHGjbHgaTGf>0(>pB(F$bO)8<|8=4yui_tW{6S61j;nTuwK zplOM8WibEQv}q24AA(?F;>!2K>Yq5xQj?geFgCi_lfnxlg_~?f40WXN^b@DVVND5$ zMJ{fbS3wLlED=*IgzzlU8kR6=!cPY;qbfC#FQ3`+jzHO(%8;AE4IFx9*j@=(q)t7F zz}eNSQT8w#O|FV*ELskl%f;{WYfi((AR{7mYI^Evpsh4ej5g3TpijhCQvG_AsH-#e z%jwz0)v-GH@#3QsDJX%`n7br-dQxaBDQN9r|NFsIlU$r#DyP$rj3@w!OJfj^`PN~6 zW-U)KZx6$hyHVD;u|J*uz^Ow+NRlLkZ{`SCb21`=#<6uzd4^~OLrf3CvvY9a5uF`} z-`jo-g|BjKT%CiwR^PTgw!KYn^|}FYI~&s|#bGE*!9T_*lp$ISv3UyE3kA$7^@DOr zK`sS7*BCq}W>%0WcJJC;udg0sQ z=-ytM&v>8QbtVhXHTfA`eK;}Cao{Hu6CR~Yll5?v@|bNAX)91zK{O;JDUr0~7okJ~ zqHGtg?VdP?OatY4SbZ5j_HTkaEwnVaf}Tdn_3$Q#Hkx5FclQ*yGIw< zT22w_)C|xoxScCFt8#*8q8f!_n!omKIl5PkY=UEv#$rD@$s>D)><|Cxa$w+8OE{-` zgx73xsU%d$sau67(Vfeq!sI$Pn#gD$6KS8y9Ov*tEVRZa+)?^M_u2<~_MQK6W|SB2JWIvNV5yrYMb>4*=~@ef--2Ih0Yv zm?*XHVjz|=5w^8q9db7H39~Ta3a}DiW0WAu!4K4+?i^R1(42R+U$U1j9A|3VDiBPOMAAt`S_{MV;o#R+S!8l`jj^6 zjdPbkbsWL?qQ_qc-Mi6nU_!Mv`f4ug4Ns;&opX7=cfdA@Vu2AU7tInCCsMA7?Ku`Z zcrol^&H9(W^p^U9{D;>go66}4J(ZFXFa$5 zHJ7V_rbgBREG@2To_IbPjx_ zuv8KJ-DDvQ2h%gh*c>a08}uQl6PT38z|!HDQEy@jUhnVqW8Ucv>XS&nub=mM_`BJl ze&+W#*=^4ZSvF}6flYL(zr6Q_PQgW)fu;qbP^iK&MDUHK8)V|wvLT-c8?#bcygd>4CWKL``fqT))Mxt z#M)5)LWa#8-1x-R7VR$7fJBl~nKV68q5*0kPzo-b0#ph!oIE9kPrY60mLBB#D$edz~R zIZv4yt>q?6hEDBEy$E|1B21h3H`?ZKmSI?N37}IQh?ikrhBYI@o`}?``Z*EIv?%jJ?5zp0 zQTEwQigAcRgrZV1z+p%v@B-`A3M@^KE)t8SoT9i~pynmk>z7!1g z^j>Irp|z>d%4yQ=q$N%B=N3{V35zzsZKKE<)&?XABUn0*;25MyXj|<*0Www;=F@)5 zfLy#{0Vz@lVIG})@d{w?>|*NWG;8DmiNWLjwZ3t98ysJr)Umh4!cxx8K6blp#?l_Q z+KS{Tjl>Nf(gqNBr_plBtgy_EJ+x*b6BQo0d4q$IlUvzuP$$~0lP>R;E_%$6kU;a{Vr zWl*JjAP>P+x&{0JGj@IJ)ZUawl!RQkWZHRKcrNQ-Xj_;#%~tic@V@BY+rm59sy}5l z0HpTr{1w~69Hop2J440W!tIsYUP^l@-3}mmDZQnX&duKI{;>OdF|>CTQ+vB@gKf0{ zfFFKnDXantk(1SnsDjXGHkh}1=K|+a0S4_DKX3K6<@DsO-kogKpQjoCQhRs)imhIP z(@?N-<4OQS6bimL-P2pW-s<&I+DmCKrMHsOxz%f?60BloZ|yp4>xUo!E*2aJ0ZkJH z7L{D3DFPy%(#pB#i&w0YE55Q1tW$C?!&n0}fh;N?0|fcL<@E*+XUS&Gsry zHfWbpbsF416JL+&P6Lbr9w%*)b@k;8hedo1nsaqB%I=4sJJbBqc6j;glN9ZOT zK`N^y<|e6E}t08%{l>}HlSrh( z*rlV6H&EtMnGoibv+Xj#yn)gzvv>o=t3I##yn!;;1{^`js7pWfol9v0UvHpz1I0^e zFQs3vl+KNhe7JnDSmseJqNzj?;E78me7z4TksRU0kr!S>H;Lx8BYJcA+B->3{#doE zL)ARhTI!ZroJ@5AlN5(8bNIZeK4+@qn9)=j0C`irCGy0Z>bu#hKLj=ar1tLo6`Sgm zg^`4@OKqs4K!s0Vc~gCFm^S_B&NQ`UqwMAvjpVND zQAs4QAoyy8vfZ5iUv-vuHV_wFD1S&%zeyPfWxnV>U%e0Ug+Chr{>f}w;Z0J->( zJZATV*;=uDbeoUU>~{M7>rVIgK>w}vv<6mo^Dej>jwivVOmA;6ob+yc-Oi*p90a$c z;r)y<9<(}l5tA~Zp_DFhQco3!ri#g3XHpUXnl1_%UQ>g0VhV2Iv1cNj{#P zo*eyCT_qyud7hXj!$JZTsUj{1Qo<;V1at8s z*Jopcl*f3xBzB&^#oNK(^|M%f{A1;zvLw~>o^3eAw6FsXu;X0MnS7A$?}be{%0BhR zTBfv61b+DD%6OH&>h0b8T*T@L<08)E*?&!N}17DZiS%c1f_Rd8W;wVYjmgA5b zQiTT0PrjKqryRFei~HVIH9r>$agiQ1>PBDR9e%EOI{{e@v)tk4!o?gg&Q!Ti{+f1i z0|trb;+0qii?}S&-hqmOTRL0g!#BU49?9UlZ~von|M1tPVE4B!4ptIFra-x#3j+~K z02jmD3L+|Pc%aM{#g#c8D1*Vgni%yu{VF=E-WlZt(@>SVRziTNlGFrBo^~d+v1&Pc zX=fPV1i_7WR)f^adkmXTw`@M*l# z!<0lgR;+;yn6$OMylMAQzu$fDU)fD?{k2H3wTadCZG(rQbyJN^-duM$(ziVf2RC_s z?KJJmBm!i{bDEl_`$ACHfRpL*RXPV_yG8|JZ!;wJN(!Q=h4mO`2l@?VXE&gV|AZ^oMhJ+q-Kuu;+A zzyIt1!u;-h5BU&jn;@N80;3R_J4V^3$A0HYuGzaa6Pw)Gfk}=nevSYMgIm2VlS*rL zY2S40LU#H|Aq}ZP5B+JG!ywCUtU!J+0WEV}H|g+s zoy01swZfTE^X#TwVl|Ixg*$5s!EHTS8@}Uip1MUA)S8Oy)>vkSI_FOeHtD`l;B)a< zbIID6MJI$Agt;S?iwW|GZC;_wh*38+&Kg*{6gkm|Dnr!P(7JdB2up&88h zgbmw#*&r)N+Z5%t$l@;!NJ1=*AaE*4T?Z5|b<7|lB9;j&Wt0#=F&1JmLG4*^6j!YK z1TlssT@9gm_X%c^^`DrU7=6CfUm)re)Mm_Zd316SFmeRjKNC{EO=yflr z+a{;&0cEA2P7vUr+JWW;^*mIc6VwL+0UY00Qsemr2 z2-~s#&fjlc?9c0)Y0O|2e#oO|*N?Zi`eNSt!i0{jN5|N*wo}H)`g6+hwQ4v6q$H)W z%Y5`#2ImDGULTw(T$_KkCr}4~n*Q9F0hg{+uMD92{4)h(5&xQ!Z2`mgF5n|f2wAqx zwg%rT({VMhi$efYS&D^$mO;EmZ;!Ic<7g1*y*!%m*&w*IeepPgX**zIb%y?yMP-#iZH|NW4S zY}D@k$`lDE5M|Or(fuHDmkr8mZVwvs>Z8msW;PlPM|q5UF?#c**Y7v}Hlxh+@Y>D$ zG``vDGUV+yTSUK_-tMhNSf1Zg7bae7ITyK%B!wxk+1af!#WoO=IOc+IZo2c%y$S{( zjpDf~-_@@o6lY=X&TgZav=XtPK9^L24eXrf;>LGHPR#Al1dVNm*)#&_V&G5-iHu{H z^6a-@`vZcdhDTYqGZ`Br&Ji3462!OjS#!b{ebyZK)a)m=Gd}AL`Ro9$&0y+Y&n3l5 z|9TVw9wsf>xx0V81;GE1Z!d>*?`G8wVGIRstB<%1pmcH@V2(*ynKI2AX7@2mm-N(^L@4Ejr_otbpsPWzKv9JH^ zf9ZT38&4Z}??8MIL&&&Ox8Zf5-N3mQ${55VIq1&TLu-z_1;gI74G+$f^P}9H4Z6b{ z(jH*0(Ss1RRGb1zIY(# z@PzDpgm(we;BSwee($zdDhjF*3vrZCrCz*8x&aMB!xZrdHyxDQxdwUC1`}Im^Dr8I z?%ia$e?r4++%w-=`uMXrPt5=RrZvm`b#GvOy-qbUEJBbXT5>CG1{{mr@}X9XQJHQR z{GVTba@{XJ3Q%45|E_QOqUnp~9WITlMvJ=cB){SRbZ4}dnc(DXnRqzTC$F4lhY!;q zjWxXcnOSH2FTJMyBNv|5fnX~+=8uQAY;MDQuZNS#@P5;~N4-yX8$Y_IPwzL`$L((a zb+g+IQ7(f2x_>+z{ytsJmd6A|A^q9#ORWID!{z3UN-w`_Ef+^Dkvv)oRlw;KfJk9K`ojo;8;Lt5bF8bi5>vh zfUB#Nc0KERHES(S05qv=UjOYFCzaZ-7AZ&poI=rv@LGw1w!`}5 zG&ol$=c*p!<|F^{vkD3*qIi90>3os=$$L;xERhKF#M>F}j0ZPhytNb*HLX&KS5WRa zfqVm;tfin(OsRxUO>jL`0y0Bf)&+_dlz%Fb0Bx+KR1wk$f|I&{-VEQA$-K!F z;AAbG#W;#d1U;X$$*2M-N(mv2Fv>~{yhNR)k`+FD^O4{CtiEELNd{$|<(e;&Z)6Yp z3Q3U)ku=gu?Tzpu+JFz&(^njaC`OHB{=5;KkPUFMmcF7uN*p=$J@7cmak6_Gi^4RC zqqynb&o5739iRSql!)U%o*n(Ff|Jt=ZD9%dkKp?t)|%|+qmv)=FX}O3KJvw$RbZNC z5sz!#a`__p&i3IX6eQ)0HBun@Mi_JF3Jg%563)EiwnYkzD@9P+%-P=aNi9y+bP}SJ zrVuynBwWn#9uFT!-OLoM6Jy{OMU{*;s`}Y!z6LXhc6xxJLM_e`b9D~Jr6?t@W zba5n(kNznxj!sYX*MGY_(xzWM^36xS>a)6%heRS-n;)4kl5cSz#$KEdsv>`gZIP}- zoGBV1kCPVZN}{E*P|XYh@HnZ($(k7-M5HW?+T96epT^#$)>{jTEImE@SzKs+7W{H{ z`s0~8ZzjM-ppqa&b*}n+k$iJ|&|w58X^2^!M)kaT2zyVYkR_~!m7p%k9>kaD%{VLx(1$sZv?St&B~^g& zMv9r=24n7CfzcSnCP($zW%bMH`H}S*lc=C(;^VI?BTrPo;tj>F{i=b!Z-Ngnh{#nJKQSu;gsh#;nDT6dVv7s>aukC+!g z9D`=Y=Drb@+_~z4f{R$#J3LNWqPjRFSW@QQ67D$JLxLlsDTKs#;#i@&E{=Xy-=AJC zwm)A$hWW_Xc~*B(#t4m3U63(fB;V8?bQi!Bass_uszthsF`34Zci*;2ccE0q1X6Ey zIpZYULor#zI8t^-h}T^;y2})qon4;P_}FR6DOQtu>nrjYc7?jG!_0h(JGH2lPnY>YD9Rg#6a6&{j-usmp|M5 zj9<>wxhf5rT7bRz$ZvaARRM}b8uH@XgL?>78dK`4m0A_tiuLiaA;-ttzzN-XMj?iZ zrb_xgh;33;G-Z+#wflMXZHM*cc^q7v1|Lt)FM{)boL{J)gQJrRb(V@mH5%Sx3`r$w zyPxNa zUA4|Q*{D)z&2AkLOCGAS3o9IK0FynmFcG>llC0h2G1?_eKU7K*2o z6VHHcQdHWOQlauk_ub-Ty}B$$M1p}2@wQ1(5s9J%HdC3i5(90A_218qE=-K-^0=Aw z3K*tnaEeU>x;%Lr+-3R+HmR;)%t8fPl170EjwusfG`2`xG0b_A)_R6LPL|cx z&I43{(KwE|_r?d1eW!nduHtvBox6&hZ+)OV)3@D5v zQRi967s)rZkK_u>HTU@GhOIKS5Ko0dUQW8}uARrMFehP}N^felN_Vj^=8`m0Mz9hC zFIackk;%!L8eB*Jlti5($EF4BD#n3y{+oZiH zCIrB+kx=|f4745A#!Yx8l?>F$#nHt-o)lO;3d~1-_p=&{NFu1L&A`kT$+xnP{0k6E z37IeNX_K!|BP2B;V9N;#^@WN$SJRt9}jMm?qU&FgeY%q6?2JA>sw)bgyQH}HDhTZ5kkgAOUi};lu@j(@8#*vg`4=H z*3womVi={s`)i$XLUwLx0SQ4$J)gMacDWVV~Z*sW@xc1PvohCf49k<|da*zNQN+n>;oGeyipzW~!MVyJB)rC3>j*reS zf@5{^WSw1E!IhDY@rro*-{=xvQ?7r@<3O)09CU z>S~kfVuAxLyBay;t;E1fvAArpR&ZpaT-NmZMI=9|hGso7L@3IgEA61Pil;gG3F&^qtcT1Z)mftO-(UH*UezD2ij zBU|uSxB>1(67;z1U1SC^z-MBh({0%9WCpVXftKjDZp)%ak97B3TwwoU|8T!#tBRCF zQlwneleWu9thOkTRp)(9oyYr&8&$h?{qgeh!`026vI@Bmx$8D|i_UJ6t>qXw@YM4F zhkJ3fUkNK7K1zszM>sLAmcGhjTi~RftA#wr#%2~mw_Gh@a0e6_m9^$-l`}M&5g}i) zdpxPPh!b(*u#RNIV-yl$Lz<|4m-JQ?xdBI>!AW=Y7N9V&absZL;AEXo4qv-PZ5;4m zKMm0~Vxa4=Ua6`5`&rm?FDjTm@7`bzj)A44nmyP6RoU$XB$sjmQxLhLm*f6Q)@E#wIL ziZSSi9x(lCI;5|7$Pt2(>FcD86YOk(la{?P>=07h*mYfs3qleEFoJujXxxZ_uEYA; z{r%$oFRN(abV;kow%XjYZ!|YhmO% zPUIM^rSE0gTi~Q!|6Cy4Ue9zR?3C7`k#F0PZBDbKj*_hi|8CuUSp4J4eSdwS%C-LA z|M|bFN1Ys!o9HP>%a**xc3EI+Y6y% zvNF1)$1tXj?*UVCeEH#z@JhCKx(ctu8|%ZB<$b)ig7EhXKU}=$`ya2a_SLTt1jMsl zuNyiHMLr6BWNge1=`e;v5Bf^N8l33quxJaMw970ETwwFaL=d~A!w|9q!uVbci;WoQ zI;``Wt!vAF7rJutvG;e&6%N|3LbF}G7ao~=v!|v{2t+Idpd;NgpyvhHwM|~UK3DQB zxU!Y@B9yy~m_Fn!wAVH`Y3G4q774S{SQMR#Br@tnG%_W~e_UPMgx2NNhnvv93A5QI z8+v!jm+YHQYAX=hA+w{Vl$h)$*)ooi4evyO2Y@+a(dR;M3odM>ttg=^vP0wlYU6}# z=w-8kPugS`a>r)EtTQ<2khX$8#-YQS!d9)2vJnGahxLcw!>f1hyl};GP4}#QtH)$U-jtJp4 z$@WwK9l4>xcPO$Efe_P8-%;CLeFqV2BZSlv#m*M{zMK@coYB>90uIARvAJvi`oenm z@qLMyR*wtEgTkgtD=A)L&6jv(F={Cgg*aQv-Du$7$MS0auNx&GZj(>L@q!sPeSbKf z&L%g42&}IUqo1aaabJp;Nit0)eJU6H5)!8XO?v_7Mttei_i7rY4S#0AC8AJp%sSzj%UMkylK5J zje*KLe+Int1W9~J=c9pjD^M8;RcGsx`x|i8yz0rJZ{cNGXYDjC~xq;{TI-%^>DXr_lhA?nks&y)3 zl-#k{Lbxawi=aT}r<5Z@OFo$@=XsI^XWxFAi0_O)$vBYw&dQ_KwRi|Y5n@cD{V8I| z@moU-sWqm+I~2E(`~-kSABx3OjY zyosj^V)U46-hlh+5qJld1-|mz+q*lRcnG&im2bwEGrpXW(nd-fDZP`F&V0GsQQH698ztjUvwPvsSr&y_ zhqKJ*fH_*Jm#v(ULZ0=$s$5RxcSMd0J+4zXVn;Na->IX~@(*u>knuZ=-(fVG(P+l+ zcrrmkP62T6-jj^Bkr#_uqGhmq1oO21qwo%tQIa9;TvK*!slA@Tx9bWZwPc^far zmTmwW0yagi1dwq-x}wUA3u2U;QEtWsSp|v+LUE3bLg30h8waH;iq1GFM)euhXB?D5JN*dwnCL{`LJC5A^_Fo^j)-X^ zrHz#C29S)D-iw1GyOGJHEe#P(JsT1VwB~a)is-V5ZAA3m(H!443cJF%gJaU&2G>_n zpH|;Uhkz(>O|oD+-xmfXvOStlfiu2u7Z}v|zNax5j8&xpr0%Z#B_Ga3F5m$+zHhhX zwvp0CN_PWDMoRC+_npq-*?g+!{6aT^%=dIG>E;9qb8EY$=7_W-2suKZaZ)j+MI8Xn>Zp~%ya(x#5e9Mu>p zZKSl3(mP4%wedt2_zFlrjzC#X@!7|Gg2%yqqa|=4%mz{axBI$&WKTSM$H*# zWaW$mj8W|CswxMjY3P7zs24w(&*^bBS!~2Cl8s zOx1WJ$Knj*jTk*=^qlcVR#hy3hi;@5YBS!5@kWf4Hd5M1>7Ar>W$F&qY9hxG^c`rL z$hC7p07HJr9i36u#sxVRI~W(lC^w_rj0>`o(%ADu;_o(f#}o_f6|c{zSity3MtyVy zNY7q-acQWe3s1M^ib;ozApJvn_Owxz8(~KzM4nR^_7-y442R zQ^#QzS(|nxcsQ9PgG<3Io?wB`8ElN3W%Q)clg7;|#1Od~xgOMMAYghun=CmarHzy} zQhFyT-FnUucL)xmaJP2+rq%O-(cJk~&qb$Ld(IK|BIHu4lfh!V;zETO^FoMxo#3_c zijTz)#`ism!Ce+oJ4tHwU}M-P4)Aq~6AjpkHJuUieb0ccOUCE= zc&I`0=dfR$5+&!!#8xDysab>J^s^NUfG*}#+}tldHk?}hbUaS_vqZpqIJ>uoW8t&h z2}8pIdgCmR=i-ouSc6Iq?z9*x$*Cl_XtWF(7hvD^b#zK|XmC#kjlo}rzh?6VMBKxn zg6iHEBp>>u=Ubwv%Hk?o;Bq40#1W>zJJpIa;V4b4{^#8Gp9pVwDukTIPL9sE-}hv2 z;hR|AgNZThyi^lo*aMKdyYlCoEj*4Nk~bQ<8HCtnJlriADi^@{6%%aOJ9bZ_t#{Yc zM_*R1jLW~b=z>-P^Af%W4L zRh~KaLN;jNuCele#t)BwT|4XiK`VvF@$As{B=hYxtdzf{mR^wy9&vG?{L`nN=y2}C zS{D~kOjXX?>WTS>3v%J{diHIUEWq|_HqaO6z$-G`t;C}2OeZicKv* zJ&Pw&L=0t+1+Z2!xnr`>p?u+Q;fUh+rEbl!K^5MunYhs@C{J zB?yWh3S{|P1z}WG2vW$qlVLon zGX&}v%PA+s_H-oYF&DeQht7945>Nd2I2!ij*)Sbj<0KhOtyyY4O5)i>Gn8@P!9{j| zatY^g9WCv)0YRvv(QVxSyu+=vC1{I#?jltSk7*%77l6>Y?XIie!y|S`j%Z0CchWzR z#Qm76gB?lU0#@l4b#hb|m*UbUq9+~)VZATgZc&p&O6AQ~EhU_`WU#XAPR6o@mE6;P zI-Oba^lOk>ccb|<7wy^mljL9XWIEH4+rWV=WI(4&Q!7!JtGXJqm4r62QK0igAdEdA z4{F!V*3uRA#QZJwzzllp?rcjx^*GXc6UhVw5I5VDHpwV**g<$cC6`z1cOOp`$K9op z`6LBCPSRN_9e|%SXpi_#;2@@xzJF>;I8m+wC4kuw>ZH!Lf{-7=kg&}VDu#ZJTeSG)f z{V&#NK3@ED6Fi{}IYY;X=Xm~bn@qBiVAF2G_02}W6(qpvmuhY9ZFM3f*RM4v&)aw% zzDK9RY%M<)^DVVvmC$ww2yC4bg>B*+HAWvm0rOq07TpX(MCG80wI`OZs3+!csR!;! z)gQ;`lg5-Xm=;0ijT~V6)D|{+n@5*LlyVgW(QbN(<1ymbi-eYz;`A};5ATLaW*x?p zB$lqpyN~b1-|;LDN$hNcFE#_paaTfI+bo1Li~1g&UH5$u=yWUNSe=>iR>);JXo_;e}&kPO~res}(^ z_+L{0K&7je9r!$W`^K84@`jTn`;L`9Du+sbVK_Up{-}Fk?ZcWLltl`}XK&|fL0No6 zd~}vteQCzaEL(AlX)>FM&+D)f;Dm*O@(-jnHWJ%2J%_FbB%#1(j;AA&_N9KQPtaO) z9@s=^7PLBvRNi9gK=6qtMhLUAr(Jtgs8V1nyuRV6u*9_adgS|?&hS27dsd*1OEyV> zLPpoF34fi)!jrqs4vE@6YtQ*{TO_R&5Zkl}Kk)zJ`oCGejO14Ex;PsSm1J0)CM#;F zyQJ8n!9)OI;8IVg@k=vv)SROiHh6^7pv9@-;?QsbI~4`j8>{4H7N zBMW>Ml|HxeRG2ksW-rOR3rBc7h$jQ(^GLf%SY5Zm&au)v^%;TEOCZJIX@t zl6&eLP17T0jQv}Gm&7`=B z*!(2lmEKn7tM$`CIXKs#n*%R`5!cEOGq^n-ZovY#*YjJ2(4Pvp6&|)Oez}coh`<)% zHiO@z;P+DI@ChZK;WxL+RAQfaQv<=65XTM>(?D=@d2jhj$Gr*S*&YpaY99bbL4bG@oSE>#%TkU^!WQSkKf45vC4BMvI+Di#aQsk=)i9AB$h7UvNj|dD-Fy zZv-G)SaFcL+;{P-3A5HifkD7H(P}MkMEtQt{Kc{vcRZb@{b45RSAzb`%1;8%G{ZV1 z@*oNE7D;ag%Lh{cwvtr~(Z32H6o0pnXAD40P^dGZBlo!7x~Ll zj;W*Xf{Y7HKGQyydboV1%cvLWwE!rl^3xgm)NUJi*9D41h`lu9( zs7dv8g6PiMrIirQJ?NlU9wMc-4gF9nxyDGyV@XI^`C2a?NvlIJ79t-J9o!ms;Q%sI zNy}VjB57gXX~`w=6Zqb%v03V&fIxtejb2TT!n2S|AICDpxd+3IMxFzKso?6AxGI`> zY;i!-_#?>Sw|FXB7p}sedboFdP6NkOcy$T)unE&8gci0UH{u5z;ri|vr`d?!(z}TT z!B5w6s%C+mH2De8h_mRGAB}Q{5riUhG};+vlt-iSXe6}6IR2y#JoXauflkOGY_vyb z@LaJRR45&r!;uzi%}yZJ6BnDzCarQ>`z}A~bxRd4+I23_^&I5euRLv!_+f-eWNf_7NC9EvMRMYf6_13%AIGy~ zF*|6pJFi)+?MH;WIwu%Lz8^ro7rgmQc*$SOYGZBD6+{6*=#}TKfRJ#?nB1=sU0o4f zOA&>(jyN*tyVS8w=}iYBj(N34K}KoYSI7-q64^Sc#*Szd)DB-OU(rNk$;Z}{C6FK& z*^Fu|HVBc2y^1Vp-a+}}Y6tm;x81=S5f$#Ibt8#7ImF6Lu4;a=nOxP3#m$kBEvT_! zQ}byehf2x8MCnw{+*}(>G)t{dlXU*LI<%cy-}lASCh5qMQ>g~=EdJrla^-!*jRj9y z{dgiQ>icv)8pwG@v-oo|&IXSTlDpwp+S!GWWSWRi)c>s~&g!5XQ8IIVBBOc~Lix#!7 z9He3H4cr`7=5rhh1}~DLmmoA${DeXA z#u_fB(@R0FMsi9U^{hmEZ)>eM5;B1GioT9%Q`Ik47@RzcxK74-*9}17V9dm&;lEW6 zD7+70?nN7 zRsnQHbUvlKwdCn6zMbZ;i=9g0D!=uZPE`-a#l{yOeYNU7zU>|n&c5~r$wR8BaE${0 zszHAkP>%gTCojXOZlk(Sur8yzjp{y<>K-IZ<*lQ;Vc=87=@xE}(cMQz`Log8D-zVw z-VStuM|7$YU#9kE0d(!?__$lw-W8d${6MdNACEuDwoqC!1#=8M5FH_uV#G&F@loK} z5+XZ8lx0a$r9d5d!a~pHkZzGP{gg&~Mu&q9j7dTS0w}9>o-9nVflXvHgy#%}HL5=0 z5$VMuZ6~W~$crolwP#rqvMgda%?lRNFXO3oo6hbF)e0JvPa_x*FWe%t?J3GHQ-e7} zDWms=o}maCJW<8>D#sU`Y4(JK5p$WZ6AWywVKp+Qy^Im)n$4$L1{w>Q4Y1ar+g1gt zMkbW4ty<0p5%=)%3!l%0k^fu-(s`yGdxE6M9OJez!bW5GAFi|&8M508Ic1|@#b2}e-ESr}~Nl${`lnnMTK zz6!f_X|WB2Tl*b}5Ls@7ipj{TzdjI7%J7fCZ0-H zvc+a^ZM)c(WTC{9z|v%}5>ZPDHB^0Hat2$Fifp4&M<*M}M2I?Zvyr%~)lUa9jw9$q z{V5{_f^1d~&w}e`R(SU5>`mncljL9XWID6%Ch0>~r1wX}Hf zEh{uhA0Fo8{GK{w&muphltUdwcSK66Uo5w67lm4jwYe}iB!3)%M_ZD8y)-Pgh7l;e zjb$~rY^|{|dI|`DQ3hshuKRfH{pBLsbl2CS^%rkt)^-GN7}THim6p@WL=eEXGGOmZ zQ@47&u}rqIOysHSAQpN__W6*SXg*6HgiMtJ{hJWC{q)m#`0uRU!f&o`eaJe32)}mz z)k+_eNvzHbvgq9O%=$34{t(V^`b8+4Q9M&^bcBl7#Dff>2nDS;yM8mP(=@5Uh7;B8 z=WIB>v+jl?SzhUWC9Jq)FClBEOR*`-m-XCH7g}P@C1C zhhOEQr|;5F!@f10OWml}DnKZKmMzs3t8ad?DONwKpiuHbHWVC7!}Bhks}_HXN}C8X zQ#QF3(uzUm)7!7TFBv=br0v&JY_|*3HEe4jn7cnqC*RJjNRZQmY(Oa9k+UQgG9tZO zN`|ce;OG!r7Bt!uG}^n#Wk63nllK_89nPfOIl$Ie9T((MfdQ`?~||n zqMVA-~4l?;+~##~%qcy-gqq%UsNU-*E+O^ioYe?AqeS@=@Rpnz)2CG?VHO;w6N zNe6TBOd0Z8oSjMUaM7=86KD=&UT*>L^Rv8rqKJroG??7&@% zSsS`OG+h_u&)NH%_+h|^jvOMk<2e!4IRZ8KdKPjBQtYE3(3u(3>QLJYDFh_j*!npY zq1*8MW^&1&xPe>_VUe!Q9;h@=%&uql>cV?blLmw*El;#ccPX^j{(hR2a^VY?mVJoh z+w{+*>>Ac1SxQ-sn2wo;81sVK=||-&`v7@^t>ZHfQL0n&cFe`LrH?0Fj3f*m#d^Q%ug*J()n1psFv`ghtjKEj%z5ENMu1Kf4cibsMb)@@?lCysTymlxTnm_>@lZ8A!~Y@*3S#a%e`BaXd_ zS%Db}0OKniA(udGA$I&a{w`lRoG{`x4Feczm32S0*uu)BUbz$Szmz#4%Jh|#7nZ^_lTqv5Bq8WWv!lKfs~%pFj=-AY24DN=omKP4+# ztp_5aZo~kqWPU(js{R7RT4Q`2u8-HVZ=+-Zi(iviW(?tcefei@)AaL0Jo$Vr3&X@m zhjN8Ts+*r=X7Mm}v(659!N!dru^Cw)a&fyo z=rzP6FQ7zcUgnO_*|=CCzOvu`bW8rPzk`wnlXR9!B~{}0dT;=YA%{#STy{Ee)GwVu z41$Qu=N8Q=;B%j=dX2`VXMwT<@23f|)_~~<=<4jK3h)Z#yh(>?-QbvXd;a~$zxA%)xxa@!|DAh%-ODp=tRHh6ys6~ajS4|P*A=~Oerv`yb34;;Q$z$)D zt{@?DA|6z5$(#^#7w*;gFWW?3I`}4SK;gLxSGA#k!k(jVGMi~paUqjv3uPtoxKzBc z%y-IXWLkg5lVLiaX56E`sV=Mhq(R>j{MF!0)I%M6kP7=k=LGo`TB4X!7I;u+Hr%td zL>KZ_r~MIhVH8GG=Lo24;)OC4LLXF2(ZdsZS-hxlPfOC=4Zpe4Eg@Iuj)i+H+uB{0 zYvq=mwd7b5)lF`Pr*t@a(FF1~^}Ho8y`cC;OH$IxHYG4(0rjd{~GuJ=5UIlCR@FZph7M}LSreGw0o56=!9R|lNp@`XC|5+C5%0bcfz!Pj{CiFaxG zc_Cl`$z+;pV|0~@uPL8b9n#6aRu}zrG=CTut}oumjtSFWy?8uW{`GtI*W%Qzxc*-z z!-4$gC-Exoku4T8J5RVPy$u0kHE&}FFWs)y4QER!(kPrGfI>I-I+rFocQNpv8c=t$ z79Hznzw>?dbF`?9tb=`eFR@bgAeAAPV^2YOR5+}bTg&fViOX~1d21)fI^#p=SdbD8fA?8Sub!HHY25NHW+P z0TfaGJ@^VL^@@7vRs!je=w?1}0iy=p`$V^ji00i5gtPD^o?6p+zn@H}ck|I%Ga1O2 z>=s*UoX86xrB9s0cShqNicu(&cuLEK+~))r+C~|T(?;V|G);-v7QfovCA_-&aMeXs zg?#98R3&!`h4%6b#A0NZ?u~L))tY9C-;WwAy0>3bCp9MsT)?Snj#QvN!f_;H^RJTn zS|K%@Owvi#B(63LSVFUW$==Hy*Ge2Ef|y`c+w$DvC?JSX$Y8}wWLQv_kELqt!qRnc z+wAa!U*m_zk=F68%c18{e0V%Q%i~)c`IuJ^w+V9ycU02I6B5Uz)WNm&Mp_{2==6+y zS4UmUm!=%1w?A%dlR_av>;ySXSuXt|p<>x>b2f>`(}&?y_9<#w_DO+!erD;-kRbs< z+yj+4RoLb(4MSyxZYMYp;w~taTN^HTnhO?#WpsF1>0#gfd?*U5$1a-hAI+tSH*fg6d+(c zih?5~EWO&XL+8W0?{j|rUq36#&SKL#`KrP%2|ZpVQesCu^0Fxw20aH6u$E;_)ZwJo zcuSx84veY?I}iqp;MJh68ktXTwky35=TmBFYj1$MUdFac? zpuu>zF}~E}%{)#uFb)YPgt|tLcOT>H5!5w|GaVl1JP=~N;_*Wd0qT=7#+&^?gWSU+ z_rv8nJ98c6Vqv5)w}M=NY#Riy?1&);qxva?l8sP z3LPf2R7FFv5D@bw%)qU&OlNB9hI4|*19b(zfjNG5UZ+>e{Vdk zvwnJ_lgM)jS5$UT*Lf}%MRk{!xLl_)KphV-x03J;8TMEdl}`h9q_R^trMpgMa?o62 z`S~zY)1y*rrJxMVvUE zIvwpUI2x87%8tKSC$v#ndZrzlPwMxwAJe36!_qQrZ?Yjwle(?fW}4LPrAZxhQ0SsS zYi`Cf)fDZT)S&>mIMAy5@0zY4wjJ19b%HM}ow&jjph|-r$R>IwP3oivAzg-|7qR3O zod#pvcCZ&ZN3g1%qs>V0S82YlB#P98MGbM+WfHoVJG{L)nmDAEU%wat$wPUSp*_<01c}N zsQH_%cdJ}Lji+sO?y;y`gMDJPa~ShtX^d7e zxBWd2)IP7C!%98%4n@`@X}vEYWeK4@@6YCwL@Vu?6B`0jEpdUwVK#_X=^!j*xP=ydHm#N_>fFxP{g`QAL6lmP98(4 zUMaw2i{y!XgU4G*Ba_IG^&q*+3Ad^Eupl^lm+qDOTX+**^{#KOKKhm5WdlJt5hT2N zE@%kiqy%yP-#VDHDYvOsK+a(9w5%*`#VioJ`Dgw$D>beJ(w9oCcN@I*>-vk7~S$4!`cDCDYQ1sjG2HQtV;$T! zJ3RWs2OxDQ;Hufjfr0THll7s8Y>eU7`5~>cqt>Is9c{6)AusLtb&8Agowq+){(U_D zl&Cy%mHyVxidX)W(Zs24Q+*C_;JAJT<%~w2i`;UTRfF==qFf@mmm&GArbP7Ei#=E8 zi`7+h0*>dEQ_lkAdIOms`m^OQ0JM7aGiEy>t!5v4LF55vr5y}^82)$}{ISR?veck} zUq4*mY&eUQy}f$g#Z3=V9cJRp4lx52W&)AzV9X3N8D@HE%#@{IX|d9!dv$U1_g?sW z_+Bd|$M#&G^Qx&X_{;S@>FXI*>L4qfoQ=-9$wEfLY;DfwX?$`LmzEADz{Y{cbdo}5Exq+LrWZW`J5W@A|?4k-h?E5y)E z1ygg5+W`=st5n$MM5G!O@A;s}f)4-TuPp>Vf=*~h;nt-ZM&KVs;OnSv4hAQ6!zeRlx7|(->5S?D{kYCj&+Occe|R zK8=CrWFUW;X$7?qyl5?g%mzVT>(8m@{lSAEi9ddCo&gP4u+1warpj_t8K{FvFeiwlRUO15<_ zm%Nf{+`Sv~-IoBR5=oH~NRbjHp>{*vAUY;o~pRI6@+7 zU0f45+D9bN{QwUv6Agvy{W-UbE=)m0IBgNEp3R+Q!vP*pNQ#qkFW5kt83uT?9pF)^ za4X@|Apr#PJ#O1z;M6%L8YiZO_?AfEBhz7k$6yC|P);Nvt%Hqo7lY0OAkXsQ4)B01 zHmRO!eF+iRUBT{-2Y7f}9IPYG2|bqq9$3H_DjqCOmlmgFX$r!&erfiU!4B{Mj8Z|| zmjvC_mF@}yJbW!@j3Cq|Sk4Bqc0sLlIKTsfh|9=p3dZ2_D28#PSUHb^P!dU0#P~bc zJc|DS4{BnDsP@6%s60~#QmDRnpuF=Vr#o{??Pw)l>-p{jw3GkYt@^v)g-6o%ryobo z9iO_cn)zfopHBWU87cW*kL>lm?dsdh0 z&+2&pydU<@L#Ciign?0uW|51MsT$&_1Q|_QSHpz^cd7%Is$BW-+i2n(y#MW|URjbJ zhA3$r%LQ(qirYE(#6LBKPlSR|Wug{fDS_*!<+@K*EHfNR9=Lmc+_gWc7T1&1nJM_n zepBZdrZ9?vt9+JxUwoe~Z$FQID9s4+D~9$_`b--sMEi2JARea_4zl)i zHjh*Q0FAVUFr0<+HIImRnnzAziB%k~{I1G8TJi8Uk3chwadPeY^2|lV-#o%tfe43N zu}bqu0i!}+duV;eXTh0A5_y_OW)w`6o0t&zeKqFMiifv(gj~Wy8h3(-oRxWm#za)8 zYu5-h#G!&2QckXhvv6kSQ7}*QNEHSKu+oI=!0*d8k5)Xq%_BDvkxBaww;&>CWgZcL zl&5MTn8&J23L^k36t3RS+dM)@k#GDckTg<;tEHU?M9c$cPYzuq8t{3ukv0{^=q95b;Pd5Msu&E*ReyfD9cCjouSId&W`Ttva)?Q-? z_EINDiZjP6xUnZ8!;<(m{xn&?Su0{VMscfocRkyf;mRGul1EXwEdU?_Y2-o^AGxcb z&bBBPsq>dKkeWVxa=50oif_jxot5G{;-KJioBhpf_T}q%{^jj*Jh!J}n$itr*gFs7 zujxlWyqn!p6s4qu8RzOg<0NzQ8ANb|P^t>D(^bKO9jn~gY(4tp&HNH=d5s`@^A6APH|eNn5NcP z15Qd|CX6Gh5wsnXEq#z}ZTJ6g*L$2>Q`)>DHW=RcZTjt!MmAQtrA4+CVAaD#JK0Yi zV~dN>2~s#ioja!Kt=3!bU%o0$QL7QegO`?ZdzWvJ`nKhj6e))drl2Rv?O$G{-#rl0 zIro1z@m8|GLl*V{jnz-Y?A2xbp*mC;{kY=6v(@CW;ym4Fqrbg=sYgFr)#1A7eVXVw zWrj-OgLrtZmO@V!xAW<@8eqsRg{ursF!jmm56xDv^oJX0Gt>E400>G*&IWYu6BP%$ zpltb#yciz_?^Wy$p6_Y6bo%ex@%{ZIjiNqJ9!BHz|G#~^pWbFr+7FY-odfc#Qvi0~ zRIQ* z(~(U@j2|vF|5Hcu26>?(nJy?p)KHXOJonHtNHIttEM2^zBtnE6Tk+Iy+D)K=M%qXF$AL|D&M(Pic|dn)#5u&oN~iR4@tjPFB&1v8RDfJxW&&% zgtpm^+C^tf?RY0gE;t*`64ch{H2gcsJ z<=lt8#KaU5_mO#bs%Gu_^9LvY}O6m~7r*NCZIPPeBb!3xJmsI&CYoelFS zhT|l{<*Wgv94dJL)WU%BDE`y!F^QB>&?g_ZstGQhsnRcV6e_;sD3q%z9!4VCx6HYg z(#?QCcI)XKi|H@HS~H6N&AYb_;LUp!Jr^iO5eRa0G}bi}y`Xe1;`wDw7K2Px=T3VckejAEd?s^9~=PIPGPPpXnL#H{IJ^!k;rN`^w%dE-9V z-hsO(d~)~8_|rt$$s6u7UD$eKe2qO}%1%lSnXgMU z_Qs^vp8HLJ1(=~E@hNT%a&RI!@G)f~j)6!@rVJY<1W1b56@g>@a?EFp1Tjg33@Vvj z0yv2%wM^Ww%fyhI4TgP=UB!0>>|{`gVr5)AA16) zR&eyy6`Mi^L$o^Ro8!~RbLhZoBuCX-PUdS4e?g>+F`dS-q}E%;$4`>A7WaS>tYK#gw@h7Pn|TNpaf$U z4geG}5StbX_vE&MDXFmW8X(cBPjvL51MTJu*r{bt=>1s`@pM#t(9RK71ahyU$18L> zNs>e=@j?bVTbHwmv{0y}FauYtHLO%nBr!Ip)gu8L{|wUD!$3mYHDk$_y4 zuZf}H+CT?}(Q9}Zu<^aO=EA+khra45L&L^D@SFDBAM^`^f9TphQSUiofWhPlK}A5l z-)ObQo2^wI14F&%Ohh2|nz-AuZ`s7FM3BUa_>{3%nyPv{(N;S-a(&E|q26N@GoieC z76P+7VYV0QJ%|W4QrlXxz?S2%<(R1V7#S%v^=aD+>>Ut$JyGu|<~U}|XLfpEZ_gE` z9_)fDGLcMLWB`Z6t}{H7oul5fI5rZ{bIj#(LKxxYIX44ax+8t7Cs@`sM7<{*M-d`N zs2u8mnE~~_E$V%xO0B{mCIkQo#hy@QI2gn_5lYb3kySm4XN61$)cc{L-ZRO0B)Jdp z+eL~&X9AFC`EXJ10SBBDRGOd^*nJSYJ4U_twm47%A(`BBzdjs`1Eqmf*qsH7)0M?3 zS(<{dtzViwWw5CCglLt}=xXJsFH_Z6X7ZoE8q|A&0R&RFxGTF@>1)K)E>Z8fAu+|} z!Ph>#IC&lgB}fs0FFB9mk9tqEq*Rp~$8oF>r49?fH)0G2)xeLa=MHdRz5XKVeR6^M-;{D{~@+qC@a)GvjKKo6j z{ka0@5m&#{2!C}Azk1r!;&sia6qKvK@;hh>cga=D-<+=h&)?6Qv`dOCBmgn&{mMc#m!yH8e_13z?ud+GW&NwdDs}n$DX7r}%xC044H8!Cm=ExE=;1ZT3?aFWo z36BJWq6KdIVOsq>JWn2T#8I2MVoj;DbFqEiqBg-LBwA|DNQ-$kM$L@3!cK4rfh3g2!fKLBwG_>82@#70i({WE%E8%+ zh$ce7Qr9^}492lTF7L@FIyx?4^M1?7n#nfxwl=zcyYO-^|h&eLS*GxXy>Xh&Z7>b)7@gO-P6Y0NG;DUYEKlU>%-e z>Z6Y20qd}_D0yKWB5lzWaOqwX7^Q`Ay13tvGhiKhZ_R~}jSqd*Q-+3hSP(JTbbR;1$gRnS}Zgq%?L@xa@ zFS~_zGTGf6! zRR#or+9Yx0KWX&3$onS#n>B*SgS?mvsbVthn~NIBV3z6R6X|qBL+MZyKLdGUJAcW`w)&P!(fwb1NJtIvdh}@|rP-m%jOLzJ1Vf-2KJ0`nFRQYw0)_$u3dQJiMx)kMPDa)QtU4|UzB$rkDSd7=p@T` z-px_I5qM_HNI7GK$viIs+pTi*S;=HA6s2fcvOtpXh6#JM&Ad&!J$uB#1E%&CBTsd= zwdDdQ@&X+uzFHg`@|0EBmJ|!?p*H5_R^4uf*KORl^D6^`9>0(KCt6sRk1{-c^`NWN zz>Iqbe&~gkNKt5U?`N6js>;xjN^%CAeNgewQ!c(`5?!zptMRY*?4-9>qdT__&9!J` zkJE`=$5#Z9SjDqGUF=w-;zZJedJe?rI(Nc#m&;xX)!Q}3d%;8=UQ8XoqgAmKHNk#@ zTM8x-IqRU%)x7t!F{N%-G~l49JFBV+S0QOTfW`m4`vXf0HPA(MbX zD_&#wQpwK4NkECA$o7^66|Tb&p+pDDavnZJ@uUlZ+6a5!N6jEfR9tU$UcpGYw&?AG zcfSg>1k~ro5n!gwvk?CL12BS}XqkpT_u`&F{~^ z7z4p5a7lpK$+;%<#VGaQk1ej3iW-pmIcy5ib18zrFr1iV%zC6@2x0Z>E6GG6a!2H- z-UK2xAXqPlY4EiFQ4Bb`!<)aTUjPiLu`W zT8K-&z* zg9xX{6cdh@aR24L<8VK9&jEr$d^Tk}+bpw3oi9N<$GG|E4F#cwvl8bnTe{#+wdv;# zp?{LYp+Snrc#bM|j~8J^a>;UanI?-!38GMdp&KXqv_P5}Rk|3e#y--T^8l(PKEKCq z{mDrL;YGAee$z+9&BGaspGTVX`xjy-;OQLdgk{7XTdVnu$In;L>$U0glDjg)Dm)gT z&IYxV1-0bM(sLn`zu!B?Sxk5{GI`yczFt6TQ5n0CJqD7i=AX=X>%5< z82?J%iuD%4FCv8%VFnqFP0@BJ4%Lx@QtX+W5WICN%s;~n0DFo0tC1!+?3e1FMQU}2 zFS($5HOKU4^5df_A?xMmY;=BeoTZ9FPY>RXMlSzj55WjQsyZ+s0QGg9Rd7|#cjqzK z>#=-(!T<2$a`3*flg70zwzT)YgLg%`$aMd}5zGlIo(ruu;-aMRR#IRf@IWei$Yw>I zwCAQZwY*PZPf9r``40t{b|jYu6290ES9%Rm!9|8i&E+uTQJo-vv;LGt!NSA!)Ym{- z`iua^#;PxKCN;Q}%z26*V(?Dmuht=*9cZK`J=!1JM@B2xM+d-@sJ=KVZlap9*T1%Nk5(L=4{DxJvPhuvp#NHrpY;SKz`MkbpHwRyILZ>fk*}vU$`f2uJdAHM9sp3&M(zy$ zklG;@2nx`wkV)frEU0mTqoAJOK62XX_gkyhx4$Z2%s4P{sgqeFBAn-U!6S6g)Kz`Q zHbZXy{bggBvQ5^dzcyD~SBsY3#SpQk#`TN5=VZ6Y#%Tk?b%xxSna;?~WDE+UVhJQ5 z^A{mLNmD3!QMGLV@vR>fp!*#hERLA8^O6_|>D(3ABt-wtqft(cam$Ocel!s2r9 zF)ToRF)_&vS%m^O|3T87r9XF+XA&Nu^zdt`J<;xe4>THxq!4SrKXX~6%yo<}(HPfF zcqa=q$~WpL`sYHyHU4vZvnB;D1+Ux7?3hTTl=K7cr zKg+hQ9;_QIgp8$Bsb_}+1Cbw*eTIRJ5or5GFEs*uGuK}9QTIq?*g>AqxLS~B&n;F4 zEz_k8{zlBc*wuV5+gb%(-}nwmC>1AJBOjPeR*&Xc9aS`^MoAA1$O_!&SYw!{1L!H1HwWD^3;_9ezR%8_iNf@!FRL> zkLDGHKase=p#D9H^gnqI;DZ^cXrToPCDgTU-q9`M>)5(sVX7yF@t)8+DrFGO zK|(qOjsHFMu|K1zfHD?{sOdAO2QQ~)k~JRiZS82GJWC)XrxWUfN$VGvxj}vnC&nZl z#c`Q4Xe)YDYEsw+@-Zh3=yE08S%KvHZevX?hh41?lXTQIMg!a{I$G3okRg|pBvD0^ zj+EM&UB`KAY}cOjW6xZ5MLE+zjRG-Qa!eaGw-ohsveZ{mcY@dt2yBIRJgqNZwV9te zxHUQql4`{THV@|*B6HACAIMvVm(l@B;9&MI?h{*`GuDZo31LV+6CK(mcuD1-vff!TY z(@r^{Di(AL9IDmqs;=1j8ed!+DCd}1UW@1$?o%1|{i+X|yk}q462y)dEwXK~wbByE zzJi|nYzA2`K_4C-Mof?2OXC}%2CcHJ<13$Nr%r6|IncHooA-xi^>?T7;9Vi%PD$wG zHQLHw1dN4W3MuPqw2>&eSzxmcch`Abmo^AiDSV0lmR^!yX-!l5x4^1X9L{ zvkRo6aXY4zh)0Kaem8r?vWMgFa2lQ#B}hP&&+kfuiGg2S@B}j9)`RV-5pP?=td|Nj zDXNPiVk$-+*OY`A01j{o07Gboyj5x0$DyQOqf(S}d0O_e?t9K?0wE{r*-6TeZ!TSW zN+;sF?fcVB0NySG;pj`%?t6(|n^=-!t@Vv^oF}UoVjvqc^F4H0xcP{a_iu7{u9CM> zR3a-JHIMO0R8ntM+(il|!vC;z$Xf*~`pm0ywF>{bsTpJS+jxA zOd6nXn#sZ%R3&xMaM&Zhr2qHJAWBrEVd=`As~d!k$47_wLiCp+5IY0KuY{tcRaq@H zKJzLr-EaNrbW}!V=MV}0nF6uq)HMrr z1=BYev+l|tM(GRgkL>*Viw(Q)TH^XRUIWF{9EgiEoGQ&rem|NK$9)-_!5-Gk{DU zOfs`CIyYI0vut%h!DsgS-AiwrIbzBuj)dUhYFqPwEAX?=6{g&F`mud0fNXN*-q0@Z z<9RbRuwVfZz$)#Nd9|fW(Z5c5Lm4A}opko$vMHzNdG6Wt$|q}P)huEodbLU2ASh3u zo3k}Yd66b>3kXcG3ye0&!Xw@Gj33vug)4%7wmPpaM_@y9U=W7Ph{fjwQkF$WECzX% znw%Tp62gK&ISqItgPu6N5WU#U(7TAkS4K8(41fBDsX)ao3DY>+49 ziuka?2>$-Ig28MQbMO~EoEgKZ^3_PXzu5AZKb`zBB)d99Ga_ACHm<`2gQ!{U`s=m5 zeO}$o47y8vy~~2VEvn@C>GoTF#)T$Th;Md9j2vC+h7wJEvl4$}Da??OYk2`c2fOXu>!{>RP;Jn_ zN4qRXI=5yzfz zQ}qT(1~iII%p=07;PINGKV1s*XDiATA<&e30oZ#6Uv4xX-`Mb|1waiHY%P@uNIxbi z{l)ry!o7pzHFH)hur9A8Qg|p3G6K@+x4zAMpWE6m{S$AkB(X?P-Cr0f28|&g{Le@E zAg_OIODl{r=w)I2`TUOaweZ>bAu%{BNI|?Y`1+_?4G3Ttx)3!w4<6D7%@5(RHuR&y zD|wR6gNIAhFf%Z0W$bLG#h9>PmV9Z90HnNGX2WlG=kfdI`g=EF-Xa(l(^OoIVF+iv zC;3<)!jE7W8Nr#^QwY_I^RJUY_8KR166$wa z1tH;-fEy19{qDN23)=3v^k>J7BgELHq&1wc_Fi5QsiD=Yx~UW8FKU{y}6IN`LdNk!kJ$(qhI+l@_q8O zo6LE9vKm16Yndcfrq4(^Ze3k`yx6#4TwF#JmzxTgNr2$)6Mt1_WG*9g{?vWcvRAV zO(|f}2J!_U7|($v&ao2dmWvMaVT|V-r}j=XzD33BlcdgTcM%ize+YTmgIv^CG~ zI9iENALi%ZazB7~+NkjT?u}pG?e`XdLU@R<**F$?FMM60(L21T=Mw%x?4ek1<^thz znJ~TSjg+Vtes_xU%2;Xe=pb&3NSJAXfePM0IQ2lD`Dslf6klodSfApdzw&Sj!t=F; z^NKl?2@9cJfAS}H+2o-blEY0cv2>T`p|Awb)Ba&S3iaSdA_Cd^N1_Hm*e(bs(9xP#8MkF&CHsqZ-lp z>A@lMz6Nl*b(+Jqa}z|@Y7))-spX#r!4xL+11#E%D;q!_3{x+0SmgnPp+A|j5?#Fp zkE7vp9O^IQ2?Q5^#1@AT;D-nO;f-~-KgwWduv|VhJ?%+Ja9lp}WXrIsh(t!oTxXv+ z9X|4N2_u5Eiu^I-S8?T%#>OOLbUNX)>pQPL?w;OmDq`Awed_Zj;Hxbnmz)!><)|xpWO=v8x(#4iZ*$CX^vnHmA9MxN-vW(5h%{LK#GmEE$E$ z7E4$DrG2;~$%8U{TUU{Y6KQ}f#e6450E8TRh$2Lq%R6YuAgeX<E z;kghD>wn1xJqaNGp|gjF9pgIB*d{aQ3L^O>z=rd#W)FVN$;5jF1oV;MS^*sDSdi=gpU48Bfh>Piz}tr^ zlNz3_0bDWfWdjAe@P{bjFr_5Y?TZGGm$8PbeGC^#tw=mOv$k_g+jqBnkdF*&TK$*4&-9#&AYpyhlF&KsitO zcHaRtIaoX*E#CzM0V2O$0|J3V({HqQuXiRVcKXvGGt~%v|<+gXXD~ zl9GI)K7@0~U*xYyu42Ee$gF(9)Z7E}f9vzaHmk=X^0K-mHgSkc%9(K`WxFYZF7dL0 z@)!?0_{|~HrUjteLajC+c@ioHK;VKHmnio;?Yfh!A)y$BkPA(hdJrZHg-M+}ccPpH z#%oT#?=Xux(h(aiuyEIZBdwhLNV|1{F;l5`~}h`b8>2ARFZv>B_^w3o0&IqIt$OF96^3pFc!E!b1|BMkbkDhZ}Y5$#n-ntzcNEXYG*ob; zzGIUNwoTrv65%uwKl!R?4q3&3H;T2u5~#{Qli!x6uy^Ji*L40yJ6HVPk8(X z+CwnWOrPwY#DZd;jyg@zP$H;sM18!;)!W&dO4HFHOe>~qZSvm!S!RB?pYGX?^EOZy2F{IL~ zGIjkjXm8~7=Kec3A=`n#UJ%>c)|$b%)2mi|IpLhaJ=LE{2;(dWb?DW3F~w~8u7@8o zzV(IDd6{ zu{sI~yH`I@wCTE)Fmg63nevGF5HZf;fopC=^v8dC_|&{Bb1TkV%P!on=jv}4?(B_3 z3t@nDLA_^}R!MQ)UqN|o413R=MHt7pKE(H(u-(a*<^L;%iLYs9Uh6t~hu`OGR>xO=sG*nN^0|v#ygn3okWVTfHlB zeo0&W%hm@aQ3=_S`znO41S(Bp2CKn-dXwMAi4$Z1jI(X3y;Vx`Z5d#5$Hs}gZTIHh zo81fMI9w0?>}B|AX7kIUZ5)RTBB1pm)ih+2CrI-ap?jdnaBQ;PSQ=(w*Dy>azd|OI80ub@@c2Ws>Lu-)yGnGl|Nz z6fgQ$BQjsh0%Su2O8w{A5H~6tr3r2dlq-Uv6927att!X2W;ZI*+M zuIkTfs?mOnEaq=8%!lZ`%N-DF7NxM;B@h;~v>q;4;y8^TL*A;>+VJ)B6_5Xj2uzA=S?ez$=M z&H-WM{UIXRR~v@Dzlq8awc#)pg$2nGoeqtaA%CDfQt;{AkZz$;?8?Gv#}3Aixb=j; z|NcfjPS{QFeR&YIBx7RwIPL`(EWr%R~?E+dUil8jBtXB zRnj0ZG=@Wkjs*<+I{uA6>N~r0#)FxD*RrN!gVooXkxP}L-$%fH$jceXkq*1F)- zhJ=YZA#Gqa**YnS!{5Z-U7`-H|D^}ZQ&X^9GA1r$$OikhO&Lvb9~OI&-a2jL-r9QL z-;`acYW4f{sqcLyk|Pm6K1Ai7Qk={!Do130=aT1fJR=p74Ao^f(OH=54!`QAA*-&KC zz#I?nVTnK$Ys>v-+}J*%N8z4ygl6@#l{Q?0s9y_!+J$0x$72ckbxcGKV`^ZOhJ=JN z`uLEqLHjk$iA4Hu+LqpFsy*{t_%wRPZ!NsxTpbPYEQR(Z{KgDKlksb>3HKr^r`WRt zV`IsvZxfauyQWP4ydA;m`^k%fyH$iPE(rILyY0h+Mpa@;>8x2J*5Turq| zw1(??XfR?7LH>5q>YEu7|GR6oS|@+Yz2*j}nU3}Tj=-qM1#5S7;_J$+Y`8cH$pQ8b zyfdF3JLIjuw(b}TO2Rb$<0uvWZIVp@mx!f7XxjNHbN}KHcM`!voV`5xaoCn=Q5|)`sTovBR*oe+OQ^A_WEHn_^)JMir&!88>g=PEd-XM5Gn3t+AnwPCj~yb7w1_Qm)1wYe7ud-H~5jvE$UNdAO{EG+aH*-;B! z4&{!=K22mXT5Ql&J54lXOeQgt$)~ii&JCl-$}+T2m1=wI?vb3?lI$@GM>{M$+@j=8 zNGGE+-E?$PVz5y-{Y#ma#9A~|?`LyUoSP3>Lz;A@ZQtghCRlnEvEr_9A;28@VMA!1 z0<4nMJec4tf9*=-0YywFeL6Mg3g{^+c^D}zFWCZTZn>XT8aGUtpJvQ8}Y1TdH|%r-((E+R?gPdR~k${MYE zd@om(mL4315;P`ENxpJ2-IS~McBzE&>{=^N#l!U(Lj)R-LC)UQX0 z-MKTb(r=$@<-JPbynAR?`eifH){&-7C(zp+RW(EVw})arN04`BLWg2!{N~7GpuFHK z!GS^0dXc2=-Y8$+G9!&6mN)^|y}0$pI-j{~e6OItdVzxV`x)4mMS%$L9FBe|B4Xvn z{|A3I^|y+%d6ol^>^2#)qOcvyMGR{>PHbri+Q=X>EuBTheV#QiK>8p zNH5T@qvs8M$c+t_nMJl57*S^JnU*9izOgY&o1@RS-Z)xDX1y)=ujxGGFPOGf&UN=Bcsqpz(R z177Xc7Cf<*x$x`gLhqav+!a=WHX9j&wLpVrMe$V5cB_5Lyr}5GggRX?{pi88U;#-` z0>&+T}!sbX46k!ZhodNEOfd|$@6Syy9 zf}c2{8)kjmPVFYmkp~j$WGT=Nn`322I_XJTE)U65zdaQ>ep>i}fd^X4xNjQl7FtZM zYGkJHnQ6T}Fpi5(^M;6?T8{u(JJS~LYT1Yfk2I5#mo(U4pkFE;H_iP1xqCW~S0ooHaUH z-L@P(oL-bx&Y0BLvo`dY`u~`@aB?1IalTUj##+5eWIj4%4ko&^k`7GY$g)ipZ^u*SgfRXX$3LAXOvCSMGm=jhH}xzV#j?4EF2!Kw|56Ykg_U@!vE zch9avXx7N;)@nBk>rc4d4S#o;4c7qrEzw%_f2@aV&|7yVYHVD0-2_wZb$Fgi+)FPl zM>|J^k}f1;zO$|(7W7awy{TLTLiW+4HK_CpJ;V0VPfpO2af|47S3wU63Ke7b(HYY; zr0gT3|FvimE5)MzAERguK6riNcG74MG8TU7I;Fd4b2Rk2wHew&cQGviXT90vaeI|6 zkvo`STC_UFuKmw05ocL6aSDle#>QQ>|IfNbEGs-AZq85FKTP>gh)SfLT%ksES=$Wv zRNFoeO32b~`puY|H|LqzS;FB8y)}-)p_rjC+SggDnYX=8tEHiL8RWFO5jwg$x26sH z@++b^`+Id1fVY~Y-9z>z%7J$W`PrUY|4!K}>}X6a+lgNgEWR$HGCLE`0GDcP= zIw9FuYk0Xjb#wO8i$|nq>dkn%zAj1WC8cNbbZLLhc2d#Q%bQ;c(gCT7BQcxqthOT4(od0vfaWJ@3k-R*V_aB;dc^IX2RJrlYp`6h1b5%)e^$rw1 zcbF{DGUPkBzMzFJmIKAtNoR05zBy(pG0Loku8e{$?&<`{z)QZBSTr9*wEVTu!K$_M z@DQo5rn%O)dPPsp!chOa4t1mABPkz)N%;Hi#gpZ0vs-H4prnWs!(Ff4geS^srL;Pu zv&-2@8oRRq%FLF5p8DXNNqBWTxbxJm+^KzzXNoObL^omrS0{-GCC%DH&@6rDB4=fP&MFt@7zUTs7#!y*|s>#SC5*m+2#f! z=(lf=k@muf)IQC4qa9XP80XdPiYV(nzWz3B_%{B{sPYx)^S3Mgy3O9P5KnL6!<%N6 zee$1pXzR<5f4)sIv8CoccV(Q#I?_-n`~e%jV}{;zipgw>d&x4~*(e;gOaHJhiZ5zg z>2NM-{M!BiExkfjj<364@pgT%mD9KbGHHudNrk*6l>Z(x9qz|Ww2`uXtW=)y5SRCs z6*FN>vYK|<|I*&_H11vk4ej*V2C;)SRx@zIztFgPuG*jGU&f*FA&E%XC;jzSWPfH? zTsynlE|pyE1u-DCcWKXc;txnGeXkgzd@3}`jol?pt+0PBA-Zk@C;LY-^I;5~IhrgM!O#VeVzFTVX8Vqd)9L z!9`VNh4IeG_XC#jBo z#>qW)d>w_+gvDfK)bcJ@gKka0b0556GMeysb7N7Zs#LshFFFjLanWLnP3#n55&BIU z>J;e<&P|(mV2+>7K^ycAy;80yUN|u+f_`hXnrX1t!6sa_;R9aiH0uc+(q|#>$!mTT z>oc@vgz}Bji6w&OdRbfMj$xsmBG<{j8?pXU#{ZH1>wLU|FC&a ziM?nTS{t?%QU}VBA?Bm8dN(*WqI^WZf>VtCySE#Adb$Xm9^#Qj%d@hO(gu!d^egp7 zQd1x;P19zLwV|4ZT1g+dRp**@`Wik&=Y(*Qpu=P%JRTX?RNa&R1|7liuzfV?^sn&J z%(3+0!&W5Z%GHdSfq&{m0}CAGpXo2mKlx$tcA5U(mON{!u9cYQQ)dgK-rHJ^r6H!l z)GMmA+K8HqMy%6l0p9tamGan@x*tkOz9!)1Dx1f>qKOW*=As3#!vdl^`B0a1r=p!W z-B4F0clbKeET=JsI}`Yr*jeFWwv};gomn&Hlade8yYeSWJG`EX5{Q=oE#55y4c z`q>5EZxhXOdndN3dl}kt+sxP$;;n6nesQACC79=N-n)Dr0S0B%oCI0B zh`;M5?dvJ#q}cg=P2$krM@ z#ad@3sm~b3HNRQ2(A~2TYHaMb`%O6vFXamQeLu&d-a62Acg_hMp;9QH5b<&=G+QMl znAa~FrrKQkD65lr)cLKk#nIl*5T$vmT3kl8?)KkE7ap}{xhI*L*yWaxNT+b3`PcBm zXwC}_lMy;m1^%?WW!G0PAHE3F>g~vQDh^ZJDnsn%3@z6yTzq(9cfEbFSXS<{&@D^* zv;3A|#`Gmi`w0a(k3^ljQAwMj6uCv7mp6s#=R`Aluj~jBt|Kp4X6 zB_j&gU(J4c+_4KMh6K_^HmYJQ{IyaO?xt$>R;+H8-}v{UAI0(ujI5X7l+IR@ z;NFaDUqNV97lqMQ$Tlq5Lw#j)Pp_Y^@`es|jw0@()$TRKmzL!D&lI7*fRy)BRwtif zNT!V}VN7a}_iXYh_}Zg+C6w*v#^qC4dA*CY$u`~dNZX7H7o-!lXY1=uFu0?Oz>$Ii zxDOqQz4Ush*|Kpj+Rr`%s=y+2+?m|la(lly)I$isyb}ju;-#P*D!NI-1i;e z%Hpb*jG{Q6Znh|XN;sDGUC+bCB`p5>%1`P2saObZGZxN;J%v%E%f)owj98<MAM~#l7){l4j%C?N~o(VzeBFCO70` z9$Pz%xq-~QwA5#4$kI$bc?$IH6gaNx%(CT7g43H`jo0*s|I zB9#t6BKwG+qCqs@&zr8N!I^BqP89!FBh+G8F@Qt$MIjNN zB>7^)T~baqjG>MIBG!xc4Ot_`yQ# z{d2BzPbvDxB8i&=rZkx&jSoOJ3ykg?2~$Z?ysO2L?8fV6{1$IAM)ykXlrNP#1md)x zy>nk!wdhJd2!)jb#T~u(ER}C4kg=UcAwq_Y;S7Jc24nw0HfwI)lhC-%MWEgoEL}u@ z6YcPt4|D${o!$JCbqUrOiL%QP6}&D0ty~BG7A35(8?kQsubrA(!fA^|6_&`4kbB?J z>B%}*dp4>-YPWwVk4hzH`efV zuN1dBcV``HHk9Pjy^yg?ykF9RFSYTDo5+4)q-IOeGkvT>|K#{sP!;IYYBn)R9hcT zwP6vaG3A9so>Z`v$vyE4$sk4RwxogI-=~dKeY=h#)AWyLZ zM4Tbh7;ZRd&wQP*e^$(&r^5xo)>f!L5j-iaDDiWZn?fq=JB7j4;lbqBzFg1{5hzR? zJ*Z7Qp?;}rrod(kG}&0MAl)s^7w+Xl7;fWC+xyx5`81e0ZkACMh>?B%T^rn73F?Oy zcbHBll1oGe!}`IRcbr?6rSO1{Sl5i`r4r$wFYB=3&&6m=oK1MX`WjAJzcBpPq=R)2=-(V&2LuU=2oaBYcx-)DmE2|E8-ij@Uqw)qi?tn6?N zlY0fX`5887p6VX2QZzh`50P?~UhUp2@(?e=V_c@SJ`S0A2}Gr!%$`({^9W>mKibb& zEzNW_83mr|$*%H1GF1#fK0hoF!h|dT0G3S(IZRe>3;3;Gn|xeZ;>GCwwdPo^z| zVm3YQ8Wkm~gf1ohcBpJWnCC)$gMQNlI*4%5e=R(!FVltkX7u;HCEDi+nq9-{oEap4 zO=wKCgC*k&)%VXqN4JsolDq!QPU$~kD-JB0Q~K6&K+CRjp4{_5^u$5qUD9Y5_Y0=9 zc?qP9CUKl9QK(RM=1anh`vT2hf(5~;umkl=F4xo8z-ltQh?n?8SO-b)mby|T=XC~tG4{MMP-<%I7qyYmv6wkGYe|~8 z`h?(Jc1Dncxs^9Df9j$wCKzuac)SYq&K;^plY0`lZ6@6k+Ut2r&ka&WkR0SP4mB@i zzvP2|kLs~Q3n33`57#lhIIchRy^wi0{1z35Yu>5m?Da!AWqW0K+{#Kfy-Q3tNV7!v zHziBOPFoI?c@=Hc{IV-1^WQnwQ=x8Gp}e@a+!v`dDd6GK_7`#e{9}8=Bjxdaj)sWd z8K=KK*%zI!=JdBcejmQ7ez6Zi*-bvGsa8wgH|4}ec7L*mAnog@W=#vo&q==9tc8H! zBS-eKG>9A9#%;xov$()RXcJ{~~FW~C^rkM*jYIpGXg^T`ygC2~)=L|wNp?yw(dF8FgQdAN&!yF&-lde*C}?(khE9QCI7(9lNae$^UQMY zEfW2hp#u0i|4r;`eglu#nSxq!T(&^7;`mQO-foUZm)yY;97-*ar#AH`zfI27ZQ3yG zTlUPPY13S$Hp~s+#hV?l-o$zkG8j@cqO75AmEisNk^6EKL|zsfOh3_E$Yp3EJ_7ae z){YJx0Sp#Bh^J5!2QPp}I`38Z80S`m#quAXxCgfBf26?Fl{_*M^&KFpVR>k>4`qLE zXkv#WE+l1!v#GnWu%>c5Wp_I8W2>B;ozCF4XKEMm?3i(eS{14vl1dTl17P?4Um^9V zXO6Q2Gq$AdQ=#+-a?S><@`B8LgYRMN!9Nxwd!MI2^$Z{4<1@$-m%u>VH`TpIsvEdm zMXwLX5E@EcG!~2Z?cpK1gq{4n&+^wpPIwlm7jl2oScd@TD*y49`8cMwU9|JnV1~yH z!{NRMPf^gtwQvBzQb*51tN?8CPgY}O%=$|eOI$CrI8i=N5R$R#6RK`XT1^9J(1AfF}zvvpvO24-)OdgR3czXF_bKq`G% z(7=#UmC(j)EfjBS{_6C<6g|Q8p!*U6_c+1tW{&r94%ZT7iHZLx(8+-B@(WXd@1lg1 z{QYpfmA8K7u%&)=j}m82rv5JZH?%eQM*ITZm-bZv#P@>TSB0*5o-Q^P3cS7*@b8%e z`0jrb7ohi8!S8}nKgaor;EQHdq80uBM&h#j>NxkK8iOa@5SY}-G4A<%QE8SHZYot^ zI0CBYoT=5oa}Lor*~=$j{H*t+-qO00DoH4!Gau~|(U`d@m@AWfdP`_PahDuRtfOf@ z)n99FedfKxc42xGf_8b$)Q}0Y$Xu86!%-)d_dEc$m0#(ZrC@pik-K*0X{6{s?r=JYl|Rb2|32k3ey?NZ+)Lp?a_?+; z{U+?*UcM-pWd!cGCFAN+L7Hs|{B?SK;_E8h3<%C~3Un@^bB-$KMk2aet|>~kefC=4 zE7dZqTK83i>E|J>{f{?x>K|@D2o4gQ_FpQ|s4pU4Xp{n$D(I8~eg6UL*&x(_gPLhr zg$SDq&;Wmo^85whmsYdWf z6FIu}Y~G%b$nvdsm%b+sM~S4PvppxAe_{7#+uPbwn>U?PAgRL_a%#Dyep-pNn+Lm5 zN>^Ja&hcXB)inQV^_P=T5;+CwGyUkTT;=PeLP|rAmlX>I65hL2E}GfDs;V|)ZMRK> zTQ=}KjnuOSd9~Ctp#R{hAZc$azhKT{nFh+5Z59E$EuGuU?LnR%_!`Rn7qRclMS%YM z)5IuZ9h=-YNm>GOI~$xx;<{{Ys-fZ}tSEXHcf*Rk-eKodGELOtA%G(N@tjqT9oX<` z?-q08BVM1JK4z)_rc;G_i)9)R~E4nUfEmfJ!e*1Id}&!XNIoPsQjTN zqEHq@{p?$*)v->h7*p3Qwck(iXG_Pyhx9vt6}T?xEZXM&)<(9mjZ@hfYondg;$gb3 z@1XO*E}v@j=2Rfg`I8KyvX_2${_bMi^yW1f`vuI*R{%%L-jXB>6h}S=jHASJx3LFa zk+T5mVsnd5pNmu8?7?9(A~lZob%pK3uQYtoy|P;k`_csaQZ(n><;>TlB!j)cOwDOk z?dyr3%gv+}Xg>wZ<`ZU|DB2#fC5|pIM@-{(fHt)(var1cbEejZslMiJBHlmf>Ea&aP>hU+@aypaH+mgi%G)Vl!T;`+x2GC3G;sepYNhU+G1+R0ZOHsgVCPN($L6bZp?G~R@lu6;$~*&TttWYxRp0CC1j@B{hOb$xbo(;Dfi{zuYQT+B z@u+|kXdUvrUQ$PKb0zPaCvzSnTPuFDIfR0_Z^?cW8mY^4Z|1lhr!DHJ2Pt+=y_QaA zL9Tm;(c9(BH{(3 z{Mdlk0sDNqmh6{_aG)tEgT0pT1c6DV=xLYgblSQG&(`su!{gcdUeQq3&Co-;1M)+? zdIl)X;qZ?RREwqBykzN}RwZl%pc{UN=~BMSualv|NQIHl6(cuvp3CAkFTTPW`0%R? zj1ulL>V{PH!j}a&Zq^}bvl57(;Cu>I6so)|RDsymJ>x6fmUvht-@s=9cLhYIWy=Vp zaThA7ppe2WPQh6cz7oF}k4A}@R~cg?5s~OTY1{Nd6v1gVkL~JP-l0veImMWE$LSOu zj=_^n)BLvlNy0GbzF~!aC{tEzr_exiNwjw*7(t1phf;+YA8(49OQIF)P0<-L={74| zUs5I?Vf$1b4}9`$Y^zG+kypgMZTA_beVwr4#|Q|ro8akkT`RRQQm8W@IE53N*&t3M zVau>?*xv0h_OpjI=yU@A!`)zm|>a6yr)Or)-#vlOfz)uhfXu zmU-T?sAU~zcj7JeB2IM^SBtdfL#=EsdUe!Q%*AV-=Zd9#|Bmz4y_oSHn!hO$$^T8D z=7J17A#8|n;`kX6RwjwSI!{$ zg~9Ofv4hcK^ZhZ8>Ec@<@)Px_(JWyiD{UBOAlGp|ga6l3ot1;{SB*|wfrzg_4@zys z?ctD%^X@{6c&{h9*jOo#tyvb+j2u5rs_KApK z!o68C$wz$Z)w!1i8>f8K#BhAP4Bq^H8FY@&s3!|NY4N$3_53-@bGRPPEKarItgP}0 zXR^09kWLJOF z?IHsQq7%J|^XxOQmo);mWToDo%CR@C!B`-1y$1<)q=J_S`%kw)l^Y`8SYPC5V=Lrx z$3o1-3#@fXyk_7P?#QQ{-K#>*LghbHVhsb-Qja_wExwte6-A{U}&r5AOSz|yA z{}BjI@s8X5f6CyF<6|eVWESj|oDM0cf2-y6A)c^*`#BIH(MxXP;xv5)(Ww8;so>Rj z{znvM;N0-)j{gz=d4G9@kc4PW-N%6?wSM(1M7w?zaA<`%GGz*&CsEvHvW{V zrEQ{UW2j>xmOhq?H8h{rbhix~0Lv|_99;b-l5;N-r~O#6PMzOQm6c%{!y2#2eb=n^D@p_8$D_?qlpZPSe1wzwcnT)d6f_IwAoLoeQGpHH0RfOfccYXn23)S zY|NdmY$#Q(#~`{vsHipekerZjMFh;mJvnthIpP9^=ZC&6H3N2PY(#Vu8Ox2GeyZ+L z)&lx3EFSSeCQVD1HE94ArV??CX?)GvzKD0wDcW9b?)lvcKsFct~4&I-&i(+$m2)e4zFPGSMhNj!m900 zM;~rChI1Yp4D7{HDD=9XTf4sNz`-%esF#q{hBpS$7tM!tgcSj{+7gPRBU=6HDAMvr zPNOi-S?n-WV#(zRNPRuX5i<(yP?){_L2j>v$3t4q^`qG4mo!EXgtZNmn*Qzb-nT;d zl0YZ<(3g}!viJNsP$!S`)*V+K_SC&iTi3;gXF<*GF-eG;&!(ex(|PNKB*o-5-X1uA z(rVmx@Q3Fp??DVC9;&U?l3uFeArj(i+?$sViq!(~(J-*Rq!x%3Og}VjxqqFF;$qYs z!_=8RZ`Lw36ne_WSo*GOHC^yaKij&G9OwK%)0ZbU7w8N>#Myah#`YP^-xJjtpH z?LE+jCIDR}6A^fxp9$BdtFg&|jYcB+47H2ujQV^s&C8_fCc1S)jPtjbv*8V7utE*+ zyoj&oHwvN()nMBTZLBgD?*r9Y#4+KiNjBg8yU0;p32usV<4}l6k=rMu}Ed!_#6@-B!U+q`!!S+ODGFST7{-&rwC z$~u~|aI?XHmAt>)u7MF|&?HC}FIlH;!ji(bCUDrrvh5TPA0;Jk591>_F7n#k4Q6d% zr@Y~<7S>w(k!7Mjdb?SnjskSo`}m7Stb$baSo$F{+Ski8;5RHZ^ zg7kz4(sdC7RERu*IvKj|Tl-OcmILFT0yj_jlpocKb58-p^yqBqT2i{#ESpWlT7QFr z%X(M90WJd3fL}MT+Uz|OhdVSIQ8IQrZ{I~nyWXtjMmlv|BQ#}FRo&2X(Apf1?k9B( z=K5@rH}~I2FwtGpl-fhN@$j_GZ|vw+9TRMB-U$v|KCf3N=S~m!vT17X-KGutc`vI; z=u4XCU$r#(3RW`N$`+56oo_}Sv2{bHX5XPSD>O}4j_2>BSxd6&O_t*p{W)ru;}`!I z_qRspuI4&-HJ+vERXJsB^pAgPKWqObg**r&o0_}pM`r|{j0o3{0x?}nH>4_CK8sQ4 ziz06T)4J4Mok_J6-`j?tTeyp=a0j3C!t1{mvQ%2YW~uyiVFc{$I=RJ8K>(8@)Yt)i5QQf|iq zNGuAXFBg)B<`Il+vQKJ<0`X%Kk%n8+^?C?wpIit`tJwsPSGh&?Mbz)VN^K_c|d?}}48liu$&ZL6z zeo#(Qlgcst3~lkdSahXb5-K5Q6k^}<1E^W1LzL-Ua^`k(;$TZMC zIb2FzAFZzcEpr&4;o6=T7<;%zPt^+wLf^1`nUweguM$Bg<~u#0`955&J0L0puTil_ zJqLs@%JL?q#=gg}ZK~cg1>5PC3M({c4#s7f4->IbU2JuNvw3oUdr@!xWTmLf0Q<1w zrclFD`GTd#!FH=GZ@$_VW8hz^s(5%)A1{cUNjbb@vmouh8$+y3h3>!ciT z5Kh;UL+x~YU^Z^(rzrDLOJL9(M%SL9Rkr0z#2)N;8V`~*so*fT=6X|~L;~qagiV`B zohNW$q~96%)^`8}juF}V9)$QQBKLY-skhSCbG13SZmo8}&e?4MDq9ZkA@dT$wtZ2T zas771YLGVnG~XDcf9FrT;~#r@F=9n2{+CbNH$!cMl)+cZKRK-w zztm$Ro+ChS`9UcgL)quOzB|aPDxd88?IO9p+56QT?UE7u<#AvCeZQ}R_`1VCNZ-K@ z*VdL$SA1;@l_-A8xd@?PTU{|5{Up!6`=KuS>{vTenVXi9w zStKL)^_u_7pP>oGAs88TcRQD1m*f4broQH)g_{b_ovRp|>z+WVvDCD-MO9}udB}84 zpwP?yfTyF#Y&@Suic(04>j(AKO%V2Z`TOE+!}@14FvDgF5iAy@nprgZ9yc@g21->6 znGI7G7pt-sVoKAlNOt%85mybZ-b%#XE^T%1ESeXux)s?28h=- zN%}#6X>i=g8gvuGwnaRSv4nAR>40*5n#Nh%HBk)zkU~R-=OP2`z32Ct2<_MLxMbBH zVzWfWts_n<--t}x+&g~zOyu(^`E#F1g^4A~u^&x5NY@QYotHj+V0`xwk73q_Y#dEI zByecxVwuV&&sjXd!UxzaCHSYlfBwb}TUm~Zao^C??FYhuy&)#te{lod-#d49CoMRR zf4H9xZ1=~uonAATxK&jogIUG8rE26Rwrw2ODjFE}Xt;Z?mpoI6R&U8Z!lGwko>a${ z=~_yLt1`rU$nfYy$D)Skh2%7r^eIWpORD-T?meV-bpB(7juTkUxx5EBt8bj|BKd?ti4z>uUDs;8Ad2@q>gh`)9mEpg;fwy_j5AiKVa=TDa#pa ziueD<)v~(Kems;?CiPFhJZK*VN%eP*4}k!LzRe$*V|0lf1g;b9om8);$T1~|w4eFJ zkPxHc`e8)(-y5OCcuX;3F2=jH4*NYhA}fp>aaN_0V=svznG-`udK#pncs(2sGs~cM zHPgN&wE>IA6)Uv&ah8lZU8!rKOmE^+uGlo8mXFBZX~Rknj;ROzCnX)V`<$n&h_j83 zNuT)%GF;tr_OGv?Cc^Rg$*Y>dm#jVk1RTnBOSbodAO`ZofShZMPKowFoCSFi@q7`i zR&wkmXTd*dvuT>gqdL8ibC&*FFh9<|{D1bog(-FA%lE5f_Ei1roa5e-_rr7dsmfDP z6b0WiQ&val}5O8X$<~!VPct6SQ)d?67Fd`uCon7ZBCZW^a>-Af!SFgr>{)$#h zkZDyCJ}rBv5Q)`z)s!O3vR=dwzq*AIG(*#LY3Z5?1s&IIp-ALa2Yo+lqw*jRgQEpa zE#z}6>G~xwhK70WD`E^@R!E*PHj}4OF#ozR2J)T+kolCnN)7o)w)Q+pUgT(!UcT7jFH178 zYv18a?GBnqH*&1Nw<9PEVon_4j&m2`u8v`U47c1UIlJBQ%x>gF zxROZFgVWhzWg9Q>;2I>aPhUoF>9{_ugTtK~bS!fg2wwljrA3lcnrHCbGz-ugLm8B@ z85W>v))4ux!2%7_dVt)6=@YJ@CW{NJa5%FQ8U{*q%z@kKbrP~pvxx;Z61e0K+vz&K z4M(#FkjlF7IOLV*vSvoN@eI{B-whKNA`x?9YxmFHg=HC1E?N)SkpNy3>DlOr%;>2e zPC=nak|a9O?tC>4zmi5KdOLF?nMBbfOTMwBw>4f6Bk&Td3rpAU{&l^e^U(C8rV}o? zPj~J6#WaIq#kV#r{)U9(mLb;XI6<7mFqFKZy)1}RfHR-skOyUkfKO5 z{|m2jt(C^hg_Ms|xSVhW_)CppjH|l6M$V>XImoB#LWxDxt1$3>^g|bUKx0Tg!Bv^j ztpzMDH{f0H>}CQ>o=wKSJ6|^7S8^?_6!xkZ2g&Mmo&rP^&u|hXd%oh;$vau~g2y~1 zG6qA7G@AzyuIP<|p{S(B#8byzRi`esbf?PaS<1C zB5N4T`{ZaLTgy7Ba`O8coL#MPN)@UcN0Cw<7i*J>X?ABRo>v%}%Cr4xs(8M1B#o#wNZ-R#|sX|~%N+(n-G^vcR-X;7S4^hQ^=!u(@07AF*KjuRK3 zQ1n^)`3^(Cy9p`3gLyT5%egbH*nKkNh2pMrg32T-VOyZFw$lp!%wgqNT~bw9%d>p> zPDGPZD7}MCAIY&2m0-x!4#j38N`H^W0Y$apTipPSmlOEWuttx576{rT>CDDF+p&lU z8)vY1aA!N6;4W^5j%j0Y9?5Q=Q(**>G9-=41O4BHu)ZvtZ3R^eP*#Al0+bb?Yzmaw zP65j{2g^u7Qz%6)U|9jnRszdjq&F8ZQ&hx!xhf_Uz-(i5Er3}8%nD#u0J8*`fofkQ z;RO;~4HDyMilG>xKw%e7NhKAA(Q@!u0k}3r(gkPD6;P~z zVg(c{pjZOMT26ci<>Y${lh_M9wi-M}k+Nto$`bpRilA5l(q4nK7wOG|)EHHgWHQ&T z`~s#|k`_;4wdl-R5wSj2BpJHCT*S6;jt|FmeSJE3nx67;FX>OC&cB z4&!8zGdMNRZrGG>W-wWS!#1Pf=HakKDtf^t^0jhhfek;>)dCx?V3M}C=kLUZpDD*P zkzo@)^n>tWk&qYQa7EydWN1AQbX)TU_p@cZ69ECiWl<4=L%f7 zF>)61xdMX~7_5lTO`)(m&+%Jvdy%vkU~DyDj1mk=R0IG`1sE&9*!vhv(=W@aerMF= zWBsaHJG!grC;R6IcbajL2b0mFU}zdH-n?Son*r1^bM#py&SuX7t8xa*ad~q5QlI(D z3^>EPTM9Eve-UNhjon42CZn}BDqZGwS{2WIPHGsY<(!cXPpYTFAu3Mt5lVtuf$uw3 z?AZ7u9K!XHiEknj-wA>Dnm|s70+w9MrF;H+0&%_x@iw0D< zjc;^yE8NBjau)96<~VXu2>&bSg57+Dbs15mC_x3=vixSA0)#DRagr=cBG=jhtw;vR z<=J#KA1oIjASKCsHB9lE+@2oQ*^mdQaWeLQ|)}`>z3;ifDTQmu3rlCIIi79p`%v+e^_bFRA!+!?!U~ zHuk&ns4&Y(MN~LD-|IBjt2Wf1#qp_$e)FuP&kFZpS#E#9x2@%j|Ujo>+mQ3Q8nZQ4ic+kK7ytuxrU#O~Hum71tAMv!HQ;fdyL)(fF zUJmhuf1wliLO*9&8-rG)JWJkRja0J-%qXg;ixQnaB?KtmpjFj}3ftJ;@n~`6yG*8K>_!b_7R0L*8PN>0pjBBbsj5>6_uQ zan#>k0%rGSmH><NJ0Z3~?yoZsulNWnK<--(FGd2&M2BP`SNh=yrB0PDl9b;9&*0+)Ft8eU*ML~vaD zNKN6J=s`rZ(b{)23_4h5#?SP+wu!!-k*_4bZfxuc(~AP4ZK4lEvOgYo1A0-2+`2_> zG}DF9ZQSBCd%lIu4U!<&M-rI`PSz0h+m4T<0&^o4Hq7H|&h%U-@2ty_n>_$Qr5V~#^F(du4}g?u^4$qEpW=xc zf#(H%*Ka{aa+0(hS)tp-Y=XF8ZjNbYUs9pev>E(Bx=tdrWZt!OVz;3?d=+c8@Xf)6GU?!hb2>uDMuiQO1K1XG&!;h+B# z>{TmzH5pT)$@NadvA+?gVbCgTKfhhNksIu4p$UStOHtp5I(!puO?Dn0k--ayA#&!& zj*mK)w2U97cvg^TL88!Pz4VMpDV$8}8~Rt|N!{S{L=M-^6-^)AKiY{MX^9u*>@(*%7Z(S8c%<$oy{v*4N{HPdA zJaA+PYH`Az0#Zab+CdZpWb8WO7&F>I-}eIKt!2UBFU!i~DA`x1dFqS<>HT0b0@Z^* z9GhqiiDTCagD7am1QJCc@FK#JEJ+#kdqx}ob5U1*l#N330p3b3C5w{=$>mbw2JpUSBBZH-urAs0ST~K#($YuuJU>T%>Suq^s==ljV`nF{8jY8y@=G z@jOt?71!?#MgcLC@SF&Z9eo+FBiz4oeWazM-3c)(&4gQ=q{{}SezyNInic5e==bZ+ z(i|(x>N0+KB4i55%RJ;dYz8pSeEGW=q?*$DJEE#XSf)?(BD{I{M=y3gyoA<3MmXpp zRsik3NUzE|sdBSZH0Qp5Z(f5-4=G=sO&iHp=(+ww#^b>nvyJ2<2!}tvQOR51>`xv1 zz>&H-7&V)yvbV?Ij^plLsnmBL+})t-_?_S(*bTxKzEaXsI!ue%xcBq;m{B##5QIEF zj>3=z52>p8_;~X1t>I&t@PZippCtA;#+a}TKH+5GZ5*2)^D~B^2(7?G{>pp}VbRax z>wbUNa~r*=@xs*o#CF_4pdJ~wck?!^0m@yj{oE27G8xcdn`w;Il44k{65Zx5R6zPLJP`@&Sjy>bc65uHU=J$x=;FNx=Ng_kPwl1V8n)Zf#xR=O-}V;cg`Ap9DdsI`9Ewc6-W&qGcI0ZH}HHgcD7TywPU zGL;zw6}L?MAR35^kcJI@Hhv$DZikM8{S!AH61S%9w&rQW8D7~Z&;1;gluT1P=+y=e zR?j|j-Lx+=-BSaY<^SO=Z1eoT_Y8C;E@2e(LJPoH%h~mvxYP{;SYVDyNY47UTWY$V z6P2Dp7G9MI%J_z}hwvs@vK`7}bQpt?d0EesS+pQ#GI3{p(M!gl8Ogv-<~DUUWzF1l zsr7sx77npK9HZ~18^Thd+X3~7ba2!T`bqpIvXB<_SD|3$tA^PV&atv8u-djw`0UOK z0;y<<_AypwbV}!NK53qpp3! zpOo>Z%J>tE67`@vl?i0<3N6D*1>A6KH<=-xp-76sdb7Kiu7OwsM14-6DTuCFL(uHB zC<+`_hUlqg6X`TPc@~zjz7a5e-w32yB@VvRNIxXc+ktf&;=ChB!hzBsKfj&276KU# zL8Z@FCr|sK*+u5nfDPd=dULOpxl+Ly&B#pn(H@%9k-oLsPRERJbQ#@CqN5QG$E8!( z@k%sF@}-XH`sn2Wa@YNqGs+mkuP?pv{*S&j)074YQoYb!qN#2&W2iOsX|Fr(7W(wz z*8C?#4;VB2fubdD@U3WxdihVViozbHHt`&4jL9>ZK^=LZLWKv|xTgj3x9vtq_=r`_S zDH#RyH43d7nx7K>9EVP9wc#Uz23~r-l(~Ut=4CSAuLvm*qR~GNY(yHf7YBGU2Fad=Y}6y%y!nL2kvuwj1TOml^b5bl^CvEp zk}J6X(Bt$Y`XArR2t@YpL>d!ShZW&i11>UElxk>#$mWS*uf|lZLi^7i3QHRbr{pTH zvIyC(#T2hCMn7{1IlA)^G64QDMq6M~SJ^|>^E(1#$gLY9B%h74zPQ(J{Nmy!%RfZA z>O4YT`2cQF1TZ5bSvCZu(tT&R2MBUPfC@bkA!zc_ z!tTN)I+i0fd3L4FH3wl}@o++T$AP=B=O=scOhT*HiAFFW%ovX>olRJ~ck;+OX)qd# z7&-^zf4wE#IqFT(4VBK7=bqAj#*ioP{N}R$j{z}=MM9J}VRiLgo2*OsDbq85N7cgU z@2Ab#y#BtZm#v-CdGnc|!~8SaSPL(X(g%tE_rL#lGIwpO_%c{Y?)e&~4~qSDAduB> zJFu3zy;JxC({UbM6g&1VQEoz7#dmDIlNc_VY6M=K7w9tmrAhz6-cG{Io&@ZlY%7Ie zuD6rPkr%hMY@K?SyTnb zR|jb}2|!P;6o?e9s$`x_3*F#VQlEt!XRH(`I!BGNqB8UII;!8X_GAw!K_4lDbjK>b zsI~)88?`j6?WP>^X7ffaPckg7y$Eq&P;J zzX*^7{^QYy5YADglBEEO7Lr{qlWPH^;4)6>Q;_#Vr_XF=EdW#G^8^1gqs#puw3E;> zeCmdQjX5^P4z+$lQZ@ zLwW^&j!oA~K92(2qq$3FF&;DqSYx5zW9gpOK|IHEh78-+-_$`W$13zphkf!6)z5j$T3arRP|EU=l|2cxMKgeDFU#%F5Feh;K$E%GPvjXibBP)EaWbqGneB3Y8 z&m0}26<($kJx{gEWjmI-=J(dfbXg9ZFPIvt1+fa6R8*d0^Ar&HD7Ay`Fxicalo0m= z{Sb*OCyE1~s9WuhYa`%#fO>^Y{*~iFsV8JDt4Nt02M+;J_O0C4W6iu8Xog3K1W#HLRa0m6j9;0T*1I#f1hE^qoW%3|^4-NpY10eN{j6etc0RuAP z>PXp-OL9RiEm^5oD;MW_<%T$^5?6AqCZC+&*=w1B%m7a$LmCQ`>&&tb zPMv|1>uNn8l4ACV{~%4y+M}t4^S=eKzDQH>$6@r~$pF|)sza2Xk4!^P(DrB~@6 z*!(ZGjyi^#7FJOS{ZV zRUC?@c4-d%{p3b&1LwT%;g6p4cP9#d0N?z;4eTY%gRjET6+gO82&QE32Zdfm;p=hG zoprezL@p%X{y+qApAqw#-k#Y&5T+L=?ag@LN9cHF>^?brR@)3Gp902)f>3iB8Qp$q z0@&%j9e?@(dg4dIMtkT1nDSP00L6lknCg+HC}*xtCwD`x~*<*#T z$+$r>;%<01Wup7j^&Y^hS_- z&6BceAMwZf;qgF{yILvGaD!FvJPgA2HSHO?>vgOuU9(T(dzBk#hSXJi=ffLC;TkUA zIAfz>aLBZzt21(lT#Cwbi#xK_YwxIX|72Z>5`!D8UsNBj`~EYhg)RReK0F^(qqC-d zR6ZB9j#j@=%|TzNJ^G>`3b#?Gb}TvcW#`mUPT1b#&C_{&CEP!`_but9G{g-aM%Rv2 zc_bfqt~9$ubD%yad7-p@ee)RVB?_r@NjHA z_MheBc5g>}x;d>iulLRVz`Z|rckapkr|v1x4KWtnyN@o)5 zm71mYu}eMHk{(``>8Kj>7l$o-=hoF)&1OY(9t_W@H=ZuZAYk=|+J4?2uuW;dW;bZ= z;5fQzKYD6^pYy`XqttM39|l)d_wfcdNYgj&d1%rPH^)87qvA7-Axn+Z4%6@7oR9-b zY&BR;w$IS5E_Yvw423=?jWeM}`sXM5C3DXQQqwtm4g<$#_m_I~Lh_F3z3|DxoCOxJqqtMld=H;5#I^Ma^z)I!$`T?hGqo3{st z&lUP&*dS|9H$#qXTn(eklU|j4md~0OP4{3pERoOk_Kn>O_^Ysc5!6ZC;Pp|>KJ933 zwM-i~gQrHLaVVZ%9s8BqK_5-E#x?hk@5LRfWBIh*Yduu2Ps6rx{dA z>g4F@oVBlUgC_ZCDb$rll^?IA!v_JW4`Ju3(&(^^8+4lLZ6*Nw3ajSaTy(c^I&4rVQ-g)o#_V~%`@aNa@{q5E94r}=_e|jE1R(5no z>oiN&NtqMM54|JiU|=;ahhe0eLZwzaQ2Cq7#!*=*iK;8}2mPeM`qRbfb*ptaeA;PM z13i+$?!eu-5!w~|q+A(v3|_|$W)>PhNOSQhx_%cj+9 z*ZkXV@6bOE1D34>%0=nIP8wvNPx|70plUZvEW7H_SxvSdOEodP7`FOPa{qeg^0xDM zR0-;(lWV$kb;5X}%!U>Fs%keyiK>Z5ovw0QIoLU9;|5DrMGWLqM$>GBmuC z>Tp0m#FghjiLKzi*{%wmo6Dox(__7HdK@1p&dtsVYu^ld(vyD3^y#Wpx7Ev}hu3Vi zr6K{kV6LNDOH%Ls-b3Z&;^Fp0>0O@ar_{N8*^MjIG1+~T?EC9#*Qaf+J@42(9kwFe;Ba_rhgSKlwqLzu=vqKmI)2BG<>Q;P$4AqCV5)*t)mr|Icr_T@ zTD|*g^`Lt`EFT>l1v^&zkqszmU$|=Cy8UC^;2<7Wk=EEzD!nF1NU0=hvY}Mu{@H~r z-^eOlRCRgKtv?7SefZ0e&yeuJ$Nk#S8cGih#_n02OBy$b4UIlL?O%mpXmo+S7F$wV zZLpMe(Qn>gwOFt0I32UDJsdtnjiFdGd$$d@ddl#RAx(PhWxL(&Fx>S~qF@hd@jyHx z@8yfqv!b|lD}3~7k+ySFs@}+@6Xl_6+T}s5f7UNmO#k+J_;4f~3Cw*@j!xX+^@BR} z<@SkujkJBDU`ahF+0STlb*>j_hkEtAW@)9eIi#5ba#*{y)VSfc;(aM>4Ihtr!5v(; zDCz0+x=YiN?FY2fY&g;L#m)8cMZ$+YW5@AH#qi~$CVjy6Z%V<1e$}Bps_aNNhi(}O zAmWeHo8Z|M=fWK(Sy)cgQV?!|ucBx+dJ zp5fCy@VK1X42-D(enEE02v8%K)^y_^7!4!9hT+*BgyV{+RS~r+qEoZ@upbOz-Zoi|i+ znNm;7zL6&xL1l4i<#)a@Nl#K~78%J|-I$SWm{~Jka_`~Hm*;K>mg1wh=J>V~I`*mA za+J_9fqUlW4?sSv57DzIcDiYfds6lwbh@A)lI@Jip7h2`y;I1YnPBNm=b`IqefM{C zeTiy4%gz$c=(sLQ56^jXQF=IEL7)$_;haH@i~WpBkC^@t7A@dlq)b6#WXiE%f(3`1 z`j#I=v1{SNaxu=y2V3C5rsR$pr@L5+62)JeH!#H!9T7VW*LYgi@2Sn!a0=FMuF4v{ z7;CacJh?Kcupmi##p43A=OeEAvctjvXm_I7(o7$L;{{b_FwT7oKTmDU**t!pJpz)K z43c8=1ZCc}wdnJ~KHiO%Y@Qnjs?u6USdsKBNKZ%X6T#tCV;~P(P>0>oX$MIbF8noT zA4ztC)>WH?QVm1bxjbH)X0SRny-RvNmrXvtJ}#pvo~LOZ0t!pX$<(kGvtIgeW(2S; z8L4>K@>Owtq&^@ur8H}7A3VPLO*YQ-ZB4HRZp!Z3slkt!;Mql-JH;Uz?CTuMNc_;S zINrs<-N>olZ99V;(!%pB!?3wd*_t`*`O-3ZXMNJbC>&)_Os-(`XOG|&hcBt9UvzZ3 zOT%`fu4g9o0I*^npCyxKh!YTZ^hO1x3b$X|J`|D zakNOY6vZWlvF3aS7J)L~p;<kTa>t@|bdF+=aJ51G50q0Mk`fNom$ebiIOrABccPY8_ z5`N)wFemqSMu;X;m3T~-oI2?1A(ZW9?G+7jJ#XFI;R+_+PK;}?nwT~52@BCMik%Kb zLDD@1$thtl+1a99<4}4+1=x{5SZ3G6ssA}^ba9F%s63S?2CzxVo}-2gP0(n3-p*T{ zonKw+^VJYu$juNF3lJ=jjP^JnppA=I;W{a>qKzYB;8P{+;o85hQ?+)LHJ-vkWxc_t zyF@t#wzRw;0-lWFh|!c08&2DNa?zYm78Atlx~SEJyOT5LgD{UGqBxn;6e&+`+2<=F zibwi|7W2e{wiyq}{xEAh#)pZdrN9G3!X}h@m@qv}s8N8=wKS7sN1TC)bydSbwrr7J zT2~~VmnB1#u(scR)*(4w(HS|U>IWw)|!}sFGIrFv%rvuWsPM(;@6yo zZDHhj%P<5{`YR2eWvuKX)w=*ttZ@LVhLvmV>Q+G0(ZP9Rl71ATe&U2}Z_k%nR@p8M z@Yk!8duAaGO%zmtUjk7VT6;ZO8~%|4ivEs%k`(-;$d}J#@;UocoUA%Ko6fXqAVSki zI3op}*GXqFp{ksr@H{$`dFaeBDk*7HuBw5B=dc!%rsSgXkc%=nUS#+@S%|-|>%g&w zBFRRcopyzC+6X5j*Fg>ZrrYX;$;OPrgZOnk2x|^y3rAve%=ybX5*#D2w8rMKWCie8 zwYpA$hlGI#_8jQMf5>_c8C+@MpA4PLbC3;5(Q=(w`3pl7Rx((g;__rE77%4q5Ty(| zL7Qd~;9ON#w*sEv{k>CEEucvOO_qix3{u@HovV;bp}RMaSJ+pPt^}zt%SFH_Q4&~5 z2D<*ncty|+iZQezuD>-rkMPQ5!c)!#8j+SM-6(+PRw^(0)dEd&CH;(@FsAvA@I7tN zw34fG)i12PXoHt1iu`R{&h6)Uq`Y+B?gj{(7rxdnRPx$vr@D+*Pl9Bc>uUXY7`@dyV$)AWPH)eh{<{!(_AD?G%WBFp zvrusKhdKIx{?5UtG$qTFra%#zUyVYu2TTeS(!1=G7s0Tc$}q{E{PBPpk>l8zB^8z$ zFbrb6*8#deeW>aT7W+^;Kz!J-3$FFcrV|S_V&dEi;k_Xi^EMNw71J`CwW3}FQ)8DnU&Q;YNVB@~ z(b32Mq%VJw%e1tcNks9CGHvUb2h$7u(WY9&wq2-N5;RdCU^6Y8D>mA#xVRd0sy;tF zC*@>=tyoz&b6WENbA9Iuxa%rG?S5oH|AYj-<{Vk zbo32;7Cidm%x4U_QrCAUMmgK8&?F_wijilB&wR5od1qZ_W%d9#$>3xmS90u%fxxHP zMb~#@^tk(slf4$Pa$(w5VcI6=yRdH?w{PiI^8(EkXzn-Qb|{Yzm0_Sl%y*&}LAl$c zM$CgVYy@U#!+nIW(*5^~q#^Il`?t(ap56E?NeMK)%)W<>TjJRR;AqhhWC{uZ7QxL; zb>^)ls`AMACa1f!5j;*1{um^Wswj=5W0%Mo%jQ^BW8^$5lJg557nEI&vWSj%V+!u} z26t)E229(#iMw~HV`SQP2!&>U=Fvq4PjZH`L~+iyL|Nwqm6@z?O>(S6B}z7({n%_o z>F?3pz@gRfE#!zIO(XE5sTw`{89cISb}$?Bpwl8EY`T~K0QZfQNy>bALeh-EvW6<< zDM9-#eez}5Yb&U^QWfMmek*Phxl|CMn#_u1o_arp3My34qzXd**WHnRqg3UZc30C+ ztF`mHihiy55%MT})c$s##xbIkaa zlCyH544!8tk=0)%7b`O2JlGvG*T5Bk;&%cRF&LFja2K~j$F#9cDJ)50Nw#L5ei=)$ zb&{XUVc;Z#XNBodsKF^Bi_g?~8QWAAz+1lRjMRX_w0sa35N{l@NvjmA37hA%B?2n0 zon$co93{5o%Kg*?m6FIjN6P&Y>ol`xfmAh#qx3wf_{H+Yss(VgFf~sQr}W%L#n4== z9X`trjhFD!WVrE49vt?bISV5_`NXl4vrfh-g*g-hDbk{{)RAm&DTWy*vu=l+Y>!CH zw@TXviRO%f%(rYzX_7$lVjk;L6vbLKlSQ!^_i`=)b2=^RN}hC-`S!7p`;;W}!yKd` zD2%=y9)XuGwkil^zFY02;jT~WaSDnNCtD9GG!C7X8zD3IRTStyf83#YPYx@)i#+pb z*;A>C7!ussiWm|mV-Y~w94CI608++(hhu;9mB1Dynb$v7I5hR2(mP9rj#y6m9YaT- zW3E?is6UJ2U&XB)KMsfA3F>>UfS1OuYI9CgR==M$EA4%lo z-+o?P-_l0oQNtxgihQG{XCkVWmTQyKK*D~80&J|uMlEl zRW%o{ZRY@3j*<~1WAQ|#IO#0o5!(UYWxY)zukvFORhkz}Rw^;59pmq3*E91BZQ&_rKyI#9zX zQ_?82c)Cj%=xd!IgkliQAnYU(i2fVNTnjl39W))G(?oVCBaE<*I9Y3rx{l>GQ|aB! zVPP0SVsyjE6V;j2Vw=}*a}+eiOM*^Mr#bzq3cBnQrfs{JHBt_E#e3iJ<*XV9?d@QO zG(C@iv_hiacKnpZohLXjzKy=%_)#9FAq7gJBsGswz z$D{u20jz>)KtmxNaa~yM!gBu(mU~>23-n-8bOQ=*;Id^!kq{Rn#Q{*b~zc)gCFxj(vqAMt|&1 zugl~w>ZK23R@o0-8$PzsZ`1~X8HXcTDH-m>Ws#+4TxD1eKb$l?YA>D;;ju(yM2%K- zhSKRbP8~YO;Uu0EIGw@b6I;p8bOct)>l`U$*xeqOsTj5G(d1g5V}{d2!Sm^PlO#&p zo=TKfDH=`7I|CTkk}$GRvtXou@Rz2Pfjv^m@z804p+B z64y2b0`g}RJy&?}gHxcT&c$?X!ikB1U&-@t3LVMNA}4+sp<@{m`I!6?yq1Ka(;?)W z{qz#<#cUhB`8axp1{>>4e;&TOxW3c$%d)E9sTH|izk|XGYW4V7KGE*>_4Bm0!sMdO zcIYgc6j{;0oUndA+Q#*mPvWD0;H0N*)ZmmTk%}bbNh>KBVwDWB@5F>T3Z0Ezz$6m8 z5(`Jk63yw;wf(o&!a;c%z0gdXXT-ORSy`*-TWHA;Ke93<=UF!>_+`bGjNV2|#tDi{ zQ@J+q77Vd!O9r;fwLJ$}GUu`9_||Y#UmI_=gu}py(GhYhv2Ls&Q3k8Lfm^o5x(O;H z&@8=#qg*iOTCLk@t$JQntCcOZaJ(!qsv_n|T`G8H#TJg)Mhhnh979UEN{JT?u?`E@ z4TCs9v?Icv@56##nj5o1uZ^S`O;h>w>h{MaIi42;x?ssov*aSXRv(8Tx6rDQydlss zmnU7S;FuL#HEA2I8plwE!YFx8vM(57l?+kOMj+EvzExT>kyfey&)&BvH;!ZNy$Um_ z+M1p8?*TvnBzrQG0&g?^|Gvd`_fAzRrJ^O;=C&+)b!oRJm8v{V9xP810Hh>RrX+|G zDayv|R@7 z%Eib!f@3B{)H9h5V6@E;0>@hSKXRQV0AaW#A)`C!7623=;%Me~$ri~HBI*8FWLmbHeD1Kx)@+%g&V^JHsO=a}T>8m-wVMgv2rCb@wfjm25Z5{>5ns6IJ1?$gOs9vn$W z!zW`li3iJLB-c-?58rz(YfiU_(<{PXFpfqOaS(J6O{45gpP2IvpY5G-9Zw(QIAc$7 zu0?)SJZrkwO1v+v!{Qy2SS*AbPQEkx{7GZJVdlD@aXsT-j7FR){W$p+XE(#SHu(Nze~515 zBHLZ3>DT*c{Pl7gjm2@mMCj^7xF+6;qf@{B(7Q_e1lb5fNb-|w)!&iI`SLpmn>IpN z?)Cg#8)}jXCQ&Bo{Qbd5SSRxlmxfmSXt5&CX$1r9I$=;Fpt$QPn1A-?bdm=z^HpgQ zleqpkl^Vw=uaVG10i3N;PUF;z!l~+Q#MvDNFccf6;cZkFd10DzD9iRzMUl~k%Oa~X zYG%gqhFff!$t$rj))XSGq2rpiO<725WSj4M)UGtxc`5j7Z&FjbR6juokx#<8AONN$ zQ>vE(Az*sIQ;BsrKJ;N_RkiXbhhL8Bp#uV_3!#b=i#k+nzxk!y2UNaTm{0i=IiLK} zhWP+zK$yQA!HKmTQ)`1+tCoE}|8TXrW@!gYOK0ntvu`5<%h-C;@YHqZPEh~1A4Q4K zckD|c4r2Vo$E{NOntC3gwy%;1z0>Z@`>+&t=hiCM7;XcXSH7V)?&ZRQw>i@4BHhQ` z56`zlFZdF0VJJ35wwaYm>3YP3f%LK+R4>^MT&?LPn=3!8E(mr#90aCHWML=Gu zJ#^xQ7`wDa0?B?FxSI~1gb{Mit@_b4H^Mj0t`32>N{*|$WU}zm$ew-Ca3~t~$vT0f z>Z-7tq|Jt{z1HBCp0{FA!c_-cR&BF5%u&kk8m9fP+)#gGBva!)9>>Ec<6$<<>VM?{ zpk%}!6B+$v+@B4(iT1Bqyi(L<_U9DNp_gPu+MU~~khEou{ z2!&qgdM&u&PSJlr0%BTV&stG1B&IEpZPV%;4W~avc}tP9Q?hzJFapqp?kPGD2n8;% zm4?2aloi&D71d z@}dUDzAluPbsrvyvY#vIIVSOk$raZK zGwcBBR^T`ek;=qNjqhAY?cVsZrk3dHiclOn7*Lg@(Bo@pyO(}Gm&pbE1`AbX(RZ{S zF(Q@qh@m(>^87LZsPN=91&Il;W%a+Js0jfISblJGvGYU5pb57);j;rT`S<19s&mzm zZ8uz)z(JP7l>C63m*Dv>FWoMAx-j3Ir_*oo_-2?seiDu%IW;ljOQAjZ!OGtsvcCua zoN;avi6xvjGbZGtX2axmbT2$n`MNB*ct1%}Io+C%6~biM01NHT3;2bq`DBs+kR zVBeCU^D5clU<^_vfPpzt{CbuHFrGq|wh{u@^{xO;u zlUctXPbN3B;n{jN5I?d<)I%PVar7nI${uwFc>rU~v@NAmQZE;I4vaV}NE2LKe7xu) z&X^%4o~70tK?@#5!6R@6b`obS6A*?%CEti9w<5WN@i>)-vBv34M5WT}WTc~|I$Ekk zy^2idTuO)^*|o|;uo$~0w!wZ8x(>xJOShDg*rW!u@eT}3nFb=(Dj!nkL`_E9RVpIS z4+w!TIZr$I9^F3-m0WnXg+dGN=ed8eA$jOq0j8!3tLQ}V=JQ77e?R}E6xwYxGxL0P z1f5v5-$s0)M7_SR@l%#cCSo`IkLy%4l*|G+#yi;&T4bf&B;R9sDBV6Ye)?HKSU^B* z96*({2E83x+764Lo3-Ekzk$4wb7{!E%usFsRZhLi2ii zqCXS?G~@bd8fVSx@6$nYBc3fTof&F!j07I=p|7I!Xxes2j$O}gZsPI(0RNMVGN2~P z%n%{x)g0to!|ovrod9Spwp*g?GVZ6N!9tehHw+YYF~UM_n)sTirzyjrvJ9hE2q^Uh zNw*PteI2y&C*2lTz+)7ff!4gbC9cGoCTfg?4_AmRu6&K9aXC3N{t+9p$l6`>P5dS= z8+Y+oSs|m)wjr}s9IKk--4kX=2s2J|6>(kZ#Gid71E4 zpP8ac7Bb|hHDK5DJ3ReL6eBpn^RX z^Qmhpov>WVSKlUeYgGd{681ocnGgb%a|}*slES6pgiD{u2^VUy=7bU|qd1{24#;oX zorTE$z6lqd$fTIhdDk}`I7?!Of~4!-IEZhOQ9LlBynV(fnI_RN`Rn)1UqKE&gjbbOx`5lJ5&(n&Ivr}jjVl44cRGvj<}+{PStB5@q{ zVU}&^tboZxRxw!?NzV$H$W8(xEppB*bFn5PFO!Y>77Kt!n4IPGx0`f0Odlt2OJDf8 z8k<5NxP+=W?CW5YM9q~D0rotPSxQv~c6no)WqhSd^1O_1egv}mJ5pJBR>oJJ1?P`xclXob`b4!$k(ONC9+e*NU%SL-L=jMIBv zLHWo~d!R`+^Va$s$)v=?fvM69d(Vw@is_ahFp)#U7R1cP!8VzhyGsRSOneI=ppuBS z)A9Q4%W)*n1a&aNE>fx|P^SM@|4bdGc;#Nz&2^W%;JDnNg)YXqMZz~e%0r`l;{!fv zjk$BH)%Ef3SVZc`j{jmsqbj+NNK$&Q=F11P;A`hJkbNz)y$N>)i^0Q|v(!l7kllKj?z28M;&wXu10#Rz;2C zE?MZ&4mFAiMua-9O52gHNf46}>}0WnRvEqPHHwvoGuIy^)0Arui+Y*5vhU7s-pxdO z^_6{h;Z&tEzQnNt3$)>XJ;^e@^`F`Kn!OZpSf@%2QjbshT|f?nF0?}>7y5~sJUi6- zAR)5@pptYB7$ku>TlQ=*%Ad5QtPmSnjQT2)X=UiyCR;zBD&=eG)K;9&K4e`b=q@SF zb)LW8=do$!>byzje`d))BETMJjA~5o(#Hw6+8%}2lJbjBb80N(?_2}O47qt4{_3=g zgDzwm?L0n}j3j_fhnnHx^MhW5J&0_V1Bbt-8{+vEEO6ZsgLAFEcjMMq+4#qClM|uh0?4MZdJM6{NFmcePqBnP-M=64rYOhnYuscJvVO>nU5`dle|ktoHU~0iJbc} zc(NOm{a*@X!+?0$WGdO5FG65cuL24jpAxF#J9;T%vlA?;ZY>l^&}7EmHUpy8ppPpC zvcf0hShliQ9;PzbC>lQf6&s28<9s4|I)C{5-gx)X`w!#4DHc7ku;m15Rp@-09Z}L- z^g{&qY4#Zal3Vj%N2g zu?>1+8yLSDpa1M#U3?CdsuDBoVT4tZDD`G&O~iF@JFFY`)|B%;MGxf(R!)B37@*O&DMg}>I6D7u#;I@p?uCGd0zA|+1+DlqrvwoMGp8t01iM$j9f%lfZ! z7}yM`V|hxMf|U&mwaaWTj{4sUB%+}YXz0}NYVIqg#7uWB*TM+J7>5djg#=+%S+27D zi5zT*ObRk-kPo)9*_A98yF%-on_bCedfx16rNvbFJ928znqAGS*NT5OHM{zJ=^DNa zKW&nTFRf{nm~|F(JN-6!pU&Si!`s%%iSxDTIr5$B9`xgDemWY(eOb9a$~IlT0o$1#$2BS4_nV!V$aEZyCO2FIzmEq= zWZWdfcycC)_NVbFxsNg^mbLuTk?3D5%0uR*v-wT`b{=XuvutCQnQ|d813%n8oSk*N zY_Sg|8FqTTmV~t=p_B}eSQz9*=A+>xHNKA0$C2pEEHt(3`j0|beoC^k&{y+4DO(V7 zD(OA8y2nt6JTI%sn!j!rfZw#vjj9%U8*M?Pzn_(O{pV`c-NYUMSf%=3o2q;DU01n_8@wfO{6KbES;Y|#ADgP zY@$-K-m)EFdN%7Ql1;CN`Hy@pu805N-+kn-5U&((Kqo`Y>n3 z3169EEWR@Ni^I44C0kJW9LcuD2Z>`Ab-#O^PN(U8-EWVR+q>GY=IqRc`XBp!{Y$;C z3tlTP|Ml=)ntq)>4PYj}CQHX&q@0AJtV0$|5q~W|^wQz%epL8;enH+cO8;=9(O~h{ zm+UXN9K(WDejg_T@#`)B$)90J#v#~ovgwvCLja|(cI7g%O&Kl9o;-ixv?L@}Hbx#! zg(URy;MpRi7GfKOa7)tCRq^}sZT;-ud|v&02M`O%_*S+F2H&G>_>()~eU?kU=;}}; zm0acFuuQo8*(-`8prMVFO6qN10u_N#m~SpH{@Gv5BNm-M^8K-k7%52e(vXxNB;@DE zxj;4g>5AH!i7B~&8wdq~BU!mILQxR7mIG9>UtThyzC^=W9r~D#DHo|R`e1Aa3|loU zuop$2%?)>RDv2N2{qe|HxVh7zt;26&6qtHTmu zfP5@8R?QMsEb;Mr8YNk8@wrsA?2_Amqb7a(&C+l3myt zux~ZMvRaDOLcWimf)BmR;PUePZm(`E?!U zdG9`623-a{ND&BqSmP9i=9v9K&+H=T0p_^GCEC_&nGzzC2yCM7#Ns4UyF~;|OV_qte~!X!z6|A|l(ed|;nS z)DO5gbZuLuc9WKFts`Pb!+yY{G_aj^(s4A?tUw~^BqXAcIwlgN(~#IcBpQi4k(eci zMc3&pD`7N8z4ZE@;)Ld8*1r>Ht?r}mMn8Ip`pNXk5T@eIIOaEtjLPPK5o2TmxUFYS zXMhn}w(WqHGNm*_s{y0vjQi33_rSaAq^U@K$e52B2m)!I*^Y~}i@0c-79v>kUGun@ zU(zhG#6^LEquKp6|5|#J`H_Wk3J|p^inj%`Oryoi4&9 zF@evZsgG5(%@PIoDTlSlc! zXe2jA^o{=#T@T|`bFn$Z1C%flFhQwPKs@9-KE$#Skd|RxKs-Nq7hDA$HuS8(v@D0Y znqyk#7>Rmy6rtwxWFR(eB446WuT7Tl<6%6Fm#{Vu1Iiv4&0!n~pdXS@+lN@0{@j2oIuHaNyZ_ZV$(sGaCjn$dCJ9;HWIrS6Vgu~%v* zcJ`XZRrJ`1A6YBY)ie7xb39w6)B_8EN5r9?XCk@$i4@8CCHcZ?ku2uFVv=v`A+3Kk8iI9e`Y}b5Z%U0&aGZVWMUWspmG$k47EA?uV2{;WIhlZ z=q|jv3-6)3@Y=-6rdzr!^y24N5g~N>;OZv46r0#r@diNVkkBVvq?ocdK+MBvP?I-J z28rXVcmp;s!83VLzFo?T!hCai(Y+?TUcDx~jUOQ3)54gk9HV^6box@e2QMvPNh6g+ zT+_pW$^nm;OrL#v@G6T~Y(wk@!KsK?Uv}VQBVWjpQu7TI{ z*at6o*Sor4A1;}9Rj)NuZJ|L#4V%uq! zI3!DW(WoB}vl=q}*?25`0Ml_anneBmkgf=7)U|+#x43FL18O7)eBin5=%bn!w}V=K zY=ZfIP$ws_X*k0c@O?^irr(8Mlv$%A1d{ksubpkv_+basbsZIN@Q!=dbO| z6-%&1BIaE*F|I{Zzi2RsSI;gy&r>PPK#U)H6ky3C$Dh=ifQ4v0(T_y)LSr0H`P07f zXP6o)SXz|pZCI(xss_sz!SW(_|M5#V;Sw<4Hen6*>LlT^ zUL)MbU3#_)Ev0zm{&1OJ(z~`AE(h{JyXa|@-tV8`u#A?U> z(oV(gkRp#Jbk_VrB!IRRycYATUW3`D@x(?JVoiivwfVIfPcoR;XnY+6Lnv4B1LtJS z63iun87iGn(HPPmhIH2|K>@+Ot5h)OC7?*Xo~#X{2(T>+JGg;pvbIWgLy61=l#Jx{N7D37jrQU?AKzI)azy1G@e)h zxM35Ch1x3F$P#|$!Fm{{q6~i$PdKuCi;ezWG`fuk2SE|b^aF>XQ?W<5c}$pH$LXz6 zq!AP;lw#~KicrP^i_#j=wUb1VdYwQUMiGnvvTV18WvEePKdWRBD=Ah!6gJ91fyAS%S?Y z>W7976HtID*`l3w=dg}x0|$b3&T(nz*bW^R!GHf8TrRpo%R@V1aM3~huqd`Yf@*}* zP7*)rwU%rcKPYfR2T@T$P2)$KEOE8W$r{K1n#Gf8c3egtleytfNpFloCH8s9M5F{C z1ZEIn48U-U0ot8_iOv6qf(BCYHwzFAGaEzi;pcbnI*2T0=zBJ>Yn%l=No1+l!LjK$ ziDNR`X~H?Jkwqm=GL9e8aSke3AU>HGH?!gJAn;)^&trjeD)0d)WTB-aC9N~#76+Fs z<=R02A*M~jkkrV}I7tAh*9@^~*A`_E*tCXwQ3J>!0VJ6kkI}>sM-~rbdSGbrz2$Am z>+TG8K!-~Mh8y5RjUBa35B7U0I?}~3yNLrYq<#(0=t*Koy-tHo8zIo69^TeUPGiWy zS>keHcX5 z0Xq+M^@q`9V$3G-#E6WmKaH!qaU98$rNd-0jYr})c|RxdeKZ7t`!Si{^Sq~T|EQnSdBeFZf0c1PO#5Ho@C<r~Jzh*CS&xji$4S@xb-dScSt7v`rJbTQtoo z4%^I3d(x+Ruoyy%L8_8u(K0jbPi!G#ZbX*~VO^~9QgLP=v|KaTjZ{bosSA z{?9NTtH^$g91CQMSeL|U^xrQ`wi^dTOtR9}>QDIXAj@5i!Ezu>PT(``nHLNmVA z$+hq1_~`Y1U$oAY{&N-JX>^?BO;=bxv3S%*p6$23cpJoNjgczv1@ZKMB-6Ws*^~%F zSG3TQ9@IA(C#Iu#!Ml0s&^hPE(??maXJnj@k|`H*va9{TxQHhYspueXL{ol&Gk9_z zpBZd8EY{`bWG%rqV4m!Sxz$L|mcK%Ll|N}pd{s|?TGT^S2`r91fi+uyC*GADTVf}{ zG(~I7%#5swFvI5JQ(k!^ST>d7H5z6Xn!KJ{Xk*E~{Ljor6R@A#aoJs5BFlX`no7(s z3jIX<8XJRz>$~FdDvW2k06WxH$rkbeVbZvIMz``O?J}OpK z2@nzeD1`tKzqdg@Dau5h35mO0J)C7tgqJufWK1Hd+{U7FVMFtz(7^O884YdOh-f^vQ9WVmQWLmLrR?PT z340JSD16WdGx1b5SdnFn-*^wsjp%S33FJZpFfQV&C=08ib)$}#4Ydwe_i9(^1qx4-@X z39uzj51#TX4o0~uEj%LWI8zi{XhR72lfJ#3a5=#hCVsj1UAVP+2?U}RP8Li3&-ulv z#h)^nJ`MS`$7C?QdrJ^KGs(mBm%CWBX?<%V=L|nge=yRwsklo%Jh_iZe~cx*UurAio8G~ zAE}bY%5D~FI-;SbtPP*Pz+9$}C1-SDtX!Unw@O$|kgiRaqf3mOSA|k`QVe(g& zQ?7=a=?ChxvOl6~6Gi zogm|Rw00E7gNZRo@45NK7vWy$fBf&+!^2ScV{>m=B>wDYM9I|*aXC|w=H%Lh7dn)% zDU?uN$JZE7*q^=66toKz!oaaD6~MM_ldHyyOQmsD?D_KThuHJgGvJuvVytv@Wo5A2 zX3$}&wN!KqB5ELe>t1JW$XuieRd(_`H+7R>YD$r%HgJHgHD&qd1cn`j!JNw{oG{Il~#Hl8_GCKlgVUE}sM+%_;_qoI)5_uljTfct1PyNUWD z^{wnoTC@8pXI5&fFqI~8S4f-=;;K`*&jbddAn*c})Kg8{j?gy0@{QwBb>rJ-GTNm9 z4Fi?rKTYJ05Si=8TnFg!<+)Z8(vW&oXqgH$ZbL;omY>xM9h)g7>S|8!3a4kPb1$h% zy?;hvmmr7uEx1ZFfe%e!;qz4>F=5yZLtm>TCrIL(WEl4*Pm^hUuS#wcFf$B*O6*0G zyJO^z`u&(+>qigKbuvt*Pb!K^KnRA8ilU1)II$JCHg!A;h(MgC z_9;_aX3Z#&8)D1$8PZa|vYwW=F4sn9GS%R(wTD0^;#mK`X31D){)-Zxw+Wx*B2}^<4Ts_Yf#%3nj-0QI z=EuYFV=`KvxnJalE>Ehgej@lY9#5lWG(YQ-jyVTUIrx5ye-;=TXU7yoiHe`|M-Nj@ zxvai$#Fj) zLSKCW%4v4QAx~cD-B#9kUgZKabeJO!+8tG|I|jjbUG|gWfvgV7M3D)QLj%_gS_toJ zCTNif1Uf5n0HmRVEUG!+WpRK|vJ^RhhLj;osr#emfD@AelX#^-V>Js9N^MG@%6Svb z0w*Q}6j^|qJIwO^7UIawvcStATr3JKDNqgguH#@=C1wOEz_PM_aq{bHo0e~vq->;6 zE;4Nfi8w~S2j2xQ22y%02b*82JJ@GzQh1)ygXS*e<*By#dn5{H<>edrKbapTKNFgr zaO)3~c$C$f6uuPUV-+ruWU~0Sth@Xv=})i6$>26NB2gbS|C%VPD85o+oWe}RJQ{PE zn1oU6C}okBsHH|nxt&%|!uiOvfJ$X9f{-UwT$MNGHy1c3k;lr1@y%2m&6JU{=d0H~ z(ui0 zL!>h0yH4M>Nm-*v;>zYdDkrWP{wjB7E|E=>+TfONaQugkximP`Zj@q{blf+(bHr8Z z?#OqHGIdQ5{Pso`I5K4M^Mq?jq5zEw#sFvqfNg6yrFDq5B)1Iz&1*Yoxvk~4mfLk$ zadUP+xAy$JFkx;vWX!^U$hHqRxQl56_tK_bp zM%R=4=X~;l=HgoqDPJT4@O&-ix4v4?iLR@(ovuO4ntm>!*SqvC&Oco#iJ2e-0k%|} z-WpOmCVQ5JS2+GaC2BLvW}%FzYHFVtwYB$R@1bh%#UWOyyCXj(@5OJK*hVU18==5w zwyk3K<$B-zQb9uJ{Z8{ww3}gt92;7@c24h`O=Ynjt^kOd>b+^&n~f6D7W2nm+3Fk& zl1V@P7LT8LB7*ToMMXf=Hkm9+U7y#=pVTx+Fd*6>SuE8$B<&PqKGSFv7!I4v@&cs7 za69Uxhv7nVF-^^dr@@6A{WZxj7NYR)YCbr}4ZKizR$i~URJ}CB^g|Pd`z85nqj7iU zex59M7e`q;>}iw?=gn_M=`^{SpQ0V7Gyd^7y%t?qXE!(Tcygw|{wB740GLu)5$!E# znDA>e9N08VmwZ#f45HWSLz;}Hvh_}OI$9d$BXJ>Tm1sPUp3V%l5EKF=Y^1!dgvyhYmc>zSOZ3PXugLRDJ)hc7vY^uVJRsd|rq5Bmd)zYR> zX(NHAp0q(ML}4TO3|iV~X|uDm!32cVS4z3i(&k9gCgv+wQ`%Ur@6!ONsIKchn_94F z!BXN#3b z&sHvTrWFOcxM4C#$1>YT$=}Z`f=s4sp0rKW!(#{wF!RZNO};gTw1FX_wRb)2z|ePz z*O^189in{OM1G0b@i0yYGg0zpsUI>Gua*XnPJ;&pj_+EQHb6QL9;LSO^*{vr z4#AGAVt}mQwi+)qUd(whRkAcp(+P>I;-}PL(QUAR4)!4yWN4X^9g-iN*{&PGV&?xJik z27d2?y;oAnLkkh8RF^@E-A;>LD`bvshf0|WTI_Z~?1~eCy~)#L8sBFkSVaOO5;_ce zS_kf^1P+A={yq2*T%2=J{EjKrLb9+M+AL6Mez875R7>G5NMX|q1EACdq~Rh42-5j< zb*k@m{&$J>o^P7dz>TQikNKzl=pnk6ee_)-MqiX~8m5nG{vHB7 zFN8|z@7mfp>Kk$F7^lahF|HoUY+B%W8ZR#Cf-Si!9J9t|5o{a3xckD2_7Uc~TNQ^8PGNx3JQw!f?2wzD`rIaG< zxeOpdAT4%JTI@pPd6utMol1+{V~X9pp;1-|pm!UqHKE2%=mH@70M_5BwOR>wh`z@l zWr;Qg9(%rDIwMj<_$F&u|D4&BD&6^*APjZ5=6EDLR6Y!_2_d0UCn${y9YBSw$?!xO z7EH=#OG(lLJ(L~ zpEW$506J9V8XFN3LR}G|^GZ(i@GwmJQFg}A`0>lfbKm&s?}fTGY9*&!ADF=rny_^V zdMt)L0No=TztqrkK1!xZiBk)s6JxV|>>{`Qv8}CQaNH#uCVyo}oyi76NCiU*h%@7g zUzWcb$;9ZVqY>9bW6p5N^o|2N7aBKFKUR72qX(y_W8g|W8eMAuB8 zHRA_r`l8894?`u-tQNS9Q;lo<)7gY85u2DV_hU3N;%gOMXtOwGJ_VkCtl{N)8eho% z*IZj0$G6FZi`;Ia$dBTrZQgBYGK{{JQDKGjE<}Ne+aBc2aNylVVj%f%S5FU|ZLj&d zj^=lEWT?Vu9$#@Owni%c85lo^+ZXpKkG`J{Q*n!YEs+1MdG&9Sj5S}=g+=P_$k%XU zJQ`H;;gOHf7mURbHx=Kgz%gdjqS6MhAKAzyaQ=yeFlC zRo|_Opk}VgY^GEeW4AEnL3()+yiiXxkDe-zZi*oMT`8RD5Qc!gUu}<*_bHpu30;hi z&>Z_{8>$=p`}0qRcNh1+8kt)|HH1m1#}z76L$qpo?M1KT#`rP!K>ep*jn9AfEp1SlzmL;@&wjgGMPKKaSJBa8mb@eYw65oui<{UnTc1~a?!}me=RypN zh41hGMOo5+*(i3>-| zzlAc)m2m}%1)u9e8loHB`lk%=+jLFIrMSoC*Hl~qYdQC?8(gu{&j@r`Gr?nuzfe+) z$A6nCqWWIEDiYzr??QG&Ve*fwa1{Xz2WA@{wm@|+jg-Z<8{>sx>_(+nEAghM*MYwj zUzoVlv2MxOq4HM@_9QCQh!_>vahZ!$1}Vkd5j(ZOt}}1veASYV%D+Ky$H_8`LLqhm z%%Xa_smIr2)aWP&ov5yATUY?uN?l|Eva9n9@73fa{E%Kvd5-Jb(eYVziAM^sX(NXB zmdNfv)9nH3r>b=vT=3zQaA!YTKf4qD**ZO)oE-gRDQ^T)A;|~TpqOA#U%-xXr$u0Q zA6I*5`jtm)$0%4ne>whGTfWoh1+BXQh0%&aUPwACfbGzFJ`VRZ_1tahKKp^4kE@3Y*~zU9v^-lEZo5h~uGuC=1 z>nwIr0+!$nsU=e#JDextvIYkJl!$|@DE!s zT{oA_{lmYx-M9$l$e=djE-^(R+M$^|x~tomaFN&E20Ato(@GQIpsI&qYCqDmw@7#N z8i(HkRJB(b|A-+YVs4)hbBi#VU#v$L8h`18AC^AgK7THS`Y=ICR0bh)a1*(+VHiGQy7$RU1%)rH?s;P^?Dcy5liBNjV zeNz5xl|Ng1oKyI7xa}T(Fz?l6VYlWwM@fQLNzSuY3iOKl?QmDy_Y$x}1gxDZk|=GS z3E#J-{`TwSfj8__R1# z&p5aAGs)qf6erW{3`J~pP@Md@D0xHZmDLw#yjJ#Bg5+|Z(5Y(~I%sxUb!e!B{JGO- zR2shY_+t=7>nh{SCrvS`Tr9&hb(x#O2aqiUF=816lA75OS%;W|B#voVJULp=*r^4! z&uCnevSXbhW-(ClN^!zK9vvmK2RcPZIp``9xl^kJpKl8yw<51BJ3ipD<4PSi4wUa$ zlsQhMx{tNqV zv$Kq*tg>D{_n!oVfLXUx7Mcv(GxO}26w_l$5Qjrt#Nwqcp1wo|lw``rB?HEF-9_be z{dKRy|H#ggX3O4WS160+K~gRVso2(O59}yaQX{GI{{Y0&RL#?<30Xz_tcX>PjTjj^ zadc@ojX{$B?TzwFpj?E@X<1^SPRi!0Ireh#rlYnt$}t`#nVi@=MoPH2zE`e-Wgu3h z&SS9V*Vp2mve-o)?cAfx>}lN?1ns~6^cf)WL{Vw%9zk*e(pu{Kjz1e~DJ{WRy#p?E zWr(|~4hiZ?6KOAuxncJz?-c~;hEci0k?p)V+qWDWx`s6iX^^qulFj$R=4mKA2|r|O zWI%h>43~ROvV*BrP+ZcrBa7$X0AlHkW5?8YtC}mmlc!4p<3`SeBiD94NY2D?brY9z zLh+r(EpB~VJ=Iic+!!Z(?~=wiBV&Wo&xp|pPaEShY9!BU+XI&lf?5Ha;Igj4vbT~h zy$bn(P2?G%JyeYtBzq~xAj3DoeN7~4z6Y*+v{wF*vQD$Es3gUoEGDUEybAdJ<xq^ST`gitzD~Np6=nIOdbJCx z9{OC%+S;CX2pGDu5UP1LO^S?PHm#=t{SYm7!T?`Iq4-N96(t5?t0b8mF@7=CzjAua zO^l!ZKly*mZ|-h#{jBR~QWi&o)@(9eb8HPk+AvD9OVu-c_G%HAAC^$n!8U=6v^>;& z3Vb#ZrUV4;kg4tD4Sj${~5Nv9;Kbsy(P(RVHK>(a_*-A2hTlJl2jD%2i+P zxY7bsT_+SG*s7C@@!k~Fnh)~y{CL|XriPPz=%ddq^N3#@u-aHyZ zpg|a>rW?eD#3v*jJEr54o||;ZZ$`&VcFq?UA!ybEayimL{@Sx3WH}ayNiFPQNT@}! zbBF9)J00$05T(Tn#af1w2SN3aYk#M}<}zW;QB9`47-ku~2 zM=)Hb>OIY3tTR8JQ#~oPvRw8R86L%@^e+iV8f3N%yMFGDDO1OBKCv|?8 zYJ#LSXQ|wX!B)!ZHki&Y?v>g6;v!fsB2o2r;}-`*loOgF>LJ2<7+G25ix(VtJ(&5o z&CifjSc-O)T(}=P1()ciV{ldQm{gvlV8>i+C{~hNRB86hX<8JdLoq_!vLIulo`(+A zJyXYy3YqaB8GHx^kM-)&BK~JoD7D(yE^rav#qw-jleG$!3%}B<5r?!~l~Zf*Lw@*s z6;NZuy$bcH7_|(Qa(QRQa%W`8P$R&i`Sm=ypInEF%gOCs^y<|&{>l0pyPD8pdU)Pu5s6bj1`gb>^?+uoy3(c z=O$ncUCuTo8VqsUvE5<%hz~YpKIWTMccu2!VLZkOY1_9n*CA3`gVGw5*5IL7gOm9T z6n$mH_P9kMBYK~45Ce0rX%_2Ys-v`&+gZy02JkL5EX_s~QkwHT+D(we!JbSc~1G2V9a}8#A2ox%; zIpg|8sj{TXs@-AnCfi>D3I`G!+Xi(ZVckRZ5uG4ymVt#7sAqE%gKCt<<|Q(fYS=V( z9KhT>8dBVAic|1wPuGUPd6FhAwikDcX|lFF{(Ca$cP91)r_rQjzhL6{6iEI9tOF|^ z-Rmyy$H$Dq9D2lIryIE8r|jpcU2WOU@nZ%2iy)Y-M9ao9h++xo;`+_|a>7F~M?i zwNkP@A(5I7n=%0UKn1@xAJdEcIvf>LMWF_&sfb^xp0=1v5N&}C)uhyeG=c8<{`Vs+ zi#6SiB}3&G*A?T|IFYp{7O;Iv02OVYk46Q6%&MsoRi}`ax&4#5XJQXTWI|j|Z$;#C z!Sf|GAlpFy1QF}Kfc|V2e?P=dz+5#Q8@DJ&NYR0qN{AR;J}%)=eprOCVcHZD3^>&2 zi2H|sbGwnvC`Sf$9IlxZ5-J*9WQ3TiK{d!+8#QP%!| zvh^}c-!GQN3;98HnY~pYIZ%K)Brajd(jn`Vc2|eeVtk|^WDvBDbH3pilyUg|=-j}3d$26=GkTDT+J_TqsrS5r_Wp9K{wt;{3EBG-n>q- zt`8e{pEf@Dx3||~4Bb3jfMgvvF~=rIV?B>$f>d*D&UafCC0V<*1bf^YSSSxFuxjog z;utbou?18at>^+v>RH5mw$RKZ2xIFe??jTP`3f3On57 zc8hwAZ5E2$DzQ~!*Qd1V@I8_<2u<_2Mj}%>?*MGah)$6Q+E6PEh3?rdl_GM0vNR7y zf+cAl^myfCQ(+NCnVs(`Eq>fzE=MH6ClA6QMqUFVFC`Is=;(hUHq@4Y|zB|8aZ;k z``GM=4^sUMCRnBVfne#`sGkFI8MbI7{`tr~7CPyl-`vx)*O0nBs(Oy;GMS?v2Z7@s zNI@Uwwq_cjWuq3w2#H^m1nP)Dg>3VWD?i;Oy&k>0QkKDg-vz70tv^a$2=ET)9v>X16~MiM;x<>Z0ci4G*BG-m48N%tJahlF&Dov8WUplSJX zW#`)Ke5o^Zk0?PNVi;uh?CuN8n(WTDXJGV%k)m~?%lYJl;MMv24 zbkl=e6sfg3mWHrpu$pZE2_!R7Lr@K9XXq|>Z3DO1ejo|gpYf6F=+140R4$g`P4ePz z;d%PXDp?SAw*+SgsIA+&%OF+H(yr`-w0+x@*tWP$bZJvcoAO23l(9$@cj{f~tC__K z^T|w&P*{NEIVi*mv%q#t*VSGO7Dg@+Qj;LJE4nb}WxiUsuL6w#9h-!;+KVEo+YfcK zQB<)-&~PRdL5A4<3Yy0BEatQjHFjAn6I4|tLjm0jpUUk1u7Tr}`GuuCiuk8Vg7 zDQ^0qV>)9aaX!9r?w+24HYFm{)eYuCQmoY^E50L1wqevlf^D(t_LzmOuIXSRX6iM& zCO!rJY`(ZmmQ3UjH!zOQ1*M{-Na)tT4AyTZZ-wFa8-MxzH1d~G{9AM34>Qqm`}c)^ z6MtZG7EUQPFhSXb^{US#VUNepV4@iq6C?e|2CHO3NW63HucEKPViqie*+~qXy$t;C z$!F@<(HDrG{Skeb`O%XFU6qfHK9uaUUmwn0%yQ@`oIC%Pxu)z>o};R2*d8b!x#Qjc zZMvp>8aPN%zQJFDXFA)ZnX{`iGwccG%aNj6rj9U$6l(2=ltbJvh*Zlk5oj4}$LgX0 zY_IzTmthnNSp;Af?6?{+R7kThgoD8u1(t2{WyI$az0zLxZi|YnY!1 z=D~rgwEXH<(WHR$SkPN;Pk=+guho_74KVfR=+l0KoM5;8G}yw9>LG}Q*XYWC;`N19 zG>B|f4g0%gdnWS8!uJ4(_Z7y6+?Wxqs@V?i=o)CYgDe|qy2RG_PcXX18i){ht9;|1 ztgjEN8P4x6E`nwJYF;>m7Z{jbXTJvNP17CMvpfwl#cqF2eL3jN!^m|k0pxRyM>s(`onRg=0JKy!OGkzU z314MyVF^Nt?+istjWf&ONhG9w7O+L+t+;L(r{LF~t_^|nBs~-9{foQBR1j4Rq?r2G z*TGEraTTN?%fz!x+~hieS!0V-Ji2QWx8gCQFiRhCxT|T}@|68N6=(uB;l~R2#qj=B zHoZSqS-%IH(R5{=a_e;%^RKeHkCoAl5<3IRDuzaxN6NV`)_REj7 zL{m}SC5hjLtO=)Yo~|NJTA(Q8YjR(Z*yjm@6!)PeOR^F<|F92j^D(_h+{00tRTOHV zOpExH3U!Nt2T^nyF*L&jEZJ-ro`%P+>4)SYSr z{S$noh*135EdF|k^{iXm(hU_-#j$?|>YI%=!>YqDR83@gCx%M=^UWjV35s1}bjryo z{~P!<50p{P?-N0p&Kg+z0l8(k0M;jjSq1?u8hrk&U&t6pz8@Xmi&EA{wr|Mj zRL43aEsA_o$PrTsu}5b)lpmIA*yC&ng|@dV>;B>2+-^8b<;aNbQJb5f)h45hjp?e1 z5EE>-4fL^1If=<--eR;^5d*{0bHP=X6ut9)jtJ01~l2rL5xx)XeKE+C?%7)X7$f1@}s*I zXxRqmHbH6+gRjO(!U{&_hRvASBGDolc^5{m9pknhFI^ZU87L=@1BcfIoZ!8amTQ@6 zn+hXI=6=YWn;xA`n$CFuZ(b+0(8*M3rF7gzE_Do%b(&^zq~RCi*;PnDlC~dwTNa_|$!^{P`z9f>X>W z3<{CHqES(%PQ`8B4xu0sl+sy4e7$(aSP5%6AmrJweP?sfBk_XNQzJP?_!;S^}-iaE(SK z{Oyf$v`UMFlgZ)Xaw$fK%i!P6pvvH17g!oEOh7aNV~lcA)U+0 zBzAi`$e?_Zfy~}?1*#O_Qh?_G-LJPXna`B>?*YOZ5#$oCj@AFx@dK|*|6BUsJKPAV z=(++`Dmtm?a)9plT0@Eqm`P(jf@QJvz{IKdQXmD>#MMngoR<^zL=HushvmrOQL$;ZCPJJODMMdd*fSDx zw}?;0AcOhh5^9QLi^eCgA*Q%=x&~#%(*326p|?%N2?{A-Y?!X|az?8Vn;}!@a?9lC zxI0#+zldoo)BEXlp!mx`sLc5ST0W^;s_wuF-=#s+7Y#)TaEM04tbt=9 z*4_n^8-IE=Uj)iu{F_@)*1#kdw?|uhMYIRRN&i^-$L&EXE?AzNo>^z^1}3ayL0O`+30PL5 zlStpm#kGIAnyiB8Rbr917`nuei8~Kx}=rJ%b3l|W-pJ*IWgdrNWA+r~`Li+t%U9LKPc>q5Dk9l;JjvN$K zHQ+8=i)yjY4@o>pG1DjiwN5&LW*CWrd1?-$QZqiukzRJ+?#wN9!YPOFAt61qgwv7p z*##NyuH#zFeK`##%(W?VcWgG6I$$>)@NWQT5=0HoK}!Um%ZO`JH$6)>D2$F>MG1GH zT=~C?5!bi#V3wGk4-E-PXZSniYY^Qn7s{9a2{lJmLN(1WT#qtHQIMqCAXEcxZK57C zuto)fCE-RvIM8N#6RRe%Aeu~4Z7}tgn>n~%+b*WkT`a?!!VX-7j?Wn5Zmp*vcpcBIjV;K=_c5j=%0FmWbj? zy(@h+8({@%&BeN{shYSbFC!L(ERX7(LKK2D0ERRGzMMz?e38y46!Qoll)ry_do7ak z^Kc;yfkcx`{p;&w=ma$K|#E%QBrRAHF|E`zEXo~nU%BWzrT!huU} zNJ-V;!QPOOUWRdSex;u>s>`sRsVofp$ujU~x#0|uh85K`xTk|A95%{7O0BU2t*`x6 z^ffjmgJm!~@h<~AChYg*GcwY%(-qnnN?shg3{op9a~->f9EVgZFI}zd`A%$3OnsXU z>nY4vg;lf=$3~v@V$$vvl7doG#fHp=jzbna218FpMP6YK3 zcRfpX@LbFnX>-bzznEPY3v_cw5eJcp|1F@7BJrQnQKtQeW5vTx`!k1|o`$8dKQMu+ z4VBYBT|~V&tRe% z7!x!7$OfypCbM{owwl~q?dtLZ(X&6I4@rtvOg`RtdkwN1n_iEHESx6)R&vmCPEt+B zAe=#r>qeZEy_Dmm-==GNF6Wn5QHID3t`VGIE$9Aq#T5mAMxg7H7$YUHx7uuAS>7aZ z55P?$re|=I?k4d-_$l5rFASq#nIfqqZ#TUT{H6H9!k(m>UoqI$5iqs5uTcF4RUPg@ zhN$g`omyaHu228utCnn2{tbdVPL^R53Sk3a78Pk0!!9KF%c-hb;?_MuNAaER(NPY% zrpF$(halyD*wSh8i``IiXGM`7vxSDw!8SNhmG;}WqvNyv4X4gCPQ973byRi6$9o2| zQsGvq)^YL3hqAsr<&8K9sNlr1iOV>|HLI4-#rvMz>g=W5tuE0~shU?Z1!QIxN{s>< zQWETopw(3!8w3~!`_*-YrWfh0^6m4N%cx+N*c$%{8b%36 zD?A{(rl_gvgmMuMYN(+KXI#H{Fve(wzSdQ4xd<{T@KF6JCs<9F^IM?%t#aMv4roE~ z*gPs-H@J$8-SWyUA_sOjyBDJEXfX>G5l+FcJzX0D=Sg}d26SKCEv88=N&I)wIvUKB zA6G$IYLXbOi3wgH`46zrt$1|Ty1e2sqtL&PINVhSw>)J(PX+3_Cj3|d{~`!PAW$rh zn+Ef&it}kqP|TlC>N~@ji>;F*kU8ReQyH%aN$W4BZ5`?7sxYf4 z)MTzj0l!k2ZZYm4icZ%kvrT7!G|)Kie`+-}mHe-@@$>x$d#isx=e!aKP7Zh!i#1S zgz{JcP`gFoDg_xal@Jjs8JAcoKP+a{a*_F*qjc^c{>|-%EG$QcsvF#J=pJew_S7$; zF+?4YweVZFMXbk(g$hO*zOr=;l@To~A}X1o|00Pxh;#E`X1A+s`x&%8VjF{xgK28& zCq_2m;UzzPtUPsa^$!PB_aN&X`o+?CAwPurhF$9#R27?ufp-2hv=Uo64ntUp;~)-X z*9g&w?hy}C>@11Xu{wQRAG#h~ko$S_XputN(KO3&i3i$#Ch0hkS(MT-=R|;Zg{UU? zOs_=%S`uF0Sv>Rny^Oo$~{)@pF)8a8T*j*!o5fpL~6<@Y7vtG2W(qu*HQ1 z^%NH7Ra2dU0+DmDZXkR}NLv;?_38!{cO$GL`QZw5OPH%V#0HJSlxl4# zwZ_BS`*dYpaN{p#!K|>qb`^jXoa(8D;ef_NN(I+7A?w6fpUM6$vZ4yCJqxjDem#%w zCpW=OxV)cC;~VfQXCb`JZUAI%0A*M+b$u9jwYlQgG)*dgsrWm%Gu0;Z#Ieq-$!GWY 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⪻9v984&B_V9 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%0AD3%r?(|nXQ3;hkiqT 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 !AISBmXFA|dBnnve7E>35!9?W-mbGAfL7a0HsH zG)rtC=WCmH^4WBSn%LK84a3TI($xZaO!@O5nxYYkg`g4HT5-RFp53jNEAZbFD(0^K z44pM+2ZzVK?W^_gIche^C$zSa;(3r!JRPze*2p{8?zsh^t<(b0l=Sq9ja&~atE~?v z%0E)vQVZ^9=FGHkP9{>+9{LymI$y}WX%Da6CDDl7AkrN9&LEa4(?@rsejk`Jm}C2p zoL{gp?pBp0TUfcN^U2tleDQ*@j-N8^nS>z%6p0(pFZl^7*@w;Xj#LdmDB6?8=}tVBW}fWL-!lwnrd6$F(eDthXWV2I!$<@;&HJ z2YZmc+qKM>N#?9AG0S)k$etIr4TH}5lHRtu)Lf2dd$k|hN!-hDK0Wk|&!a0RmaKKk zu2qnFq9&`LLzuq0u~a#+0)ur`ye2gKkzV=kPo{r>2-as?73O^_`*3#cmTc6OQzt{3 zUAGcUe~Rq05yVV5)iOoL%hv3%N~%@I&YV@rO%%fYVLP-oi0>K1b9nk&E$UY?*&RDK z*ouj~j}LB(DV2R%Avy~$-+xw?y+VTYx(`o-uvuRp8f27EI+Qs#Kbi5 zN*bIVN9?>lt$f6Kux4Dpv)p2J^!?cXq0RvH&$Ln#zux7>7CXQDM)$V6nhT-OY{Hhf z5D)ZZeA&=RrLZh25t}Z@P@J}sti@Pw*}iJ&t=+`lTh9hn8C5kUibM|N$5WZfFL0JnWJbot@f*tZ`AbOUJIZi#3T$0t#74l#)vaKt;?AS3 zUH$Q?{W+HxeJQo2tjS$`I(k(vR8pG>Ri^cBBn=H@ejjLb`A#qDW?E;^SmG&+!6^1B z94acgAz2?zT{}|ohW7QFBE#YFHqdi*hvcxdw+df&Lpd0EJOp(IyH=Y;@|vaVSnC}Z zFj^XZVP^~Dnss8(bUMm?R$zGuZLdwPTSYJM9 z>HW!I3F1r9?3s3hI@1Df#fJ^7Jj}h73FFNFzQ4lEk`({Jw@=V7i!@z1_tA ziNfDb4yN~dkvCHkcwH6beMusP#S%o5(m6Yy_8CZZGu(A^^s`fK-=QhSizTk{WPqqS zbL%IV=$@$JsT_&ilJ(T1?-27WjTwnrNi-yiH$7O_CSVk49bu{P`IN(mi{i1sv!1d| z@4{*8sw+klj${Je7@DLOV4^O6QXr$BxAd)Zk#ut&vi3I_#}W_3H2NlfD6RpR?9GQ^ z_rtM$=!Sx~q;hFT5een8{(x%PTLsC=w480%ey_Lw+xcS@ijuTnA11DLHuAYpN#DfQ z>l!T$=ox#z^Q?Kyqcty7m0tf#4XJocU{-IpUln*tuJ-Bgec_^W0 z7i;mhYgUzJ<$>3G{Ow!gUrN}^%#A#jGgntqN7nT$Uls=IYD7p>^T&DEYq184TIKNa zxw1T+lAj_D7EN}E1`34r9_!4E67}ACSG_-{*q7Y+fy^p{#TJ?G{mT`{mV24zi|(lV zj)z9F9|8~2E#BR@nG?$HUC7mx*;hIqeJhec!ji25dvmB|ffm|-zhZl!;4=`>*k3q3 zUN#Z?>SIpXjoMD*_fmgRN-qvuZEw!n4D^Hb)D@#^R-c=V= zZksct&=Vm`lciVMT5c46USqG=Wo7u_RRsDSoBFntr|R22y{ztqrFQQVmv`vek@0F@ zDou~Fm;H@VHd5#qRi08?NzMFOOwZ+EOxrfb-OUfyr3+aaiaVY;C zEH;WFVQ-58AtT<&eXSV*=80i2^nXM=b=W(>orc>?7mQ}|I2gGP9m~R)_;{H+Y%2`SdiHwTu4!y*bP;H^4Zb@ITDd5+4qaIWM_U4@G~Ahg7*b zs75=o^V(6Q=o*z|XLBBCFb6rd`0m%Ayqi&r`UZjPtq*XSSZI(yph_TC`%kikrIVwZ zmZ_ON_$=9CI2ock!A10LjqwLn-lQ}^LSb$m4Ul#z^yyYD;40iNSvG2_W)ZXkk2;TxyzWYG&VT`MoFQLi2M zA+2qQ)w?OHm>3{l{d&+mWiv?`uP%s8r9jPcI*SX@Vp7**I_5*rSHQdPOF&t_qUcL_7K@w$Mj&4YIQ zy8miPHAwMoe4%3{F_Y33kd8tTIoW~vF_;zsOMJ#>@j$#D(L}QA4RxD$-&#{vR*Gts zmW-Vip$2i*eYq`Js-yr@V{D=t<)Vj22udZcWg$e(q5i4Y4|A%08no#jo9~GQ=(|42 zlGn^9l#D@+7GBRZM#(|>n#_}zVaMa~Cgo#O7)?FxDf-=@_0SoP6$Z06P)N9;RPQ2- zLRMH5&0vf-DLZXfdRR(~R6(_9TwIER;xo&aA#&8{loTv!a@Mke;ZfZjl!OO&Ury-- z$yc;}Wqp*liemdo!KRr_^;%ZaS8S?8Ny(Pelb!Ylt)8CPRU+RgJ~&$L&^Zt*f#kbF z12C3K2{kC2>GK)|?%OdvN#Kx?swvRFBmN#|K=|3nkK*nY5LT`*?+Dn{ayWx!Cxmj2 zM6GR0AmI5&Eu{TEng9;GI0&kLoj>|+rRK+Xv18W>8dJ3>-CV2as!Z-}M%VA(7iix*K%$U#*bG9VYIAnJ5+~MK-TGGjfRogH$<^6=gRLwld^%|Nrc6bLKm!<9BzPk_ND|-~w zNBs(@!94ltAAp57m?ggx(%GTpT(K$_rf&kn9NW#3osC?cy}Fa z@h8X+lLudQDWnsj1|>%CWO{G!i+umyWD_sAE+r~THx?9!{K8Ff!b|Q($;5UBJwmW{ zo->K11*X5@X1-*BJtkC)?X_K7en_lL0(P*yl|}F%~>A6|+&{oAesn)|m6F-#`!2AA8TV#8Jk%c)Ju;?mp;o{4W0 zn2Oo6jol}YpQ>5%W&!J3&KXcqya9BzWeP>&wU!l-TDSBN=X0TF;u^;ph8wq;D3ZRm)2)Oy`1oeaA8>Dg>!cNhHxq+`-%! zEu3HX&G5gG4S)sYkrTWVv8#zppKv!9ZD|V_oS+BJ$1AtrjvP0D*nh!2#H9_jtso;d z>t_1OtcsVPk^h=-v3$!C>aqA9E1ouI zqK(c6y&z-lkR<X3DJHOqQg(_wG&RQR3FO<#s*H&PFO5BCy`Cs zUgbDIKiq@%o@#avEO!YWW5Am}Zj!=YZvo$60w&iDVESC7M4PyP?OiVtqdCt0{O>mK zd;5#(tBL?}n^9+uXQtb%oiZlXKj0TBF}sN>;xt~DS5P+Z(Y3XKyqPa#bz5W#KRwiF zY7Uk);;t^T&MCH4C!@laP(}4UJhnxO=zQ14K{U`xMP4_xAn)DLsfe+$LRgw7Bfu5; z%&Bbb6B(OMB3E+HU142j^DMWQtM-h;CJ&oG@3$aKB^2WX`umVwS3ee?6%~li_o%1y zz##iB8|+R>a;>QH$bu!hxHHfCQ()i4Wl7|n zX6oP_g*PtL4bFY<7t|h=X?NBVPYT7}NlR*~t`m{&uB75DPW3kkV7%A(j6^GwE(sj4 z0R;Esl_C-QBJunx4GYD@i$j7Ovo}`mpYq$3=tlNU-ijffqZ671YpeIX|GE3gESbf? zj8E4&kByx`!-WTzbcRgpb)m3e2QF=t7e0hF^Ev_B(-X>kyC(Aml~PWt(QF z{{ss{yaGjJ@lm^V8>c^3{J`4>QO4MNX+w4Wb}ag_Xx{EKA6b5oZ6Fum7+EP{J24wJ zpp~E`eg4tzRge(*L05&q6ti1=F;fVKqKB5=mlX8Y1Sxd#fXc-~(L*9OMr}G*P>_7x z^7OV07eiiq|4@H>!)VF4h&y9c!WUZcJ0i@B=%Y^R9NKys7~1lI$o!D>2^I|-G3BEkp%nRhG1oh&1DXqMEN^Q$)JoNLrqA8_>$+}&AccV3!jDTkW+Qao|+1g{==oE7M z$Qre}b2)i561%Ryvj6?X*&YFr9f;!myRzf{9U!L(nBDVOXGbL1W*`kbH-jDBOtd^7 zfL)Ewrn|b*Rf1G)Uz8I7Hw%yeB@UOGFfs`Ad!zWZKfnUZLzbR*%pih_m05q?TXTW5 z299gLN+f_sjfW0E_wf3aW9U_V1N13G3<6PJV1eHZ77>5Le|LVF1g;=^Zj`AC08<#S z3F2L4S~+L32Af)dU3lQh(U|qMR30vRaP=H*>Ez@FcKKBx18W2}KBS$yaMAJ`SI}_# zZcg6zf{$+Ax*|C*JjvBqaMa;T&bxaB4Oem~kqJ8!uwgU-gMQU&&-boK4!3VF9Ru-5 zK<}FX`YcFu(M}F8p&bFk{YtWfwVUR0*7Gm`PY&SEEOL!CHp_ZGwMh60&eE{FR)X34# z(GFk>8&ggj2U9CBH^&1?O&yfJr zClZ!IUinW7eh%=T6ndFqFKp}q`PC6X0+-YPfsD=1DZoI9lZD0EMO2(BHs&r)u1=P2 zCV$zu+Bli;aB=an^KfxV9C8Y_FPX-8XU!^FBMgBe3m+UPFPtzgl7@h8CfQmD)nkICeQ~Yl<<+;%GVj-pg z)qhMR1%cfD7?28klt082f+yy8)e%!HGeArZ1mL`Wxw!|OQ(O|$$sVj`W(ziVTiuMiSs}D2QNIU z-9Q%02r32oKt}}eU)C7ZzV(0T16~*Vv5K3~d;quc*;CNvmX?k7hZ_G_l?R?xxu&F< z5m7;)XB4o$Xg++-E$* z?p}aFpjHQTcxAk>1Xqh&FB42T!V+ARy27%+)_7jl-=U;Ut6ktY!KrFzICUBmp J%{i-?{uc{-(Zv7& literal 0 HcmV?d00001 diff --git a/docs/USBX_Device_Stack_User_Guide_Supplemental.pdf b/docs/USBX_Device_Stack_User_Guide_Supplemental.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5e1318bf3c400641f2bf71c78c76eaba82bc0650 GIT binary patch literal 678046 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 iWb~Hu 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 tl7GD+P)IwEJB+y)= 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�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�l{;DBa}LGHtZm;o5lf4ndZB(zVD8Q-{ERRVTWYt}5+llq8jbvF znp9@sTH3OC0Od4FxyQG-n#4*Fe=oO#qeU7rKFRVJ%h7j*8qB}Q;TUZDz_Ya-@x90O zkq}_27OySzYB*-qHf8S&V;HWEG1?L3a(WxWf)!Sfl0)#C;rYPY70`ZM6@iFLZ1B3A zg89yV*~Vo5Wbb>9tr7;VvmyJk^mVd43!LP0DG!L05`1z_(u>NRb-3BSgUGIPnd9<@ zh8wPTx(0b~lD%^URta<|ekFr$pFnEfN!aswzzw92i;;4Bo352M z<{PE0yud#49wckW3Aztl*Es5NLO$OOJ4hv}iTzWY83M>4o)m>u+~GZPG}AzyRh?|T za^%M_m_+eI(ck0AnegA#8?wYnyzFVhTZnA$X_61JBGpln{5lKfzusiQob_mk7>V#2 zbbpP586D^DHW{k`>ZS%vEuTp9Gkj^4XRx6mWT^VKXLtdm)iWA`rUtd*HH8dut8R&9 z_k=aW^10PT@KO)X>A7YqCS%t(OLo6>@T+7W>VX3*iXic%LU%P{soq+nz3MH_AI+SL z9244}^kapsGKI2W96a47tm)1Yy?qtM&cPT=Z}SivxlT}lH}BOuD)9bS?lzSGfdfxR zmZMdK>RVR45{|{6qcByXn3_+sTzUqhWRWR>l0Jpw=sp^k7F9j9SFxEBs+td6xOVd_ zx4AM)=4;PXUY)X57HdYD*t(+^t$uX5nAP-U+G`GO`l4CfN8xl*dj2-t9Q4K{8V6aJ z2Xn@Y*(i?<-2%)Wi|9LfhPTRO3#_MER#qH|cq z{V>k=;sFBzQ>(ME;_AH#c~Cr8j167_d9*#L{x6MM zUDa*k`^G^}E`_yv^OOzUbdlj5v>MoP43S0wT<3ItT5wOPJyo9O$#2o5^z{s8%wWd6 zF*D}FdxZq!#}!Xd3CdT_#tW2b^1wP_Dff$b%;hT+(sk9!YI8CJC3y-d_SlizBz$5C zEO9pd{$BC9$}NJ;0&dj=i%FC)11R{FG9-JJ_fyFp=OKH%WX^Zo)?tpP;(=@)2D2!B zSieztZ{P8$uv;H5yvl;8+@A$;@Q};>;d9qkL32wBRB5##pm0r>(VS(x-kbiKgjuEyIS3@ zQt3IJQa+gmEC^yHJ()~h9*b3WMM^Mdx*}W5s|k&C5o~9rZCZ zvp5E-T}D9Tm-A_qZx_h^#0CVmaoDnw91cso73a|G{(d^5Q^I;&O3bBvsS+4R|CGru z{%<)%A>!(mD6xGPi8bEoAV+@3C+7+(|n@+peL+;`X`*x=Bn>$XJytNZ&j>=UlQ z`Qlv3grB}z(o5E#H<@G~eC8ePK?_V6o-HXDgh1dAq);b?o9OpGA>dpAo+~LJ!!wCx z%05FscT)Juj!AmZ0=Ih3l@_pJB4|qQ-*WD>P(~$fZ4|&h^54%J;Z!}~0)~NAZHKe- z5EgDLC8!wd7I0-3`aKXOunk;!A>Xn{rH%IC8F4iAso5np8tuwf|7fz$hIs)Kr^z7j7C&tybhv>4`ND$<9`M3&^?T z$N4=l)Mx;;W61K6hk!GPvMsn^+mcE_y-KK+GWwo{jR&j+n%GA^%1)~U5vo(bE^qu% ztrA2$Y+-ajYI)PbwwqO=S~@UoQ}z-5E3-;g84m0;vIkN+Stm&Kh!4mCaWYsZ!pPBp z>zY`sfBb-)8gkr(hdlLh;dir7u&P7NG!A&Qa*o)zy7Vu8{BmvIUR+(aqlA6rPwccs zF!WvIsi$m<09~b9@PT`K&`VQimR)Sy%@#qv2W?e)yUluJN!~JQoJ_)e$sL$FNXJoB z0+N1zwBxk;U8=)V?#meFo^O*Zm>$N#0uXV0`8{L?K9ga>VB06A?#eE1GdNhbV5pk` zgH)YZDn6hi{|s@##hEhY@{HH93bJ4se7UOiwk4D4S#lKXw)q|>(0SX8-fHOcOJH*J z3GO{SdMnShK;S~nqA8S~Dw+gI&w~VUJ^H%brriQhq4Gr|MYdg+id%Jeq&|o@k!?4dZ5ag)(1GUpgD{{JR zXbhdw53~j~zSX%(ZK{i9ep?TmblR9;=(c* zja(NG#`rc|a6!h(-7M0O7fg$lVE(X}Q3_*MH;bjwi)lCw$Jt<|Z=KO#r4LqmU7KT@ zSoTSK%Qej;8adZw>Z8h0+(Vsp4F7?q>mzeeXWP&a)s$5aoOCkKRn;WObB1ualQhsB zWFSn27_08cxris>w;l-UW}mC3VF5>zT@v?o2{euh<}0UGQ4~Kkl&WP{2UC17#RpSd zJa7df1Rejd4YfL&;?PiilRq(NuWlmK=%Kl~nc^5DUDJ#Mnigk>hS5)UMs?O`k?lWh z1?b2^rau_sr)Y>{!vsLx)xK~*P6KkBD4PXOJuuYG5{HN?Ih{*DnHV}lfY*K9V`_E; zV@g0#nnmL#DYhYqEEN-5dIH@ULeeu7XOSwOlV?_}QUG+whbqRl>}b$)1RYjx!s&en z*g+jS$VReLmd+7&x^f-Pk}Ony4t@<6O|o+Xc*Jr&f{Q(ZG~jK|t6X3EnI}(r=>2;@ zA(PCTY6^e8IrHv*xIcI!so%4Gx%~k!Q|)@Jjtvq)-8@j zJ*3JL)3eI`JelzftijBi6?#7u^aKXsI$vvPXrQHtfEK^OlL!LR`?F<)aio zrbEvcaTd+;MvNoY`H0$)GoJaiU^V&qtA3axQrA&e{lTd9EZtO}&C%%VG;yXK> z7DsjB>yAV8gBDtPHJ&fCokm)b)6Izwfevk;4^I4TX!t4k#=Q_du+c4&20|Ai>0Pr1 z2fhpkJ}?}|_4VN_vO98Sb`R`y%sF)c(M&KnPnfWZW{X*|6Qc%Yx|s0>2P}SuHIXg$ z0kxeTZ%oUKlcILUuuJG^5f8hBVVAJRku6YpF5B-G{7AX)eleYv?Vu@UgX#MQxw$U+ z3`>I;OAo)@m*i_+gibWWw4LGX^#l3FXe8yGWED-B*~%i0=;(h8-#@&6r|3ZWyF&M0 z^u`Y&)S(don1Afdsl(MEG-L<;pCNTzCRzAbWjG&sp+*|C^q{5JSgQpp^DuYiQEg3i zHLD`G#&t|@Qw#df+}x&8n-!o7r}yP|5gDyJ(DQs#c4*iciZ?G8YiWKM)CBvLu{2)H z=X6_44W=5rmFigsYpN2Z6kl;TW>t0go`-2u3gd7dubo%DqqmgB(LFDms>!E20l=Lm>oY6vRA!d^7S8Xd$@Aw$JkH65^_09} z0?;FC-!Jp;lmA_$*)?lic@a23Q@$XRN(1;Vs}~U~D_JpOdLnlXJp~Uu zoM;=nA=gzsPd{jA*vl5Ut;zM58|N1)pzaxOQ(?!PI?^q{)lBwk-mPBq%566Bh3@CA zvaGAsh;8$)B=w`D%)c(L8b$UfDT>R z?vCxjmehbz@@~ZWu~a_Ws%XkSFo{Y93(K=yj5FoSFL$*LFn1ogaj!2v-(Fqc-P~S( zac>3CpzEGx>5gnu=jDt2r@-V@L)RekWcyFLO`b7rG*2^T%(XA)%f;obQv54gr2b8W zOAJ@jZO1yd71rCWiL4Z0)kmf+yRgSzR(kXC>bh8KwIz$lG7SrAw#jFIRtT+?cotcx zW|+{h`<(fGp0kUl23>=hgoj!EQgcj$_@Nx>e*S0wy2V;-oUQ0M8{pln+$Tm%#Pf;W z!*G6sFs|2K5dkl>5pki==57G*Xtv6w6{20+A~rT<=Thxk5SGzKLi7VM_6Rtx@@`tt zzqZ+qLdx33!1dvNSLP?lGOi{IMrrX`-X2fGV9v&UN>WyOs$_!JpO_ex;3$Wauv;)p z-^Lb^4T?@PD-bKZO=BqmgI{9%n+Lx{I~Mormxxk@GFQgi4y_&M&~I5lW%aVMP&iQn zA$J3`9HPo^VJ<;{AQxLcF^;?OY0?tA+zrA$2G!D`tsi+4+q6Ov9|N?2g^4DGaEd#h z*(F^wESxi=(0Up)AY0r@0h)MSyY;koK3!e@xULL}H|I0?u=sY@M|HeZC!D{qmn_9Q zi0ACX-D40>ruB;OuN+tTiq8p2LfZ<`;%J?tSOwBIbK z_{>RO<`ECjb>c`$TsjBf4MO3pg~DnlC?**YLZ+O<+B1_37nir>+P7WOBnmc|;ZFlM zoFg}Iinv>LArxT=-M0+~Vv%UM*R3ptH6-*c#{~M@(~yHuI7^`r(=qFK6N)8qENqyb zl!&V{lM5dCdNJt9*KIYji~tcfz}Utf1hP{y_i-vK6lY^nzU=X@{T^pDQwRTO`YB0%El1-5Kq(~Xby6ah$FwiM@tbZk zUCiRj{n~{%`RD{Ob1>xJzLwvXb664(%Uof9KG07(hLYEi&@jrETf3|S3$KPKpWIz+ zNkvo` zdlWe-)ykT96FnaO@HL=UM^utt2D3ixYcW#sNniv69l1t9q^|-aWl-}dBpKk67P_Ce z?je+@INu$ptuM9pJ0CxLGW$z^LxMlls3;5_TDmD)QS4Q2f8^+AadsPp6W$L@HHh%z zMT6e5a(DDU%6ri`V0E_`g$9!etG-kcEL&g@j|&23_3^mBZ!r&*`=p^5NS&r@LV$hb z6RLrz$!Ik!VncUVxaLufRt-bXA)f4r(!I)}!G;_J&{1qi9)r{_86=qpn&lD)=|`=t z(iJkS>%(9jh`f1V&UP$H>4vLA%AO+4Rta+8K`VW}l*0$5JMi^UlII?}2Iev-f5njqOed zbb+Nx@nrpFb9f9OMy4SzFKfN-foT9>Nip>shSzrItb%45$g`@FrydIbF$^YA z`~ZtNITQYydLmbx#LHYLo_e%B7b+iQCA&sR^6M;^|9X?LB7>At88jhJs*0(R_xC^C z-6mrdK;6`USxlt)8Q#pL@(eaKgbbBuFSR|xMg8g-4M9_bTJf4fUM>r5FEcLI&dJ(& z>7}CXBxKmiHM=_fI;UjRbPZW0B@mtjTGCsMNUFE=_FH9QTvL@FjBL*_q;PD7J)fgV z^6XN{Jm>j&Ira)_-6pqpB7DV=`Tt%t7y~yeYTHCP%Tl4N8_%+Z^6Hn*5j8;K^-`mP z{Pxmm@6|Nv!gR0GW|07a15ZcR)q-Y|Bt#V%GAHb1^jz z=vx^oT>d^Sr?nZdsQk@kS>RF_kH$46?M4)Y5I zyiRd6UQ8*%s^^KU2~rWFUS3?6Co&MYtqWPTo^sL2I>-WL9%hSq98Q!`9#a1!xvkWc zqqiwo(`@DS%Ecx9{mw}G4SfVW75GSYZ=SvDqvq9MA0ddmx2s)iGNE3qJD5HP-&sS@ zg0zCx`rU(49hB`o-QUKmBblqoSbpXD5IuQn)gEvmIlenR0`tB zUve!UQOct{5sc}>QA+I@-cgxS;cvm5ss7-eJ8xH>+ea$lM`6g@F>o2fIy@3|dK>c^CmyW{6;J1iR zWs(oEw%Q`ag}yc!M*(X^y#!Oft{cjE%W9iTBZ)3%k5ZS+@amUqCpJp5HH?TAmzVE@mUD6MM|0@4K1CJKn;V=JZXm>pI}d@1t-!N%>&}HBR_R zMzPb(iKX@YfbY!<%6(TH8|1F}AYQX+1Z|7zEAp*t8d7qeG!FFNfBz?ao3zR?Rxo}r zO}Rx`LEk%Nw8)Ok$%i560useea|^8$z*@mcr9YcQ;~@9EFhb^Ge4Fl%Q8-PwBdRg^ z?tN`pe`&N2RRmPe)X(nhrFk`U8VHhIMNi+7_mcU%AA{dQD%iePcS$Sp#x-k{y<YB}=vJINahbz_QB%!_To{f_CsU>W8ur5MECb0_q4CZX7GsS;Vfh;o`g?yMDF3v?VMmSiIU24 z?m774@plnc!?uN0;2gDlPK)}nfddWutjbX^%P)`2St{^y^HV>JVTLF(JH>ivJ6KaK z?*wrKseOQK9*`?@X7$+QQ!CS5f?{acQD~c2)vzP@o-EjEa`fFt-$L$u$#0%uHxElS z^ZC`TYQQMipmy0*o35&AU)FBY(=I7yq{$q7ZOvQq*v5NjW8a^mah*S^r(#-&32WL8bAvZ$TfY*K@_6p=uU1 zec8EzN5G%Y+!8&|$K=YnVo%o%S2cav0kcQI-c9s-pF~(L{?8SAD)J2A=&ykHz6R4p zAM|lGeYWT`J;yZhD`38sl))Mf*+>5KdE?5eQpv}L>f^(fsc15yT2V)LTDF6s?EQHU z)G=$nd??@j^+9I)C=8vT2WK`KHLdW}P0O`Se@BH-xx4&BKyLIYj32Vc9`I|jDq6uu z_Z+rCRN3`rPm~F%#wMOc>aii~9n7(fZIs3>**{Liw;Hm!3;w;Bhma3+C zw*0fyS0tUfDNDn(h->Hvt%!LLWw|mVS?pI2ICfH+hNeS)HR{!chh{yYj;jM;C@v#vxFN(<5b_dIZx z1>{V^Z)K5|U_KANvk}HgTxL2~65EP`HD&#lz0}R@wfH@!5cQbRt4}K015A!S!M-&< z+5a2mN!57QJYU^5B@;E#f?0q|;*hl{Eow`;tD2Z!$hr~Fht_8k9AaP{7@=lTmx7CS6Js7j&5s3v& z=*X$FesIRzriH0sBW377|A-9LukSiv8rk(b%2EZ6)8UYLmg__y3Wlq!H#ny5X^GhOM(t0ca> zTwXg$*+)LhoywY8PKN1`9fyU-RMtB7EJ&o=b|=cMdJjhJrjoG%Rivu2YY84-B`=R* zTvkM~2OK*oWo%e3048hcF(~CmaCQy#b3Ija-b&O$PZI4KvYRtbLR< zhqp3LCR7>IzoLht4E~oJ=Z6L~Y*^qrFmfJ;j!uR0U4LXAZ^8E*2_>S`s_*vYG5v-&aYH>^BJQ8=M!N>J9! zW0-v}fes{C+{Gh~JhOq6)|=KJfY$O|$v1C<(|$$Z&EM+D-4K>DBbK(xaM?>>OF zr_-VyDL3cy3M0h{TV94Hmp!b8Mj?Hg;8-XD_K^=fZv<6U&^y?J>aNh5qbBO;ngb2% zjbujPcC5C7vzryKVE|kmohNSzS@AcE(LoG(fGyY2{Wp?ha)1La?kJJj`0LzV%e=LG& zZblcqz_`Iu)`DDfr4)yQxv#S2?xsP%G%#liUcM}^w7L4t?(x_Tpk|r|)V?~fWu-X? zn=aasV0={nsG+KwZA$4iT%qL?mA6I@LEABK$h0ief(LD~ept)u^E}CtaWd@z%1)LQ z(xGNyPqwG|?OImA(=6n@5l(*rm3H)!TDA`G!M9Q;XjzpYZ?mjeQSld;5oHUm%{^YO z^V7UfOSMGsvPmnq`2M`?;9L}E2&S<-LBml53GB2SK!Vg2Y11{5X9^wpTJIg+OVmOY`Z(Qu}Ae(x`ywV z;Ej|NGJB}ku4$hx)Q};&y4KMl7cI3|4-j^;qk*G4Iv^)dpX`;`(H3-EjNeG;#sy0; z>;cnmMzpF!13>V0@;y0NnU$Dka~9ZFsVkKKIm&Z9yQxt2kyrFy69J~F=?;;-_1g-V zbztW6-I$pRTvM}7ur9ogeao`&K?`>DA;1b%HK%6Mz1J_rW`)^oEDWL&D^ckUwb{|B zx=5P7#H{844cG_u>#_Olw)a)tRDFO&Y&2hk7%=MFm|1bBGPC42igLOe`n-t88HX|J zG;qUqYaUtqewlxt{O=;ou30;Si+I8>vDw(uGE$|$U8Rjxm(>?|6x_^>=Ja0f%y|kP zIF)SWOhkO_9Jk+Fw*_v?bT512HSU|ERH3WRm@}t#_*k?d50@UDa6(RCO1gG*PyQKu-t2^`zLJRe;#5BcjeyQAb?#vyPwCvSe*Qy6@7(x8@Mo z7Le0__0Rwi*}WTv?pvmPTobJ_`4%#?4B$DIbTZuJ%k2|y=pe~f!N$xRNSe5CLAU*a z;%?V3j9p`3bqTX_LfcIvtHm<3&!eV(tuu@E4u^T)EkYWlm&EU?P8mAF}>i=u+;-Lfj!T~QTG z$m2pZ-vmI4Yjj|3`Izp0Vzga|tad{})ZruFhH}zp2I?pU$qJ_*uWoMd+)wt+jSwdP zROfq9N2BH;-_+$aD;uy_V)5(Mh1VHfcg&0nVlj83P8^kJatav?c-!PYa_N1(qGEL? z@9j;G*C~Y0{jRM2C1#jIm#ChU&G0kW#<=VczxVr44aI1Cx*B`86=e2n?Uew^=)SVj;5!*6V5m#ZsNDW+9zw>svt{KD{ z_H4(KQ+eYhAnxN6uOFQX5fpF?8^c3J8*K-_3JhQYcu1H1`h<#DXZlf?KBH+7eO?JY zUmZj>N7&Pcn!C)W6ob={4V3;)8HN9m%Nag;wOu?Ph*3b_XGckvC9}Ry*E-&JeV9tg zXcB&W+2ddPJfQgr8Qb>mDq(m$~s$G8LH{E2qn8lU*wF`0b(Fx+o z^4r()+j0&|0%CFe?9T`KNykt)IYESBzTDbn-L#C=5ap8sRdcapTaF==#a59C6zJ6v6;_wQtWOq~Zt;Rn3##F`Q@kgF89aFig!* zHTU=&+loc#6Gt_q7b4p$zWSf*3cg~mz2#P~Ipb4eNh{pXTRZo3>n3a4dC3Au_c+&_ z4KID<=r0RTtda(mTb@LiDs<`5IG9fJauva3La~>wexG}g^Y1M4pF21f^Ki2Z(0eH& zl|}Aas#pi-BZ*Y3LDx19+kQr>Nact7?bwpUEyx8n@Zni;%MF9fCbLMP?ElZ+x9GNw zYzh7f(F+XHy@|U>)`RTc&VbL3F}Y6GcKS9j7#Orf+q{uUuSmsl4+fau*dN$G*dN$W znJ?L@A}Ns)DHZ9W9<~}6(X}X&Rp)(9od<>x18q}b&~7OkYXqqJPP4C{McIoJ=fTMv zs2F>eE!#XE6jp7?%904&)m`X&8%)jJ61d~qK619&l}jpt^Ml>NX&br(U~jwAT%HT2 z&+HsCoU*<|OnB!VpV)TuPF=}k%zAdo?>&SdK|0tp%5=9>4|PC{P3=bao$BZ541<%u zt{IwvtqpGdUXy>_aG$fopqzkBgZQSm2l2~R%xa!vdbV}A(U5xD#^9Ww>R2bxV*z`a z@T#BY_(`%jc3C}`98JPt%AT8_OzQcW;hLWq7%m?xZj<#miWXD#rr~XrW~_Wnd5p5V zK{yXW!_>g079G3gJYd++B8SU)^%R@oInT0@=^^)kbr6GC#oeF^C>cf6zn(J!FNRzs zd(?U1@fhUYQqqj1jSlnH&avJOVw1SoF@dqq4lA-P7n#!gHf&F9_Q_*AGWe0f2S3cw zpzf)rU5JFkjBy-!>&RQr<*o7`K^kmXfAYvWT9l`R9o!rC(bSt18ouTjSoR@X|HMWO z`aWy;*nL5H0f+4&74XcrmQ@y4c(SQx5a^oi3;z%XZ18)LefD6F`ETm!f^ibhGwyhT z+xm>V{2>d;be$w$?}O>ri;Q(Kq14I40equza8I9eb(xG*pd*92b}(N^^F6#xdgUHW zGzf{xbL86Y;evc|4;9virfbD(=3G&OEGN(RWwWz+uFuj@P&-e(i0t`!*kN(Fa~L`F zYfxH0?^sZb$1|);x~vgX^-@tB)pW@$X@8a7iD6>s8?s~ObvCw>@e_k$LDpSNhvrr5 zCP4^m!^Z2LvFZ}rq6Uu_jP4epwB-mwt*_EuK=lrN8r4LLJ1R$|S{Yo+e4a8i}pO*cNv%_IL=VQ8JE3 zQTFtf&rV`rj_GSgJOrm$joiZ$#e&IW@Wcl0WV2~8;3!aj_bw|f0w_=2-?N97$zewi zWi9s{J&z6y^!}tqKtDGsK#T|`z9u_^i85sJ=+FgDd67qMhzaPwjkX)xMY}o&u5Th$ z`rNObsdLsaah$J=t6v7qR_$J$=2a6!Y^>KPnZ@jv!J^AzRo1;pe{6hS|1@2SS-0}+GKD48yujrf(~>I0|AW5w9slQPs_Cy)b(W!wNBrjjaUOw0w7`d1vLyjzz%*-M*-z z;NU5lvEAh8yNSMs+`h~&?qS6+n;FU1SNoQaK3x5}XeGGxEuTua`ZYvzq0p)57-0x) zds7c($!FkAqchp%VJciHS>3UiU%t#TRjf*09QhM^)#v}sACWJ6=^Q{cY#j;pf$t*J z_H@qQr<)sUR5ZwX4xcqBZXD-%C^#n2unh~!PQKbx9Q$mnZx0kR`E;;cY(QkH_?2Mz zBKoUO#$w|D)I-5A@IAw|knGMldy3&dg2}88in-Zvs3;~naDnN+k`czTn^)+TWdedX z6nSmI@@p5;)UAh*YnfMoU(kbnyADk^9E)tXu4x;PE9Ox4fM1gd(+WPU0u!tH9<(Uh z22h8en6CSt=GuH7_$z>CK31~_P#aZrD^Q8!TMkgSS~nsMe0@fyGuw8acvw9sik0i9 ztT|9wfSi5gPuZv}Rl|3vGTLsZr%uWesD|fSy6h917bKm!DN76x@{u7Mp1W6;^^wLs z;MhrNBF8o~g*w%J?9d%Ndx`_?!N-zJ}Q$EsqFi%>32rOKdti^2}FqTvrln53qLAmsB?RE;hH?A*zR_>j>FC zij?IvE_KbEr!*#c3K>i2s%0`>m&FD3EJLOGEP5%7S!o(ia=b|jx$&?g8545pivQAY;^gJIuK>1Di^yPAn03jXdsVb0uw6F#E?-J1E8V6_#T9diV*XVL#{+p!xyj&pBi8TGtoSvYtMhgDAbWK&DvV+==_$C2eDgS?V6 z4{D9x2M;}f+VR9g^EB1m)co#sL~x>1xnW7r^YBdG6r!|({_8*gcd7()@u57j+Cb_v z(A&xiZt5${SUpQ#!tYe;y8mFQnI{eST*e5*zKus*tG!ciA4Z}j%?NWN@35vxilHwO_Mm82?Z_yu(5ZH^htrg z%4px7P3nDvngOq>CeT&qDEy@Wo)bQ^>87&>kst=vUCn{ss|sRh#3jBX-Aj3$6nA>V zdx)@p4-0u)X}xsK%n@{QqA7V7i9GIKqs+&8&mD~*%@F9hBX1LH8U$4rxlORLrWp6L zG;c+)I3PJuD7-6x=FPC47-)|W-2l;XcoVTEH{H&ZHQh(P241LH(6VQjy2x1i)TCGJ zj_FkX{i0kv#hh=Wzw&mAf?gR0$iTMoLRIvZ(ZRj4vt$_;0%*`}O@+26dxG98cF?S} zK!TWu1_(BhVfSzp^47YBraL?xe}A#U8n48+<##vJcm8<`vKqIxfsm`4ucYMwY@ombtF7SdmV2lAmgbwzR{P~{19Fdl z3%-A*(lpF^Hlpj4eFrTBF@Ue6<3OLv-j3OKs_**xrm^lX1)f`K)1zTS^9``=a_K$H zzWdvJgbfoAO3K>#@WRM>nHo#83m$^&3Gefh((hTK9W^ng;V6v0u|If59>1$h$dfj4 zrq{Lx9Zu70MK65P<{&WH`2uB4(doyUEl{CMmWGpXl!fDy206Jb>3~U8KsK4NM~_Iz zBk~hA0cSPAgl9ld#C&79#tcKVG#o2gy^?f^a*yc{^sU7z^0aLV&@8%VQ-b*4>F6#T z&n6+O&MhVa7E?5iVBG-Lhj9At&3!bQCO_Ux0}4_eM=7S%rFo-+hiEB^yaY&b$tr2t z11oJwtK17k&TuDLe{Tsqd%0+NdVXM;avwasWnE2z_$UH5h`=PiO_*zP9i$Oe&%r30 z1(V4W)6B|*HF#MJ`poVErg4ILfcXg2R&_njHAvA@XBs{vQ)U81H%d%h!ZZ!0Px*Y* z9O_4~ZUF0o6uI5_EeiNp*qDt(p1h@>@w>c-5ziexf^!2n??W~lFiP)*rlcXD>!xbG zPzR}b&lq9i0qFyFUawQgMoU(lL0)d4f|C=Vqz~aJy2&S*FUCbLVr_5AM%h0K5v!|0 z7yB>tJvQZ*uM1946nI# zT01#W)tdCk+r_aMd@vqIEQA|Ol#CTu2fS^Tk~}a&Y!=gNA)hp_@DKox0El1_snB;c z^T=p2uvnz{$5g_rH7wQf@}mG=i0v7==^u-NrQn4nwWw7VdZYEgiAn(Up!5c;uQ>Tj ztKmfNffHSIbm+rlLY@?ybWHxz90#hVaV%$+0sPzqK-2`#YX!^PMr$g_711J=3V{V> zYxfNxS>E|FVDq)I)pwk5fHiHmux%o{Qg1KwcK9G~>M?}VaQr#A4eeUV5DdMvk*Q+(UoD6DuB)$%LJl%@hCFmppNRyRSFlv|e)gc+3|N zSmki}0?i9=c=P)?{*E~VLsQ=la|Yg@G|1p(DGG>b5$GS&0c8N(`_8D89oa^)!ow6P z%Zpw_E!cROhcaP0&uS|U&69_arQy==^FaKY=p;OOtAr8#%~Vn8GpI05!%;#VKKe)s zaOMB~-&A42U9poQXd751+HpdK1VlH>C3ci@Gn-7_{oHb((wrSyy6;7nBNffJAtj#V zo?@N4^D54hE0$HQz;>hjUnkwd=I6f2PEY!Ch?e=bLD4q&`$H0kh5ZsJ^$MLq7ie|^ z-8*%cz-wL{hv=_LcyddnQE;R}5h_e>6sX*X)W)I^qjEh;#_W{NP~Pm2_TIyHbw_v0 zKoL?>MAJ3TB1a`91L6yPG)W#yb+me(pJ(RflCzxKo_Alr51yD}TIeDTArUNU7@4*X zjy=gFV6olQs2UktB>_P!9RK!eDfF#G{A|Tcl}L z;((2EZ8ZV}I#}IEH#On|*^%50U|NR8vUJMV%2vO~=U^Y#lhqxl!1AD@Z?y?W^LkgJ zLbgbz7&Ei7-@^BfebNe-_0{%-CV2Omj)J7{A`JCBCkij;R6J z$H5X%q}!&AAePOT4;R_fBrLfvO=iGbUK%JnXs^V83lpU$r?F3-O>mm;!81)2s;*$E<@w)F2J>$ITm5?3}+tXn5Lnq*Bq zZlYK{|?}(k7yR-&t{5{d>t@cusSAqIxC6o8CtZcWqQrT`o zUEdpOWqzK;ql~XHm}SWYb0%GH>F=}r@9{roX?7mn-esrpn197qV@;yl_@2d#i~X_t znt>+KoW5p+nP*Hs1h=KB)CxpoV;^8FyC!D94X(>2XNeJQ?gMqN>83MY9~m!x!NO7j zm7UYK%Hv%$x?{g9#|(EJ!v$RgsdXva`5KS`#W+oOF||^p=YI?B>zap#){{*AHyivL zVW$ST0I9^2efT`!u0>oRl@QHve+=e@X>q6YjzF(Dcy zEQe3e_jKKnoVRpO;aw#zArlxFx<}+xf*o0M!IEhfWI_%d@sSSoK9f3fYq|jR3&2X_ zzSg4Zk58xWGNw8|Wsbg|s`v}Xb>#Ja@;cn>2C{*z_jcxAGwwS_kDo;wRnl14^+G{J z;+W7?do*J>;#kareimYDOdnJ1%>NS8o)*xc+Ix)I5xESx6tCX7Z#mwu*yWjtH`H7c zinCfK20-o6*XD@X1!i6EkJF*ininkzaVs%AWV=#MJPrnZf|$BIx6Uqn?_4?e{^u9( zVw0flZUJptN)@GLO(<`!9;QN~ShX>>fpb7{qFM}IDDdu@rtL!6y|#)4JtwU|b$uUd zzU(Y}2`j_UHVCpry8jieSQ=JVY0~nXl@*Cb^rIkw>q*gVpwen!5vZq^?PNL*r@635 z9_CaS#kZ4?-ID%Ase=EXJPg5~!viDL2QlH&_ulIy%aZ%PcTd^5MBkgEn4%&4@Vwjq z=yy9~#ya?~Cm)mKYxy)DZBhz}e4dmj{iKgz%6 zYnYP?8_>f3?=Agwc$P1HkFFDxZ@2a}G7bK9{)qBUfw_Xvb3l8u75&+w_N97q@g*NA3*2t##mThJ6F#cB|q3e=}`qVDAW%s`8(cWaeI@p|<_D4X!WrMBG zg8M#$@u<*Z#|OK|2rz8l>@(+lZ!uD)5-pDrt09Jc@tVUw2YK!ehk2{WImt8RWjibRp062XtMgc&%Ss3hgT@bVGAVpnn?mNz=VLAC~@kA*?k=Q;#$ZXRa zYcSbOD5|Du0N<0H!n!q~$WNNuAufsDiD_Zz5-G8hZ9(aRL1u;Ar4XlTxGK?68~aNm zPGp%jRoVwjoQ>^yP(>WA3*|bS^p$>3a4@es76fP)LMsJ^M}X~pgM-CXQ(@kOm+l* z%VBdn*d+;4)g-EJ7_uYigMz9h;Z_cf>aJlLNN?-oEsaL8j-ao)TdkIrNu#x6^TEj! zI*t!0Vh&i}Gk`6~mQ8n5uER+Z-=^g+QZ^WOeqb$!oB>d&5pT3bf25H!@*PumY=(-1 zBr#M+Brmv29vU>SXCj$z-UXkkw(Q7lLR64ptRv? zTEyTaj8)Avh;_&$Ot-=crHbaa^O_tv@^IQ#{8nHfCBY(((t*(>g>wPf&dXcfJ%g0|Lh6p}08t{0EW=Ux^@~B=?+b?M)HLvn2@NCys@g7PM zYIE3{YRk5hr8hKO-c5@A($@Pv@Kd1TvB0s!jez01Yqbz4NWu3-@i87+A3`XokfGSbm4m&IiQPy<4=n7oewQLSe z`HXw2IHHG1sIW$Iv*4Ds(*FBh7?+`0_Re6{q|N)z7r$XI z&Yv^?;?yk~7LS$dJXM#j@7<|?B>p}kzx*n%LiT~~xxUVm{p?nQ_y*^9eoFtDQ>(%IDc^$_J^e2!i~`yf|=7S!cjtI;HZ3Wh_4?@2;P+uvn17N{-V~ z@GKa=IM7j)Q|e&fC9}zxt(v;s>_~_`oUc3A1m*jq+%E?Tl|A=S9MIoV*PRkV7>sk# zltzEe)254?@`ce`N6G!etXM%hyUv*4}vI_AccjDLxnwwH(F1bBe>HLrt+}Xq@Yzd_>YEDjJ zV3=cV@M3IAR(I@NF<<5+45rKE#gRXuSAG89{1Js9uM0xW@eL2lPOjKN1^1_$8|vaU z$bP=}HR$8?dZ_rr)NXNn=^4{Iz@N`%!S+BOlPd?yeZ<1PZppUQc7Q!L}i(kvx$#~kG((Ge7GLy<7)a)(T9MFH6*>O*bdMqyJrcsRR=q*)g;C3Td<lRXe~P=)rc47E3FvbY1gZ+urH8!>&*%#+3DdO_Mp$3L5AE+p{(M@X4q?K3NP@ zjmo#3j4Wu{z46jY>x~d>8269xENyAua*N3s5?Jq(wKSqv}ZWd?;I546AhN zVdpd)9L(`^azIsCOXJeL(hE@93`K80Jf`Qv29l-SiI92^GTrK{-2*c_P5S zf`97)o=z%9H5{bd&MT#)ZYl?>rcNE67Z6!Lo84b8H5d~EST}`3u;mj6?tR-#A%#=P z%~95bXxK-7$3{JaEgLzmy#YKs^=~{4s4kXW0=@%^KRM|^?Hv`3szM#3gK9qZU;p`k z23Ik_CrEP~*_kzmLPXtkjOs$&#L}At?EqMmw0c0Rn}UJR!#>#mn3^zL*hl`mjoL%^ zOcfKB^S`g!1KF0U>&^=}4L$2Y$sLu423^gE2i4K1^itx%RS3bVt-9_DP$9#6DZoU~ z@~oqmA__1S*e;;5YTsF}vzHRLgN}XV-!-;=!Zx%FTY7MHNBQq`84!53$SIAl=@@Xa zy}X;^N@X|6w{UthNghAX;!(y|$r4U3Scb3bE&Y9#|2_W4EX~eYR?BHT=3lYZSd-{B zzGq$OiVaq>nWcb5jJh^fTwJrOIZr(Vw|ppGm9$n=nhud+X|mP-p_FN_t55lq-G$TU z8iu^h#!3xCjg=qkbz2qd?zxLzZFnRF7TJ4|5)93CHQPWUk=icP`woXso52Ezk0@AV zA2J^+Q##*!tV}sprmTM5w=zYwh-L$_fv=C1DGM*quv`~O&mcKgrfdm+y6-xMNsg5% z2Z=pknVL!L7ZA;Rjg=`nbal_NUO)uxkJnh40)gwgjxW0*!3(HN0mx82ebdamohEBJ zp5f?R@*g!=6kr&(t&$hu{|&Ey zh^9dssj_p7Gtx zhqQ;)J9#6_9eG=DDou*+S*vthF3<;d&)yqWsfb%hVdMSfMgJYgUC zca4dV$U_G5)D2#KXF0K}!70A#5{zV98@o#DG>~REhHTsns@`~4@iA{f&YFbBN&Jp? z5})u8a!Xg^j*W~}4|+&?bEh3486xOl$Cq6~TJK$*CbQ`%WC9~!am2?ovV%Y7%9pFH zB0PJ~>;gO!a-o(4-Hvef}>0U10G# zQXQ=OvP)NbFTMrDu1?)%OSbImwo0W)b^A#9za8jbPR}lt;=kf>=~n?fAY^0DgRomHnRA&9nJe=XDnux6SwP%;nX2-9_?l^F2H|w=%E0cuhgU zJ}VXAb51KUT#-@G1=yM{ah_?u#8Z%4q=1H?X7ZAxof)~!E7MdPThNppa~jnAuHaSf z@WjPS1`UraUb7dY5O12mDK+*XzTsisDl3vvCBnSIE10B-@-#9Y^4 zHjWZD;V$?ZPT%HtmILpW7Q`3BCr0sm@}JT-PD5%Vh2PjPF8aP;bVq+mwKW@D#pd

81o&Tqfr^Y!APKB=*Fi*Us*ggTO)RVhKZLPImCzw&ytKR@hP)&69i zCg*Ti(f*~q)_e~?$6V3=#cK)%h&QIz8V@2Kc!P9WUXEBOsDFSuNF=Yn=S~5YZ zcu21Q5ssMb9Dff!O`}_OEUiK==f_XwHxQUb=r4#zcgd75&bNJ6N^uUR}q!&^db34?SCu2%vi!m%5>3hKRia`?CuvF#lPN#tkB{Y1k{J@2POF>VVC(+ zqx;}C90ysTOgY!l_fjX0{v?Ys{v)=X;^puENAk<3kKU(zJGt!e?ytk~56b6ha(n9j z_QM}h8YL(0G++olVVPSO>`r#6zH0Upi`g_3tSlT(VfTZQKbikn#gLd9F;uF%7c1k! z;^z{g8}M!EzE6b4+f(OI1&gV+6?_+01o7Rx5V~K_he8pkVHbjZ2LewhEHvywpihWv z3H2Krcp*^5J`gH}9d;qQ4_t%{DM9Dr3O7_S0>pBvYil%A)ds26+NPwQ`3Nz58lq+Q>ESb*p?++&`+x1MWS&_alp!@ zykfuk(wwGNshzkcvHVRPTlp3btdr1n{;!kr&q}DjAkXwKhuxKb$E{)p_`2=ZO!b_v zSGdbu=cRGKw}HE~iksH48IkVm4gsKtoGM1ex1zH_c$q3TsoJbyxZTU%1iqyZcOf(i z%APY-ZISHCIOc6Fmrt*$o1^kP^R2m`b+Ok6%FRz;u%wPfmN5Aw0$OT7AtE&Nvx{0;f7IKw zFNgjnq6WoT_}D~u&8tTcIJV)7)S0!n?dp0^-DCA@@Y!-_s}YjtFRYiGj;7hR&37d5 z%AAm@1XP#Ot)jQbp5DLV>woi8`jlqj{nh9$h;PHIGS=~<0L21o>gwc~X<2n@;F@!_ zd)s<4Xb{mXd0`=;2@yG)-yY>OTF~7d9F&w&Bf2Ff>P!(IKb`&lmL+a{_;hi3<$Sa* zE|i~s6rlwTU`M6C!=QS3FzJO|6DW%jPwdQtdTrEAD;=73Tj@X^ zjmF<4tAa0OyYd)K*s1T_9xRV;xjD#H%{sDqh3OD&^wp&(qz2z(e#Cl$ZC-zVj5}o!3%Ig_ zrdb-ED5p0(iSmo%cvKNn4xZBYPvda>w)i#sCKx|0e7RA=bMq%l)pJ9k_(di(Edlb# zs=Y{>op0s|JyS{~bRRs#a8e`7pPStZ4`P7HPN!fruT1lahJd!!DS|_WCr;ysS*Cmr zSXM;VSa7=x0q7IM+t{vl_huZnp@H0i3q9&)ToKRgL}t%JU$f_4!=lIwDv#t|CAHT z=1VB5*k@O7JEGEJ8^wihvQM}t*6BICjv52HEi89?N=genqi>3s!LQX zq8GZrU=I^;D(N=ss%<4bl@XtpSN8}|qUM?q61>ey^Yvt*IR@oo>4!MITM!h;nHRdJ zS&+?Ap{j4!(Nzn{Ch-2Cg}}u`vt_qodp>A?{z=)K*YZ|*jIuj6sfc?Gg|l#?C_nuy zgbd%YktMr>?z8vatnP9mQ+``_#LVPDmFK%88L(60Kd;WdeEj&MP?HAD)u180(xr3b z58cpwXo0;$A}3(hKZav1{9m(Rl2&5=MV$XGNR{g_jFoh5qfV9!mvYQbb0ySb3MrLC zzi|VjoQQD*Fm;2gdkMfSt~?`Sve-ENQHOMI-t?~_A$4u)_Hub*k&wES$XLIJgJU^! zQ!i~H_K$dyCjHR^i_2YQ(;!Z7!s%6TolKYWrkfHfi}7Ynm)eWlP+70iq!2uy6H9u_ z((PwOS`31`4vYge;33ph2{H=tJIW+kq6GOR3U7s17*men1=O)pB1A-sVB%StKUCr* z(RJUF?-ulM^1C3SJxtIkaneD)VGB)|FC|dd3z1%l-vFb z4D;jrA!(bxgZ{-5@2JyMTh?As`$qaMqdQk0tTXqcclG7$U(P=L{cK2jNBVH|?iG5S zdYkch=}7;vZ^hHPR}VcDXw9qlqi}@7S@zhUXY6`=E0H+tXB};~>@!N)$-YAGiG-Tz zVOQ53pvfLHd9*_38#?`ign@vFrXrif)24)SGZ^%N^vx5E}La8%zw1D87Y%9pD4 zAGrE_PF1pZt|-b+4^!^9iX@(>0Bgn`49-~HT-RhU&GoQ~wwesqBf*9>1|qIdVc2%z z{qs-C?_nN>XR46J{CPZ|&xJ4AnhRyUAP4CNQH4(M5%74~!d7-zaE0=lz$SoAsOo#> zQKYjtv6Z!n)`X?no*wI_O9hzu@oa5)5Iu4vMTDk$x^Mj~{b- zEB<2X30bK8R&TCzb$WJjX`MOV)y0P|mo9ZSh0=q8t62bVH=m$00U{gumg(-F2Brk~ z_fUz$;=Gn8fYlCw*%%B;q1Us?SA}H_KSWD;NK104;zGTM#{X=;2h{8Ty{N!olP{3E z+ktWX%ZkZf!he}chsy32$&YH0I<{@7s893MLHW6P*S+kCie1zE)b^%p+q<>Lp80=G z6hp11W@W`h-xU>ob8-<&_NSY{I!F9I>m`gl%Dy&fi3qC7mm`pQ>NF@mwGk*$YLBA=a%ah~i_{E5T5HmlV4?fL~pv zT{BYw6A);4Ta98b^G^%A6GOKg>5W)75EL^-hKa9tsRU6~13H#a3+>%jf_Rv^?_j&Z z(xzR!rQ2gm@85iJK3M0a36PzDpRD>Hy#Nmb8FoW1ISCi{ATPv9z7;K~?nB>o)TKr>u6OVg%0T z(;m&hqZzn2y?hj_AW9Z!=2MN6YX&=TJD~XwARc7XGs?RvcZxvAn|Z6xjRVO zP;tt!r^!uDXWZ6N?q_Mn%70kfk9Sn+-bZW{H=kj;PSR=)J8bT>iWa`73?hBQEw7q{ z@ojd;01#!$o1$^di`!)llT6BFX*jw0agOOYQ6|CkmMPdfDrcyz`#0;);>_~Q`kQz4 z(L4M7@`H$U!5$>oG>1KQ*}}vw36cv!%Oc2pp6#sqNH=OjF*j)W;cVy6K~YU6QlNq? z>^hbzk~XnZ(-{KKbF^npHQGe4yDleN2=`@)BfJpK895hK3(6MOID5K?fS_K#>1bfs zz8HG}a4E31=DyGDVa(O=^%IBs+)H9ry7`Rr!p=;OK2py&AAO|t_#L7sohN68TzoJy zY)-l&vIN=)I=(CWaJTBp@K&fwrOs*58&!gd0DK!0%a&apxib@?YZ)uXhHp9c9+mMn zuTuw}hOqP;yiFw}5ar^h{TB%4QgT%40wZvr(lC2fC zzGbP4d5pB8@o%DVGG=+UH&Ly5$M7N4S6D8%=VrvQp0LjRCju&oVAU{@x9w`d{Sftg z*0ErtC|?}OZl!$N98Rh`TntdLJ&V7_$zxo)dHj$-gl;uMcYvxN18)=*Dj*t>Uh4P^ zt5S?onx4S$-l0psWm2eHvWu0TvGeA2sv0Jq?QgeQWRnOkJ-Z4qcCKHo)U964b-GA4 zwN6}J)3@Arekh3KMX&S!#H(L*3phYG_Ga=x^D2qytD1}VGN-&uo2{HVFO|QFC6DV6 zQwa)1_Ep#2|0I8}u*g)p2bL7qc2&6N_( ztP=PZ3r_MRk=25ebyFWOU+rqa30ij#&s~=Mik!W-s0xd|L9Ag9(N$DQ^xJM${2I(1 zN=-zIUg)u}_ivmJ*7?=v%kxUQpory*UF?`*RJjq`#T9NC6h9UwfQ^=~md|S1Adj|3 zGiwXjE4%;y?0su<+eWhXUqPJ@d2JuZnfm}|E3U$2kf_#?va-CJ{iLEO$YL!mdU;9W zJ$3&3i@}8iKmZ&71CW%Esw6f+VrF{!Ha*=>Hyf+6#9WmRB36Et)W-x*>*crG$^Cse zYo&@9Zo8gMI(9G0^GQG4kc8w>saKo$Bdbydd3s^Vq)i1x;m)`Bio9j>R zjq&ES3Tt42sf)DdLkv_bg?#S%F4gS?cQC%{JhE>~j@CPenZo9luaY-=R38Q~N?lRx z@I`&px_ndEW!-3E~#OVjyY_CH3OL)jk zpxL6J!=o`-{f~;aV5ScUQF1FFcZdY@+czY+C$S-kKk9K$D)a}pk?XiBsj(f`lZ_Wa zNx}Zvs@yvK?UT6vr@~evhxr01wIqHnq>){A{%+q01?q4CJc|pJB=+Y*9Z!Ug#gz){ zK-YxW6)HCwXQ3rH@l9kEkr1hAht5;far9)@({Sg{y&z6dB0^=}eaRGJ7`LVnj&E~F_DkEox{(yK8*=t`ZiR_5jyT61K5JeFnOOR;?i=dtf|KgXsLqe zw!!_DvF%l$yp3B-L2_!iU8sr2Q<;>XvET62^MdTpvbgtIRi$~_O0rI{1zcCVWg4#d4uuCP^BLU}H!i;OfJYpq4Lqs~6l!qS6-x2o zMrlPZhk}~fsUNx#qB5>qG$y%kjIH+yTm1a%@si373EUL5%id+p&A+MWQ4E;NMZ}vr z)zHYxT6p;&_x>V&xw`(RZ0+&M_b=QFad}f@wd@v-#gvt^X;3uzCHS%ky|wUz&BMcN zItw{uES&7Ga5Ae(A?mfXIo#L4NVZqr4X)+iGvx~k3YB=ohQ!rUte-p?{e`3I*v$Qp z{oz{JUZiht><=G=*Bdj+f={zQUjKQqS|>N=lld~3+(+sx_90Zb90}kc$fZ+$)}fYK zVix(SoD|%J01HRu`m?tXy9#S$xjL!oR7rDyj36dydvZ4jzyN_je!mfrd1_4(wWVtv zQo7xW7S(GN8A8}5wr;<@xGj^_bT-SUYlWXa+E1&(r-|;_YMz|)bYLG{=6j5tWINAY zfkXszmIS-A+-!F1}+}tS@I3nRv?AYC+ z|9NWnQsAi2nVamT{^~1%+#_xr@m8vVq`oNti!wKK#_TlLK!&P>SOO25#;c3|~U&MkF*Q##6|Zu>_~&E!wy2qDIuy z?TNK9TvP#9aIkPfn`>W4EYkwcvuQomq>LLo zKB1!6(5ZCoP27C#dxXr;)3uNaY87ML^miY1aP-So@%+oU>pjL6%X|N-(9LfLWXg0{ew8Ja#Q?ta)W6?0C{_HS&1U ztli%6q&c26Uj(zhkMROh*_J8zDU*i!_Gz(gWfW}7U{VU-2Qn>a8QgECGB0TNKwbj= zIa_~wXIxLh&zEnFU^0CyXleQS$vX6^AHz47cS11ci)mEdnGF0zcJYis4svqyL#Z8v z5LzLHbwk58GIHfJLkL^Ik8lS&T+W?owI4vh_Z_NoSASGD3fss+qGuH7=hGamv=QBC z=0-DjDrQbWHBnRezK#3Mp*oEQlvQV?CN!;={qZz5p2kl2G#2veU66zkl#+abMSYLD zgWiD@CBJ-T*tJZDJGz64E%b{X4V5jPRM;3|xvs;wN`}u-9ZKSvfH_!u?c{!_UNtSZ z`WtEfxTJnxwGh!5My3`5n|!RDJX-DK4mD7xVm9h;Hm2?o1?m-XTq17Sg%xaWZat>77}cOL|Vxrm^iuIlSnSUw+u9ZUe% z%_H5CQX(j|cnK}a)As&RuU8|&@f_sIlgRl~BUcmr>vc#Rah>VCs8mWLOw6cKhr2Fv zIz;^*haO~roMgSt)t$7MX-R*+8DUeezpHcdjQwxTUP1#?FK=^|s-l3&Ov`k&6r3ba z`sshT-vy?1;bx+bN-8(gwN+(|N^`eklPD)NmZ96pJiek<6q?faX>#-PFYQmfnnS~+ zL~1!;vxVsxqlv_oPebPOUS8eWA1;2nd|QiTnBlfUrfSSQ zXsLUPTt*>x$z|;^v`*I}7#7XynC4$eAz)-82cVPbnO#0hYTK@Zyl12^3b_ zpx_H|o?D0Kjwn7ABZj(PMt~)}Npd1k^s6~cjnc)n-qE~;y!-^*u4AfP~qLGu>-%nodVYQ}A76Q~5` z;~ukUhPo69rUc*1e8^a7hwY}vp0ZWEAXKw*53$AiI>4G?=D42Fa&wYAiEE`|9#Q6m zgRo|BFF)zH%u}0dl39JTvGx;F8ZtsDUYbg{5m>H?*(p|CG9YXKE#3GKN3$Nyz|eq8 zTw-CBaMV%l2ctIRG&+Op+@+GhSVN{#*{tmLrl<&B!^vxYKtU^0Fn|TDpNse zji^jIhkmJ%pb>T^$Nn{GlZhvwhS|t0=y2ibK75UuT#F_@sJnMphq4~^#=dmL?WfDX zUS9phn>b_pZ;A1`{LD-U|#4jS`YKckgm+{zIudLxt!1&^p0Xy6}yj zFOVGYS%fy!f%b^W9qjA59*QPYh$T7{aRP=dQ|=2Xt}|{Pqu2^XKzErBE;{OP?ZnwMwyxWaozq(6LZV_Bkwh2=v&NR-3sZO*L4^fx5=~Z<%kj{50K59 zmAYaL!v5v6q%NSy5_0EGPDtg+I%VWgGVg-f(`RG#xLF(bi$9)JWt4b`0ciDmU9S(a zMVijQh)~JOf+Zh+{&eGA{o_&zla@i|VrN){-o_lrk?g{v-QsECz_cnbYbq3LV;Qut zU{qpBoYb59o87^E07snS)&YPm17CXVEg2Rkdg!r*f3?^da$l z3fz;?o8_~l3|aOGA>U=S#|R4&lWT+p*}{S-1IJdW64l?A0nhF(+SE)Kq<*Mqk(S_8 z@?>au2Ohzt?!!BqO z_6Sx#iXv3yyU&G9kV$mw|KXt>|*CJDjN4?Zjm$S$oEr=)U&yd14bgE>WsD9gzU4hHQ8)-*+ZjtgSr? z0OJB8Z&Bz1C=Ycq2RfK|2 zU<6oUd z`8*Nw1;vi;BBZ(!uq1t?b}0>bp!9H@2cd|Y#*7#gtwtV?%eC7(@NxNFbbXu>*jPO5 zm^#i;O_Lz+!!PBMRPUqu#AR^5k(G*>2Qq)`&)NFhJL7s1e!hHb1e57wK~&`H2S|S% zrD;Zk7gw(*=$?WKb_3ysb!ZB=Cjp0@|IH{ zHkwpu{ZsAH#OgB-VPWx?v1`NNriA+#pVBM#Qe|5pb)m_kZ1lmLu_DdOXNFyh98Psx z_7*#Ci7VtT~n!=h(j{ z;q+m#c~S#YF_}eeS9g-ksKvEt@fQlFvJ8w#`0vxg_##`fkD7hd>@Q2RzaQiRX_SKY zQZVADoCIX2*6q5cmq-TT7(lakAcw&s8S3~c&_M#4rk2=1G?_vq7%^W!?M`dA zT*0H^ryvVcmpj9j!uCZC4frX*$F}Wwy6I6Anj!F0sObqztYLwI6iZvL?xX=Uz67C< z{;dF>G8VLIbDK3A{U!yisy)D|ZxL*vZaD*eaA`RNPJuX?P&(xSotNfw#|N?) zqxk!pFW<)UbUT^P7t_hQ;b}MjHccLU{-yniS_6?vluk;dmIF3hn2s?TNuR8+xxBiy zKV1BDsZvxy34+#7{|Ock<)}`tm2fVwi#N-@{VhpK<7~NFe+ZBB z)r*X8+$ER&B7V8L{-=z$e)9bb_d;CWk`Ev*|2&TA!0oqofeR z)2qk0E#>?qM6E(?Sx|U39=3Q&@pSNUmqVO-zDXhyQbo`5ZO*Bl7=q5#mGi82^sI_1 z-Ow$FD{T$*_H=-v0HegU>|p~k&0*#|*Mi(UJp(!dMZKtV^}&04H3f;7W!c=sI@mdR zGB`GqoO}s3Q*sGk{u>!jFTrNA>V6}OPWG{x5(ubhejzg{Vl&+bzt2JiJzYFK%sy|H zlf5d}bCky>a+h8BIk|p{Fj`6aaM`2if~x#cg!5G{sLXd8nx}$a8J&n0p7@ zuDkfZNxJZS^7J|7+lS@%{&j`&NJoxAi$aU)h8j}uXmTf;0ACT?A)c#*EK>;Odhu)bub@w&K03&R)2u*(jAtqA&0*1ujmV$g08R z(7p*nwWNLM*l(>HxRh^tB1GapA7vACxhw6)@57^MHF3af4^{r@g~+8Dz~5*S^#@?3y>y7W0^c@g*TTC22ui|yQgjOft6H5bf=!A z%pgWK0sreush)y59TlktDjZ=-t(2#B`A~<%1k|#b@LODkk9wO7O~2i;Y@r)M91QqLRu3f#uuCZILpeJ4g{Lt4vByisGw-9I1*bCj#Y|0#E49 zXDXZau)({w$A&UY*+v7mj~c4u&mxOq)CzIqbYq6p^;~X6_pANoI-C$z?RX7!k|y{i zUqcyp6N$F9kZ_rC{K5!*=KDecU0*AEmi!shZ)D6?j43-C7c^y@lKC#jkUx~WcM!SI zvUTS$G%^A2LIj!t976RJ_U%yZQ}RaU-;bU;mS5YzIpr5z%6 z)ElGT81+Uv*exKUnOlU{f&nJ%Yg4YT!IUs^cWy+n?=A@53RUPB8WRm27k_0 zYZaCTmS_8pkeoWQ^!c%LE_rZB&W58GGI}AS7oy1v(U*@g^7+7d8zY0OFnA2hn?i3j zcT+I%Jr8<=R->n@k~2#Gzdtib7!%OZ?LZyagV{ws`t~YvU=1d%oN+j9I?owqipZLICu--Gn5R z(YgK#gH(&9o!N%UB*xF-iCYmM4LZQYTnP(gSk}k_=6?U(=k;HVZ`!`F^^+jzGy@-^1I$^Z>M-bI=VTg6RLqjQK6kjv9qQmZ-A-jSbflMGCiD5XNU<-T z+67O#o(#`FjG_6c*5JIge*ab`9V}zWNL=TqXj5M%sOZ13V}{!P*J0`76k zCMq{Jx+VCq?krzkcLe7;@+JUFHSel`MXLElhhS~~Rhq(6`IkPM0Bf^l=@_GV%&5p3 zHSpm%$5TO+IVFsHzLpbT@+8KfXsGuNIg|u%r9JOZwp9qV>M}F?f>RokIq%Tt0YGYA zr8tv#4DfbzInK`IQ0qV8Qkx^M-|FpV=MV~rg6EEik6l5IJMjw7{V>G-C`{)iOb2s| z)SOjpV%GswOEPitWN;vTd7>`?(l=uXDgRBhv^^kwoFs^p|3-TB`#}0h@W|r(?t9Va9 zsx&RMpHX$C5@gu0CT$=EEy;0Wxlm$EfC*{e9hV#2#h1^MxVGnF?e)eT=Bct>)j0!= zfbk(gmMv87h4QyQ1J~uqc&g`mDy_s46icN&sy{)Lt)c*V)?nu}4X$1;E_1HSRVCv@5M*{UXZh=wp(3+G zDO{y|fo89aaOn%+!98i7L3Pdx(>BM{Gp#jDH^x=!$PK8COfqctXA5mmK6~o99AZy* zdP~{-x;yv>EkPN0Zp-P&m#72cVv7P19+ecxc+|532Sm73=-%#<`-HxDpfog;2f2;i z0SR(DMv4?h9wSB7yV!H2=$-M8$Kc6WOZLq}y@VgEf^{Cv@U3z8E&O97hi{EwGJQ-F zrK6kkhj63_vUDbSf3;dnXOnf5F&#d9lIgJF8`ra^&jygMpM=8ld-Mf~D778rT|Ni< zhuy2=?$LkDR*%6-PHZZRQG@8a>5CT+vfJroxt>io^NDO^pZxf3IH@R6JNo5pwc3PJ zt;-u~s;cv+_3gN~$;`^*?>e?q{y!>hp3LX(6f6a_fGzandTO^66xlXGO8IEj^E_LS zSkv)r;Huo29Mzl?&-IwA1Rd1g_Z(r}Mp(DOmp#NBda(IbaKx23ZW5Jv78%96zzCiu zck|F7$2F+o$B9W+&{))UE#v^Hh)&w9AlIY_!t*$9Dk!I>U~j;t7*O-%FO`r0CbBHu z==CGoiu^SC(Fpc7!mN}H{p=lr5rrbd?y)i5ESHk(p}oAHMILv=&k~;^irn38WNB0q zj~jiM(T91CD*nBS`i8<5COD;<1+|rzy+JNk9X}9p?lIrhf~Fo#ra)MP89;Ww&6%2} zpdJlB5PJ~V%$JKie`;vJ4vspgHF8uV-ep(eiO|;2Ym*li}N`dmF zP}gE6aCJL?oon68)w~Wk!dgU(%r#CaB1R+RKH_l?x+)dfh*M;0DFQk+avx@v$;^(I ziFtBF>HNt@+O$Uo38I1asBu1u9a>N=UTz=G$TW5!bh=X$=ci z+ZfvqJBUJys2G4@MSH~di+)`=GdwqJknfEh;$PJ^#&uj7wV4nlJYdI>jV+sPz6Q(H zR3_Hv$+v#LQo`=Fh-o9cTLD*7^?p43z)^nk`Gm7|o++Mb^-W3Q3^FU5>P4X6ZhxRH8!aIINWDs$?#E?no0r$n2|{ z9Gl*Qx1)(YH!Llsqa9~15T>hgXiCB`CDN7&(DZY(W6%K*FqO#f;77X={PDR-j!Lef zf@oozBZm1)IfG@AFV)<23aMw>mKHdcvR2?nf1KcEehH^&&2d-b=<%lucsVoFjW!u#V#$rRz4~@JTxHyc-PO3Ym61 zO23Swbae@Sc$&^pMXfQDEok_!N>uEK9zJqB*L0N{GabXs)_eGiuR&6=TBt%%RJI&2 zY+BtKtCd7NhC~UEU>)~S!mXIlrN4>be@b-b?M(Jle=A*#I0MOe^Co_xl7EClSHeb43s2&7`LaT*0O*^ZtHo!^Eb915 zxh*r7K{_@XxV<%K0RKo$p9-@DUexK@g3!;kTuW(9?N2WLVi7u5@ytsuCa3dYvXt-5 z-AvYe#bhd(mIsJf!s^Puk*tzOql{A%B^qVXdo=DQs~CPyBKAcJ@|__|rMIYl;U`tR zFR8nKXg@T$UMq@r!}J$usbc#E0hlaxM-0n)Uaqt3am6PZyi{z418tU1CQ59xNyGNF0`n`8-&zBJ|3MarbSVzU7NN z>qS+j5j@sn^Z9XKM2yvA^xEWWGL*EDf)!0KlfOT?zYmwN+JbJ$(!LZS_ML*3rwkFB zo&j_t6OQXS-41r=3f+i&0)&rMYD@~ISeMmR+obUL$T(E$F4Nw;9viwb>-*>SPUW?k z7j5(W)LbQ%MT>!7##Gy`L}NZcGHHELPF z5!MmoPHGa0fC!Z+QPCs=&9fVSG=(apjt7{}<+4s&lQIej0&+$=_i2Qcke^0B>H;f~ z-A@IOiDv*+xThTxK#2&NeU&K6;9fxjap;B#%hsJq^rAVt9Ty+u+c>D%n6I*L>5BDM z!S1vLyO+Jni`jbf^slGIA5RM0O9>McV%=BY`t3YNKGolW{b+Vx9oK?}XC=S}1)iwi z3e1R$Mbw&ms0kfbhsca?!ZXTFCBY&=!cYgkKnzd_kcz+6g%5`SdO!{_ZQ>7$Xrh?) z@?kNR00Z(I^DtYk*1J%WOk4a-wINnCa zDaj^Fkfh3g6I~_lWs@bTUeWy2v&nY7l)|q^ks!6qGP5wl?4u+&R`w`$uFF!Xpc(F; z_9)(wGRvNfl%5Uq$vAdx)~`+);99m3fh*Lrk1UKkfGj*Kxb%L#l4N`HCMBFwTA)ohxACyb zMK8(97sfE+j_2qGChNzt9aL6F>>6O&wF|X6UF!5(*2Np6EARlE0o#ob%eHG~j_! zH@6}WuFUb>rNYG-L}|}ieO(lG6Fd7HxZ`$5P+ws`qYjtl7Gi_0Wc#|D$?wIjB-8113P4r(;JD{U8qU-1`6l7m znOj>0k=r zD5qtMa%qei`w|0NY-TIZwthcO0epv=8$pd`&4^T)|e5djq^I> zFqSE+midZhuadcmCCaWmJ5fdHQ2=e5Y0rTenXw@=7T9kau+gt6k&xY z-4;y+EfZltbXOCMOxTSH(};dwJlLW$VA-ogF1FKx{Qxqt?~!M72d$nw3SEn1*Xaq^ zrf!Ah^WhLcWI7VCslS6z)7;Ucby1_lB$y7|YO615Xy~HGd_pqAB)k?%0w3Q~PZkVM*fXCo0WUt?i?-_3Wu$_ky!b|E!Y z@PFV6LDycn`IV=ZBnDTaB%&~21GPngZ0nGBQrAa38YSiZZv+Cv-RRR7l}d%M>rz6M zjPki`tRj65ZgS@hkXr54_UN^1mb^7MS${lr#?u?l$OxS3NIqNVw zHP-oBx{xXlZbLSr>0Mk4_vYg2@^xZpwogK_d>s>AmGXo`i;zA5cRP!Y9?CZ$a#jBx zr4BxwcUW1-zS!&aa(1^_2kHWbd~Q(fJpSq4k(eX;b6__~}Bri>8Peaiv85Vi2*Yj>k=KUV$3go8(QGvm6V-j7cub1BS z6#hR2f-+%ZVQIhiXjpSnNy7x)A-?G`wzv-4ziY@K8kXLa+BP=zM^1+<`C@K3cR8HrA6Op4>_PzCj<$u)6z8|Oex69!3Y_%?wCGQ5$mTlAgyt0oH#wvRh z-Bp+GI}cBaK2wT^G?BC6i@mQCB1$;ik4^^eHcil#pYC_-s*ncYA!4Jk`a4u7f)nV8 z7g_y{xo!JI35mE##HYjBE9OZ!pqTWFAzw6>3Wlk@Nz3fmsZ@X0XzCWLdjHD(*}lI0 z_2$};FP7FsrFxkX92zJ`d#|r{7YP|p37heC?{It(v%sr2l6-0PQ;^%QMu^ zu6O(K=K7O+W4w8-LR*GRQ&>cc%X#*+jS%G}X(e^Oz_$s;{PvSVa$<#EvY1Vo_WJ6k z>Dg)O2)917Z^2jY1Vg4Us&F-rkRfs8GWgG1Iq;*(sC@*SB}NOlk7TlgRPue?3}u zUbfZwp94n;lW@Ue<=t&CSsmntT68H91r`tWcP>7(Jl<%~_o0KPeI@UslS;rq}~e3?lY28rEJIiwuWwsGAltbv4vVmZ(*8S~)j^ZHS4&}?nIk{yl~o65$H zXS>SWPKL&2wq6DEhu7Pkep(pw$?|ht$^KN!tD$ovyr_$FW~Rx9jd(4(7mH7Cm-a8> z_Jg?m>E>tsJguVD0rwCmOiS8cq}+625E-!GpU7N+a;jYfV7 z4sv`sR#X;E7$9dhFRmm|WfDUVxgomQ$^_E`dH?oKfj6+pxet0SA3B*0KxG?HC>m(8 zC-Gif|1D4X(S;dZm?KySr9c&PDWhCZZ`MfKisciOT1-3mVGFs}qdA(w)?v%Esig#d zbktZCa4g_sqy?lYc~X(VatMNiu(+9D^*2&vCrSOjaGRnrj4bp5NYivBaYiJ&!!AkY z@?Q2g+j{pAT(<0eHvgugpeS+$1YCRAWaP}woLS(-U&Jq0*Z;h|xcvCZ_b=QFad}f@ zrtEHwzD}L5leqHX2b+h7*>o1}=;-gLD>A4F0`XEp%{?tPpC8l1RUGxnB`2wP8+lw+O?o-`71SWR@2iJC(ApCP!W^ z0^d!NWA4%)R;VmjE#E)nhi4+!KabvX2Nz#g@Y(?DmYJR<%Y}QPN!{AZ=R+YO$g@um zCo}=BRmZ8tzQrxm9Tv5>W+fG~q^^fK>E_KqMol-Tbh^`9HiF-&=}DFSDG*5&+b*X} zIVDz{^BafCJC4=(#-i)-{3OoZS3kQK9qt%3AEp&g$qje*%jD}N z|0RDBeoa{c)VrnY_y46-GwiyaF!f}_B~Mz_wH`V_rVW1OilVQo?OZ*RXOKqsFNEVQ zJg^}ddvbU~Zg<|iTFEg*0Mc$wU|xDodTUDdp;Fl-+U#wp*1$vw+m-vkhTzB`Ob4AD z5^@x_?PD}-Z=n~Z=kt&_nnv*N&1AmHfWE#hMy`r?M76S6X@Ft|*q(;f><#ig0v#|U z2uA%za}GUZd&fhgtMO)K&0D6Y?$@vL*)&Sni)(+vU0%p0xtp)X#F$Rz^IVt9<>spj z=ryLHYJ=?L)?cBkjHC6h&ycQl9Etf1IY_As zbR!RZNN_z_s8<56mjsW?e-qV%?t|+^A@YjfM18{d!Sxahzi58y;Cdm0JoAn+R-Tw?4UY+RS=^7`b zT;qAK9p7D#+x@=4WZOx znKSHo{_(m=rL_XB_O^UY#N>|0EZu7=n_u_%&A~Iu(J;Fy6ofKccLUuHWGtU2_C3!= zmK?r67Ct^^w=85|RmFMlCKA9R{N6-<#2MZwvy$4FyC1;fWe}8723gDQi zWBJ6H;t3NAFvn7=ggvTXf)H>V!nNdrWae4tmkbZhrQO@88md>lUp446_e9z*dx$yo z2{`d?>z9Cr)MR%hLK24v)P#gWCV&OC;Kyb5qY<&oX9g$(9>>on`Kl=bA@h+d zY@||&eq8T1>~jnVcKKjW$|w4YIji?-h{f|ccG>{ zf!oXczVx3au+6*~Z_a+q;vVjrOxw>Pgt(@|knYl;+L`rv2vG;-LKr~P6j~03(PRpQ zb%hW<2{m>g2SiUtiG~EK?lx3U zrS1?;9o+W(i#0jv`IkN$m%Q1sbd1s5-7YdpZDDfqomV;Hyfq|tkxJ%msE!e(rj{$= z)8EP7I(Ik5ZT0cz{!C% zDd93n_@wyuQU=6Uo9Q%ItsXXWMH4^;4ri_hH!idGLrT7UGC&ZIKue47ot2RRAv7$> zMI~?;H1X0)e-q7i4-O;EG|evCw2-jkX6xz*89S|(-WaZec`#K#Ua%5KjDM|}V+DC+ALi;jw(GX+D9>V>c^ z9MdPdBV*dxp+ezUf>N94cJlgBIHy|~#Dq&YRmyHVs_p{N3%wB3FBbl>D}!RSuremb z>T58aJ4nl&Sj*P7M4*scGqk>~92&l_3R z7Bc5l-qHoe!*cQEK(ILc^2B%w{@6v2qx`tf3*#;{t0|*b{{EfukHLqh*PTga37(|weVC#SlcgkM+5bj4k(SHIL_oI@oS_blA|i>I%fwejmDsZ~+fHAXnGY;^L5l9|Nz z(4dC$+|zRlH3VhpY=4CAd|A?3s6^7V_E?puC;(_Nk0GaD$NN0O29R)QS@1mS`xO|o zaf&bnw(puJA3EUBD@SINww@Pp#5b@Xh0g~uVCsj#E+4`(p23*8#~ko zCzEQEW&ggDSU7TQW@#@-Y_}1dFxz!ILM-N=44%XyOHfPtz|{71iSmc{6JJ=}Z)BzZ ze&S0SACmu3&vIEWCr_(~V0pU?nJfrDp4=^#DmgC6aAl7Q3(h`@5_-xxfB@tJi|Eb| zA3puXmJ|%F*4JlScKk#=Qvb%7x1Gw%}w%AKo9UzE0Tb8TOK z^u@JtE&ldNe7sR$YzTx$%#H}Rc@AmC2L~fYWP6WX7b_o*bxsuR_)XCODZw1ukOD}C zJ%?*kWe|D3?ZETEL^<8GC1g1mAtlI%gCcTY}@OAH0?rO2K;~azO}h+99jFX=+mh^Tf14@FXye9DgXp#Qk6+^5@#=; zT#BPiTvWk+qkx+ohPY9Ftrh%Ddp{WibyW&E2jqdf7e)=ejL{r*Z_eRzMZ^qY5A z+91 zXw0iuUbZW%W7`Uayp3S3BGWc3v><(v(c~%vprjU=nAOr*EPZR|R9K{N{(s-6HlxxR zktNA}DX;8U%^X^5voaLkHj)c1ZRV$`-y>u>x1KM(&g z{+`eY6t*N+ztH16j}PzllO0_ia&1{wzvo{BYjK7ZXI5D5dbC$p-A<$-$NEUB_72*K zDPbsmFnTfv5T7uRhvA0EtR+pVFA1oT1U&2C8Fu}g0RSygz30fG2q>p}nKR%G=;!HO z?lJwMcv9%}C|{7~K5;glPdqhwz$DCmyNym500_WLgWe^jv_ zogq*%FIGR}S3BDo0_C}UXkYUTfekZIb;`eyy|8*|quHyM#A1XiCZZ$~7|`wpZwFY8 z?voMTm8*sE+Oi^I{$oU~76W|7cbZuQ5^W)@48_SJ{W@6=RB(liirMBeC*9`B&8Wnn z+p?@xy?9QU3Y*`q-?Pdbg;ApuYYZ&qS$3vG8_YpPSfEJhzm4hdi8Zwl69Hq^6(==U zh?sn>f8?p)A!R)^(MG-i`8=I{^=HY;;A0#vy%@c@ALVVD{ZV~tZStz7E*BhX3i) zcs|bW{=+j_{AF}Ex&Jl4#h0vldg*_^7`gmgCU5U-dKWamSkkY4IB&_85~7Xm(h1m%MqUXGMnTL{4#!+EQ@1#%)Nj2vRxrvBV>x)O#PObZ++f|+i<%l z+#srgL$2VWLO@+VnetQ4cC{TnPIMU&5Y{J_ z;f_)3cL#bN7y^tPq{-_H_A?Wy?mBYs(fNT)q#|i?%SbG3fwwOHx%-LVCm-jL>FvFb zUW+YsfBWrh83a7ND8@=41{HuXb+CW3*;hAJh*%|vQ88=V(=N)Sghq?hbMH`I5FxPj ztRG6`de#r&e4t;6epSu1oJGw0DUX=_G2GoBh4m=|7QcN|Cu(_$4*?dx?2*7so-BYqd->*VV+IOd1P&!2B^ zZgYtan;P-f5GEo4dhHp1?x&Bxei@Y#@f@Oe`2yV2bF2qZ@;?#=t*rh z6<`HOUn2UrrY=YRZ+{+{*~FWuarHK$w_NY-)p|+g<@zx~RLEJ5foYc&wrT%&)z2Hf zo{qw%^fIPhu9)L$Az{ziYx`HBa&woVtjYEG`_no0;&kb{*b3hCj;n27@#e>dT)l{; ziXPYfPU5(zM!>V{i$5$vjHNK6j4H62@g5d0i%{C~%H-2^d3yB22t?)F!c22vV$Q}P zC02x*T>a|v_3|{}hZu9Lb4?>rWa7JpyuKq9GDgeMi5`PPE95{+S`5tM)`($?qKYUs zRW^NE6p0(is8cq^mmOq-IhD5f9JeSN zCo;=4!pEh|_h87|9$5A6r`$wl_U|u^jk1jrC}~Ll7MC?_<8S`-CdxPh4i(`h=J2>x zUa&)2v;Z>iFplB!Xl0KW7I5sXLHyH)_dmaRclF<)rcyf$k(bzR5gY{<_KUv5RB?BobLvrN&epUi58;1Rz1mbWFq-i9vmz>)mn(bv_;a zY_iea_>ajfa_f-LOb9{SO?K)ruDa?)D~RgFZE=Wd>xe*6NjBXmE~11|b0m#uON!e% zcp3u342n_geC-1>A2w|Dy}XjJ?>q6zJ8U754(q6sr;T2e;4=zkK{mA6H3y&!6kkt) z4x@mpHHy3S-j*g$PZoIMzZ!H%?xh;97jltN(xP4QH z+ge+I@>vlEdc%BShq**Ci_C*Frs9h$(?4L8Zb}*7_*A5)S00Af z8}WTo64sYB+AHLnLO4Yx*b_xbr4OinMQGJ=t?*}j6OW?^t*QR_kG=qF5juX%$Kv)r z|HpiMdl!=-z->N>rAUImi&GrJB{W>cm|CYeWlz7_uQ+8RR_sxXZ+SZ_l90pjM%s^y zSR{R0?fCgDl0ME#qyO{E?D+|8+37PASmpaPq0;d1zWI;p@zd>eREa^J|M=X*JT3;~ z5(Hg@KhSY13fo+E_I~F0PSM{)^~-_x@hR`FI`n&gH;A{AY2wAco&Z7VrBQvxQ#07mbRXlWoe+Jq12dNK3Au z{0sz{yPgTdvbE=3ekQaphVnCL-gUW|bDiJtX=560ZzTIJuC;6RQrNDqz7$wQsx=4* z;5bTC40c5`PQku8{xteLn|>`gg33LzWi{osv8^5oiWP<`^sv2CwDX|h#F&UIlQ9dh zIzzFyWo-iQ>J=MhifA2^Jc~H70gbcO_wq`bzE`)CK+A-ZG1&^c7s+)GeXDxWFB(oQ zFg0uC+4p^NcN)P#^W30Ufr9hrH}9{m%`5f({q@^-d57$~(VxrD{3Sv?31J`?S&~GK z^2+AvUF{=qELrfqE66xR7pAK#mHF|5`B~*-=>c}v0i6^#px9ZgEsV+LI5>|kY(QVV zdHvHO>1c_8iWyVr@^I1VK}mOv>7U0p^T+XBIigWTVyVk8cBwsb&Eac0R@Hq1nE}wa zfrz&%WQix@g%%Mcc<%!iW-uU#by@kbdm>&RC_L<0hpZHI-J}&=VHAB>5)l=App)A` z{W-ob=s-PP=r&W4&$TH@ie8LVXi4A__31C3V${DrZ{f$=zka%%O+s_WcZ(KAF@-?H zA)!H5olV#{-GWvi;i9Ua?rzz+mVSj$b1I^a%TByv$ru8n%i7W16E2qXskSFrdB-lE z#kIsAPt7#

Jrv^tF2JEey{e(WA^to^8zkkB^_dpC5nvl&i@yX?B(6JVq28=saA6?ecM)S`?d$)+FM1Rd9e5b++>e zYI6HqTs>G{@f?DH*1g;zUt7|q9Pi@AAvTU5a!6bzNK{&VM{CRKc2Z-pmMAsm?1*+A zrv-Hhi}SD(D;EWP={RS;gf-86qJoJEGCL;tU*i)MQF`1O9}`r;SGf#>vRoi87k|N49J zuLXK%wf#TMZa?|I{+j>H-%&I+U(l5sJ8QodMUlXb-(tuYhlwlio3BMBP!kxiXvMFJ zZFvWfRpo44&QXmi5Qp}(u2!E9U%M&(EW346N`**THR=yvyinFp&O$6$3NH-u-QANY zeb>r&Z|q+vgP0XDR($^uY%e_5ZVx-1EB!51Iti1nZ*^0O-o>4{&dd-=6sIdGF z66<0D2QltZ7g?kWw5BZtKyDb0q5!*0ZE00c_G?B3A9e{dllNmxzou&Y=T4H2qoq{wQ3t> zUa$->6=g}z;*_B*q*Ecct)3Hw^2KaRC4}lr0uyX9;QCj>qjs?zXpqcg@ zU?jvaD!aSe@&4PNH`u9s4Z>Uitw2)0r$@Q7$gS#ZJfHZg$$3=nGeT~C=3o>b9+r?| zduXSr`wpikdW=J8_aXVXX~9_OW0E2$`1wP31FTU9k;94y=*8p7AG0 z+aTofGogL;LCD`{xAV<>(keW0nOszLsRVgfT?(Y2JDipfPUz%}y4|CY|KUe)Gheq3 z2$@rxNhxAB(VJVBGXWuU#FRFv@ggT%wA2A1Gbp9fQB{(-q1ry}1+4VFyppExJ6yIS z7BLu;=vE38G9{4fENWTZ0cdtL7zVU=xZ@S|*yv*lecZQn zita*Vq%X>hM)1f|W0#@`4Y9q9Sm-cs7Z`bXz+mCT8x2@>$A~7(VR0TFyuScnuAL%A z$LunnG#DU;X4JvdE8~i_?}5hGkb3o@!5XADxZQ`HL48Mv-UvOx6f(!74v>;fw@A&% zFce6~go(RbcCPST?l?dM`EK@l`T~QvQ=;9i@&TT^GK>Q}cPXy{p1Vi9t;2IUF&IMK zB{jzY&)p#kB@D-=-SfN4hvz=z{fZiHNigA|K-T3Bcl*%X*x^Prj{;$q8hqYhToxl+ z-A=%TP(>Bv{;ymkG@v#t7f=d`jY;n4IA*O6%k3rT7Qm9PcLQ`=p82icOp2|n;>AaT zZiym>XIZMWYro639QHPHU^TFg@)#U{&7{b6y}bw|Qq3uLAp1HP`=y(R9qF`f=r_g} zaBHlj1GHtoS~k^_mN33lw4BI|m;stco#78^aX}bggBWz?fHmlkXT^d_tz)(&FW9pl z<3&&DJ)?0`J;paNuCbI0|z0Jr5@Fi0ztqd=s>kVPJZLZ(>?%B5}LylAYk2u%p;mcHEhD!k+BV z{0u>@NX$lEx8vOwz9|o}F=G7#(6x?&#%2%m~Tw!DkRcIMab1l8#t}i zOQC@~CYH>&aQyu7OKc@9fbki$Zv|6YamksQttJsJv(zAASOVFfgJRR zhdM?jcCSq8fS7!7S;)eH>!H4Pe2Bc^8ibR3N6X@Y%454PH~do!KVZX{SYR zG6)ACPcbMBh%w<%^LY#xiy5+p$Cag<>EHZ0_u1%=t-8THemw^Mn?q6!)p2 z7~hKUvUf;PH-^O7-M-cJDD%LAbi9}@+2>`IbYQg=}syxY}=I&|GZwaCW$_AC`;>z#O6?Cp~b{zX&J^*teHaw>+?x$rum}T z8PNh5V^E8OYT>gfiv5nR5}LU-mNR`|m!vl?lxV7?F)`8J1-sDX?((cFG_>p|nZ+5^ z≪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

{ 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

Aeo$(`EBOcmD@^Jj_pZM5)3zX(8a_S>%;YFJ zQHi8@&yXs0e%|Ae)oZ-p73glL}bE8?+qbZ87*CE*NJX}0lD_lH9M6MtsNwyA!+@RYmDrv1vX9V1h zrTJ&3XL}&R)DMg5_~d1iMli;B!UONY_IO3xPN&EdZtsL{>Q+602k>g!wv<{>WuVNv z>6PW`jVJzR@W$2nQZxQsG3Mg%O$*S1zSXFUnKXG2iDt*eEYXSN?mCJT-m*SzL9B4A zC8(9N%`@&jw9}Kye%wEC6<>E4hO>$V73ri9W9Dc;3u-0bT8~|Acx!K6kGKUG&Q4Dx z{8v=kDoj&grSz%vU@|vq4K>li_5czuw%Tk&Z?TaT_4mvBbv>*_`bR#ZPPV?OZLuKA z?kmY@aIonkbgK8Vw3}2FuBjYy&&Bm^pIgqT4 zue+}Z={w$YSYkL83>Bk7&;`GaE_}RTHYf%I=k~~+qVmc7oSL4DQ!3&J=-&9 z#c3lgm!71=nFtJ7TV@XMbf(QORFVB|^i^VJJ({%O#+phT752o*BT|u|2I;JE>u2a_ zC-I`vy9Bj1WgTkCkB(R)!cnRA;b^S16|+&c$wdUW5iTHoLiT&}E=J{yPXFN;1nQvN z*-PPF+~JYw<15i;qTTcT&~CB)8wQ`E0B`3QvzPWva(&DWf3&@qC;<}bbK<6e_-ww1 zK>l}HChxGxM`ib!QD;|G!in|B0_>W`YqYOb)8V!>T6$oKD+#v!1gMqDXjAqaTCK0n}$)6fI)u*>Qjn0 zQz`+zzDH$yn%{GnIE@X-Sa43STC$-|*(yK&l#5+orR=PKKJ7LB@h<+T5;bLU>C5T-G zcE1ozU(}qQY-WyWtiN29bMPnuwv)bwS#NTgBMT3+!k+-;TL)}I% zV#@3Oj>h)y_r|RK^VNS53}Xgfm|{BNMBYu{rvsHKu~tFtL0tXVCHstv1C-xgrh-0S z@mx559*y%U=?Nt=|HHbeTTOSwKddp^@@9+t$&{OpHqvf#irDffp&)Ub!K|Jcq*mzx z_~en?{}24aLg3xKr7YQB&}{d*mW5l$bxQou(-YL!W+Bhzpxr$=vQ;o5lVPQ7(=M>7K0Pq)ktYKV3MH}E#e#hQns;6 z)E#{__3C3PXLeuC@T_k2T14vGlX0q)VTw?ChEheidq~r(MEb~?_4sq&!5>9htRG$- z-n=-Aq7+q9t|^kLP{t`oC+}WxTs=LRhHdhwNkt-M9lI`U{4ykpVoAjBI}$oNYW0(*g#hJGuy9Y?V&%(P_^p;k^AB+ZOP$)`V>M2+vC!j4d$`i7N)XP@H_UcP z*OZ4{9^w0*bhH}Y4sm2=E9PI2tU)x02IUCAJ9XB*(%0o=whs~Tx~Hz@D8O9!&me;o zPbI5V5)0cFouc#g26wlEYZB;!51`#!@t0h(B&?z7u{mKAjHZ21m_g2z>C+EVJUpr7 z1>n*S^gMc(0NEbBd{BPQZ>u9~SK~&lV_6%o75vn*JOpo(4dvQ5?gZASa6WylHsxlt z;M23m<|PdPq~cM>J;(y}mn^3IMwj4`COrwRg!j`6 zpB*CLAz2@rYw%xzhAFZKDuix}JQwe*;-6EqUUd>)ruZP@3TTN-m|0$7Pw*Ds_E535 zJPCv0M+N}K-xoJi{5$i}) zv^Vg00!7fd)-->o z(9VB%iX>lL#AoaFxr?U*-u{q2sRiXM9*o8Z1#GtMqEOzK1n$+rS`GVxH&Q!^K=R0q zwr6OzGONo42rky3k$O-;9b3?hN7TjEk_y|y z#sv?eb_RWRW>||TcJ;YSRnKszxm-)@!@)p`7DltlT3ajxJ-R-Sk!v4^2+W@J@x@{E z>D7wEZhNyXSCKvFb$n;V;ui~abmqlFN3&*e%Xj7RGC4`QJY}*r-&Z**NDz%QG1zfY{0ieXVjHt_imXr0VQeAu#;gwa5Zv@~xSt z?KbbxogqQcS~P*$2b7E=36oV^vv0KbhW}4(X`$v|cE2V3i(sS2B<_E7dA7v1B=C`? z{mD`USfWPt{}JXp^pzyMCTZ~hr?$jFi3on;B4!Uu!M4J?{m)=-js!F7D8v^9DAXpA z&zmMZIv9UjTz_>vB>sH?AP-Vz!|ar^bVQt@p_0V#8vh+Jz_4pi0;y?#utDBG%xL4B z9uRhb0b$z;k$*y&Vzp5B6gZ8l)`7+H(v!sEjX4H_8;v%{{!=ryx zvW#FM2TeHjSOT!=CHyCh-qhlcz!Wh;Qyg)YPFPyN(s)u}(FLzlU9~H;@!!_}_q z!b;E#)OzyiYk7R%m5mcAGXE}kQMt_|Q?C|@r^13GkA*acgZR=-qA{d(E{WxB);UFr zWOKif0=}D{f9z=_W4#980F~62%iDR7d$61}wSJ)SbCXr4*Tn5?e^oSaofxvD@DlZl zt*+hAzi@)cK)?;aAf%K;QtM36MqXv!Z)}Tw{2x!@q`60`H>5h43H-K_4!cZLN0LSU z*(*)ZEQo%LOVXfIY*aeyKK;z4nYBSR8yozOAXgLCmFuh~>M(+TR_7{_!Hrw6<;H4V zLU&`fsHm6BGOl%3m=Ts8Oq(w@sqy}ypPDVTbq$r9e!>;Z%8El#s9%h2K`Q;{LErek zAeFJ@Yk2H(!|u8$F+r9iqrFFJA1C6y^J}Q7rAaIm;T7or^He9uo@btJ++HfXxpi}! zR9&`|Jqg-xc9N3Cz5_0zou}Pk3!8nuA|=2ocuSfH#fS-|Tg7IWztfz`HZpjt*8+;rEl^v-B%A@B z0QC)h_+9%9{}01(AEkveMVb54&NNmxgv>v`ehIF&26i~eh-R<%*~VJ{SB+Mc=7LnN z-b-_A;+;OxE;6!%q9^$b%McnDX^w3e_zZ(M?rfC|gT{aJgq2%UC5CpAPg0eoAWv97 zMU!f26=7vqScs7E==ihWfpQ3OeP9C)9GcF3%(Quu*X z*^xXK<$hgU32!6U&+W^EpE+}k&@?d^zhcadDT{gtRf11Z^>Oqm+{h|R$b(vnf9hZw z72|05w<2pR-RvA??cdDfpYxW8emcr^XAfPc&a~LvdYGHZfX^=FSgTix_194Ub=#WW zGHR`UKJ%l7lJPGirCIkGJp3rL%{+=n9-6}xchu&>BWSBJg^Es62J6tP&^2D$N#W^) zj|FFiwLnZ>fD8(o*Bv7Z6qpxG0ej5G*1b^e-6fF9r=hlz=D4t`?Rf30DZ%Q!48Ij` zpYrdWI;4%hQ7D*))Wr1g9%8wb@~hweAB7|8{Lo)hO5_Vb!kuPx@zNhTTxzy_MIlt#y9QMc*#V3=aZoMf$BRwJN;*pw(jvM|K}O z_k3XR@K$Txi7&K9jBwT*Eg2*}l8#n%HQUA)T?gyTQ*ORdL`qA^|Jo3XmDDt#98qKY z$l~DF5>24E%rHlvwVX-p7qjC3xh45)Y{FQ_*9rY3*c|?YNW3ScjIY!2jj=JM2Azf2 zR}YycIv=Pto%LHZ4j!;wz2gKyk1{jIxvv9p_V9*Ah~Rt2IE6FbnJ+glr8EW^u1i{PH4)#=Or!jX+olDHy z2qy+r+&$T|p_waf6s^MR8dAlQNbeqhkBg-XNj@PunlH*;p8_fV)ajh zGk^krFETs1U81i=MUhBqRy@T8J;6nDy@+k zFkEX(*oT48XMw;MlaGe(`rQD8Kfy~BE*oqGB12-15{EPRB0{J*;A_CGT{os7eXWGn z^)`Q?B!YuR$>DR5ToUW-CqOPU3a>&TCGnnMVY-;Bmg>R_KpF>gb-6_`^sC7O$zYrq zYOF*%=yZK9OYmjUBJqqa&l0mxbwbp(i{XwcAk^8+G+XgPj&9$|)GBOImf`<(Xq)y| z?d{E<|`R+vO}oJKgd)4xWz5L^EuVooI{5k9kRT7fTtj8tkzyY z)m=9yUB>(rlC=}j=rn*^(G}lTXBz4wXN84)kg#QWOCKc~Pg36*^(?YqkHJ`_ua+UB zM9d1$p@&Dw1F)nU3AhJ=AD`*DQLBJh1i~UI6Sa+4ra&G``SsIkGF(O@WO(Olm-NYB zfPh`}7@ZIH`^WG_A@Kyl@cXBEAXz91QJrcSt~JI*{2iX4ZX<65baIO0Zo?O|oiMQ- z>-VQh4-jn$(8}SRlnvzN(R*=a5(SC8N1^6F0~uB!h%&4;?Co!cc-!6=q;+MCJjm2E%8ZF2|o{c5besh zyeH;5<{z#Y5}F>&zDjTaTR?PrwB5WQ(36~}-ScqUc;dQRTy6uy-N$~L6hT4~p$WT$ zEca8M9aQFQLqo`kh_>r;OK_x@y#3u<1*@vK{e*0^qUDMKfrbul^oii6Gwt2v1zi>C znFUTotaK?T(gqK2`Zg(I2fb~b`nUO9L9j3ZoAB*hUsaqGJH~Z{bWPNt{<%Jqdgp^m z#Z)cCi1Pcg7@fJq05zg7`3DfQ&VZc4YJ%+y`+IC2oygi~31HnU)I>+nX%M;Z$c^>0 z3!g3P>$%&%s=R342|!O#{3ro_k&1Fk+Sg2z{G;9&zot>L5CF)X3zfmtAlzy;#li9I2!Psx>+rJl8 zF%#+H5=%1o_8d1FnkL69(|CLIJR&-|#xO*$=gPPH#Fot)F|!|aO_;;$vAmh4fZ|f= zlO{t9JNu3U{_x zJEb@zb+8*3gf(>H8rOJFB|70i`S0L^z+^-<|0PhNM8rjR$%K+sKySeYCEEUj| z*OjA^EJz3q>3Eab!BF)5CqAwE^zDv4%TI4RU`a<%E>3U!Dv~-2(vKi;S0HBeXKL4W z&=zWx5yo^$?ge;D+XEkn2QKev{Jzd3D}*qHD|l<{wDh@mF_0TOrD=p#1|lw@`sxoX z7^ABX5n+1m*3(&g>FhgPIFOB2&2Ys7rR3 z@wI-2s@CwMul&;#8+rlpGnEo!RbUkD>!|Hzz~I%-y67O`XfS%PnR2zmHDGEdE6M+< zBL@#Q(#Rfzm{m<4cYx75zs3VUdq;3td@+%)h3TR5FA-?rCY{!E)PFrR z`=uVtrdp{n$C3A8YS^8022{vUE@&|3qhm5trLk)v^=U$?vvur+k|T@Oy8ENzjFcGf zd!->!WG4fUOY1%8EFS6`>LieH`PDq=&e=l~i!rvh9isU}osk3o;drS9IB$w|*VR5i z06;w1-LS$R1hD65oRjN1DXPPiwaZrb+H*K#P!ndbk4k!Ta;RVE97V1}eft0MhiO)* z_8|=6J3nB9a!4(>Ljyt@Em?l&!jKXj7g~nHC4FJ-aS}qs$6y?hu5rDk#S@h*w)!)-34}xw`NuX#@yX(1{}9a2447?1~FM zAURhqN!|*RyTCA<5dQc#b^G|@#M(qj4WE89X#pN(ltwP!pi0Kqp|}u*9f4B)UZU0wmhX7=MXdE1 zr#Q{hLJ|Ey{IJ2B$A^iWgid3xJeI-{8P3tBjYK;y!pa?6vXh;&8CNl>8xx3g6Z_ri zis((tC3m|G-MWb>1@iw8iCB?5sHFg0#APe%yUe-lWGviy`d*)u5zRxn+aA9_sq!uf*;>Lbhs=s&wQ5YPUEx#F-m_z`@arJ# zXL!TgWg*;gjhlD+*p)M7t=uRF*3PkdC!xugJJfJitm*Ftu}#6M-RjC6885;k1RSV} z-hCm971E?)#1*68I!Q~NCaA#4QzC-hP3t-=genFEzG};;noPO1BL3M*Z9i37u8R&e zJ_IevvSupG&@|~}KPh-x0SxmOR_RU3?K>Bid0Jru6EQUWjsUC88aBo(gA46i9{M?E zs})y=V;tgejBEI*0CV!__5<0Q6~}bM9xBT;%X33BCN4Jd1;Vf@jT#^3UKsGF%K$kPdr_$&KbGB(4TMumB))C~yRUete4t)r%2&WPD=y zYAS3KF@)hjBwa zKnEQNkFH3;5Ov$z#iMQ3(W4DevqiN2Y&LWC73+<}VMEtjSUnweN|FCtIJt7%T9DHhK8K!kNTZNF+YmddE>s4BH zji*GUDaGKqy>V@vMnYJkQP33GtZq6N)MDw%*^A_NegqI(gk=lXPp#lOE1P8OjRT{g z7s%A~*Uk}L28dm{KDtJ)cWFqKR&7$L5sZ!^cbuv5opDn2F|k(O6yp%}`mBlmsFy}+ z2X0a4v1H0B)1^n`I~|UtaN4S6S4$nS)(bP5b3_j%+f?OSJsFP+ zOeDa8%V8?lt@M>e(Tctx0kj|))@Ywro78gdIm`60NsPJTN0S+tx0a5J-uK*Q-x~CY zY~NGM57)PI-#n}Sp{W`Kgj#n;5C{%iNvSY3N;2+mb}8sO8|5qaX_9lg|0-Fx-&M^% z+iV+hJzCC5omiAu!cR{mYr>B@xTThR_mJ`OtEzplZx~LhxAJcFXa=X9zfpcuYr!Zl zgIt)E6Aa8RctnT6KT^H{cN6-vJmyimop|nG4H|T(Ktc5}b>Zi}sv%pfXKhqP392q( zl(%p5&K#i#9vjQFu5W#1IJEVy^!iH`NckjtcGCyjX~@)=I^3$1$RDoKMA>wa+kT2l z?Gm#AY$00L%A&wU$2)3!YXnly^F5OJVo`+upm(8R+Heg%gGj>oP-0xj!56BB>P3M& z>oSl&&3)hhGIH#}7EV@y)H+6J$FxI#JybeMtR;5j!+9l4$)>PD*J%&nq1$B`V8A5! zngzUe$LA0I)6IdrX)+C`f?0a`r@C}~%QG`51a(R;ph1G@j=4R9v8zUW{S_S(Orh+e zEe~_uhZJ50nbaS=zW;ZdCE62X-{e%dtA14ag6zyqt0LVF(@Bm#1Abx~?lP?DecQ%V zbaDN26^CRc%p6do;XfiUWkJvYI?^F!vsk|^_~#iR#Zt-26ZnYK!=b=!%KXVSvN@&e z>FsrUcUSM@tu8R`e2et=*c~OPYw@N5N}Ig|zg`SPd449)Cgze!f;eKZR8Z6V>WIyo zYe(?h@E?L}Le++L-Vi1Kk->B!6O{{FZ7DtkDcR@52jms@8_NXNu!;R9#u68sD5id=jp>GFur zHYyoM>-a@(gV(c*_KWKxy*@oTpOF*F{^&$XN}-}6LUES6b*CB1U^dk!9*0>(u{#C7 zvP_+xlH{ec(3b5CsgXAMAiVVmMPJz{g1oi_!02T!4hBUfJuTQ19z2@4fngh}%xu{6 zYtLCVKjlMp(UUaZL$%9In#k3ZA!-+>u*J}c(*JmWbLpP{ae8HXae2Le?cP(?eL1=L zf<|%J2W_c5t)NY~}ku=>jv6m6;XIo6}=QU(n)u$F=OmY-VA8{phfIRnxEWAa00Ccg-oWagURoGVd zbjhdDC452Ad*K}+Ourl!!u*bW1Ndh>hpz4a6@JEL|Bn0Weg#xLDWK1l6->M8SS3ya z#vI#M6+#3a2QRPf{oHmM}c}xNg=|4T{@|BZK53@>K7LhLmNyeLx=iKnZGHfe_BP)Ef~or zVxt|8XdNlX4>WyHfRxQ5Ias>_LqSlg{<6nMzBB6Mp-V3!L@$*& z&F)*VTr_ad7TyUxp?%Y-UA|TnNK7=71wLVN*~u*d({j;jQQ)Mb#Se~`_^!S+^;;+1 zyw}p7D@sZt?gn)#Jj8{q@e~K@JK191uuBzePaizAQGNa55Q{8m!6U#+y{s1C^WId0 z4qAbj9h*p}3w5k6zki9c2!iyvgsTHx!`Q9>Cfa9ph27wjeV9%Fg50A~F2r=hF$gKN zHWuU2b|&cbU5gFCcu>9#ksWJ9I!queNOx=LV|*Jw>J>KS5lWusf~i2_lE~FUU{9zFe%?KCGyh2QS_!hzhaKCeCV%+4?66X~ zEUW&Hi&$(mab{r@@y@?*wwDU*z%s~{Cu`Q8!Z>%I3-r{$sGI(vpeTi&4oCJ7<%kOy z@;7-ps?JhjDPASd6fksqoE}4aR)DC}1-tpvQ(?{^aU(_!o4EgSa&xNm=k+NxBgjW0G zt|L-Ju6R`4Lu*7g$|*ux;yY7GVVfl1%W4~$+7qAFk7vAzuUK?4#+%)S(KpALM&=MR z9RCP!?+;{TIPV5;858`whb`B4c6N)-x8FFAspoMl%p*YA4F=N(RrK-|I)f3|Wq+J> zgFLeGN|B~vj~GE7KJa*Xorb~N+*M;(z|jd84w5?%shfsdGe_!_?W1qht2|ud6V&Tw zH(qP}=wETq?oJ2W*HQ<0S;UmGkb`hKt|Y3kUOkTV$O!@A(_uZ4U*iktV#no5JYYi% zpS~y*)T^}RDw}anX?>(%eGWZCJz*5L3b5K1Y=t1;*Zqlvp5ACZ3BpZ(-{O+(J;w5+ zFI%4N&&tL{Ms$*9^`KXDgNz=b^~TUMFddq40c^%59Z{}BZ+HX_ne|gu=F6P`_3m3w!vB{&&@@>u8Sf-Qv0q5nWUgbrJNkj*T?RBI z%`jKPD0c*J%If=rH1NdS9HL)m#6^fMIMXZD3sM(xK*}OZ^g1wbAsV6;Aj3*bLqwOJ z;yX?dTkLHqB@BVIo!25os2I&UWvpmO=H>+U_IWD)doKm0 zC@~Nx6P1G4NL>N8@CY+SDSyM=~U#JwwYR zN#ww0I`-Zocex*ls*VYToVv zNBiv&Zm~7npay#*MU4_Zjq;>5dV9;2!0pjE2R?%U?SW+>NK02PY037FIq_cNCbKSf zb{l}lD13-N7mi_jJZ$$LQ{xTonNP)bEN4Di!O+~$-=g50KH?RJm#Ui;o6~_6d%@6d zGbPIYitSXwfl$6s^lH`Yo#3si#4B!loMmOlq%)`TYR;@nwqcnUFgQzX?dBNka2e6M z5Zt}xW`_3sRWxX065Dw**@1STr2S4HW9CHzbP?*$7%?Yn@U)1DpAmHZpI6BOd{x2k zI2&nrcPd-G<7V`1(4-g^%aZUNeO_{q50Mh`@TaIh`*v`jSDSE;HQhk@kh-@49eos4 zxOvdm46T(Ux`mex{=RQU3v%=oH_gfL5pGg!jEZ?w++=9zYv!E3&Gf3aUke-&mlMDtbF2^SZi55K+vTbJ{Z{D%?-B*U2WaIMr_Zydq5(w#vU(1njj_UG*t`Q zzn{Bbw~n}bPg$J%1um(w=GptHLzWw{&*nWpnR-ft6~ev$0<*P&o)!kPTGZ(Hv;w}j z>5d=U^`px2>Yv)kej^-CO9zHC?T4QVa95wjm7smD1z}gR_?;fq-!f%iV%|HS1?p)g z_)r^9w>nnb#we8OZQ#|-y`HRZvHH7AM=}gOi`QK$ODVaVxV9fAIuTVEhovr)T*ho+ zdmIs<_Lpol^tIPPa6dGBCc$Y|DwIaftUi%g3Cq^hFt;3Qwbt52<$ z__wWW_4n63!%ZN|Cdc+JtS&C!e%DY-j=wz+L9&@PG_r8MXhfzeu@g>AO6>Uij93Gx z7bf}zEVUX}JMs~+i@ZUJs>l;gEp}2oOij2YBs~S;#k|R|gsW1kd(zafjInB0tdXll zXwC+fxi$CCviq6dBbyiK8>W|#3;V&p?QfQscU3QQa3w?<`h0(;q zCoaJnY0nq52hZEN2@pJkIrQkRb^E>6Q!{k-Ar9g7kgC7UJw6&&7w} zB6w2<@ffQUC(dWLbTk~Xcn*uvR62iVKyWXAeVzN6N;Z3wg#VI@rIg2*isjM9O5WV` zo(Pzy)}GY+D1bQ{T@ca}iyDmdqeot^z8U>gUT!3O?cKB=^B!CDAV*aJ`@3_i2bRPpNuCVSlCCvL2qX{q8meln=)8s zA+ZiW2W9O`{bXkJr3{2qA@&TSOcKTHHKN0V!8zo!AFNf*&YGdy&#;@FluO-yI6&L% z=cg|~3V_QbI@M(~8Kf0ApY%z_zfE*)uJ#u*PUY|XGInXq@bSGLBsPO{bpQ1Ij0|n; zf+2y%k{jb_4kaJq=)8~t+5LKaUY*LG=Vew+@puhKIh%?!t%@si96S?9Le!wXX;#h5 z!Uo4xH z5-2xiApLu)Z(hX|fW*8_F>-{zt}D904BI+3nEm8n_+rwMH#7e-dLV>9%H-yKt?;Yt zom8&FcT9JFB~CTVt}Ch}rS)s}TQdsbiP%ijt%5i0V!axBtWuY0r1C-J0Wl)*$!C0K z%I^#wN{hv+r9_o)vb=EWe6V!4r4-HG-!$2z9YrS6^{XN4dg1W0(!uIFqR=u;$#tt4 zhBn%Y2k(F}Q_AY2&P6pkjwa=LdTN(GYzvNH-sUKok|)tyN$C@)MAD&HaFV5;%mJ{A zzR~C>sy0JC$tG6pNW%y6R+c<|w)TXL`z7N}oW|p~`|7%vI77&27VS+yT^*Jv?5=0| z&R6CO-QCyh_^MDH4(#Pi7XTYxSn&7d#yooVAjhVm zx?(c*;T3#Kuz zu&XC`pQe79dnJTVT-8qY{MXgXr#-lQKXeQpCG5zqNVmEAmyF!F6^2oz+f%rOUNe^P zT}yYMEGZGWSoN@aCaum=M$RG-1(uGLER^FO%>sph;vK|O2-yjqYLc-+P^ue!$+6KY z$xr>yfs+V#lL)MqB2#W4V%>g$q+VI{-nBbsESj8JRI%(B`C1FRDzPiv$(zT! zVq=v`!}YPP#X^Kni^s+kzxfhKWN_tfyx34q`Cv{Ce%NcZ!jjDXuiaPh%QM-JbE##R zQ&!XELz8Rut0>NV2dnm7d*&|`&$i})h0FpbH;&#BchMcQ-&{7@Lda+nor{PBd>U;YAFD3%wA+&6KcM-|o zPlQ9kaPBli0tP+r*?$?trqo<~J#pmv>g))Wr}eHo?2_lqRx1YyH-atN$v6H_FO+yK z0l|FSjZVhu^eqTJN&&VBEF{w|!UjrJFd6v~WX$^RLawEvM^O12X@etkM5xxyY94`G zLe}4{UDUUl-H+ZUl1HiWxY-J!&Cj8q z)I0YY(wte=57k{Tdh`DQr9fK0U5CqbXMgM=T<(yr-efcDCNp_p@(^~1ZxG=@gu4~t z#>q?zIh48@Wxkim%t5pV(QcH?G@0iy-)lXWRUZ1&kO@W!1U`Dt(Eq1s5$Ov}8;p)5 zeWJ_8hKtvyS!osHi9D!GwPJ1NKD4^Uqu{*ckG@5#b^fy+iqezecWT&X^VM2m%cx!*kNGaL74Y{ zA&MuI-iy`b{CeP8H(X2HgY)S`9k@^a9ar`0NaWEu0Qm-lIm{Lqa$#=ZVi#P@@F+#H zBDl+T8+$d{IMPbKy2cDs)%&J5moloz?AbMqDpH4w8!aqSe<|+y2;jmG+xdEK-p zM!rKChE%i-96lI_(?0rt{TD=EQ6_vhz>u_OttNB5RZYlqk%j0p&-0qtGtFAOa~L7gF= za7vjugqXIbc8K|pzvVYE|39L|;7S!Aj=ZvdPnp&$BA z0BLtP82-WVpRVB_qTO~$9irV97OB6KpY9btfYGCl$Eei=$nZ(s;6rj9&~StA(BfL{ z=|SqrOlFY)H7RZ%IM*HLl346RG}0~oS~`AZF2(^06jz7P>sV|Zj$j)RR)}!=cmg|_ z);NG=*dea)`kJ7|XoypHY^SQ7GsLNnduE7J+a+~~Q(IW1{!)Irr?PS4w*FC+4B9Y5 z4#XjR!oe%Gd>5JivpJCKiwsx$%Kr}_zt~1D^BJzsxfGKKHC^$@zC4pOsaI;jRR0J` z=;&*-Q3u6jk-)>)4j>L@u8nZa56i}ks!46ry4_nic{n9d^N*|Vx6yjpf0BOkWcn2) zL?_Q&&1T>3MvHGBV??vp6GF7?*^YYVdY%z`q;Wm$1S^OIXSCaY-B9bZC zNq&+Ov=pt%oqn#Y_)gV$aJ8a^{jV6M%>e1IqO|iu<3>4aUA%7P0s@w+8Y5*?eFY2 zdVeP$X_9iOJLGwmCmM_xuUr!{9#vx8SSLXk9d^qpBkA5RHDvDUMx|7$xX3UQA~uU< zZ+y6!j8!A6{A;Ryqfs@ZYvX=Cn^uSRsqx>AXVYmEcWz2Dq|$6?VCOSKiDo<<<6$&e zsebm04Jhz;Oke$ zM2W$Cl0XSdSz2k7gIJKXNSsffokU1_pRz!#+aRi|Y;&n6w9-?3^hU87(F#FI$@=+M zT6yRmaY@%W4I3|oWU^e|NAlN6CkgeDRyJQ2v%Bm&lrED0oG)g-Pp-9^y`9bGS5hL4 zyJ&nfnogE?##OX>h@z}I(o`mb`1rQvSw<#*EDmQw#E@_yK~Z7!k^8VZ`ba)1@J=YR zJ;Gc_Zox;U!&Ugb?TAnmnyQ!kX2k7;dSSd{W#b+FN6j!!1~P~uP+_(2%c*Ws0P#Xn zYT8@{fm_qcPI!-L6^3Mvx`DSHk|l1f{#`}%RlJGhW}GYJmrC<&5{ItMGY4wVJOq30 zN#w~u+^XE(RX@+lPnIw7=+bHU&3Hc#xtMJ%FD2{Bi21r~7wuw}i5ZY6YTT zjdat99Lo#BzDMZCqG}f|sPBPtJvnl#)tnbl%ZxWuNuE%p+uX%ubX%0;Dak2 zW5s+NT9A?^`vr?(em}=>tr>$l}}Hk!5zz(W-(Ij?MEG$lC;HG-l>3UelwfyFd3~a zkDRJ1qr3gCvI!$&zR(?3b+aLL*K=gN7q5(HI=Z@58HxDe_$l>o`I~XHSdAv-8Ps+b z25L&%TbI$pXc4W~1EdF#>&cLpKmzweZmnWsX%Hx7o@>K`xX%J%ZL$YP&;$GPE=j;{ zwyoY!anY#W=#jqO{3z|Ov=Ug0{v_53Dy`qGOefG8^i$JWyTd=t9-`mX zv~^3dV{P0zQ+*q=D4EiOQp%(GJd)nSXlYzUS{0?8|MBeZPW~;Gyxhari^=a&Ad)QU z`YqWVotn3rHFl|R;%0q&azmM)EGL$$*iAN6%I`nDH(n}g z?xl|Xq#jkLT$1F~OvYYSQVk6vH}rOzIoJ%-a)KbscoZTbc$%C#S%a!|?lfP~YL)`N z)ZL6)Aejv;-4myC87- zLepGp1}O^&OXn~DE!3y+-Cun~yi+AZt!&lH6^oUp_GPBh3d$i>9pGiaJo>sA-K`tI zul>>=rJ&DeQ>AUwr||y!kBQs&=m4pYVvDU>kL!CS+KT^cc4f@QW9dOadPy@^Ab}C2 z$6pWRO9&d8Ah_P+yB*AQeQMeeAL)=Qt7YNg_3Y|jDqr*P`udO2#bWaH%?pb%qVG;$ ze@{MfsFh>^M$_@lY!Rn2T*om(D#!o*_3~ykm(jEQ`0@{BDK00Li6wX9D-HjVe7I6K z3zq8cz?&Bjlk3&ZYl_$zov;3K6HUI}tX^Ye%~vms+3T5lM8A1*Cj%V~=(NyBo^ot# zsz+9nm3~OgEFs|SUuUl`ehx0;8O3@#xVwt3UnpDn_1u5+;vbXcWOn8+Mr!|^S=I~V zqxz-#DoeR~#9hwii&W`=zJ&F{h~KOqwiw|K;J7*LKYQQaoHnxU z`&G2_$F59nZl)hnOPXCtl^(4b<~okIADQfP>J%k|urUgtC& zFLD<2l67G5peQEHmh_6k=wT`=y~ZYFyX$<@(H*B#UV8H3SMA`l+|5&W;15^j9}?l| zXoFF&msK{#gJJq^MjRKbF*CVtG8f{wIJRNi)1a%Q#8|fF%ZDt0?^8)D;14@~ zK(i-p)r%?KS~A%EZwD@oX>Ws((sJYd=3Ew#*PK9JQYB zNDfEwYC~Q@T8m*EFyb&sdTLI=XXbV^%vnqY;(Cnw6jGG*BB)Ro5v{ORd1JL=M{w>< z_0wc9BhIBMtT4W+FOK}A7JgjeDo>Kn2qKNAY=&e0J=Nle+xrGWnA};qc@qfX^nU zA}q-ET&kGhFf1+Dg(2)h0YMuWVzL`4uU zCxU2Ia8?Bp`xDL=7f9#?8!pn7<8eU8Bljh<$bpz8&oPf2O7TN$&X*kjIQ(R)tjl4u zz``ph5O>Znu!VwPZjaNcxpUYgwoQr3DbxlZgge)eyIMM-N(OIhsI_qAvb{=kTvcw| z{si;Ixp5TBeIu?4#p}VddEYL}6V7A>Y4$VA98Ubad0el_WU9=~p?y18W|ak0Hwb96 z{R{T(bVFy1xR9C?g%A7k?fAIyr*iFHj%zpR=Bhk9<=HJqf!AyH_EuV7Tdm);W4TiG z7pCeTegXz;mTtH%g6v&Y4%uEDvLay16YxeUFv}A2`r0KO!r!KW>qCw|dBK4P^5tGp z6?oJ~&Q;*?GF=cAcpOwNk1y~5pCF@@zTR=i`sd;(MSo$64h=gDlY5RuH`sZu+_4=1 z@xrjf%J&d#74R=umq&Z+m|P?Luy4oA0{+{o+Y+HuzH z{VF>btRdSMXBX$EYvldjXD-a-4jZP9-T|zaiK%WGbc6K95rBD>!R9PL=5cyX^|W7f zdT_gzoqrT{;$GCwJ0{9Qu%tM@)rcln8HSmd4#hWwD*QZ%T2cHhJLye!<(8tIot!le z@Un}eZ&43+COp#}-N7#8;NO=b&)D&O)7*aJ@6(m%#qfR8>&9OrEfL5&nCX2z+eCbr zpRZMFp&j=7-SH6}?Iap%y(HCoQGO&`t}62ExE%7+Vbtxs(=Nm8F>xw1;al>xXoK4s z_3|?#XEfn))*2(n@OXYM_?}^4^u4)kV5UJ0Th^(SW4q$b1B`ZhAbhjkALb3tn?u>J z9x#amjQ5@9^t9-ED5XtW{M)FTJT4om!;xn!K4fIaLxpt3r9P#$3+jBcOmN0;mauI0 zlX?f!W)KkDa3QTjpHGFND|v|uadxypH|nJFke%!dhXatNB)9tjg~fxB0-J487-jhraMoq(1`7J!6ZB`5*|3s~gG}pnfFW+&kmyXL**4RYY=e`lI;} zJtTu4%~sO?0gxh{bK40(`QB~)f+h+^8wpeYZ)0k!gD$Y6v>=GMVBC^?vugID~!j1Gop$z9xf^%m2gAnOIX zX;VX({bwn%G6yGa4ot_9N4P<-L79V^nuGozX^&bd+#XQudIWTih_VLrY<8P6efLu}p zupD9JbJvs{P==t6h5)}q8h(eNh5&KIG4r06C)$X0C(fwV`U>El?pwa62at5VQoNm8 z`|4c$uphPJPTXo1P^-PqD(Mh{sHj0ahX+PoAos-i{PP>_N>*>ouiNI89q%;YF$P?B z0}N`XqTo>f~uCnC9y?a%-Vdp)9b&75B+~kKL@!d z24S{q`=?#FvY0f~M;@|FRnt=oxsN-q3&8l>d+p{+^XkU)_|?@<02#;F;f7&BDr*%Z z4`T~7O&dh%XwZXT7PGK!FcZ>3ZY|c8C^9D5_6f6DaX1TfgFAR*cV?~7ay1>~+#H$R zhR*7V^p(_Pdzo;V>`A&NkM}dymM@Awt);Ct=brr1T1d@u+`+tUZy;mFbvO^h|+=cETiumYT8c6y`r)YpiqYI zBBI-nZWLQd?UnLDW1dxbzG;$gvP;D_DtQ3>*KN}hj>yJ-r4ovr z3Pr)qOez4}Bn-Je@ge3>*@_+7if5Nn5bjXZv^TD=Q);{}W*!3-B zLpJ9s<=Q$6*IKcL;HMurLh~fw6n@r&#lL8u!|o^&PE9J4j%yShn1pgpG31DGQ%A&< z=T$!C+sr4MCU2M1x?=d)b`j!`vZm@zoZA`xhP_ADPBZt!B(-50^<~!v+0I6$n2X*a z<5o`h$uJXy;g4SHevtI!{&V@+ZKMsNu>C`R7JYBshrK)597o*cA!{fC0dq#o!1WA> z#8hkg>?gY-`Qix}HnSNIKnton7WqBM%;JTvYuQIh{MVMvY4rk_7Jx2 zWeZXnU&?;Hkv74HruXUN#Tmcm`K5Vh(^l5kLK9PrwAb0?7gvP~{*(47yHwv5%}b|g z3T+VGg#+2}w`g3Q_4n1J11~#Xx59pSE4`UG0(JStI>A;>>$+(jz5slI#jwt*9?Tyj)haKa<g1# zU*7Pl=Vwp1bsKFV>GDo%q(W1Aa@wu?sP#3H>A}r=z^?UhNS=pqlQ8T`cCD@83h%>3YZt2&5O; zGh@0myd4&=i*KTIB?^j?OE|L_0&U*Lv)f<2oMNdC$2uxrT+pKxaO zlX~YW44)v=wILnfwmjiYV#itKFCP1>n^z*?c~)m3Bxn*d#g1?6qbP2Fos|OK= z2v0Eg>;suXDKdV)C%0bG>%@1OOvznH^=SyVQJ#4*YNaEgV!*>LOl%{I;|zh7@?D17feqioz zkiITae4is-mcBle>ddCE|9Rt_HLe@{s_FB*RQ6EX1+g_I)tQ4qHP*`3C|mPlY|Vvp!7rOvjsIBB+L)$8b;6YoevG*o zX;{)9{RrlT>%Q(9WKVfD#eap2wL-@0e_gDHjE3*%j&DKwj45QS88UX_ZUhHLj2(W#M%hVv>Gq!kD$OzFX)DGGvN+VW3`X`-%%=28 zS)4k#;^)rCP0S9#9;arY5*0^icH)QdE=u}o{1E>&dx}9lH0MOjjv45>Pw4a3+-E;I zauuqnpzV8Di!H}0_hau+Giyv5A06@Y)5Ue;W40XlDF|TUi%zTnbRed(BYR{n#Ex{s zAIS)q)gg{$Qf};LmzQGxKJBmav6sN&ne(qtH|x6ACbE$U*;S+*}MaUiwNu7v-SOS zI#BFaxL6Zh^e#VLTr|#qUCUq8ndK2iPo{xJ1!UNcj&ZObSE>e7sEq{w{6@PNBzJ@G zp=bfz?cRp1ugmn`cXiL-d+V`Ix9#1Bav;TT*FX9Kvu>lixHrDCCPR&T!*p;hdloGQ z*DqXqB`=>`2OfPZ=_c}$?6D~OSMlf%V@_`Pq&m>4z9X*?#;Dg`D2jEo@SRbwl{N5| z`>17Qjg zH-#V@BFLm)+FakqX?ryZCNa?I!wz>HlR{kf=OmsH*X;IOnvi7^i7Dmz}&?zeu7ujP*hIY=a+J5b+uY z#d2>5Aqq6pwr?N94k0JUO2`u*u?}B)7n#{SOYAsFg~{J zxH@Q;smgF{D3CwDnK`u0fjlc*yDK_N_mliw#{A#_h%3PZZX4!)x(h27 zA0c3`o83LE<`k;Z63+_bUL;TG9v`8R>(AtH4d7`%)O0NeF$H(KXWD!1_05;&)tU2| zH@!3G>Z*Cs_~;awD(%frl8Mume*o`v#5reHjT$1%uID3NIj~2EhjnydmleT)b|hFR zK!lz6aVm`Xo~84XIa;SS;wz6vNfUYA)zsPTB!h==X)_^*BhPU5Kib(NF6%)b-@0)vIf2JGt?$Y}?w{)%0Lud_U0fFocdl5WdOa+RF#~nij+549CydWgfWF@uS zBNz9yn_k>X+VG?aHHeiT{BIZqg-}gzsi;e8nTb}tGDBo z7wGD`$t{EEx)hW;I*DrI)KmnNllV+suU?)hwzq%^NDUFFV*k{a*$tO-#_`Eep*C_X zoCv4a)`n>~$g4&FOm3m>12Yf_h_k_70L33De*|OF(Qqz7QyN{#13Lr;Rt@X0L@U)j zwfn85B+~kuLD6f^$B%1>fCvxsEoN~TAs5(A^5SHAxvDR zV>z{z0g-_Ob+$HPISFSFhu!h;J^5}v0lfmDo^>B7?bibDOS=wpMbUc6qjn$4PAvJ! zmaat}f$N?Z4txya7j(Qdm*>#O)iD!!D)A{^}ulDuVN*zb4aR+#lt zOYaBC=q~@>Gg~RHG0knS53TX&7uD);i}T*!NPsIc&()-gdA6fY+>6@d{RW*l>b3!P z23d~lVr%1?Q$`T=ua1w@I@= z0NbeV872-6;2x~aj0)@LxY%yr!ZXV>9!4?Nr&Pq2AQrMVSdm&+5&vwBDVU$3uv)e@0kSe^c}f3=>_r^r zKDUG|NwwVJ9tp=o%iNH>9b(?=;SOTEY+~467SHUMWV3=7BuN?#a=I-@-nY6@IFL7- z$=)xy#9}L}K$fzj0@-DnE661Ou!gaWOPiU*MR$KlA5E@{RYKbE&;4)b4iF=|qFK+RfRg^IwYshGG-K z+?#m`7);tN;#vL%>yyd?Y*z)!q~2``X-%y-unJ^y{U&Usqp({D_D=x8rWQji0Kxb& zl|LG3UrYtYY!nc*!6ve!aq#^pZP1m|D3|dD zQ7Sa%TOe9usu}eEHiM~TEUv4`M8F+ynsw)W>1q9Gt7oaQ0X?AC!fjA zOpna0InJagCLGGW6~i>_wKm=U{8BAq6}Q$YPnn^wvY0D;K8Ph0Fzr}$lbEgq5S$x% z+p}E)#BL11Ode>N#;ylH3m&y*evH|SQyuiY`6n$F+!Ac94TIH`(-u%}v7|B12(^4r zSFjm*DhM+7W{t#e;W(c!lX)7lN+#ES*g|=f_x|HP8VJIOC{Mg(RnOT0wle$++nI@* z27drP>o##Yb0OZ6;`21Kyn8;M<(ruE2H+yJ9}RjDs8&sbV~CGULf5?eF2_inAp7Iu<{qJK0qo zdAh-6BEBZ`^?9|K{A>E5mvl!Dz0&97H?m85$!~7hYft|Aod1R8LnTW}O9c_!1P`4%#L@Ium+=BYI14FN?q$G`DBes#3~PBO+U5de&h%u?@608H1{ zJpzkFl>h*4Bw73#eMe}~+U_cLGc(JM?~dKXBL=l8r~+QG8_4c#l`rW>gM1U+V>r}? zqgE>#4m+dnyJ|6zFZnGtvo!|B%y10Jwqvk0y613<>Y%LnOO(CY6#14c&YY#;mzSR| z>u6^TpL35;P(-AF2M9dU&ft358COKVjt5%IRO|+_TW*Id@78W5qi$R4CF!(eMCJ@v zTO9e4hl2ApMYe8JcY~u6>$yNbbIHO}NUN{V5gl(}8V{* zhEzB!UECt0U&gM6gJ}tL3s3Rp`uiVriy4f$E3zC)FSgFw{|#V{X%ND6cR%GNHDFGK zh*C2klZMS(xK<$6bnQed?VApn-pQYb+4|Grao~>#gPAOd#BrCU8 zxwQZ-n(5c|tdR;qv6ibm!4mQPBu?ek{ku zzNwg68>Y@i(c1gjSQ~VxfoU7I3EExvg5ioFpBWg`Pg6 zF^K*>iiWB9E61;ghOPkf+m)KsSmVAF%tJ+cSWco;^s&F*;tPaEw(Bj4 zm25E{%dD9!)|nick$3EQ2S5ny26kY2kPTCZChFn|7=fePh(b)wOOc9fgOcELu^+dc z$kQRhvQ%Vp6ZO8ugCsBGii^fgckwXW?46z3@(|{&Dduo!2I$lQDwU&IqeI0-w?ETql&)LY`~a`dXYsI<^zb$9*7Te|Wvj zUe$KF^-$Cr{t%pe$a!2kXI4fzkOfuba34_$L6153o0<0b&E=4?{(`E`@-lfjk=iV% z)(VA=XF*k|?}CBN>#cC+2R~q$NbF#aEy#wp=WDKV2Bs{FJFg4V|F`#ADSZs!ndumY z<64kz2a0FAhtA?(z7hb^IqG1a2OC(+ngk1!20F7%NK)}-WK(3z>%4tr9wpvIy=V}( zw2SKttsDLj@-vLntn=Azls-m9SL~ZBH_x$TUh^^wtMVsnk7kX$$}AsLZe{#d~{Fhfrv8u zMVdM8#i_Vw{8wJmRg#T%K5y-tY+g2;o-nA%yTvW>)-o(bI>N!?Xk{(b59O1)U0?!` zSq?(31zBLIil%cSD-AI0A_?OVv?3Y{svip=fFJw28q4}oY zy-m_IdD!&Z+1Z(!epvu@+>U;G-q$~G_H`+Lv*fR5$2v+_CUSFcGb94c$t@PQrkm4> z+|g&qGyu44@6ro{qCGm7OTfS^Tbz6DxL z4~}g`vV(J>2wSie3y1lvn@f-_nSrWP>@!j?VJV||iN@}l%~6VdL^g0qPLidcD|(O? ze3UQwm7C##?z-3~tD3pi1`kZ1A;vaw6t*Hf^%WDpKm07LTb%U%6a5#!<1(4A%rA`VUmDBDyB}BkOV?V?0Ljz{(ek-@zzlg@^jQ{Cxfeg)QUTCtDtGMML3{k zw_VHQ7}C7t`5fF$0KXd!%1*Ed%V_}gK*t_qt`FI0b)rwij)fIC zobfhHPEF5K3NX*OnI^5xf8ID}jq3)#YWh5IZqBX&I-&cNnYzE9r1MK4?fIw6kIor@ zveYNO3ERW2D4PlGr4v9z7VFsY%xs^LA}oZkLF1fP!@7LQZ!j6wYSNY2VN(%V@fDfT z+Bo-=5}}t^spY)%T!j5-a25Xr_U;{rxWwF`dhK)w30r034zn#EQuKYYgarBbx}{T! zL0fH4mXHe_{3oiddj@fh{jB>QQ=n8Lq+ZKNiO7l<2Xt=6X>o4WH@a{x_+|5|@gHl& zO4#Du4?ySpDdOhD)f-<}143FJSlCo@y9Q|?F{p4Ybk0Gip(?WGWF-)~BFmtG@9w7> zR&95txyR`{Jv29vrV5yYmLKjQ!K?8=NlzYev zm~#4;5E2xIB7+k<*km`_DLDN(As2?B1%MsS>1R0b9M|2C^K`rk(cBtc|LbBcr=J>z zL99J_rZ0oI7iUi696(y(^gGz~RZY$sr2XyF^`~kKk}vr^CL>!-y>jBm^9?5mRwT!3 zl*_ktk0Di*Jug5(+>O@op^)i#oY4JjPdH9G!dxiM0x@5;GFeSWP$J*3b?n)6KXg1M zS9D6pdKT*Fu8$ZY%2`@&(SybrU)vGIx$s=LmnUehN*$fNoi7vjQvh7Gj{YO;4Jb_4K-^;E`izT__c?s+vD$(Q^tld-MV|FXn$*Jac?mQJr& z#}=mg4)#+Vxd=5L!n^2GKaC&Y`_4?qvA7jLszy#pAD0~?JV$1b(?z|&_bgC*tkX(! z%@MPvsV*K+M-No03$7%y=B)A25kEg&TsJbOq7Njr^JO zuTM8?r4S9z!Y20=Hp2*un>QCt_tT}na7XJraT(_hwLty46>+bcjdP!W$+L(6;u6R5 zUEff|U0ZU`_|;W4$jg`fPLuJihSjn+jRMo6`id3M+Bp9t;FJdT4%&hp{Tk!kr(B*2aPY>&pIfr z5zL#8?3Yd3#fyk@pcoTxwcew>stF(Oojs%s8SXvX@QlHy6y@BsO%z` ztmoChBVY3O(%bGLT2n9ZoQ>N!Q^)XZSt|U5*86FAwOE35;u?g4YH5kftn5U^!wJi? z<>LB2(uQf6M%qKzx{rI2mUOgKyy;(DY4^!6)gFbd&~C+5QL3eh)@XMlE$p?mK@_%s z$alh4E9$3OsP&>ptuMD@YJVoT?*Q6L9bz*EvM+PmdbG68NZL;ZX`J*VsW0MIdOL{Q zcahc#ds;7f%xIm4gEVSu;V|Px^gVp&3$ouvUGe$wK6xC@Q1@|)8glv$N1ow4A2WUd zu#jQh#|D=4uR9AXCSZ`jHS~=mZAuMVy6(BoX*g`f@%vX^G8)9uKs%2f}n6mPf9TWM-WL+!n`48M?Y47j`ZQGXbHZ&nTK zpyd28)qrN90nN-py#Bke3;&pY`c~-5L-^A_0IGqo>0@{Eb+Thqjm1=;re!l1ZM7MtHaF$JL6nXLJqRY5#N(Fj zL(Da`m`N7K>9e1deaUPZb5FD^q5T1W;h{eQ%aBIALG7HLXy(FwLC9P3aLe9|v(7Y3~7iHB3ZZ&jcM2^lT$tB5XnV zG`t-auVcD&qfRQn)lUTd4B=B1WEx+p9_hMjGDM4jw z>D85l1)tCTeV+f_{&O@;FJ-s%Mz5W{B}e0QqKvtw@0*Z<96QiCm!y9}(b-Qj_S=}*CMpxT zf0h2|M&mjB7KLpgth5+kam{U#e0>N9U$5lqiMTWtV^DkG|7+7PR}bHKy%AiX}km zo(n{O2=e4PpWANI`YOvMvfNQe1X@|bNPGxDAk#vwj|0eN@@JB0Rs@42Nuxndrv*OK zq&72insl0*za^JgY^7hd0pyvMMg3q^JK=3mXAOkfL~<72K}yHPAW0JmSrB1igiz)| zYUfv4fc406pe0aj+dc|FPD{0Y-iZJE4+2VKI(!gt(>T9g8VHp9h{KPza-=zBcRAKv ztydIoY-nxkAs`=K&y?o^V2av|n5HZBbCuR|1959%sG+p>C5P?@gpg&qghQ#=n^Kw7 zjrW^#sl6${;da#NM1!~5&Dp2(Uxky|8Aj<_fDU48o6L0}u0plvR8Lo7%({cgOnp>n zj&+9>;#@?9GaGiB-Kck$-p>l^0VuZtOxBR3IWa+%lctRv>iZicwDO4>9(Jw8W$;h4 zT=fkbtbk&}ht%2~YuM;V!`2|~OFt4I7CN#W-OTng?KyBdD_K*UYO^~=r z`-faVZuHtwFCFLGmd$Px2A5eno7xt?%cc0Jov*l#cRghgHqd%e)E>(6q(PJljrkU7 zVV-`P@-X>Is~gLtthhMe|0I5qABhXYZc*Dbcn1(7W^kcYpk(<8rm8UZx#79b z%XQ+wpi}s*U8+h<^&t|xnBCx2K@Z1f9-;@xRXrPWh;G=rY3w0wy$o`))qyod^Km|) zDeO6rOl4+IBa#_rLnNqlo6L31WT&WyEWoE!583L5!=WbYb;4e&G6f#CH*>-_?=v`57$Dl0;i$WyM2^B-Cyt=6RsOduc4BEFY*W zA;@JNAXdci4PU2__FtzXRy!A|g#xv)?8w`kO<14G@E>eGut^<10Bw=3sSC1rUbj{lXxj+d)qDg!N~+(QJOcc3ltwW zvQjc}fk{f2O!~!%E6$SS>sb7d?qpYW;=wSzOvKl+Zk1WJoBV6~p_g<=553ao<2SNP zddY8Y*pqHp{@3UHFDxG_SyCGRuOdv9zut*gagThk#Q6X0J=<>MII_PY%)>#?ptmE6 zq%OOd0J=^GnCZ@$bkEMi0)Zkcv9@i=N0O7yEcV|INy)Zk%Qo#;4jprWowRkKidDtB zu~>zy3c%K7K&zszUdF1{7}%a+`ZSYeSM^t>4}-wdHJc~&ycq>6Jl%er@pNOpHBSqc-P$2ZI;XqmuvC~V$`W)4 z7e=NH>;M2}MnP}vKtz%6TU;rjZ;~Aj#sE!{zf-@fYF818ZRFMs)EMfbgq46VxZa+P3lfMa_uPYIS&!+!UKjleft^BC6YIk4N7qKIiDRj4!Jp0i1S z4OG>|N7&{+Z<@_Z|1%=p)ZbXZE~0IxA@=BsMR3qYkwLnoGN3$k0Sj?D$^}ZiJq% zsiwx`2SZpF%WTfgwsh5AJl7yj`*E?kQG>ebnq2)t4Fd-1@&@uwSkSR**sb&h5GwJWkPrH`s#9H8My2=y4UgFRO8QN6anG6ebb(iU6-&`rvIm?fy|JhKH zvs>r7!*D`RsIAj)MaLifN&Z+Xtt zb}v)IQFY7rcw*e^muZKzY%tUsow3JeG%{5as@5JdrOu~;z%ds}pw}5Rh}HTy_dshG zGFzU-bV7Un78_T!=cuMnZG;!XaY_B=Re`XM8##wPr!HT6h=>DCub;2(*c0WD%(!Nr zvl=|%D4~Vq>ES%GQ0mSnn;6$Lq3$ml|Lwm>(qk;eHM1xDrBv5W43klKN6e(Uw;3VX;E5iV9>hiRkbY>v-YtC%4|=ZSQaQU#`fi$ShxBbf-lP7| zoxpHBt{LbUgU+Ow_MHQ%ik%p(tpKE4mU4L;?RsduSj&+pBQk`_o1zS(ZyywATC!^T zR3MevTq?g;l9EN<>%O7IB#x5%Bw7+xALwu9do<EyjtbQ|5att&Id%rP@FL(I&~ z%*@OTF*7sUF~)Yx%*=Mo%*=KSNBP&@=j?meI*<41N=;2ARg+3;)km*yjG8sa%rgJG zXZ4%cg%BX{HaS`NwrRFJh%h_EU9v>ZW1plEY|E8tn(EkCD$UNI9u-s=C!5u-6MEQ|WudYZQ@-Z$WcE$)FG4`V+vR>z5 z?qU}#q;?eU%;bhU@27XHtbQZa>+dwp_QxUjG=9&3MPKn$$ERH*EcnR%%4=rrVI??8 zy2co{Zx#iEMhH>FQHk-FXUt4;t0}kN}=~GtWpbq%`E6_Hf$T_bCWvC25PIyU2R03+}+ia2Sajy1^&*)d#|^EWz(8 zFi^oEIhsG58OjD(aqE}PFjMNAG&FT==TxBp@ORlx-oBbOL1t2m%IGycZ9flt;R=te-yIR4GJ;-6_x#9Khh5Weteyv4*OHblK_!)HB$E zxmic;L7MEU{rY_~ne#J&@IJtjk=IuE`1R6_q3mPlu)9L_x`4Lg!2Z!np3eW=R!g;g zs$trmV60E8f~kp4ZJ4B5_6w-V&HgWZWv#pXqQD4g{^&4S3inNrh@5R0^piV@ys~ig zR$scMLvqu>2rNa!8=JJM=n67^?yXMvj|mUywM1zxiiMo2eM zgifB+IGtU>1}h`K66`J!Y(&@i%zHE&65VUfnoJIqiVcq#!4_2U%5_{J|FC}WFgA{_ zw!CG=CfYApr5~`*YzqoiWzQZ=R=#(p;cz*TnA2Rm&a9VN>XwsDE`}L;Wchy*uuFoTavHoC}z)inzqv z$_{5<9?eaw1dPFTJBCXnTtNkzUKG%UqCB(4*>VZUsA-{5#F>5>aD z9F2FS7DeVC^5m?{EwE;z&5*84_DsjHxk`Z-Unac}8~-_8i8C@2Pf>~8mdY{54yX08HhD0f3(~K?IUExk~U#|tU=tE4!QF>u_1Uti3HjGJI>trwws`cMQd3tLe z1jGcM)G@aZ1>;!95nfGus&CeQc(vYnt!fKHhIEPXI~gDcfVe39;jL*|72F7B@3gi9 zvz$SQ{uZR@JtGEwBV&y2n$19!T-8hYk-vZ=^y5V)xx^B87C;ciz?&w#i&ImE`>q_b zo`|x(ii?c_fV5V3MOj^kb2(q}w!f0FmAjI*^@~1vxLYi;x?5w&v*!o4gl^FL=qOY} zWCgQuh~r)4C|T^qZ#>bhsxr&3u+V0hJgeu^YmR%qm-ih>?(~W93vadh~>u`F5!O2ayTSrC-fn6m(1BS1~Mf(O6(5i8x!a@@9Iu%7IeK zQ~p>5i=1j}d4U_cM<$p?=e6_Fz60Xw{Z;ZBhzKM%f~^SBJb|T`EuOXi$Ou1hQ2pF8 zYQ#qcqy-Fw-9fc>S54+4#;_XJLG^3pPSslTUCC|gm^Sql7?r)@y92HT`x5UFk(imIE_1KD!aYV*g4I zEu}#I?{blt3@?>4%nN`S z(?(2;H2N+fCfE=*9PDe;6qW!(kD(|P7IVCUS79Cm@yK@l4Gkntc7uztUqodwLH)#K zV01k7T}dhZo`$IzAnkLw6{i|R*3gp5X21h`IgtwbFfQrS6O;9N>JM6V>Z)PQ%;XTP zMr%_}>g4ukE@@4Oq)JZ_&dO7xNj>__`6Uz+wlv9sO0!8lT$zmX?w9vWIvCCGcVs#Y z(vD6e+lX`@ZO1AcndZ+cOFq2|vK)2X18oHmyymKU1RAOw3BkIv0UIfid}i`0He@2O ziS48Mjf8~g9!i$dC7m&Doo)`V`EN3UV;2)K6@((&&#R2K9fsmNzXK2t&KcZI|~bV6;6;wrWYJjg7LutHr$oWL?cCj{Z5-l4%eb9~2p8*3y{c*(HmIrv*=zxN_1C^Rl1kfv1kT;zciG!>=O?ythS#Eur#&E!~bzoWtkEXMwdu)cbfwM1UwW zgysdi-LJ^YyxaI@hz`QT=nH$1K(YQkP2pPCt0@zpRb;2nia_>(Zp)p3_LFhZ`+&-B zNFysDfb^S6ohyq%C!_&~|EnydsXqI`o}g3t_60_&K8w87A*K8D7%8pA%Lf=cRr0#u z0-nm~CGR=k6}7lCKsS&Mp zVNWS8FYZF?%g+;we6&XF^Ph>sim8YiBg8Rz)Uxu`Yh~BkKV&Ed#&VDV753@~cbq?{ zZ^g+v*}TKqpw}DSJbt<~SF6@c;7u>t_ADAI$h=gwr`j63$B}?DXB~+qYz)(4Q+{6` zPWZzqv?r-EB5y@-w6Uzu@BbQl(oVp&@E!IW-s>qo54RF^cZA&M4k5yA00_K^Gt%&I zr9C}8+wn{n$+KJ5RkwzOpvbBq=KKZDHJSW`FrP89rJJ z*A1b<)ZAxQoXq!j)1oLt@ZrWxxAiX1nfq1*JFiOA&K2xXVnsZD?Sp>IpB>d}=22-Y z4fVz5Yo_b9lHl5jLw*dbg@7uDY7Gpyz|S@~!KTZIF2#v#L8m;7 z;ED^&%oi-gWJ;Noor$~dX|ba7$jgj+bGTez8XG{lCb>NJ1ewj!k1o{jRnqWiW%G{J zZDMgn*eF=~KfTrOsGS^!&hI!v^5s-jeuf@Vv<6PY#K`>IOuG!tCOql%EAi+nCFCGzje_gPWeuBZ^HuO)boDUe~CxQ##gl z+p9fyL2061`t&uA$g;9iEyTYFG&Le6^V`)c;-F=gNj|rPnQb>P_DRSd6}W8Dp*{V%H}+s}8w*hlInyK)CC-`q9JC1&>U1&I z)IPkr5jku&zObWFnt6E4Z30(qo+e!*(Mmk+E9zv|PlzLcSnh4J@p!7?0%mPsQqH$O zwtOKtOJD#A4PIK^%D}tR3VZIT&EmppZQ{5c>IUY~<97%LxdFfXp=hRBuwJuyyZbS9_wx++^)HAt znjt^NK0M?nH?vLqihobQ8N*XWI%Ckd&r?%`5w)HCMt79_owkHtBrmRdp!iDp(;+uZ zl9d$W$)b3DaEvsvYY@z9J;THI9@`A|8~mP%$uNFBNyJ^eb6ZL7-5bAl%xoBdnaRS#S3_IfS-mmYU$Hi0P30+tnZtnXYdG zO~ut-1&g35W4(eyc0O+|lV`L-m{rv3zC!xLmD0x8G>yrAJ2sA81;qnt=~)dupq+Pa z6MC9DfqAGLww=&TADFZGYkw4<^WP>4=<|0td+rdrxb)629(%ub(RJe2e>Y=6DPJy9 zSn4C+*${YUBHCCvyGdE|PLZk61@fMdv4(7-#1B;q%Px1&vQ{C0luwTfzSF zQMXU~h8lV$U~Z8@#&L<^F1Td}HoNzou-M}(-wd{2V&{_arHG@y@(sXQxsxDkjfnU1 zwOUk3{J3IVGeECR%DT;fM4-{}i56a+HArpI;&)Cn-5N6dxG4SN{Jl9OF~fA=^BerI ziPSIs1_|Mrh4?W9sHGO{Q1H)?UwgdxI!T&5p16Kh(2kN4DE|6NXg{x>8^`MKGMe7fSVH78It#)F`do_+z zw_^%SAT7<_+tYLIHdzeiCCw0)io$#TzRl&!BESlreYw*yZ(sDxv&DmttagEPiqe?* zRmtMLwa$`X47%w#L2kI$qe}$RF*%wh)$Es9w1Q=#SL4#3q;MQ+&0Odd);@F@>e&Wd zGgHesC_iJTx(V`q?n?a86C>TXj`1;O90ot-BG(&h2@f`M(7BnFX;|^QJjD6L_iX>m zc^8>bx>i+%V;Fm;0?sd0=mJC4xujZ&g;h(`F05{z@uJ=PFbCs`i9^eh*zlT$yI!~G zI6owy?An7r%(JxGU-w`9sXxD?!M=6_C%6Mpry!j*ZiHQ7GU*B*s4KhSs~HiRApS@3DGMUqXMx>mDGumm^E)y9Hk_ZR_cIwlO+ zQqtTKeAHL`J`)Aj$a$9(Uf)Y?dhiVAT@i9(7+pZWc(^=r&2wcVB%T+;RA@l2&+#KuVw@Qx5ccg`H|}8px1q+QXfh&Oe3~?sul$;NQfngp}a@gvdB+ z^X%lyP}FXX1W6*omkA@=MhOKahK(3e6c9-T)j&t4T={xlxKND(ssV>2TX6>2pS?eawsPx zXy3x4BYj(FOa%z4C2t0f(#j81c2*O-(pf1j9YkIS!WM|e>Vt;E5XqjIQ2PA(TDbBy z7m(!~qU|;oa2@xsfU_x!_E@7H(tK=d0}w+LMk`~O2nuNtUYPPLT9AhvGNUi*=nAa> zRRKu^6_7y?1<`1?Dq^vARS+^z1yM8@0RbE=0P;a{grF2a1o(1bu`F`rI5T1tD>EXx zCbHa&m5Ae16fIXlgaq;X$gzURxbnz8B4uGFvf~;OEyNu|n=+5=w1chsp{9*k>G)%( z*v_3I`-(ew&7{wUiyGa$qpp6bqMev&a>>(ZKdbrV^*~W$L~3}zsfviU8mdCSHE1Or zaM`ey+D3|BR%pvB4z%B2yrP23?*D$Km&k47z3B`r#wGo z9v?mWW#uB{R@46p?%AkZ5A(9Nk5{ZBG1c z%Tu?dUN+*oTN~EzcgnGKmS4d$EALn}In9lhA#O^L))k@gNx&Tm=RtCQ^bGX~*1%eN zQh9P)xRWkh^PH3;QZTj~jsahRlt zmr~GIeB!V=ksq!;&8<1%s}SEHWv7{rlt(~O8Y&?u6dD4+ITVP^-Xk;@g$DXRj8a#T z8cL&oeD=aDB<6V34{f=^L!C9=72Ufplicm4xDy+~6B#grC*u;kJu^Wx+RK2LW>Dn= zTl840r-QDtmj>L9qRp%`LnO;%Oq0X7$>JF(H;ZjVj~l8b3sFc%T4?0WV06SQ;vxI$ zB7@0--hL<=>92WqL(H|Rxt&rBDnq~FnTDS!aN!L1Y!$e2ZB z3U82DxP(lO0A708Ck<_hgwxC$+Jv3a?e|Vf7}|PD@w*E|^;i9045Az`Wm&OBiOZxm+Z&c9tUFe#DcFQ7A6NwethK*^>j9Kp!` zSwhsk0UcDvkP*l7Ay?>>IXsreD--pHQ+;I3R7?*o{!*t&0DgSwq9?JUR{PlRA&btc zBHC044s-z~nFAA?vWAy`%?*jslzHCFL@qHh;z3*Vyu?qc3A^>{-f3Xmx6gASzV?jU zr1MA(x;;jCg54X(MAm^jwkgJVqJ7AT#P)^GnuG#lL@#DAhp5AG&DcMZX%)*d{jRR0 z&KJ!I+&E-Uk}h+NL%_xw8;PJ(p}|6)zYf--9rpJ}$Z+;jKKjb#OXMz|qvzhH4pq@PQDk2Nvn(oz zJc~n4LU3U9nHqqsm1DOdVtB_S{h;9mwd`mLJ>jmPXsvC(ldgjUmH=YEU3qhY-4#*z zx4ak5-YlrW7^M#e6L9)gJJcnlpymnAO|c_dM1tLU1bA-f*BUbKR67#a++xIrk5i4w>U?HZ!2347xO9C7S{I7w22*x-mI#O0x12&^0K3CTHsM&whkUPUzuwbc;Y4ToW{JsDk`Wcjb{KWQaihJB*8rNqXRV#zvPL zc^}3mGGlk7cVJ_>4FvY&vOsRFH#lrLc>y+&HC&gszcOJ)%85_?>NFIcGe}_b5GL+iS9O_5xza z;s+1etW_kN@qeV>T=Gm!GgBNV)vzBJvFVRB{J6^#X8ma8prQy)TyUeue_IE0N)0)0 zxo~$;_51iahpjICIk+>K8fk>|NtF9(sR+;L+42&#?oFJ&z$)^K^MkLL2cO(XcoxbJ z?j&T~MyRIf6nW?>D$iY0(`;|>O3`Plh3Cg|NI`v`OJU6afy1{FP|mMmi`1mXs_rlh ziuVbcpKo9r^4axdcv0UNM+)^LAjAzN=AOB!iim9KDwZuOMt*6SZDgBq4j44&lw4Tq z2DgFHLVo7cehMNSl*7?fey&!o|cdk`+n*{H9_k_ED|4tVfiqg}Ecc;-?>m zG?m-%jXP3iq%g)P0TZgd2h=1Cck&#{C|Khrz0_nxBs`4!hL0Z`@0jK3u2~bA%j6Dv z#iJvutFOXn89wkq848Pp8)-_Gy+5>1fu*Onlxo64N8U)6WvGnzLlzHe=EA)1q-4E5 z>iuL>s+zaB_^MsVgclhsg-TR#tDYua5bPuunPF4NkzN29U(oHojSrWufS{jC>0yLp zd`K?LUn17(PM~(!P=_w5hf7Ez++xUr$oZz#(S7tj0@ia012lx{&HavO;L~&-Ax-wv zvgW3KH=LRrTJ-HYouPt{2(=|1lO7rfT8Dgm63Z$8^nx9TI8dT-TznuzNRq3;C{M8H z4Dyb~W7@mjmiYxaGZjj|6c?i@D}6--GYSc6X$KXiz!L6+_32_#i@b`35f?D@Xb@Wu z5?~voQG7hO39rMKrPS>lGJ*ebjA7d@HcW8ZH2k3K8YG7gcAtif0a0tT*>g*y(asQ! z8WD?p5UfsmM3PYGbG<4`uM&fH(Fg{AtZXA7-$DKQ!aYcl53=}{aioQ=KGWtkhn+VF%rR*-Fh%S=ZBQO^KJyNZ?;4-Cvks1L503ZS5@1IDu zFf}wWbz=S-qQ$9ev002rVOKN{c=1=L!XuW7BbBmVEA6Vkd;ztNK~2wuYh0ba!{9<% ztLQ|nJp*QZpDel9o;Mz7qIKp&PGrhx@WN<}lBz=o?4sR%es~j#26zeBRLLe(+)PpU zz_z8d2$H!OfFC8Y2$`jn?s61Z+L51G7ZD+SpgePKOEq6v%#93jEC{-xns9j00YO5Eo7(e!e$%PQ-x!_ zgaB2gn=2+bA()c=lOSe0_k)1SF6r=HqS^)J0$e>)Qe&NxBqF%mEDD0jp645&jy$&e zHmDtyvEMR=NT=&T0bC6;DB@=p3=6Hw(09%%Dr!+4lB6*|hx(_vq@TWQ3mBzQpXNzT zg*?5*G~b59(2?NCY^NSd`b71=(jKyzeCDEhD=uu|xh&M{d@ScK9VxK)(BD=)NA3Q2 z^EE&wg;dD+e(m@mMS| z>n_G@hNMUD@DNHcPO)B;+6{E}f7jgrc|5b^|66xv_VzBOPE7w%cNLT-i{z`+?yBy8 z0D8utT0Zd?A^R6RdL=aK48QAxYA8(ebVik+{8gQs7--jrFs)=IZWHQN3q#vnBlPL3)CK_jy za+$UybN8b1UsrLIkeLqksNoRD4&;9omw&g2Bqzh-*+!d>wiuVABWb~5?$^I=fXn;> z2nq79!K&~x`m<^-t8OugPh@q5DtG*0-By3ri&KAEXE{E$m>!V(oZ{GS= z`^}c2z$ValfBEKUQ1oSZpy$ew7{RgWm}F=A8x9$6L*0glf`wfW&b~(_Ee2>4{kOs<0tU-V9vti^Y9&-;LFD8%V zm)jX<$3X3K!+9jhAGNHpiIbDqOgX!as1%7YBfE%$h!RMt3M?p8qF-ar(7siG7kDB_ zi>BK<+NpEiF2!Qdbby(I;Sx3sNzMNv>F>m17BNO+6?{h^jHtU}SKJ(=B*0dMpUFps zR$-Iqw=3=jKXNB|$SPLkNL(g`*pP_X8_0yxkr*>%M*eOHFWe;brD{3m97Mw?hkl-R z(c6IAFQ%8u{~{l{0|ESS5EE4_A~M}fPe*xRV3QJG?V~feI{`oSLsi+koc$(^o<)#7 z+DTU=7HyIcGwA3{1>cVIOe%QP75;Y@=0K|HgFDYja|it=wfs-!6mbKb7u63-b^Y)w;4D(Si&AWTXy5YVma4A z4VIjj%#XE(*mtT!@c8+0q<$^&E^`Xx(Z%7|i(nKS&*Bn-qHp+7GgBSl^aiTKpZ8LS z1gLk7Lnv0LBs+WG;X>1}DTDfLdOaJz<1u#ERhLq=-A^)y_H+fNN#mbYYEsTK<%Mcz z%bss~FkTPKG2Ysmm>CITTmvT^d~ss->d_Qi?0VGPMsMn}T|v=PTuQ5$(PES)O|AF; z*3Wx)zZ@0>k&`qViE+Gs*K%IfuwzsZA|ch?r^b!j73@da5=Lz)hO&znfuY){?LMUl@SpzrFaMpa3!e@m4VER19<#y~|o>Roexa2cGbc z7{mtnz+YFKq#^7V)Zp~H2Il`_fENcePX_}a1EBsF1B@Ne3wHbQ!DCR?#f~KJOErM! zq>Ca}#Sx^6W|xoytS$G1=D|&;POyxnS;!;sVC7;Otj@wzV16`f%VE$r0h{O)*2k_%#N-yIuR+P;g>R4-xPq za<7*6=n{OwLCqLC&Hfu&;9@A@(8**77Y4QI3uiSIwV*f2-oF@N@Bc7BOriI`7~tS9 z0}TBa1DLVxU+1DiB`j~DbDwI^e_G3DIaA^oV7{oniTbI1@G-&iWJieW(6&iDJ5Osx z2GH8DU!rGLZHwXINWfz?0a(Vk+21nj%dyhg(NDQLj4c_Kr{&yD?~T0u43 zaE!Fh>L?r!5;ix|n)PE=&%%bTioS8kmS1xO;%KP8>ZcWV%1=m(vEGvl?j7Z*gmoO| zLnm?t_+V$9fhEP;+peWn_tDW7@}NHoN8YU!Cac5gncuns@he(|b2y z4_i}wB@r~lsvgc;j!Ub6UrY48tWlGCbyr4UxJ(X?eipa|_cbOph1^+0QP+R2_v!m) z^?>uAbQeTa=kkBi-T!8Qf79K4oqLrX5CMjftoMR#gtsqv5MT;q+hGHvI9D>JKcmgX z^!RW^@p04Zbw#uQFytyszK2WqJ79M|K} zu@wW15wi_4k@r^W4MjMFOP^8o8U>5+39lW{niTi46j{zyn*@^d2o)BIfzY4idy*&Wo3vx+HX{1^WB zo>L*Dy?e7hT)315pX?>Hl>E*FM^pZm74MUq-(6p=;N}NEe~yaHb{fp3kl?-1O^+x?c%unN=hZTw^H$XymEzfNhG0<2R|H@K_5pXZp4DpVMTIwhvA%my z2ZXjAo~hb(C8_0B;_6a*&0g%nRe0}dHaC-8&C$c2sa1coNnU$ZO5%sj*v?>%Q#>+(L~j07bnygXht}>X9Dk~O zi+J^a_Pe~0cZvTCd;V{~`yV-v^9YbVn`9eRFDw5F&-lM{9w<;Ex{L4eItE58d7s$R z(H%bAk$pUj#;u7kutR*e>BZCsPHFl2{Q6>W`UUZeQU1he)7Q%+#RSpI6W7^9RQLC7No9<3x`#hv?|_=2@Bcwe43yA-&IVf#(~%sM#7@o#~z0xKXBl>eY-;a~K$7~T07 zdY0^8{~z=;eIhE2LGZ@lz(3glqUU6Jr=8^+(`vJ=(C{s}15}mV=UQT8T%_k@WY{LD zMaPDu>4-MScfyQsBjUB(fwwTjS_q3YJf5ciLw60K(HVBR{T25Br+K=urQ1prX~JJtTno_4^j$NBGp7D%6VMStm2ebrr{OFl+eT6v*(mHo8x zm$wICnKM%LUrCR<3}GFaHga1lBjev}8(+S~e`TC_)$uGu%~_p8{r#mmtQNx&y*!S%V}NkF9!{CbzG|+_RP_EYYvhdDQFW`W$GW_ zEh8VS$gqY>nz?d^NNX;m7o&Ng;P@1qiVzk#n+e3m>F9d70JThO3TH1}rtwik9M1^T zulXm?NFtnMzA8>k)PsVmh%Vn?!7IIrvmA?AdQ0{s=J`(CaH&p`%=RQYof6+cm&0h% zbu4Kr!o}N%5?6m!X8=|d81zJm-ayhFR}IP)>`Bl9mm>+2-i-R_Qn`;r6FJbniECFi ztol3MBF_CR^7pmmJ&%8mqnDm+PNdNnjiqBC#MEkyVJt(XAD%OCnip^*fT&Lrz0DjxjeQe|{ZGb>X&_B}05I-P5mE<)L2i@Ig66xErW|tyWF>JpsUvNp3hGdV2WV`@T z8iHbf3uCInPH&=Mbr|R=nstb6N8v1#rNv$u-i$f{;C6p;oIiGe`W-p)gI~RQt9mmt zm7Gu9+cHZ5y9-NoY^GwDN|&?qI~FwO34? z9b@WGCUHlZ+(di%IEwki+T-TyoD6|qhMaUUm`^PYy%v@A37%@q^J8#L)m~@QPq%w4 zr}HKmyosz4ul}dgmCuxkQ^DGA|1m446pk)*07}#kC{g79mZ+(n$^TSn?EmwS9I%Md zjQ(rmFA0Jz4MF`5>sfQhMG@ja3ss=UWtsB%7{-_9_2rIWU10$_BMX}AKh>bV1G6x{ z?)PIE1z0Mgu0Mk!09tK2Y_cAII?O3n5HWz|xR zbV`p^=V9`WOa}NQn;baGp+$1c<+kzYtOxu87!EeK)h z(EQ_$!4OOYJR2Z?-SIBDorJMpF%sBQxKROI&phau!X0%;qdcVKy|}m>dlefS^CRbH zcN%`)Ta@^TVdj9plO}Z=s!SWNlZ^(fbwTlllP}*+AssyAFESHo$<^4;cV4TXc(Tk5PSyzIFC$Mkw#DgjdAXXg%_=7{ z*A;uZZ3uKlwkOTi}$7kSv_>E=_n{mxe}= zjPYuzNL~|7>ru3d51BE(M6pg9eRmfk^Vs&Xn@y4e_FY(^CianTRNmP=vTGDcz@yaO zMjEy*{Nl^U*zuEy#3wYE-C_nbVp2CJrg-Xttsu^gUdO0;BY;PU-wQ838} z7FC#lK1D{yuIr9hTSL=*jXoFMp4dJpL{(;kGv#s9Y$1DDXuS^W;=uPtU3Ra_krL?$ zVCYRdh%1`?HtJmY*fbaPgYwn9KlM6V$bT}F30Q9-rulRTdKvnuj{}S1BQ+#GByf~U zVXB`3j|oR6qIHVipj4m<^)BH6wp@%S>EMASRX75M3(fI;z1IMrD-^BEa!86|kw8M& zF|ifN`oCV7W5OOW8GCYTiu;E!a1QAkU87MHDY*QoJJ zQE>9?R?o9-O1KqjiOC<+Kld=zU*^l(S8T@!rJ`7v+9z7t9S7Ja91hq?L5JW>h+;q^ zbpXC88=PM+);U)CrG5DxDO;^r@9586&QZQP!Ab7NrF5_W!(pPfP&~77L|&z+X}mQY zI&~{~X+H7x4a|ZVZy4O(RlLuB8cRO&iCQv6g)B~ZdrToKX8B%X&I#x#k}>A(u6VU& z6Wzxk9inToYbU0J4K|hwZad8u@{<`J%N*A?yQ?2p-Yb(LiBsF+4*dSySR3)Ire4~| z6C*(dc{F_C>S(WSWYsCSqgY%oPwMGg#4qheguS(+&pjB&G9t-OuA9(Y^GZXldgSBi zW3Dp<(uM_6Z3&?MJO#v=>wUh*I&MR#-RK?}XnvMUl7BWfx2?P`iitYj3&RVJ{`9u^ za&=LQF4K}%EvT8BWmv3UA=ETDRIbB5jN2)ziI`HCm~y*+Mq((zvL7nxoSNow#tdDL zi<(|JRuPssHwPFs-F4_>LmNMkwCu(zTc8!xsaTq&>#^8}n5A)`hRoy<9NXPGuw-oV zuj}#=4^y&jA#MHMWW*F3r^e{W%8<1Xr{nNy?}z$&WiutxxV0QwHR7Fd=9AuZjYaVO zKk9KnWO-(Ef87yrL%}tr;9qh~_739R>VZZ+U6uhK_ zh!Ow*mJM9)fCh#uAS;=j4)_FVBrPri`22g%?<`9Kz60YRq3H|&APoF{fTYkM01GpO z07(%+6_4zT4S$cMhtAKN>Bk&>6FiCsog-ie`z-t_Mwd-08k%zJ%3EO*_7RuwP4Ku0 zpi(KSZ@u}8^Qmx1`&AgC0=)4{-wTHmxM>yQ!8t%gCD;cFP2s%+&wj_bWXF1X=0sf) zaBN0NW|fY1v~`_=$YuUz5n|c0z{ClfQ)M3 z6`}=vm5yJ40emVIFNXlGI{E)<)fST%2p~Vjfbsb54{ECrA=2>|@C7aTU8icbDqzJC z-6kpG=L4! zBGe2Co2?VF)QDU-dV0G5eEb$Lph=67MM~8TPlFYn`dzYLAdzqZG!t63WQQPpLALYH zBX%7pAhRA5or)lQDNNXzaqR&>hh!C~5o$I+L%uK*BO9Uu2uArDloI1!P>oD#KqwZX zwM}sK=LuWYvz9=B8Tuk3BD&i}5D=nO%v{p^8iUy7_mo#j9gtkC0-WOqs$^Yy1CuIR z0S@)fqqBg3GBAZm8Gw|-EFcThHR#sUNhKp)OG=SpZx)JFV zOm4yjAqXd@6u25GW16)(0Khl#SHtK*b z;%pta_hJ5E!8V|_h7$i^GqbSRvS;Zmm9#`LhOFMBW9wy50|1Peo>E|jm_Z6z2>XrH zpUS!4MGw#c@vHk|HN0`>KBouUkt*WlvQ$S&)`TksiwHEqJM2_uFPb#~9h_;qb-_EU*VT zFAf?GAn;w$$gXSBtVt{C!mUU>88~2lRmscn07;qz1R$WCW~R^TJrqaZvF-2LzULht zWZ&g05{~nGSkG~Mz<==`L)ZW9@f7p1+*{UKE>>2|zGeuzi5j>Ur5HfgS^*sWvB6!E!uYvcWky06@abIxQ~GvwaMBW|ZmAECE|* zfhu;>d2GE-?Hq10z>3!_`AWf}q=OxFgE&C`|0oDIT{cboIQYw7rBUn6Co-ALv+R-U*BY|wKl&(MSmhM@@9z7p z`(#zG*!|L~27Xy9lGSw5cs+jY^4kV*&#kZ?|oCX!!{$+?`NTCSWcsXV!d^ffypkGzLzp~A5` zKx?18ARhs0yMG7bJ8*~D*FM)g^-wtK8_NA&Da+$5-%S)ETLduVNG7r_?!<%D%vvkz zONFiq5Y|mznEk{A01{l%V@fzz^%!l{hRt>TBlnuLYL({`+RO@Un6pkDiYL%QRiK-k z;$7P}a6cJVT%=&H;MPn-Y%)Lo3>Hj#3jn5Y~1jEA}F1r#y2m=#-`@>8N8YxV@ilnOV)}JnoNM(Z_Ed7zusnI&gEFcDz11@_O@)zj$T{)nT3!wWP}Y{rCVmddpC$Qq^H8#etGzU4{HS{*uCM7E)u>@(FLE8q`z+bJ=_bFNu8;@d6}h;ABh;eOgq84xo{d`KsZ{ z)WpQp)c5mGe<+gq)LTnpQ`iRwVZX?*aIL5&`!q=I=-a(tbpxmQP=&Jjd@I}Awf%S*80=T-{85B77&;rx zPUZ&eSxx^JZ*Lt{RrIwBgD9ZV-3SuW0@9^)hjf=5O1k0D-2&2a2&EeiU6Rt$-QC@B zzK#Cgd+$5GcZ~br{p&3EkiF)5W~}+lx%S?x&w1oYdF(anA9u#`YK|;q?pL>!yZ>Yf zrGv*5N*PkPV2|$<&PWBD9!hFwFZVB7YV~WOKhZZ}_kWI9#JnE2|14>-G@n;C@o&@~ z?JqF2Jic8226IU%y&7{T*^RT# zl}-EOwLfiuU4lW$z;_pTu{Re@+E2i%5+3+7kKOdKaPsFNBh$jdrPWqB@mQmZOg_#=?xJfR28c;bs>{kKbzVty9*EQ`Q&YR=B< zd3sjX8QMw|FfBFpEk1DFFoR>dpd5vwnEg>@SY>6EzL$y2!D`p89v%?$6N+!=099x$ z)7kaqXQ5*mEcj&gTyG3|w9w)7! z5S=licBZ&z@s&hbLozK;s#ja}+x`Aj#2)*IJX5_%PXC1I3AA~w6X7*^ik7|1wtuVj zLe>NproZxn{@0}m=d*Ebz-mOjHvv<5wqxO`7B4u-L8@^Hj+u0!8@LiYkvzF4q*p%y zJj*?pMe~9S%c?Ra=OC$FSlu`$muvL>_$|2Q1eV=g+kzR_K2P2*7tvv-s8pu;)Yc5&;_X(aQMPrY-HL+D{8OYT-K={GE6-}BXrc_Y1|!EqE)9DZvFg1q@-t$R9R!X92B{$j(FbV^Gx~r zDw+K)`Fx|P!oGUGww6$BcKaFgTPD?n-wM{vro*MWjhubhxAT}Gouqr)DUI9FykCiw zM=H0%38XA3Q7tDU=orskRjmuvc*Z9urxcPtgnA+TK!I|)vhwQj5sC!M*O}X|uL%)w z&|ijEyQFQGUEm?!uE(v7K-%s(BEic?j?Cpr)Kk`W_ySc?#vQUWwx{a>QgIf0d#X*j z7FnDGC{YmZ3i*jH_%dWLXhE?r(}xuS`G<(3)rRa19CJW*!&;s?)>uR@_koSDz;YWx&uv)%7))sLQDt+1@X*K1yG z_gO%O*pQO4>cs<>Pks6Kj9F;*uKy@JGn$K3kb#`lepVN^`Y6$ z?4-{!QZEog_`_Fv5S7&{2M&%dfmY5(MZUd7k2NnQxcu!ve56{#-<d zFK`52_8Na#G`Y0w=4d`2ee^$?6OWDpw-*f#3N`IYTU3kBOrQIr(Qb>^cIj51!3sM-{rq}IfOV}lQh!rvxxHJ;0XyrgL{IQ?QS6JCadIUtI7HK{2VofX zb-tX_S`0gaT2ArTf;Yl|$dcbk^*h@;ysO>i+SbA0tgIH6LZj88$re<{70oc9e)A@u zfRx|JWng+3uFUaIje}{0=O1IP)#CAn>raFq*=AabtTc6X!DZ>dUh0p8w#a&*=9Ktc zwOIE|M^5e{VM-5hSO1JfmMy{A#R;Yz?>!(QJCK`DSv7>Pn06CdM3)Ppe2gssqOG-< z7`SY(Z^-K=o(UR`U(`rNWxaTR2Vjnioc5hBE1CCeoiVBISpxXy+4}$>zbmrvAXttU zTosq1O}+)rzgz&eJH|ZaVO+}3 zdwk@oWlf^Pd}#gL95j&hbaSefv>VciDfl;7jWLc6zhRB5#5QS zF6sTAzB<>F0(vbhFX3ghu)Li2ucO7A(y4oS3UV>~`NmkyqJt!?} zQVJqCD&pluj=j8g{BT4&Rk35f4Wt5q^K_G0c>{4!i{&MPyN873IjzbNq-%OCS9YJ%*I1_gn6W&C0(%pb zFcK@{C?fPTrUg3>+)Dy$wu=mAza#^#E?fjX{0M8GxvSjfiGFDRJ06y{{=_ zB8`ak!K{Z<&p;-1yN!>rAsJtRgi7unucL}N zivM@UT1>=*$L0RP*il#|dg$i{O)$&b4j_nzE4j@?4(r)Z{1y>s5k;l|(g}Nk1$Oa1 z4tEO0xE7Pb;e+aFWPMQW^w@Sv;5UspLozq|(Pp4*(nS$6bSTTYc*&bb1+oZ5rY zCvV8X;o+nMYsg#=1Z;7RHR=cCC2y~yC1XVyh!gB~15IGL02wQ3Leo-{h;ISod&op2O zq#8gq313wmeK-u}+D(T|8*oA9=;GwI_#iirFA8x!T0FbUA*Vi#e>G8)6QBPb<@W2s zOWbBpWQ$}~PMVk4C0`qimw+sbsQDFDpHapBV*Qu}l*S$tS01No=4+$N;SY59O>Kab4&-fY#$8#Dd)ZqpVK}?rhQWvz{z=ud~GmR?oK$A})&wC!w zu1l^UshrgIo}EVWGa3j;u3bie8*hG(v6PdI{jtF3GW&JK*bh-A-PnFZ$C73AqK^L~ zlXi`Dq#&mm5Vww!oGnSz^{(a}VoYX%jK15BPBbfGr__Br>k|jX_r-}G@wOV$k~tS> zK~-gS;_tepXFI!qOGTds011+Q?P18>ZAjNTacQx~wL^|NYk5PkkST$m)J%Aoid405 zi6TS5?}35&W=njBazk-I+l#u+F6T4>iTX32RW%9kIM{>GJe?<$V>+;!nnFE<}>q~97|YPM9T^sLTOc@3?cC{lwS%=gx#yiW(B0B+S$8A$1 zj!t@di*KX(i3pP#8XM?AOZ`)lx-Uj2?sDV@J)reB>CGM+(M)*#(c;l9kKM#b_hdS1 z>grXF1zO>vKV+&{+#QVdKBFFojr^;h2sG;xHDib>^LTp=#!_40n&GAdXeIaT0cq`@ zqbo~C&yTNxOcQ`0da;*F!-`J}Uqw|3HYTZRvH|Qk*iFd#ZzE-sxec}ZwdwWB8;gq0 zOLv;qvgi&FT}fdgBO%^n;IMWerL|D^fVR9|eJeG(t6H=ws7JvYKZh=iJ9iL_MmAq( zh=9;)Knd87VEJ(tuzRX)fS)g|bO2z~yX}_zpgsUU-k63qrIp3xs_~OSzZNgWiMr+{i7!Aa6Q4f`uNhr!iY$z*e$bit1UI;*c(m z(Dbiv)dslGxZ&9hzz~OCBmiG2Hk0*xWW?)7h>u4ffRwqnej*SME{QOEtxTNkGIPbo zc6ui9n&NBh0R(nK?1A}sztwtuFG$fD^EOz)#pSHF%y+$1v%ZyQzyEw?r2|+91Z;ml zhE01-1Rv99%`jcQD#BDsGP|p#Y@+W7*_1<;n0wtvetoYYQC-3LC&k)MEmuE39@7dQ z$X8}36gy6TQX7YOoYl312Z1BMWEFg%ebccd3dg7_wq}u%_K9f?ZFPKZx6yBnK(V!p zfX>BZOR*(1)Ph+=yue5Z87VuXD z@re)ZmEI%ojF`OzxhiWf5WGz<`edr>bdz?XJgfS;w{71dQ;<+o1ApV?eqTossz#tu zfQpO!oRf>QL5KNOEgamo)ELNTJ8%@*-!t6g8I5;^%Ko>)Y1oq72r?Y(wc|}^@9PJ8 z6cr3oVTwi5o6e03*6PY}Bd>wd>V-L1tXgNes-=paGvK=0ww)b-I%?IyQy$CMB{tu! zfw_lHPk|!X$ZH;!df^gA0+a@AtqgOsM6Ph%fEGqyUlgcu9h43D0Q@L`wV|fnr+N2c{S_i%lPE?3MtGRJ~TaMdL=N zIwkrTD)YjQ7!;-|1U89$)o<2-{=Vh*xObh1xBgej47;V%K>D{i^UYpmj4Yl7uJ6aT zwF_&9#swxO@iL&WKL#kGc_BVN{il@GyTdc{t?!YZx1s)8jC7yPe;y3hMsGW?jM;tv zF6+b{cHoR{q8X?uV)f1qm%ZnW zU;%~&t{`N1?ZUlDuMbdIzz8oljVm}YmwNp@iU@`+aN++PNY z%u86d@NnibWAJ*y&El#HG~kyMM4Y%npIg--9)yx;m8WH2QW=X%$I+o#$aQc&AAUGx z>;5e~JAv$VQ#?mfiO(FlL9z*j9T=muzk{U96p5u@)&asdDnt)C!oi*A?ZB~Q{mMdg zm%k44&=J9s9)3ei?Eu^q|RD86dnq*h@=$LK;A0Xc2 zTh8>A8jPo$EIhx7nn~7xGWqh^4cH3?v?Q2c!1=hD3$)3Yah^^5ud;X#t8u$Z2gbVn zSD;l9_v3rARwkbCLECrA(%~&*9A>yS97i*G)rxq|(SP1|`+*{aiY&-ru(x97>vv;g zpaGEfw_Vo@EcQ!ohbCfSEg+Rcd*UDNoF3*6U9(hl0*%INBKdO2g!pjZ0w$Tsh0ZPo zW_9&*)Mh2MVBrCp$D%6@xd;sl^5S8tud~$T?7QO;4l!FHUOI3-kB-wOKb07)sM2Y{ zJM1W*Vt;_8V*MHE)Wia`ta?3$coE>Ed$5t8zuR~Q_u*%U_9bttT-k+W<@-YXEFeKv zD}vV5aZg#8xFqHxLmNIz*QfjXq`451BLYuMk4(q3f^9p?k1VFQ+>6&>jKwoKXF-J$ z3}q~YFrX{5a5D6oT)*Y0s!7ufKn04*1)|I1cO8AojgjH=pWlR>Opl)m6J zuU|O)t+;pMz9DK^p%KwX5HLGFrBD0k#;i1RUl>`-2_7!jn1voo7zZE!H3bkR zHsl=g`}I_)@VfxQtx%$cdx`DWzk#KX-yRu;4YUray@!M2odPkojIAZ7e^vr5rvx20 zT#6Od8(K$p=nv7wSDV)6b_t0L(MbNkteBXDLx58I+`M&8f<(Spoz1-zCYJL?U+H`| z#`gX`%Jc1+7jXG9%oiJ4C6n_y^;SN8mZ!G7>Gt^xo6Z0LE;8Xd_x4y8hOd+!Wn=mq zwv<`i@Zm62&Fod#7)++t)xXwiJJ+q{KTNdK(5o) z3c&D;68(dng*O{^x|PTu{0XePN`3mwCp4f-?dNjeH{Ws2oF*MKXeu%Bh9X0vITN0Y znTQ_+GW2}}TS8XrGO^YMuKgt{;tORWxR3a!D)4Uf2^fAB3;`INsdBMYtxHE&cN=H{ zKtDq`q=1@QoF!5*oC@z@SnlVaq91zo>FQooj*skcZ-A&QA)jw=T!h?P{9OxkA>#Bj zn9NH9`9xNvBjVKF?RDhX&Ifnh0Iei7Mp`@A+avMk`ZbmClB%bij3ep>8*ofi)`cAu z!CF^S{%AmQ+pE}0J5Cv|X?(Y)edvIp?{XFPZJ%M=8SdMMbuT!~ST&IzU22iSkG$nt zCH6?d%j;4=xmPlZtpsSZQ?A?iZF^I{+t9;|^^bqB@~c6D^Ute>4=Hoe=_P0ICQ;HB z!*VaLw4dCc5k_`1zCy{V4e3ROek>N=Ye7&G{hn<@PLAY%+7n>#<{}_S#Uwob8YC4z z_T0cZ>YJ5eEERR{FtC!3*je7jUDp|zpC53S27ayXorRe=A|Tu<@+zC0?i5#+tMm=g z|41Dekd=@M<Kj5JS`c+1zYF z1rJZWH}~WI;&j8rHg&+=WWB>ygdPIiTd_?8OeK1}rF}9qG0~u~?~#gG!hCYLF*ZIJ zQoh{pyk{kD&|)Fj#&Z$&pB`M8v7XLJRHss^e9?LAE)phUy7l5g8rqTMVWSWG!?n>N zdc*Jy?$t*p;Fw1NoxA(wA@o+#R)W6@0{4SV_~{UfEKrVP0e;~masHp20r`J<-sFFp z>%sda$oj=cyxy3RAym>=I=zl&T#K!Dm1*%T9RQYb2 zu^cXgTf15?r+wx3?75P%30x5+{VPTUiGbZGmyZ~qz8OSh5DX6-1}{7SpZ^_AmS081 z6vY&?lFN12>|fvann`D$+j5K&2MROKKYiy+7mSm0IXdK-S?bVElE13U5lCLY|9l~M z1UtRMKe`*2nTwi#fZ%iV!_1Vu6CZ_))*CNu#Lcljq$F9FiDQ=$j`c$|LPEo)%-rO{ z^rDbF&yPl-1#EPxIgeaXut)tN@3TR!7)Ea#yCyv9AC@gD+UAcQ1Fg|8-SMNG?d+Q+ zQI^^bNQ9)g$`5&!FS@#slgN7P#?>iH5}t*gd`gr>l7bVZ<61YSKh*|K_Y%kNJB-b# z^{aAPZp1kfB}QWMFO<+CW2Ru(y7WEE5Lk^4PgGSGtu}F#fJ4|Qx7(o1hwc>PjqlVw zM2l{;2Q2b6k0ximzL&IIG9g>Fc^EN@jj+NopQVillE z>F(59a|Mw^Jc)KU*m}tf^t`;rSKh8$DredZ&K8SjLg_*oi~09wW3}z!3NY*Yv$B^5 zLJqhp+e`A_fAdQPJ~ob(t%I*jA6Lg_67m{TuHC;<9@RjA8_XUmqXsYCS8P&M;joQ) zkAL;>t)CA{Otl=ncg_~wl%g42I?^fVcX;qWp=Awf# zwmy1qP9fABN74&f?so<(WS6yMa@V!i-lS=I;yC7FLlC+)REtf`reHsRV{=mQkn=DM z8_#f=5Q1gE#(2Cf3;#fDrM_gW-?i*cc-&g7+Axk@vZTN&FKnya7hq;LdAW;IEM5n# z^Nw+w*vz2=ap0vXuiH~cm;t!U=+?R6=$>RkKqmeCt`UK)#CwSJ*dDTAxAig+5WnS! z@8iS%aEU0gAvIVWc+U6UCq7M?f z6HHTsKMtp6sdvd-GDewrHtvotVVs8yd$A7w2X$vZta|B|gobqlvGX@i`Zq_C$|hep zWn3Q-m32Ot=aoUrFkuCk-w!#`!LX(}<%ad^%)^E2GHCC~=|exkL$hH1okmmL2un!W zAIRu~Fw2tAQohV{C+m)>S!Rl*)`$@_8AbIko_0AFtF(i8juUt{k2m5xlbMHa_oik^ ziomc{apJEX}$Zr%XnzYwbYrEHdO5CzeG9t zAH3aDpM07y_(I=0F?RSyeDDJq@&M9!290e4-B}Fa#DPlP zzo*x4B7MQloirW(UeF7sM?{ws$L+-BUd$t4x zJ+IGK*nNiI2#TDOhq_A6+DU~lV@Qo?2MD@XVT_~(P^1uMCsXsjlO2KojR1-SZEpW} zF|$#7&TR3v*y|vmps|5d3U<%mU&-0l%s~Q!1Ext2jF4Y$x3yvgWU&@HUN0dl4)_p{ zYjBhT`P0N}W6=sieU$*uYmfIXrCJ8`Mn*V+I zB*ino#3O`@14&-jgDjE?OmcGrXYt(MomY;G$=gUo>{2BAgiSxg3|pY|!f4cNB5Rb| zk1z?+0EME4EmZtnzRBkFe=VPE3PX08J*fX3${KqBfS><0$}#{X+|ytcxbj{w=bSv; zRg%|s#PYRhrE~auGp^S;&T;R}&jL038D}#UtDkN;v02`daptvp-+A}@EaoT&Z3-g_ zKNEG!p$=jaR5f59lsxo)ju6D(Uu>FuLsC_HLa3AJ?NPi-QP8!)Jm2w8gKPgZc#X31 zR}=&Q+EJvdlR6}qEIbR1q9A&sOU%Z^wC450tZp)kgRD(Gi=&@Cg6^s#RVj-cS`#9% z*$f!o2uFK0A?S{&UFM`yEm4Gy1KF>`92(%(;j4C&G<%wJ&i+s9987sl(m5RpjWMR4 zl162K7LQkdo)bssYbH5&9GNRo7N9b`{-oo3Z8(ot!ERkOjaw_VAJvcVv6@89{O+>r z#T)b_4(8yC|3dSx&DRCl5(cw^#g7TQO$i_r;Gko|c3@!mbfv>I?GYOjT?(H$==zyq z#1iGe$$s*Q&fUpPJ5j6B^s0Y58MzPt?+xx?r^9sDOM~}4|L}}ba9o{+$G2)^eTf9CN%P3kyi>+O;RX(`1d|1wRtRIxt^ zQoXYU@PKMwuKz5WJNM2;ZeH*r-OIw@FNn@Gcp+MOAqmjj}u51rr&A-kM>X_TbUN+tLx;HqjjtnC*u#e33FbTvf`BV=*8w|1 zxF_En*Nmj;F%qa+EaRrxNZtFNu(Zk+m6(aW zg*MN^Cc;-$&92N>1!XtptJ3?ji3U1aXRi~5^@kP)|A0^QB4{&3{-?YyQxu3Dxch{# z2N)=UfjAUI!H<|pPL!De^;eV_fH$)IT|iNrLNqa6m=n+PXF5FYaIkkK*65W}myaHX=J_wqP1d`d@BgxuKYBYX(Gtxo7}K4C|2gyo zV~?gst~8v8*`hvwC$}b*jLiM#=$OpWhi1Ul{TF&)7}>BbL3{U;dv)%52fht3_I|Q^ z!vp8mR@^&~Hqab5K!$P``(MG6Y?%#J`8wbvxzqF+QerTSqjN{8U-!H!B=b1r$%Dd302!Zqyq^w?<|n@q+_gnraecwO27Y-Z z58D2cfCkz%s6hkmf0>jAwRG+$-k_BJY30+r`1WCi4Y}hw0thsK&akg#SFyWkBNPaL zd1;ObdrVNoj|qZ4nfZxN5-nq}B88T`&5N4XvKaqdl#t>yUYgVM$(=OY6rq_oaF?Q& z4JX9m4`{h@vRt#8xb6~lz`y`cL{Wzg*nW~G+6E90N&O+*E&Ztc*zG*O{Zxy8iV(5t z87cQlQE&wk(Tz)IfnGtCcUFF?6A6s%H1}n?OKuuMDn9OWo$6YC8UA2@Kzt@v5~CUM z$?gvUU_5HZXnXT~;+F>boVWiK+L)`}`wxE*e8TbN+JBZp2hKf7{$-l*=m0>-!pw<1 zY?jN^Zf-RE!R{dSR4mLqN0Q@3;1pnw|Gb+N+v7dA96z=ns=Hl7kTJUB8u-Ok|G=Lv zM%{t&uCKZS(_O`h$v3a6T+Rs3|G-j|F1}SSvpoY`V2#}ufDV}NIJw)D2x9&;Xs3Mium@=v%zB0AaNt25CKtnf%=Q_{5&-Qj=ciU5Hi$riyvEt?p&!V=u$QHA9Z}3 z*_WW8OIh}*79M@aWUjFO&CEI@w2x%#_Gy4;M30um4@HyH|i%_ zOdwfatT+}xLt^B|7RF^5p8DfJfsd+9kEoMhxQ!j$IEdpTR*i!Jw1mt}S>$ z_G{TSwikx{GzKJES%J{Ee?$iijfi{A-_-gNp^jT*f)Jp@J9bHWa_9WQM|ffv--@g7 zLc;$TZZu9dXgX@vUzXgoF-`)_chrw$?~6Eq2u@n`WQisBdhqy$q{W6z#FZhvRL;0O zgzF&1xBgm$;{%zO^zL6q8NxGW&h?B{1*l|f+q$coBagJ^KuR`iv$Y!?;v;6S!#ZmI zS7e;N?>y19Ub}k54JUKW-@Sp!xXd0sECSvc;B`)B zGQMFm=N8L@{bc7-Pmh-0YrkGRJs5`lXJ$)UkJ5!4S{QN+g&%j zhf_&e!J6(#1;I1!Nfp7GWvpjTprZ};Iw_bo=Y)toN}?3<8hhk0ieT0Cz$$w%AOhLy z83rK06PzZ9xhKdSR3!d6#ZckbtP&wsG{%fK8^JWUG9rdCdSlhyl&3%tvN9SY1Q=wPi>z zjWY+2<~m5}ZMc@^_(1lhh5MH=OH_fW(L3DWT?)5XNa_4JJU69Im8QJ)m+3dzz4b~T z`1~<2B+}fPHie&jTK>|&%=T0k8EJ)1P^OOGR}4(HQso0ccXmCn24MOZ!*)|l2<^Fk zn5(2% z#}pp&;-4hX07>{eP0&CmNxtUX@HQ2fG$hoy@zY$t%_(XFiI~PMvOuq)OPLZ3%~7Lg za_+4gp9QGO8eVteZabgp?tz9K9S$1L#L7H!>Q>N%Kh8UDous^e+Fk2r{Ihxn*(r2< z)qf4Apig2}B}{Xye~C+#i~MR~qRgH;Yq?#KptrZZlT*6{WT`CyB4il{0^lk4X?B5B z(JESfl9Tu0!O8saCqco40t36fhModjGyW^(!AlIw7~s%h{5t+XYH-H#839BLjCZ2h z3BU;8yLR>*0$d)2g#(6(tuw0ZTBESi$QdZWZW5=F9?Mdx%o)Z(2bd<5?w^+Sc!tSP zlIZ|IcaC4mdxx({*dlGs)xjbNty%k-Kn-1AYm-j!svaYfZP@Qf1^uJ9VYOrvBBz`{ zhz|bW8tef)W$UG2^od)6$) zLC(1=@XYJBTiPP+XCPSWS@_ny5-0#q*Hq1;_h%9VkF1bi z!#l80^^0$5?Hd#WYiG#+Tbl{TH@Jc_*NiKGZhPt&+u!nUu27`;#T7TB(uVNJ?`yA< zyuBjeh*H`k;D}bbV{#|Sn|kTqB6k#LUKmXEZRkwkJoijDbtogbMe3eWK!09->D}QE zBnmjUZ-B9uZSi}&pO@XbdR2!AKi#IqypC~3U$NJF61SHZ6s_fDaa+)wK~^@(5S&qe zzWA-w{rTvG(A>=-?CR@=!hqiXFt{6g19DURsQy?#gX}Ya1CC~w6B-@$t8=$X0f?~92EK>5V za)Sf))ZAx3x=V^@@mmyqhL6Vxo6Ws{U#RFNPzc;EUFm}0wIoFvv1&76LoR{y$Js|>xbW5~1RCC&ZMxHGygP=@mC zga%Q_+4_SA&MB(WLEU2m?m@}JY@Z2;Glk%EW|u9>zM2w zXEAyv7mT5tpB6>uXS5+J`|`UmVb5Gv+8JL+(VLdoCX5>jAVu^|=OEs~F=Fi0`pp_= znH!X>g=e9aU-G9zoP|$-log-^&*+Wnt(TFG-S;_7OuuGTGSna^Q70rD$@mf)Cv(*N zxBCTrXC>(R%c~Gi2s=&TAn*X7zyRZkX`K-MglTk&jE4H_HL6M>A`n35aN-gy^|haO zg=I7Oetx&+%4a)l=7ORA2~6!qaVj$Yk`6zMw1-n?cnyqpO*daq`EIo z;@1Ozqbt$=sQcBG;_u*W^tDr~w<6Z^XHdMBn#F9sRbH^a9Ox!ua3wc-!(BEmgV^%fTX6GCY%j8X3Wec0VI{>{h1i(=)V~1io>?Q7eVJ=S_P== zm+5nWqymF(;L0cP?a5BZ#uG!$Hz^5nnCMi&Y=opetRLoeds!UhfpDQkJ4zyDTJDno zLyfI|UP^q8G1N8_VAWS2AiMzx%)DVDwni) zbgH3U(oRNy?Ry|a1JF;p=;dO!JQw0<`~xu*(z&!V2yNn2jkGZG!uoHsm3V-!ZEgk-O zpmbx4WPLu18t?hvIL2Pol6E%F=YDA*u(WaeAE!=)stmb>*kJ?YRO}x)Eda=AFj>L* z=_tEg8NjLxT-c^i_x%gMTn>CGeORY_r-nxL*L0dl`*?<*cxl>zi<|vlwe%q*b(_9| zyax!q>@vIW@bf2@xXd2r%!hi=vOB()L1K4$uT9JD%(h1jY8lv9-(E^Dc9%@C zCGayO2n3XM0`Oo!S%QX7On4QL;J>`1ALGNPw;#%#LmZGo3*Od5rD@q)7AVP-@LEhN z-MtY&8!7V5h!no~_Jy#9ek&7|xlIhgBiGk*N%47CAGOH;p<={K1B9iGpeOhOzN>y4 z(jwYw>imI~DlGr1o^D(+OYIeha>2n*38LoFo%;XKQoEu`^1`g{IyoK#Miq-lG3oRQ z4IVSbf3_X@qs~c27+7A0<5j22c9Le&VY^{m9iH*-1bpF%GDmB$PE7Y3}xW1xgAf?iOU( z&IkfTJ5EU%gET;m#A>s_*LmAVp4}t2UU7#FpDuB*s}I-VpW|6DZ}C5|(rKN4&RiGt zCJu@+^bS28Ib*DFaQrl0SFZ*r;K^63H_WvF@7Xgz{Zf_CRWEC#hMD?F9&k1yBa+!MGY628#du)+Aa0c3dQo>1c2Qc{)kNWr1VTuPqdeUQ{3_;W{B{P$f zKnIBsbDEmaG#dNV{1MR-8VgS4J7fW^?Fs4O9OnstD>G>;9DIO3l1Pmf=)q8Cpt(5aRUaLZCjXv;n)0Gu+N<$r>tmt_*1A~+UNl)8g99y`lHyHHM~f) z*E#-F3vpnV{>e{@{9If5b%F9t5BGxa9QR`T1PGYtjoAE=brCI?|3<2CipDh)xg&JZh@)~mnG2}j~tKo!qW zEow*C-Zd}&*wSVOC|F_Y9%4flQfd7R`-0BhA>~F;s^&a(pPb9zRUbaB8sMP|;KW8Z zngI1*=z^1zcGgU+mLY>Qw5ju7Exsr-q^4NrHz$zoZExh{{o|oi0{3W$jfT;tsg+a8 z`TJ)$baw(>irFe|*9)T^M@bn6#fc7HNOriL>VNlv^lzsx_`3j}L<FS!h)?_>7GtfdxK8UNOWR%>O{qEBj(7vg6_&KC6p{D0hGR{Mw$%CzF?zMT5?d>5v z2ve}Lcsw-KRbHEj#~xM_3@s*ctHFxTEKPH-sbsG_RZoEtX^zl7Ry^$d?yJ3QjVk_t zeb#g7ecfZ<_CLO}ZQywum$Eo;R6raajtuzu`v!j#DeN zapb*pwC4eCwgbfuXhOEm_#HM=dZC~rTD_~brT9>v7bMl#%l9ipK|U<<*R2pfvlK5o zH)OxZ)>ENeev#$jE!(3rSTfHRr7JRR0DCrhwov;ER=7{+Oz_KL7;gfQxdM*!w2pM5e!m$#ZL;cO?6dn0dSV<_vo>Pdw`4SQnVjzl4 ziGvx0EFtXVJby9#RKyOzuUxx66)Q7l*h;|l*+=RRcnZkIDDJ_a694ION7w|?D}^9- zrA!t0yH!#+G7#6Crk-3${^*3y;n$yY8MAfeb7+0xsRu?)8WBdX6clCscXurm_4O6} zk6P}~z_&YjYUP$w%=$QlFDnISzOVaTUheDB}d;@PbT>AIqs%_UOOtX3*MWC*m&KR znJ9StO5F#{Q-kvX8kWF4m^s0->z6(MA=I!&OIfkqh@-lZJHb|QpRdtOicLKowSA3= zv#{cTXhM1|4U%#Y+g&4CkI#mpK=9lz1~a_Lop{5z!fCd#VYNOW$3shm&$>dcCNl=% z^UQm<2Lb&Gu+S8?1I3 zx=AV%-&~57$^v+Nq+#MOL|~=!+;_hUME6Mq>Xxjo1jP6?5CR@Tlo|1Bxx~6MLSjUg za6CoTJXn6%i|9OmKuZj{OAOJK?nIF8e9pT93}rg~uqtfT%J&{+@~P2l6^Ezc^&ft> zfBVU^80{BalwDD1rl$Hq4|mYHG;C8v%%N!gBSjLAG%Ev0>oYR!;oVfB*(X&;zjFH7 z5_&BaT&B{nQpd8e%lz0xJ+UiCQk7hSx6>}Dr1J3Xq%rm)s~L98!scm>$K53R-DzwI z9}bu0_(dG49_Kpa3|;!>hHQDqFoQJ0)~wIe(8c(6E`UuK~gZ`XzzS1g@Wvf)9#Ob&r(5taD<%37R=Jac%r z|9iM&j@YtimtB`w7pYj4a3;Zz?XvaeVpr7|Qssq^=+QL9vIJ5d)U5_p~9Ss|~0MFH}X1w^rrSvS5Fy@+Q*r zZTfJDWAj1FHOk@pQ#t6u7J0*@iw@DHhs&!c3B% zcBu}Di$4OcN5&_M_9tz$+UG@01%BAIXhLZ_H$OV)ZZ*svj*1>z371>K@eiWW)M&db z2_aDjlRq9frssbreU89Tr&)ThlX#pvCT}JG7LM)>QvJ&D$2wC*oaD6R^@{yekBped zXhdFURd5Nfw%~dC0oxx*eLg28X9?%F`+7Gxr|?*=fb-VJP67}r`w}-R-y}-Q+gAgY z*1^ZMkJD_0+hi~`yJROux8Lrww^~=}ymPevA9XthDpMgdf>M{@=Al%yYNTEuklSbo%(k7xYDE%!lgFuKW-6!B>4ALG}S{!wpPW-Ay-Y z>*)%MFBciMGQLxL(iIg&yu&SMsQo077gHNJtsq$@OW<*%G|sRUM{k_YiIQf*t?#^C zIYp_dhc(GD+T2X-A+7ga1y^x;8A62#>)O#SG=f64iV$%vc5oNp)SD;JZ5fR;`|!V4 z!Y?BBc*AnKk)yRBXW5MFsJ*1;pn7F_^GPh)`r^gOkfms^!NWkk6rV^sB3)B&_Zl^O zmD=vkjW>D|ixim(z8J!++qlCAtw;oF~mOF`!mref(d*~9u5YVLu zUO)9(QlxxEV5X-MrDLw4qk*0GxFAd1WzJ`>>-PWS>z$%Q3AV1$*tTu&*tTuk*|BZg zcCusJwr$(?pL5PP?&E#v9$n~BRb6w*v;wo}gbNU+a;gWT|j7=LooBuEgkfRd0l!m0M5_o?ov4T!xg zPzHq;3dbxCffz$+$Xep``P>6Kg};TN;EZ2+$v(z<`G5U>%1m0RMB2pUiS2m*LdL?& z_Da=d!UM&e#lKL}c_>K{R{HAsYWphqitSY1%)v7NgcA-jtTBxA5{Vp!w!gsa8VzU+ z$Ov`iZ|qt>{)u7OcR0A^TzMNN6|Ji4Z=HeQW%4oloHPH!%Y;Oxr&I>nnajIS#9}U8 zTyj-HwSZo-Us0kb!-{O`!UWIix(w0l_8{$;pkYMEV#lVI-op@BajZAR%4F)?P_2vX zNVf^~Tr<;I+nZrT+iY8R-I7x)NvGjk->iKSk0~lkcoIJpIi_+*<^b;= z#Vz-}i~KY%;0s@|#JS&B3kK;$v5wUaPC+{M0?<}~Lh8Rzg`W6nLf|zTN;y~G7D!K4 zJPKuEWujktIp7C8mOD{~nLE4`${Xh|iy3=gP_!sal_FUJMNu2#k;y3?{jIgnA+wlm zZ%6Yeo=}T=9ib?xid~t;g^@Hwg+e9LT_9a2WG`;-!HMEq@hW1V!e1=^pZ5+;jKZ`U zoq}aD>UiSfuE$I{1r}dLRCNWsKdr&+JnwJ;4rMe4x%>=nmIkcozrw}4bEl_Jz&~aa zi(fnaP}G~TWj%^|N;_&T6&#wo`M4#xHQD7d^OlVtNx4&i1D1TVR4y_V9aT>XQSzgx zev6CUT=@|g@1 znpOC;o_Tr%2bi5ONaY4SmqC{a){D+FL<3;JZY%l>UN)@;WlT<(Sy9`f2*yQG_QNPf z`Sw-zrFTNauk+@N+f2=vYh`BCgjrN_*9%!zn*^9i>8Rhz z?RKJoEjVj)BI{ClMyo32%b%IsMcd-%E9n~Z4~hySF-0bbRu=_PxIqvLml|{~NCChF zN#z#LE+C7O*n6*_h)mfmw#wP30NexATp-&bLKV!;>jt3a6RS<^x zlZycftd5phvGjbwbmy@DeE`xn#3}_`LLXR82;5rDo-zhWd>%&EA5Qa37APJOMXWR` z=s=5Etdrf0ru%gk=}hvP)XBe1mN{Bl6d9ESI?+p_5kMoPO0q%Vsb(7IO$*Fs>|9T$ zp;ybVV2>i64^`f@DEsgE4i72V1I0~zE~%nl53xG!pSZFyW$y)4wqecwLU~Q$VH9{4 zfMkJrxUqw&k8Lv)+SZk9+I(ia0fm~CwUqBX9E0ZtKtM_6Rb7$PloDBuHgi0A-Kudd zo7{m2LLx_~f{Rg|P5G7XR3B1`Fl+PO;Z3WulGqv_wh`8bby9p_V2ykDE$f^xH>s+F z#q!J4b0R54rHXR>>!-fEw-^N`?eLvBW4dy$BS94ieK2p0!^?iBf6<7ki+89aX=F6k z6Qh>=Yh}60yeV*RgyMb6nYz*}gPEbUxtZ?TMO2+O;$qj`xcL+%YsSQKq5GfJ=WI4g4_uPi#I)ZZL# zUK?ny%)vfsSaAkyY^v#WmtB0xd%$(#B+AKa6`ChQ1pJZzm_ofCpF3Le)c7-_X=AE30^D-z^XDioL}HP|*)~38dQx4P z08$!LQc6%X1}0^4v`CLzu$a7@rOh(>pWn3dNmQITQA4h&RMzMH9LmvxMMkkjIkDeC zx*SIeNm^ohO_3RSg=i6t=DyTNsIYJp7AQ`<=eD48X+JTqQC2I=9F)@D z*GPioj^vJEz|eVP>WYh92tKZT@aEHv$KZ`KSS}z2AQJ;jqv6;B8DJ;{$UR5`#Io_F zWa$Ws@_a^YSI-C=vWC(uZ6ZhW0OmsQ6J4%P>c z*%Ik@^cgFZUk(~z(9$PbRNCSYblP7chgfls=j6mJtaE|JyAw)yquw@1+SS@wy6x~$ ztAd7z2lnR&=>s9qs$2IXVPlWp<~KidM3$G)O%BO#<>-2515|9fGG$q>r|$iNd;6X= z9C!g&p%0=mAY`9!15NlVmir@?P;gGkFrCg)ObmL|e}AR9tU+HKY+j^})IUYphuB;q ze!y*2s?U5zdLGoNr}&o4U2wC&`tHlWIUXw;lXS5Q7K);5BtERABCJOyF=is7+sd&t zm#o<5KbspbliLPJ6pa~cN>dI+%kP<#mXGE!IsRE&cr<6|l1wy*dS zKw~chQ>au|e@h6yD|OTIRI&nqyH(|KyEf72ao2e5pV0l7nlQux2=HR>B8$oNMB1GK zQQz^;x@0}ZT$_PJIWTo4V8gZ#+d46ob5zth-Xg_A^b67%OS1yS8RZ;ARH3JeO=q!EkaMTOq9-SX#xJLPR4x( z-v03?_N(oRS!!cxddbDvyCfArF#tV~0PXAj-|ctXwT_b_%yR|Cyb}@!xXG^R`_s~> z6#pxW%)s^1M81mnn_$i=ut#zsg;jEGg$5wqtNS+7n< zN=W+>vziq5w=)w>VZICeEa2Dl6i5{p%p*qE!=2SGLMT{fuSE5feagiAT=P=?Y@>(s zX=W({p*C>yex+j`LW(yu8}Pdfk6&o7T3!#eXnQ;-6K*?8QbL4{tPU68Q^Km~g-uL( zwN^qXs+2iAh6O+_CV7XD<*IqN(-TqCwc>e5ZX!6Q@g&~k^AKZ;nD zfm4ZJN*PpqFas~WNOHCg$=vLDe?X%b3D0qWiG>A+oQ(yN74JML;V4ZUx0Hp|M5-jz z1Cxbl07=FqjE91Ne2v#!dBWvnRwLpr1yUZCktf&Ef>Dv@&`8Jd=rdv+1%5MjNA~gK zN8)y+7&R2Z#58pH@YhX7j0i0k?nVX2x| zi7*fg7}8f&Nyp%y9O3bi>OZ_u(SL_$1L&NX z!Jt3(s_A5T@mn1UhEer_yn?`~XZ3Cq^y@QM9R1h9<3|qfj(Rf#gDPhsNof16D4v00 zwJJpnn?+&NAXkGoSOZz;!`b5=;)oI8(gyssm*jFf!R9)U?+#5r$lzQ8yrY2+a|D8- z1BC1K8B=I6*hs*np2Oh+A_vb2D{*cL0G3vlYKkp~`=c8i0Bj>QWOyFtAVrYv)# zYk8bH))nhrs?8^y7)qgYqi#f&^*bPH+iDkF)d94f3*_FP_r&Q6bc)%kGL@ zvgeg;mU?>_zN@~a!@%qeghW+#l~!X9m1&|l)XSch@QmWacV=>4N{`xm@5V?`MrZd; zJ;&rEDqILBZl`G{300&Rmk_{u>=74JQ5)4g9F6LpT@sat)TwdmWn>MnUJPXU2fxkV zDJZJIMO!nfL`ctPk%?fPWJg9l<1q8*kfV41?N%@uGW=ZI@d zY6OwQyoiNUd(qj`D7g2)-Q$y1LkIZBu! z7q%DRP&1(RAtFLNjSuRzPka=})uv}B!QJX#&8Lr5BrK=-$3tdr(vQo73(`%OJ2#`q zQTmQ;^(AFYkeVpqG&{@_c;I9S{)6En{=|c;iw9fz);h64zUxk;qBnMcFWsc;Z)BW+ zmCg^{f(+1AiY@34sh1Lx)m+){f7KQK3Jepab^{-|%PibbbU#;I$)U8({E;?iI`7-a zm1~AZY>kC~i9f!~iLH@<1MYx7-Z9oZk6_vh-WQUe^C;3DN~u2v3U|l- zy=#yeYC4{5zL{hUG;h_o$}bt16S|Oxq!6AW3b?SMhMRN9ubx$vVMD+jEOXYQT+bNx z1<;WTwld~tr-S}Z;!#J*1=iGQUWo|vjhfgUt1fY?{MpE;xd$T@alV;K{v+0wj`4)6*f#vNdpzHQK{8Io+B5}^O8pGK)vPxIs zx8*;m0BSySu87STGu3)aXc~O7^{>U7FaqDpW-pAZjd&mUVGVM_`=S|^jzDgdrqa|u zu^kjPYzFk&!>394gtu~SdN95fL1I>_Pi@;IKMJ55#k$Yq-5HRea~4}{UfOzKFkTLK z8vT7=+>C0F!^1iQwujo2s+iIKWe`V!&7QaIK3TgeCdwKG-b7Qx$xy zaMZ_EmsUwu&xKN|PC1>@)tGnsLUmA|?$m;~5PI9V+%`8VjNjF!Ek0UWB>$XwteYI zjLcAEQQN(nt^f7E=U-_SDqo>X0n~XH{+yF^cn`}okXV9@#pJ_VH zjQW(TUDmtxv3h#QJ`x>9M-Vnj7hQm2?y;AM=3CO^s&j8|31bR#B|n4Q0)lOWoB{zK+xR~ikU?W7&Ux!C0!7wsW9XVQpjdL5%Dnyf@Y zZBcdg46k7h>P0f>C}#JMr{QhhhYte*d>XW+&#qjrzaRJ=?`DNz=H&zL23Q_uYoc(4 z(uFdHqFYg0X;Y1Ty)@Y3Q_SKMoAn_{SF+q;UoiEtIGmh7{F6jTqi8eFOU**wr>Zj_ z#Ce>KRPUrpx1xB_pag{_%}ldG*W_LPt9ZSryVO<*xOrKpV~+XyN%+7j81mReGtSsU z4ErCGWsQ|p6YhFXx__x{t8Kt_h(>XyKHb9AtiMqELi>%AX|~SDe56aE0BkAw`2l`Q zA4!*}-oKq{aFuuZM>8Si+m!Fr+Ss*hk6!MKWGpf)L)ftppn-oFr)^8O{(D7x^?POm zLPDYYAZm~#JB=~bsX6IatlRA7Qj3jm>g)6A4fio^&-i@zKXtBTe`Rp&@N;GO`TIyA z@RE__6)sQ#1(r^hRE@am zxG!BVp+bzp_IYqxsp^viBI>d%B8+8I47tE@TD3v2hp_I=GHfI2VyQK|cr9Klf z3$#bq@*ju##-M$)9k?lWC(5rx97~(QRo%okC3}bX(1rI7S~1zzB-zSCA`XySr}H7> z74&ch<#zqBAZkh1Yhs(<<1l3XZu-CCXTsYC8HqAi#c+swgFQ*9YY<;SG(SHilo!!0Y&&)h~wBFPurHU1T$P;^FdB z6XST|FV;L)cgdT7y)*Azn`w)YH@`#@0AhCcP4#JkGt23ndG2W@%GUlq4u}@mmE?_F z>_Hn^;4M+@6ltnz?9Y5zcdEc8TUSei6US^y>xh5P6LBM0ff;x0n9 z1mA1E#Jg2!Z>xShES%GAPr925P9h+m!iB=f!V(1&2g~6uk-Xs`kqt57bYbHnKM|Ef zvis04y=L*e`w`$esw+`=9CoNT=;&cTNc1h1cjDHh9P0}6TK&mE;sqq(NV}8tW3NY> z4r8igX3_Kq5s% zy7AQj8$KHLv|4|x`~By=hB!pZ_s@xglj{rXTM{aQq8B>1%tUZ`m4+!!sMRQY6nQx+ zKd+FMIC&7>z-J1Qt zA3KJ=PdM#6#L9c&NRd_N)0el<29p|%a-RtMi=9FrVVl==-?k?BCdFS%If7t$}L zVRxb065ATuQlwh7T0OllefFtZS6W}%{BYFX`5E{TI%mu-%==IT{EW`>!=r?df7af| zU>kRUh8Jq$N;%vEGVVzEVHrTfm&HrM>B1X?cj+(vP9a?+4lCWL1c&GhcL|Rp#Ue|? zKB(STxswOxRVig7rchi5-H}p?8U~esQX*=`97|JfBvUJsi>Q5*1~-+dNK;XyDFNuN ziXA;kaxK-o?|EmyzULXvgefuI_E#KV&f!4V$)2j;nJ-En>l%wE6_&+JkI6r*gO4yY zF22==q%}a}J0+g8FUu|~D}|rspQG)dX9c#EuBp*SiI48?o!&IvxpuYdKepSv?~U%) z{5e#8wK3*dXRqhjqFB+>V3q|gV$`P?7Cb-+M$$tlEJMO@gCHBF2pTUR%uWk`&KiHeT)C$8;A8-R~k2DxF+!k2pilXBAQH4d&qh6Ubf{$ zCV>sx>HJ-o`dRjUypenmIj(YLWcPu1%_Ap1=33EPa#fq*=fB5C^l9J$@-iy6AGI8l z9TO0hwm<6cU%fASKr&4)veb1VVIr9N>Zb@9`Gj!YSn&CRMz4siC<(G{$17HT) z7Ux=DOQ(lEyBZz6S{&&y68>4+oGH1TTRRin^G&=_gf#hOk(FQ{eL?PtK!Ve)-S?#Dd|bG+>gmYI zz81*I z&dkp?AmeWCjbjdkW@MJgbpZe(LJVEyD{Xz`s#U*TJhwd{Sium}VB{e7&`!jkJIuQx zch3ae@UtT^?$~PPp9_*@dpi1#&hxxx zUpzxI#ioyoIXAfpzqCBGplG&!BhM0D8RJ_^bM4VXC;Erir`MOhPLu=&ZBa%OtKV1t z6L`bpYS#pqoJPhC7(SX#p^u-qwIVeQJ>0lX(VM;whV2iiyTcIgMf*I>?SXs9`1`a; zcu4|AXXMHNWJJkH&5bIAf}bmjGnyBh`UgLS2jd~pFyS(F5~EK~lUW@Sj@;?QYqSl~ z?54TQ++`&kCx|1mLA1@n2ZZ`%{$>j*c{f3^_!E974XY1p?D6)!19?>H=@u(2c98fs zigChOf`b5gCu-u(>k~&eqUFE^6@c+FJj&yph}*A6J5CnBT^_`WJYMWmR-5o|0T%9-akdcVzL+_qds_|WaHZjfU+^XDrQIrD!;i6^8J|8p zu8FPZOKggisXFfJ0oE8tR@`rdN8&a_HPreta~;KuAjoCOXUF$e^als}tqcJ;3m|G+ z@V?2GM za3BO#Zo@A9SODb^l!q8-Iw)JnZ{*c5Ady}fW_1)gkG0^SVmAzX*~cgU4h~czvq^!h zO_@t#JTg3Tv`D4FKU@g{)GK2Vf&0k&Jwq)+GlR{J0^5BXpxwcP;bpkwM&u}7)B1P)7CVZ1}Q<$1HT858CXe$v>u!B7G{bHksOY|LUi_>5xh8S1#AgBla)v89k zpyC9(%k2o>`nawX)E{J`CL$j^ks6kVhcT;HIn-7wO;T6e-y2OP=Qxec>=#$DO*k z?G35-9&-Neu=j2qXgz3E97Cg2AKU696(TjM@C-^D``4Pcag0Z3?VqV%OQ6jl5l0J< z9xw&eMxggsR-TIBATEWv2Qk!X${v5>wEvWhIBn2#V8GP-t8LP7q~Pzj=7}eDzcI3j zj$;I$Ef44Mp=RhZ!8)9dXn^DaUy?DP~+klU~dY9qkAJM$ulj-1@Zr{vjV13%hacs;- z^X+p!T8B?vr}t^x{-OQhbl-UY=YkxZ335cD7<}nxf3cSoJWy;-yv?)zS9|=?nT?#>7G3+b>%VPC(mzW)#*&qGVIAh3-23mR z>I`4YL~V9qmmAf;k$%rr(2-reJ=_nD_G2rcSU;^$b?)O;oTLzRY*BUGAL#FZG7sk~ z3n%gG7BFfU_a4!$mPeGe$CcSb>D$pA^dIfNA@9Bt3sI+u6?Gh>;AF$AM!CLbxk+87 zET2B|H6m9k#rX#GN#I*>usOOY7@KdhB460VhDFIl54Fd+4zQ81?yDGaXm(CtT*o;~ z&LV7IpFi_FM~Ap?4<$9JBSW$0%17Yyb#q`wCesPy**_ty=kCU z6wt&L!`g~$%LM|?p=l`@239+}>>U)|QL?86(hr%q(S1|v2Bju{d;N?F?KMG%fWDi#tpQMBkhrv!5g`wV%!Es++D<+yD zza;}4oo*ps2Y(V0%SWCiLCG@PG3GP88aZu1D3f_tE65(R%Sa*KI`~v7-pRbeK7yIkCRlCKHF_O_ocPI@Gwgxd*;C z8JBX9O5Qv(4x25Xn!zJCM1vpf6{i7;9GI_VS*5!Kx29)%%O%Fm7ZpQql)oJuLdrzx z5s07-#s%SV4~IAVvIo~bfzxieSSf@~STV%x!Iz5vqiQP8T>`+J|_SSXZiUbnYvHwG9^R34DAm;wVXhAZ1^f^wsA0+jUy{h%bkz*4CuSI(3 z+9~Vogw1(_Ry5#c(dGrs&GiPi7w%)G7)onqi{LBV({S~EG@yLr`_uX=3n+ALJjEfy)K@jfr=_ZQZJ4~|rXE}90s2OYct+X(GC&~!GJr&2N(hdh_vlrUSCU&m zn;4#V~5)(-@^a~D7oox~vJ0yBwaCIBSnzR|1E9mXbrfml>Ye=#-G_81_ zM}&TeK&002-c2MxaH&UMdq-Ui9Pbu`ru-Jxa*0*$u46v}NDV-EP4w*FWetqt_29$p zj(uFQR~dJn7krI=MG_p2oB#bDP48GBL$G6L7Yg5C+dKIOqZvdgOieo~RZbX*IwA{C z1VsR9EX*4d8L&pe0x|1D-_tlO2nN;L+gU$udi)Q9Wq|*BOLub5=JScRr;BV@)lRIB z%Vm{|j}PX-R}2@Q)JjdgSR1YJx@Szl*}x*z^J$>?96i-rLns8vnG*o)KTB0HQ~WtlXdKu;Be`f zX}u45P!zsmfKX)c>gTR}9@;omYIKeIoowk*@3JAtUT>9)0#op)v;n(gID!U|u_<)CHG6|z*d;on97H^_prdIW0wo&N zvDR=!@O?k!=ypKLRh+5~=ElmkCOh#^!_{iv%yWfY+tzI)n4qTwC_Op%?%0&vy95;p z>a@ir%T!nY!gdQRfA zUwZpiVsdlVQQBP{xqdP2nEhm0dth>k=9n~(79?z@zAW@|IjKlSYhsyPPm3X3WDBSmnCqB(@C<0OP@Af9X99vriB ze-HM3em>N9Sx^YYRlH-uGw0)o{lL&iD091iG0_+LTpzL*5xAh$7K-WMs^a%$IR~|ApGvvr63;Y{oPt^}%Td~cQpxSor*f(l38v2MM z51ta{Qi_zNCy&VlPmWYWjjiet1P(Pr4co1n5%m+WI(2;Q*t0k-d8~|S|_FY@J{2|~&w^kBu8POy@w8?VGf==12VHfsTb#&i!Y=(=}NeDc+WS)v0|CBh;nhx-ysxr1jyzY*`5Dww_UwCN2 z_9r=d!M5<@nHG-_sDB(VDSgAcqQ;EbrsYiQN>2XpW*KJ7`b6uJ?r_yJqis{FeN9)C z=atE(75#y(C+5uhgxj+2aKkfWPE+cco+qZAWeMlv#S#7e&?)3T<`S^8gwlSf$V>)d zOeTUJ6Ma-n;Dl_=v#&zFcNuJVR%!E_L$_X+`;5nf?$hzdO6Ch#vQ0(oq@TtDak#}_ z`ocU-ZOQ6!xTTDhF`|DB@2yWFSd%VR>>FKs*0CWCA1ql{;mYtq)69k2F;)Jb`w;2k zqx4Z}&W~bX9!h$`yJ}i^wO{f4cIUWOkpB>F@b`jLVr=mbijdBjK?mZc0M+ zNwLI@lP@LHe`Z#IX`wsI9}rD2;b^HZXjlL>9nN1TgHvwK)l~&1Vx4Jr_$R_SQ;cR6 zl}8j|Xjd-O@%W*2M+~QqiRI4K+xa7tJkiclOFVsr}rFJ+|n9c91jMhMC z@t5i@LsRCQX-e}<1aYMx8wZ!`(l{RC-JNM-W)seov=(${>(@g$6O~c`%-_aC-}hln zFrjI|y~|u5D5ZcJacT}4Kr|!H)tiN+EN~|f&C4r|IN)iI@t2!7L(oDw(U{PA_ccfM zA!u{(6?fM1W9s%|f#Y}K%jvJ=Q&diX5bbd%e9SdeM;!1pyQ_>KsrQIW&(%x%St@kZ z#Yd5aW%vQzK@x4sIJ`u{KCw{Wn!T@TAcB2W-{U+C){$JabUPVrV9m?P=UTnh}WxvDd zFDcUozRIt+&^v)=;df>)* zPp|O4M|*tMfwQ-14)Jo%_hZ#>EuUxmu9ta2v12z9WDM5rV)WtGX#H5!9Q1hZ&wU~M zw$@XQZFVjdTzr4+ zVcP&bGq^Obu6dZ|vKX250ejoY{fNPD{G$;WaR7>gS2?$*dLs4($kbYm0Vp!JdD;^H zOU>0#LkC6>inHEq7kCLNPl&E!xf(PbP~D+&u9VX}a9yE8bGiM2$vR*M6j%PTR7zC^ zUdQ=w(RceDW_SD6>2L}PB<8`E06zjC0w`vykpK2uf7zG&a37w@hFIlX0bu*|QIUI( za68h*tUjPG)q58=GM2C2yzVL~9rseS9?8KPv9uX(@AmsdJf>O^aNf8y)+V?+(krpo!l;K#T41Ry}tjtyi#zzfdA z7{0`3L#ku!hw>+8GcF#%GdH~&kwh`wnVu~oAXxtg_{Wr&aU35va#K_bc4s=vnENUE zG_7Xb-V3LMzXh*!IYV(@65Ik%8l%1@!~#(o+CUQI0#aH^ey z=z?Y1a&IGAi8Di0s0FYzw!u2cf@xZFFG(OJcIW4HQaD>+^9_1@X-~yjy{AgRShHt6 zjlhiUj@~)6jj9t{)_K?5X!`%Hg(0;EX*rQXMLyXW{QKMSsJ z2d<|Usb<>xX!by*rpmEH;y^VY2_wh{~X%}I*c~kYf>j#(eOXbw&cj7X5T7Xykt@E z;yeW#V?lEqw(b%wiDXdF(0eEY3|%Eq!3EzD=K8A_-Q6bc&X)XMlM7B<7XJUxyi36b z{iv-qH7w;|KW_8KL>ygQ$R2mx`S|#%tQPSHpk(y0fPolOPhIALRL0(AVZ#8e3N1{^ zd8S53XX~#^{C@0JTa+A!cEv0n@^@}I{?px?S~`77z2Dp;+-1^+A;m4(sbp96)VU*t z_H1d4+(n~yZ)uF|3gMFfbo@m14rWW1EJ|+KVT<>--fQ~|HiO{*Za(^Fn|eIKRVh__ z)?M$OV7dFCuAz}$Op3YvMrUAURnc(>M%vmLYD?}dPnEog;D-n>Y)LP=F$+B(Tfpi4 z*k2SW;07a~&Ff(>KSTgx26pZ;4{mgB$Q)H8mFWijk|fi98Z1o~s1z=&IMZFYxv1;( zpt%9vbpMXXA_&aa!hBYMu58^~aRD9)P&a3zU-a0)a5 zBxSq`v5jtSMeh&XI2?Vm*|tz4@p!f%_B&~h(f3Pg=&e3mT>0T-sSwNg(_Qncx@g=M z1lO-nVwD9I}GpVKr8Sq1)gd!AeB7X0uw%(>r&Dk6B(NlyPV}6cn1XK zqNQ3k8F`W2nR&pKUBe)WR@U_Cv;i!hnNImKMox3fx?y7#%x-E6pU!isEoEH@E#J$h z(rHflu-VW3B<~guv#GQ#Qts4;@wE*nuJWpe17yn^4i2>nxB`c8p+i zzjE%X2%by_28aT`?6;P5J4Vo+TG;NceqQ*xH}@(k+Z)C#IT_h;unsZU&j?A$vmwI# z{qV&v%o6Fq^1Jd6^kv#HK(uDnS`v9LcY1cVx4-v?BHMK=jIz!HOzIjNneTBi^{O_+ z=y$j35)&5+#bTX0x=DW_goOD431(VNYj(BX)Q%5e(Z8{w^^1)|hqmVIKM;Sv+Fb>a zW#|wqg|<41<4LM;Sr;CRGiKd$E<5-y0>6*>z6=3XbNkTr8-(}#zCpHb@KKi2hn z>Gs*67&>TdoEqjcL2%CYqBg6kc`5XN5sfm~LXHc;KH;aQ4#8<&ll@rU&zjq2mjzSg-%Z!xjI)$@Z|c%-tt!1H0Zuu%4EuJFW=9_N;fY9+TUT2(fB9 zjg1DDbAEs38QYs0{_p<}IuHZdgJl*rr>BheawW|)3yv5Wv+dXPk1HuA^TA|JPVQ0W z>`O{%*Dn^o;|u(Bu+jLN-PhNrtPus!X)dYTL$|0J^^1G5i+Or=E40qIv&}})R_$t8 z{AlKT-gnSn^SPmSx(*c0&@LzuV~L%6&v9Fbn>ea%k3u%7s-cqsP64$&b&f4wCVsjL zZO?w(8;vKv+GtsM8MVV2o&)_Y9c7Ovp>Gv@{SUP$WhDr>*>(HF1OLu`AJNmlUt(f7 zGeiKWUHpa7@ZykpC!~(k$7!$xdLYBQ<1hXFw>W1rV(EV{3uu>YiPf#t?OI=6US_uo z$0}{t*Ql(jihe6Y2*8tG{;g$VVp7|ML2kSGU!p<6TD7g6p`M4-T zm3+PlK0st~eqhCPCBnpWQVN-U@)>LTvh;sX4RV@!D7uI4^fbZ7gfq5_4yzm*RYb|o z7)0hrsi8#zeVth}b=_aFY0v!TD}A5tzxEfdNIz*o`4et>BYG;>Rj~28`Y#0h&rT)X z;+4d~greuatL0y6knm#ENaT_*T8D1xAC|-3jc!5ro+xP0c3Ou<8=bw}g3u{D(U0DF z^DroQa%*UpC$-6-Shoz9^|au>9&SYU&h!Fi7C_Iosl|h}6WpmkrTOIqhs431$0AD}!GgkeJx|R>Ffh*oho9wt) zm~7Q`UEFDA`D-6C!Wt_ddf1$EYUss^<&OBw_wPh|P0u4KLlzEpoE@$=V8apEY;ZMP zw%SIXnEHfsr<=x6m~6HlR5&CmBm{5?wkveNdF?*(;TS;UMio@}UzbRGgvZlpC% zZqt(@dUwUal(w4$-1<}h#~;FGTXgUG^Fs^}fzBVNfxq+bSK{dW{$C{M-~MFYPO<$e z#ihDldK-IsdJ_NikW?w4FXf*0J(APY&rD26wd9gD-b9){E*;Gkiv7p-YlUmI+rM=4 z$L=0JN5ef2yF6didh)9h1p>j&Tz)rYJ|{{Rue*p6zn>?>#{;$$ILhf3TaX4c zl|KA*nj6vd{aR53exU^Mh#qr-?)KyBp8>=9VpdT7%4VAXQt3n~Ta#(JQ$8fFPK@P` zuSALcFU8CSL(F;f*!|c1+GI#9CoI*`D<$m1VK?o%?R6eu14~O_KYgh+uS^scKE{-r zPPvvu9nI-OXc2LBXqxz1tkqLXQ2XROPhYWDM0Ob5EZ2Jn_&M99ADiY(%P;}ILI&&iJhT3lOitxf(|(AAIr(CWimGY|Cc3xS%VXCyn+-?`+`DLh zkDp$>@~_V!qNlS>)adJTNxO@?d}LSA@PF=1Upm;gPyHYFP-j436i)99+Fuu^eA~`` z?$;6X&|bJ&u>LKg^vaAhJ^V>FlKyo+s`d=b;3=%FzP^7#nrHz`Z)=dHY4xFEvQTj0 z({9o4H!UF-!bV?%PT9w&%XFt9>BOev+YTAKwszZhZxrSfAmbOvWun29v%w}a@HlKF zlN^)W-ks7&V*;CrXIez|zr3WR%GM55W?E_?#ymCHa*~4mGzGiz zhI(kTrcIU^cGf@DE`^I(&2>MDK!smT3ggi?m)>p9osaDo*_ns0JMWneH&uuFnYD#= z&o3XVs{!1^+Vcrw+ofGQW0+q5=sn~-KXM2l-dxIYwUiE(A zW9$1--q8)CP{qSw{hV?=kjMP68wbnp;{)vaHq8w!Xz}ntIXzm#n4M^H z+-^<>^7vu^e{{I(YdMnp1>mIE4)AufN$Ss8drcmCgK2A1NLJ{pz)(D{yT^C^C_QL2C0PtW$uPb1K2b_)+$ z9W=J|O?pF@9M{WNN8zo=t5t5S_t*E*_tnL1G><&c^Y_p4_0P7O=nwcqOYFx#1?fGgdrWx+1wczHE6l$i&o+NASN~vnJ+GPN5a3Um9nXOOO1ppw zNXsMq#()QIOeql%69d=x)T*RQhMCx#L9e4jqh2uPv8aowwo`R#{0pg8&|cVd=U!+f zY^K-`|ebhr${HNE{}FETs!p4^P4H(vCKUq8%S7W6B)K z%pt|J$T;96qbdu?TUk9+X9NZ3PObZC8jb(u9#YIS|5#>zmW@_=R(Lthl2($3a0iex`5tz`J$G~%th zk)#f^L7gr?t&u)hhVm+|FlOr_3>(U0pbqJ17#M?j>@wa*RG9Jpp z)pwm134ekk5ByYY2eO`IWfN|Mtu#?e zEcV?4S;O|8_f~ta2JWr`y$^Fa?a>Mg%C)cZ@JT92K7q=G{NlQ-C~Kg?J`l|Qij}G$ z#oe?^5@FX%-s5WQZO0~n;{8Q=qKtc{jpIZU|G=e)LjqM4@K;tRK<&~aKm~_*(G@#z z!xU$6DK*G1&?5}VB3VX51S}+~D--9YVoX6r9gB%L6c%wPM8S4d5F6nxz}gOnM-di= zfEN~tC?FbAh?iA}M|u>(LOsJlJwqy{xbF_mKz*kW67jswqh`ko6APv`quh2z*@1FJ zgP+O?H<=l<3-v;K@S~CS&_X3Yx|{q*hq}B+lPLcG*!t$! z-kSK$+O}=mwtdT6+qP}nc6;mgR^QsTZQI)W?k2zNCYwxhPBJq&^L#RMGJibaKZy5C z<-K?>k95-NJG*p3R3NFNBctFHkY7Y=6P`p=aEDUZSt;Lq6Ct-cikP9Jm99mtL+ufb z%{VyabBRwssW!~80B0D_l=3QP_pC+f7P>76sIau$O^QPLj52H7V?Vry2~K zRK)pYB%|7y@Qc4(zg2j2%q2k`!I5XwO{%6*#j9vk1dn@F8KdxSfiR`>+3x*n{=k+gwSc2WubJp(^~ zc;K)t-AgTm!4j9SH;!m1gRT83g24crU!ycQw^SeEUR~E5*6tG+_K$KJGdE?wIGNDRI^Z#umiP9Q|9C1cKOVFH-sR>#eUY6S1{b7z1rCeRY18`-{jA?S=3f## zc%el@Q&iA%u+^Invc3q;B}@NJVi#IICGgf@S$w8-(UIl0%TG(sA#@5l(*O#Uo%tl@ zi2W~vP4GoXWsm^aSGN71STgM3q$lTq^Wh7Kn#6NkA>R)BjE5v>P~|}PR2A7>x7+VV z|3)|;2EWm`3vZA~ahoo!F8PIZVsdA~nR;lwHz!(aIQKZWERwQ3pCd3)tn_TDMM(H0 z_Nln)=2&;i-*Q_X%94?1Q2dVWD1tNN_X~~td}Io#;C((nRmp_qExT_RK>9~wfz4zv z1`*V+NB_Pc#e%d`dtfiEK^Yx?j-#o@;(bNh{cg|d?hTyFkh}7 zzU#0t&1!>-w5VE(q(_`jtWUNN2DJ)7uEUtl5>WGpfGcUxmz`U`SlW!o$=0D`(i13m z2kS%9Cje9226ux@P-!z(zH;#7S0jVz^^{^|{No{QQaD{dZNrhN(sIHR5799cL9d2` z^jn+E&*XhLH*Gmas_+JhG33Cx>?Qxl_8hC_j33v%(bM-4pqdmyAKA~#4Opx+RZA+x1S#h zZX#n8=M(Xr6yKH4&a7G}Ehmw#0m=@Y1-6nw8Wc@xj`zkTn|(&$T=IU+2uu{GqHd~8 zsES*;wdhJQu!!EzEi?4l&x@$$TE$t_Z7v+PV0jQfyq>(|-Xs4ibVqSZR2$_9s`gQd4HB)nOSmlO^cV-ej{)cnJ1a7lhu>en&VlF zQzMhRRd0#yeVdkUCDJ?r1hTY7XYr?>@XLco5P}Ll#XJsy9CJ%9tW#z=nZ%Qwa2b;o z8C|xVDEFE2lX}Nwpnq87Gcl<^F_1tb4UG7J1&E@~@E?i2Fdb9=itv#M@y`iAl0znZ zn}D_3L`y@VjM_M{x2J9@=8@Vbo1w5J{rycsNQaoJm@<=UeOMp_&;ls>$@qzWSAUk~ zB!UtjiPLYWBqIq=6&$O2qEQvPsq84DFY=#LFUj1cAlr?vUJqUmDvyV&M(Ue`2)4*% zR??~cqg1KqDQ~LSubXhXBkZ)OcBQ_gI-%%v3YL1lh-$^)Yd)Y(`S_AmQT(8nNPF4`Z$uL zuUO=vn3H|C7T6J~d0qrR6Dna)NUzfQ^PyJ0f?Hz z{1p9k@tA*xc;2&8j%+|qyzB_a5LOt*G=L`BC7fnT#bU$kW4!t)==59b^6 z73*aD*Rey{7ECr~Xc%82{Eaf!^?hU*Q^J51Mh;ZIuk5Z2Q4Us)R?Z;!OmA(ajI}Jg zY)7scIxBGX(wM+Bj$?*{kaK&@y|_uOi{m8`EN|Rl4dkLG21UoUegqST27IjQloxlI+`eSs$O!5@VFFt zglZX0I<4TZ^Hj-{^i;%@Af6*!a%o$SQy$IKYZmeZcylnF;}8W6+XpTun@kw2Qg=jq zReYsfjW6k6=-!7^H>EV@NA}>;3R%Kgb6GF!1_&@o5A4}&-t3g@E9~*?1@ToslT*TR z>#F3Egi7lqFl3fJh09iL);&vlvsukc+Ou73b!aQnF2Sya9g!XFJOy*;ryLj0X9{O_ zr+!Pn6d`q{vH@9G-piNOJK_rS`ozLveyISt9SuM28^#s=L$jdrcXj@mT{A2@EaM1f zG8~~e!&t-Yy@0*0J;g8@(PZHI7?t9>JNyH(13u$0$5h7yd+Q!1zk5T!mW|FA`yBHI zKW4qI8Eb*>1xDPNSkGNU5I>4g%TxZjt+ldHOh@0s$|0YDE}n{`DEtQq(#e5I1w?QJ zMHy>r8|%=OI<;(L5dJ^DS#Qou!sp0aO4bY|8LZBqt=m4&A@c=f#pg?gJbVu)*dx|B zR<rXa;gKjUlB;P2)fg!1wQD=%nUNh*g3j(PVpg?siX)8Cnfa31l>EYNbq4OwgBm*&%j zo6*dyM@~5jNZ(>q%!xqG(D1@ad5Z%39f;0^rGwPiEbua;D#?qmi`+4EWW783X|-id zDoblK+FE%qWpG_1{ZnsTpuk`co-GKp${e=4%7k6MH5C`* z9AEJef(ip+<4plKH@Co&_llQSN?W%^#eP_((1L4(uIIeGCRB=4xn8~F*CGm=2fQD@ z;nM?I+nh@>O+E}`%{Npnb6-loc@PhBO#`QBXx3y~t;2F_UiROd{r}d0M=x%x#=7dIbj7za3U0pxYPS?`0e?A1q7L!ax)--5iO83No&`y(cr^uC>(Rk2`YUsG;eYWCpM(YBiw;DB*6YO;0vEA^-ga{1F(q z>=!2P3M|xW4MbEk&D9n~rYXAmCjk$%R0s;6n9xO(gNROUj%V~#2FrpTq&ma(0t*gF zJO&$*Z!62AKFxBV=AseEoj!PC!=t0fL#Yew4+LZTP(f@P4iY)sz}^|wa236!@hY~L z=3srAMJ9(W@j&bOUWRc&?3~p`9QP;mZ$SyZUPLD@#0GvKGoM!Ua7!R3$8uMos@jRY z4qGDFaBRYcew9E?O)vtMtp&Pe=yUP&8Z`>(G6hA&sZq1VM)FE->P9??+;mb|%@jK0 zB(`Xb%48=M^?k#DXf<*G_Ce=(j*G9SAl#=V;DGOHeQ(EVc zz5Rn(&lJYK&iqUBMgv+;!+zZH==!X2@)Ggy2jHp_?JKGZbmQHE+iOV>G(jJlygD_* z_Z4}*eT~fpxy;jk5&@iS?Cdc3Tc>H=|599Sk&L@X6e_I^{Vf_02!q#N!jju*tuS)_ zxm{p|FRlMAF8k{5cL)g7Y!+QdRNmn+in7Pgj%&D>QC1awrTDE4r^V>J66~Sn>x@2> zXVYYK`CH(0*MBmwsa}f-Pb5X$QLtBt3l5`Ba$ItKoohJrW|vw9tzjp95|a_bG^*?w zACzC5oMe}{A5umulf)kxE|CB=78M^=9{DaVQUFOef#+2GjKE77Fha)fg@zF;ywTv# z{7m6iV%z{Va8C(LuC(fgQM?a9Sm(UQLA)##D!Kpk5Z7>zP2-caa^aG{bNV~qTuly2 zA2>{#xhC}oB>kU7XL1(;9zK}%9Rso1Fga6IT}HgIh_Jt9WQ3_H^w_h&6(~Mr6SqRH zg`4w>0~Zk8%iXzMqxul|HacaN z&)AQ!*4LaTw^-vgBVTd6yo=_EJ!Mi(a_W4li)`MM_<{hc;l}<#P^s$r+-wL&g>ZCX z8ok4={ktm9OuS_}l31)FAN`Rvy#)d1qi`2A5g3I&A0WTyl3N|oys63u5!-3$+uO|K zsyB4TS)1hmb&;};9JVn^a(dG9_A*-Z8nyA}^yDImG7Gahxy8noUEZmx8gA&s%dZLl z=#CQk1rW+qgSTLq5C#nEjv*eb=2#$2QPnvSjt^J;=BumlMNB?yrNZAG#K1(ar*^;w zB8Q411GAT=jCoyqi~qNMM20#fX|!~quYPf>Wn+iF#`+N*Y4%p@VAbqNn_NZWiWe(vwsP0@fw7uUIb=@YPag`p3t0P@ZLTcS{ozY=kIFO6nnqq*c7*4(BDheuO{#h7(E5y zMWZYCdI-!C!3goz=kgrA_Il+S^9_mQs$=U9hj1A{H9O%a=ntMen?m1~M@FP_zwJ@I zWLRJH8`u%1%_iP7%(P<}{YIuRdTZYW=oJAMIV#v8BW*yuIUt(Y2J-z8u>N>$KsQI~1SXeg1#z&g!=Lv4oB;X~F@Q>dnsMYvzi^=4lBsg4 zQ)E9Ow|FqI?QZ{$RhfQQD9NYCXwZRdbXHXYYOd}_ zI2-}SLL1%#c{@D&7q}SKyxBKRuBZ)aliNr7;j(vElqqNBhKNYUZ0||*mjP^b5X4>h zlj^XQpDsDc_|g;`5gdjFX>64{tRXoj*Vux0EJU0r&hV1V_$0%+x3vxL2{Y!iJuJL7 zytCT|7%RXl4-?kstNm-YeVGRaL}eEb;VW=ey%|3hz7yxu`z5H46(hPVPU7)ks$n7C zrs0o!{V1P{^)DiS4Iwthf~vajF+1&RaQ#e>ogxHOPX*kP_&@e%M~wzEf{$*(Z1 z{jyhESvf=}a2W($(~aLj<`NUp_XfRE)3-%+XflhPNIyqR;?#05y-8g;^`AI#d;5XT@g*yhjnv5u|xB_|d zD5X77Fetf3#HsN?(jdp9W_G@y68Qd#40B!^_58d4-UN{>G#oxh& zAXjprJ8-(Zz4)tA)Fuj8=2!5+T}PnRR(O|Jql$MBTK^KCpsrH;s|$O6$G)~aR2iJC zBE5R9Kg5<9Ysmx7P;kj??2Tt>SXPw!97&LndAphXzI9I3iT2t2=*Prtazew@7|Q^G z=AkR41?e#$@laHC-o~$GX=zg_{?t9Kk+JZ$Ee0XFa^2V3^2yY-%Ffr}(j=pu)^_n- zZcsj7l40KJ&*d zyBjddSAO}u0v;~i|7SmZU{n&%dekM#&|X&oGG=|{U@=-bsw^#5)tu5)UT*4j zoYk3tmdD`r7lYWCqKlA=5PlEH)Y|2k1aeJJq?Mm}ysYuzgT!uD0?tM8-K#^6*|8-1 zWeQs#L8ZEC75Aiq!qVv&Z`PLG-fJW@Wsy(gE)zXnz#nL{d=1Mw5eCbg7LVarXwIUF z%Pnmv)e*+$Zc2X||K%v68QF5W2mcVHI>1|8G}Li4((Dm<9X@(zf6xAyB&^> z%KkjlQo$Z|4)Chx5H7!db{id4sH@`o(V}1LDbvR=r8%1}b%FQ^h8l2pWx_6w8yUJ} zpc)OZRiz4z@Q98>WQQPxj8&!ExbRuwqbg0xLJIMi43Q2F^Gl_=Ru$b^@gNE2L6ai#RhVW70y(+cZ4DL-hEJ9Otiu@ewFIKD9_Ntbu3&EUe zvRR;L3e6^oi^`VEa`moPvp5L4`IIb9OPoo03d#2dvq)^YBI7er#0|KcBZA_CG#!6% zNI9L;url7vw^BjteCy8Uf_#NC*xx6}2|(r-qCtpFeK21TUkqQ$U+CREm3ljckhaIb zVK0$T18@UAqIrdbyUhy@S+3`YeTLL>=WO|O%+uW;v#L=q1kl0TtcD+n4h9QF=I&!r zO%m1Co(BM}Z+h-JQcUGB{xjYQGWI~gAdpd#WghiF^SGLsRdJu?LVTIbLi&B`BW!yc z_uy}2)9=|FyEqb#tS2^v07}oVzPd%!2Rj&ZvjtI0qQcMAyGJ-vq)3EqcRq0IR?;WH-^=&VD3dWzGph;=(k*DP3)M6R74U9RA0oqcidhP5!n8C=*zSoC z*FC1wB_-(ex1+F8ehOENn=mH>(XSu8ZRd4mylSS2 z5I$Mlo3@6Z+3BwxYGN<%+BM4yh=kSyPP})SVnuC+a`4G=UE+$lBpKams#f=xj{-ky zK|SEnYG#IZJmbZ$w-%Evc>%C zL1L==dcY3voYhZKcaY+|=Qwpl=&!|dTzv0)K0>K9_7)m6V@CG1-Aq_Ke9&Ld#Bteg zC*B;*eg_^7N#VK8;40VX6Y(dWqseEKFYAo3i$?J0Cd>83nElB1=u-!>%p|)1!RgBE z&qdAZyY6`z{iXhn^0Yfsys9#_soEqB`a10|RzhFRmhP&WbFrnjs^js0k*ulDBlM6< zuov>WbqLShL{Sn;C(BEeIr!&%+n(03`$}f)7q!f=xtaDn`c;I@MPK_34Hf}e0LM=r z#=sWthMRC)BsXFW*WjjmxJhVopNQ~ z$C%UkRO^g@p8Fuyj;qgA&jc|l#}w~T(6N)l(H<^4##%JE4B5&6sgl}^O%h(J*M^y@*Kam&eL)E=0HOz3U&V+^QMw|J#YMz zLc?h!CLMAp`O1F~C9CryMCMb})a$!UzNwh)Lr2Xu96a%Ho(5cVv?zb0zH z+(AK?O%R`P)2s0=ZFP8p$Jow1Y=1a;=f4od=+B_nfH7Ju)E?H8i4Bxwlh#-A)Y=GG zEUlz(_HRC~7G7_=ULZbhFJxeyTC%5UgX_Zcw8-N17OA0?10M*vJj|^$W&?L08sEku z8ywfM#sS0kD7{lH8+_)`jjO&*C+1b%G-sz3{qao;4^xPAx;}fPcG#NDxs|Ahbm+fm z?C`7Na%*RgU1g@#?Hlr56x%vFIiO0HZe*RtO(*Gb{7Fn~T;)$Ui+Uv`0Y+R$$14n< z%(vOEe^A;Ut_6!FLH5|W*q`$Jr_~wTwUSiOp0s>h-$|dYS(KLjnFRbgK@FG!Z|#cOaBlhu$_cV*e7+j zibf%&MR2R|xI>a*Xy1)FW{IAQ#WLWe;S==yjCslPj2=o=IZEyR96>VCp6St9FGr;k zBQx@$`BHlxI{DE*D3{h|Dok>45Yf3}9!E3H7B3OcUCzR2x;=JD#iksqwoUdjg-Mf6 zqd_gGX7>~DaC=QZxV%i0-N-sfj#7L7xCrOCd@AxH^3yLKIss!1qsvGjnLYq;1b7`2 zPHRYfYg-WbZe;W*@kpjo@v|F+kv6-^nyA^t%ak-ECT#cSL&ZlrPq%lk#m=C{-&@EvHkOyjW*k^bYL4SLBdc3#?&ar zRK-M=xvsc_q5p`sll*Ry#S}lay<_WW(~8a_0vO}$JaC~~Cq`^MYB4UJQuNq0k3SmD z;J=8iCUfnbSZebbOe{Y9P2?=tX#y4y!cEZ|ET{bAC+oRGpc}n@Sv*nQo&dh`22`Ia zx8+*M-32h~HBDYaE)n+K7WYLI^V>dBjz6411kl=Sc~TGxyTYu^bHIILZl`>`#Ucp9 zJMzqFd)pc;dp=jLTf3u!JQ@k`=u@_KOzNP0=q5>!_?@oPcrk@8WFs%(%y4UJTiFSL ze6{Lq6H3yT0+NTV@=BG>iTE!`Te*t5U$XU6p8mP}ZL@?#v` z6c=)f&IQAiYK%7i0c~A(#OXK5J{j2y5GD{-%~F=`01od}M!+;Qknt5Z1K>IWsjXW4 zfYP9T+VL)TYhV_ru%R9F++p8~&Os=~F{>3DM4}!v2_9Z7IAqwKyPzRZibK<)YZ$m) z+({5J%AM?`05nO$$v?1vU+Nkj5)F6k%1sEHmZbgoq}{`nOoKx&mP6dhcc}shCVI)j zos3f}7D1F)GFCy%$-PRM9WAbEZb|A|i`^>nuysedcv_0#0tEAh8Hdo6r7Oc;n`My! z>Q=kI&2)zPm^bCt6r}L3dgybE{4-F*c&*Cl`w>Mz)*?q^^2bUYS}?V{Eu&|4@-D=f zS>Nz@Rd-U6Vb8sVL3gz4_oPM^_xe)CgM0d|t;sN;p@?(>uQ*`bOc+SvQN!$-2y_DBw{3M->oUd^(pJ>^5)a>B z@noi=c=BJ*UJIfa6-#I7!k~(J{%L*ZZ--Cj*h?^UTC*{ea&5E9SDKoAze2qURuUCYY2(A$=)GaR)xd`ip9Tgvg(@N8MAM-FlfVFD-V zD2Mx?N2lk5E7TKod9 z#f$4aan8kyv4$P=5imM}k5^Q%6xRaFVZJzaA>ngEArN>(K?jBSR)p#?>4wlJV)HkT zo0z(NcZwR4WrrIJc)_~AsDn4BMY(OWJmKS+wT{~*99rmhVTPJ5CG?h)+>dK8?+KnZ zPwS6s4DAlt+bplpVTLfx1=|@On-Sj|{5XpP%2@y{-DNcJ5*9s}<<$p;rm*H+sE~>R z^F>-BbeO;Y#XNl2d`6;2Tf2$$+V9vILa(@-xF9IJRf#9h7gY)yYqQ<++S=G`lm(8d z?#G6`Lv6k$aZD**ftEvu^|ic>K`!CKLnLpP-AVKWo5D2R3)^fPhLlNz)776!{xW!1 zpnWy8QSG#icjUz7ExWIs8U`#BX+7}bu5demZQ=Ft_33FKxVdavTRLXMGvpsCRvaRp z8f6D<;R*x>mvgJ%yDP9SfiP{pa&GYUBWWI8TQQ^wV#O=@Wt#~T+%PBk5M_+^P2?~J z5dWK`tV;*Ix4W|`CSbEr84Px^^$LP13mZ?-2RBW?$EF4*%DUxml41pdxe*#=@cxz* z`La^-y`L@>4Q~y5BbWr}+ew}fJ{TtWdkW%OiJ|tJs4y9e97Ps2pyD;3N^B^Nj0lFJ z8VMb%Vp;e#4ud?R=J7Fe6wSkMwFKV&8 zX!~l5zXGc&o}b+E9xS8`jF{{`n>W`XA7qV8EEh4dQ(KBP2{rf|BJk5dZa?jF&SzAm z;#oYB3a?hIfePVF0Z5rS`E%RMTEI)oPm9k;3 z8O|4u+8X%tLxoZCo_sVL8C`WssHtE>wa^#SVgzB>3d$)cPcAqMh^vxnS1L3IbY!DI z0lfJp@PzGlIYDE%REC+i>x(U0275#Ax^rhVDInjC)1-A*|9~OAGDnx#k8gB4N+v*uJ&O6 zlE*~C9(W|Ezt5{N(d5k-89RK}K^L7K25)rb$H0gqsOrFV8o6mCk1Gb#BxylSax=bCF-u;oB=s|CY zuqLESOi?tiMUQNOEhv)P_mm=XcMwy&7@g@o@mAJBVINg`lPM74>k4R~-Oi&{T5yyH z_!~A!lp#!xWDfpAcmb;Q{X!C}=sgjvn~0SdnEayXhg-#V4r?Z2=2EQfm|>n+SnJd@CSVy;q6V;0(v$9ERu=#o!Xf;0ngz z8pogxn`sqB^AbYN$>IN$dlY+x1knd)YH(*jrc@;QmIE!&SqV-dbh(HKviFXy!Wkrs z{x$=o4tlg+DDAPBF-CwF!*?&~_Ay%ac;W9QtwLhfoQXoPwO34vB_PfijT3EE`+m7d z{S^d0Bs*zJQ5$#-f9})3R$8c$Ct^3k{2ueJ(EZoP>l%sAC$6HM z1I{9RcSXMEd(NZ4(&YL%91?|54&WeEBxT&kQ+jIi%l=>D5-biR8-)~w2N7FG-my0} zoZ=u_SY)>k#z4q(evqLT?JQ>4zmmY&8z^B>ctgVsu)|^Sj4=uCu*l8nh0(9~If%T) zSX!`>0@<2~1&Yxe;&E+5xY88(BJ!07wqcl@3q7NpR){_+0YIN}h0IxgSr9qnvKSu| z(VEdTV}Nr5#gz-^qR!d`j-YwS3x$Cr|8fM&nX0|^0b3DP0|H#lVGWs9y zN;mjF{p*b(?;D^I1C)l$3K{@xf_Er}7ybjd{Tv=XlrXlURRjcm*P(0HXvZjhkYd8} z0^e}Uf@N!_gk{Qu zX#l=DKx%-A80bp}%BuzOe2=s`MQR{O+Z&e$;q8TSX$88v(rIAcxtk7;#SR%01yp`q&|HkQi`_{TMZg8+@-q1arNM73~FjHLQlIMli z+^Lxa>&{}?g#W%nIt+cQrv$CT!!1->y*m-wMi?S=1I@{nXQv5L;td;@J!Ddzhm_EY|o>7XC`PNMh6T zX@4VNx&LMYX4kNioOJC>{{G`HVdXb>Z&|M~H-8{g#}1u9%6xQj(>t>RSzzh!LTCWE z=5<=!ViGw2O2g`I?TSHU##IG;8-9NJ0%bk>8%~j4Y|@GCC4Zv$9{R0EGYP*N{aG2~ zFsnw5RH=BeEnYw1mH)2r{ObSZ??lQ{buB=l9Tc{5~bg-gD-1ehB3m_tUkut@rSy^p~=d^HUoGn527vtt(j$ zknr>)_=WrbQ*iB;Wx(uU0|9N@0Rf@^6LS6UKyh1B6H7xzOIt&8Qw|1qTeHpIurBIL zsRAdP51;;d)U)HX1rb_*n2@xVic*1!(xg14`(`1a2cgsmhzlYQz+_6IsI7xmS=d!G zwaZMg(rf6#TK{O*s(8}b;D!x4jxr_mhSZc|v7ut8nws5s-;DYHXl$z)Ql9L**U3*cSop1mbc-EVHcpOgNQzrAVSf@`01tcXNXP27&e_=+HU$bJ!S-Z<}jP&JL7Yo!;$?Qgg_l6l{g7sgXK+$nt&2Yjla2?AQFn|9bg~s zyrHSgoFq^HQhfSXl4sr)3aUX~1tNN&&)A9V1ANkndJ4QfVCYXsPay=-%pP34DD#C> zJt0hce@3LsC372G@>zqv2c-+M@q{(F(|Z9B^-U0$zNnYmC+qh4zXqDhz|RdL{J+>y zbn`g`)dtEud(BlCzjBwC)$7LjXxdUgII!8#eg1-Uy-H;_FO{z7`)_@Oq{QNKdPU|f z$~Hs$`TZUIXzzDcm_ zf2h<0xa8eYR%O;)6M|w*PU%S_@*^OL7r9~t6&R02?`MBErq@zj?p+#=B_-a`_zGCX zxzsP_SLYa2yg4~(a5~Ju7}Sz{9k4DDdll6p`Q)eI|CFCc{`aJnYVgUjNx2wXaH4)| z$Px9UE>F-ffq09?6U8}l4XF$?DS=7kO(8?15+(o#fbM?~eN}YCEmkrg{-{AfUr?K5 z>%H@>{YgHDmJLZJ4f9GeNar%FpL&;` zvAfKRWw%{FYL|*RFH5>Dx~J@ku2c(oRYAV0a$uJ2g45iOLq9 zI!pIV)}3lwY`prw7WzeNdda>nS8WN}Eo5D3M0pB-Y0DFeAG#TScF^lC;v-n{leshx zu`I;cWX98LXp@rMKg?~sT032PnXQ14eR&AP81R~$06;zTUIjWqcgw^#gRjZ@%FH$U z&?4O!6EKKxiq}N2Hd77Q0dfRC!#uTAm#^;28!%X*HP|7jccxtZAY0Y#o&6Te7~naD za?YTnKF-0K6vwe;^5%rv4G4Yx8$n`!ddB@mQ#3NTO~V(_9*Y}ni@^L6k2EcWKOOq~ zl#j&GXfdg!rJsc7JVCq({#RT6zVMrn0(X=9!0YdDGgKav0k?1$aU9*BptG_1P>zAg z7$1a06{K$j5!09{^{^xICQs58%Q4kjGr2oq@dMx_bG0CLQYPH9pU*EehN< zhc{qrbixLnxyf&+PmO(0bqXAluHH!3xASJV|4=E9PI7r-gMZIYnTw<$d)}2lLhYpRj);s2=-uZW{PIz zN=g^2Dok@RCMHiS-y_tpRpW;P4q}QZbM5C@r$yFJYYL~PF=Sp9E&ab4>$Shwr*`WW z&OYfo5(vv^1Ji|kcuQ^|-hfBlBkT*=ec)>kV9!22vztTXi}D@ATl-})B8p2=d&C3| zj!HrWWws%C&wKN5>MxL1-GMGQ;h*gwJ7u}n^PxUoYw!&TAIOFTM5wHMdGj_ypa_UA zQ>qtFDC<|Vfvq%*f0WpoF1|$nVbUdu%L}>13mKZl-h{X(ytBa5!mhGLlw<`ux&W5F z0QzaardhEZkU?UN@a>ZL?Fwj|qfDrmgG?auNlM4768^LV1~4%IVb>2{wZQ9j6hz;~15;&;$>pf<20+52&q zJU#UpV`NNwYJ>`a0pmvonKv59&jHixOcU{{PG#3Gw_`+l6Rf(X?-CeBCGXl4!Gchy!_=wa)LPIlgE?bb-vfP0qqP`b1^Ue3YCMG;M)H1 z&NnF4wOcX5pqkRRA7W(Si=GdlSl`Gfn?L)DFfZ_y_3Q~6(FpYMBP4$;)hGS;=#Q4d z3F8FZH*e;6{Mh$rGg+572e0q1;^eYvu7xFON{#M=lnSeIkXL(RJ@_-ifZu0k<{=b5 zLDw_o=V}77=C`3;Tt0bFOycUoz}j_!kKfV?TR$AtnHQMBXO#^Q}cs4opCCUSY66 zp%T0C<%IM!_4P{dxGN%e@AYCnQnr7Dn#tSo;eEvknWF! zzJLFiah{#A_lA~Y7hW^iuN1q zO?5}UGncn}{i^P7zw~JcKGfaVjR{{CFG4vPPs{l(&1Xrt6OYHFUH;@qP)@A5Ouq#yqNC{%p&BN~Ndg(RKHj#ubMc-Z4WQwE0yb(5(MVW>do`2zW`fxN4l&5^VSFIoFH4@LBBjut0;Dh_vGopIK?j1E&deR zvfq~iE66Q)KHgCj@__b*-|HccF=*}1$&e|_$*?lzLL;+Y*a>#v+@(jL9dbe)kuF%-zvIPzg3louXNKudM>J7ou!59P_|AVr{S@6SWMBH$ zkzjT~&L%rk)^=gZCa+i&{3JHcCScw8-M5cl*BSm2iyQd?&*WE_lYs5^Apg>SmcjY()};-GDiO22D$+?`cW$F{@!bB_!0$VN}Xg%vn|LKpP6ys zo2AVrP?w|wTgwNuI|p=Y_Zq+(+;JH+!qj`>F=>Np)`CywfnIU|hcSYtiI#=Ivp&&n zb&ar0h1=v=n!WW#69eGq-2=PkyN0%zQftQ5>Q&29*t3o80UAu z?@8C}AvoVYA(@>z9V48LOXvlD3TN`~xm(3cY6bjae7DR==yoZq zDE*^OA)1@L1>c9d1w}Tt_VjV6%$mC>lU_KDmfF=>?c#ZnEJ(Xtz4{mQRzT{ejc=L;X>Kdj^k(JP$(&~Y_ z`S1b8dB_2svrh-&ZqBX{KT^Kzve5E5;yvkuJ%5rHPg%x4yS()wkHzgx-TpV9N()V!$@+f1T+o-6U zC)xyPHnw}%pNi>Luo5Ejc3R3;0UM1SgLD`EwXly@G(k^!|V;!woIrfm+%_{E(iXFy8*jkd~i1k}{)2RMS3! zqaGa+rdZ&srVYB*m9O`!}lK0A{u6Ulomi38Dnv?the0c*!{#93yOI|29^`LIXfGty$ zD@ApCjxU+(qfH;Ad_x7{%_0K%9R#8pydC$lEiZZzy0$JYdYG_n4&j)Tl-1uo9#}#ui=NDt)!+ zIU4`GV70bt5ZF}(Pq|#YwhC3gH&cKb;({iz3^8is$uF_gJ<_K8*Wm?-asg(9k#G2F ziDZT1u^J7m}WM>rW;Z(RMe;kVd<&?W1?KU2epC}F2yD?DTaY~OfCP>adGGbhZ8 zwDK1+I3s>5zoC&4;7*?SPv9GWW82BbwzaWs+qSW>ZQHhOZfx6ja{2wQuIe6K)jjT- zn(3*Tp04Rnzus8Ad-lCXsCxJ8dX3<<8qldc5G&TeFuLH>A(H|OlS*MujtWNU^9I>v z$p6<^b%Ac1s+~V>g<2xzZ^jHgDC`*0Y9taa3yYXCrT$gMF-Vuz^^s3;9CgIt19edb z+_ClRow9FQTko2+wqqstQ@f8l+7MSC@7@XR-ff8oQ<#2MstWIP;-1x5I7{C`eN1<0~@B zJI<12ko{_e;LGsYqVmE*tztI~rqXS&L%U*k^=B4Ty6J8&yF~xrt1Hu?BIvy+lVD-1 zbyX&_5X5<@0sE-T^?LaP-L;s5r@Yk;xgMm=hN~=mmpYy>vZ&>*Af0@?$nGu^AA7b^({cHVkW}+t#YN}d`N%im_TL{jA6lxniPxDG_{*?1SX|olKr(cjaqU_ zt8MXMj8PG^6K0Q&--Bcn{!ce@Y>$o%v$R9$s}Rz_0!P)N{-)^V;^zYEjwKxx91;L;W^3)FK2noWS;@37U^|_6 zHNUkcmhs}8kfc53NIuA2oq=%gvgxG6Qf64Lf~zXJAnwecrP{Gb(j*x4h{mFU4s@gf z^mO;@Zv;?8#IJ-^zbRqAxuNh>ZkB~g1!U>Cbp7U1oCd)v#*W4PBR@(O(FGO7igBr* z34r93-e;@$e8uD?=ZMiMCaI<@^Q%WG#q1!;xuZt{Z>8j;PM5GPNH^Nvy>nf5A@qY; zR?PsXrmlkeTW7m=j*|IXD^_I;Me*EMwH_}!omjB@wJKUf3T1{dp1&)^37_UG#{isO5w3!Du_NvRJNy;7tpf&3g2LLUFAi@3BDa}(5K)9*2H-A(@%ri3f~ILl6;bF zbea26>o_x+ayv&&aGE5gQU4p`PCev}JDi;cv|AO>1|GzR3Q%;6patwsJKBvmoZS|v zTMtYte44UJs{2M^hC+gQbGm!+@C&Qh&nH_V!ElS7Nh&GLOeQU_cn-l4QKHQV z?p$_C$9vW4e{Rg9Fq1f2`rI4JiXxax7toV(KvqZZmbIR1oxpp?KR(cNCiQQ>)2vJU z3!c!Z>1=I*2bjN$^RqIJh1CbPIs4dM!q-AydM+DJr`qlZMec+|QKN`Trfr7NURR-DOA*B19SbyiPy5$tWb;(NhuN4gN$S2J&I)nk~}Q(SrH} z)yJh~?iM0|`4=*6B#o=*Em8Pl(CLGP=7N`&>79#Thcc**Q0>e}3%Lj7kcVR_@;Pyf zi7`d@q3et7!O}-szDLC19Mby}Gs1le#g}+o=9!7dW3$U_NS4j*nahazM)QxS7{dZu zBn~`JyTPhpK{20FtJP+B)A@2`hvn}`7~P;a1A4C~LJVxs9!2C!l50$^hj`>e63;>AaH}j>;*+Ab3z(sVUDqo0te(AH*mH;F8np(GjF5uv z?wGeWpWA9f9;QK>qC{^Qdml7=hnX(Q{Z0>>4`A^|)h^SEPSub)lnX>c^Rubnj^$wb zxzm-Z_d>RA&J8ElHRbFzI?z2dh?mO$oz-G}hAQ^fPnjF{z@_N-y~)Vi>e8y-^3Qna zcs0EcxpiVyb`u1()Q~5>avfeFbsus|p$fA5c z!`3)Y%~Hjcn{(`ycg^rjYjse|OMX{5=dSW{mh~-J=(C7UZS(~@?zQrvC(F9iNQH6n zbC-oFia?Fd=e4=8XH!W7shV;u^UXW)RpuCBTFg#9#)zh7NVaeJpQLKvtJPW$Oxi>T ze9W^qC%g4Gz-Puv3-t!>HfaW(zBn7F%t8nIoO3@{s-^pOf}63>R@JEwW}K^WyROK= zX37fVn(hAW{fOl*PqL8n?C0w~Y#t`lFq8zN`6Ok172GBDzVTL;`tZzaQVy36v@NBO z=i~S!Zr`%CvzaO8=It55=DSYRe@&)y1zi?7a~@3<@7(gp67dlS(IU-Sr|)Fz7Y?8E zS+}K=P9rU2Ceh9C@Y!t9)yRu|l}wCqpfm8UJ&~jCJO5os&TYev<*t=&K4<8r^O@KC zgiF-;IW)b2eQ>W`~W~1qPv3mObC~mbizizvKFNE&)3f~{N!NH2Fj&Eo`Ej=1eFO*K2IUPP4nPiI3V{BD z4_dzr=7%5N$c?~_*on}I_(t>~n2TTp;01I8d;nhnAiyUgH~}aT zj1WfP4S2ua?0OHu1C1y^1Rwwq1_%N~F~Sl25yI$U2%+@Z`!fJ@09&9ATM&L?as&ee z14I#`$UmWi7*X`#gm{ENyVikqfPI8vVkv$(qTfVPLaBkVpdRlgBfbgJz*rBeeoI7f zKL(z=9>a^P6coGfl{*)HD9>fiFQupKX4%7SjOg6|lt5o{0oETSaN>)^5ftNN;`Za7 z%VX-Q!sV~oA&VZ~sKBQfh~xJzkME37sW%tI+VgDBTKDJMhU&;mg$wfR#f$*YtLxO( zR}YuZ`$Er=b4Smb>P6G_SHtJ`_R;pY>Gz6WP2~3oc%0}?bXWKHvgh{n_de77J@aci z2UtBt+G`rtYH8;}P}=?W`CJqQq&TF6x{zEgH3cz?Xx26{IZp~$S35O1aH9~edzb20 z_wF}x!Uu$j;icy@UrxaHrH?11%Xx_=S3oBycJoay_Fr8e>t*yGmhn+E%lDVZbA~rg z_||&F#hT&rs?cmj6O+zj8j#1cKG4bH9!SkNK2jIfB$>zK<74KB?Z|Y1&U893qX2IJ zcv%j3;om=lL1%a*FUynIO<(D<7rZw!)h*Z%yJgz^eY(xx=|J~#@Cx$pPxY_gwRjz` z`A^^fa=uStb6q!{)CO&E3V))G*Vh{@w|mwj_y>EQZdaDCb*+5?{lcE~Ko^(akDcG1 zpO{bhk=t9_5H&b?uBzZNczidHOHOt3Jhe4uGgwJ|cvTPGGk?{|ZzzJhZsC5-)aNko zWy4CB&MEXZv(`;zsX(l30mdV;nI*!AD8ebY`4OQhsi^w)M`F}(=4JW(f5d0To&6ZT zCm>-xI4)B-3_c%jfZ;&DUr7m>ULy<$Cnx8xk!=I#8Ux4?KKS@e8kOcTj|~G{mF>zC zGkXG_(e&u0VX`Q^rpwJ~G%C;p(*l;*yi{5`CYJXwVC1DH0I3~43`U<4bM2}m|CbNg zk@E5$Cy*+N-oE;jmvrAAPSa#;;TRS9@OG)C{b{P-UtntU+fHWKY}jC#X0_9|qjM)@ zNxN%q^Tb6qSw%M@-Df{Re!yBvbv94eU9*}d_WIgSrcLux%ee+1!Z{I48ax#T_&g&6 zYk#!N$UxE1`557)kfESDs8MYuKb?Ud6PEI=z0HzE3l_h}?|fE9W*7nyCn720szoiq z9gXUzr;Tb^v+&Gf=n+jZL+yFZpET|y$uNW;Xz6)~Am*VV+CsI0)I);`+_1L_19u67 z3BwKqa5_W`K0L)H;+Wnzs$zIO^na{U)oS4)QY5r%0-?M%>V;mE%9NxENeruzWa<(% zsh!D_!Ec_RR5JyXe}jx6j^vkI(SAyBZ`;jF{;t-IP(eyMnZ)PMByUz588dy{MkL8T z)Rmq{9#5~FtWNcKroOHGvK+Kq*PC=**YjHU`}Hb{bOo%$JSVXJ?YWGk>QlGCAT;{w z#8873nl-A}8c{fiisGKwIu4%P>Lab}@t9|v_pU7@Go)~AP$pT|IP+ydp-zp8A{jKK zFt|BXgSrr7n&)KbiiSqg4$aUg9&cS&N6!lv5i_vAA0PI@F5`v6#yd@@Q}?9! zHX6Ep61+>17qFtX`}M*u@0H2UD}#i89PT5wzrV4!zp;;M{Kc1V5d<_&K6iC~0*D6R z8^t(e9?GEI!#CU*{CiiT_o{ehNa(_vTlZbrrg! zI-f&TE)fvjHD_186CtsFJ@Hsh-+uS{*EmkW$KkbEFgq39KIY+yi%}S{Bt2Vna_3~+ zJ~f22{4(P-GF~qPoHJ!P_@~d`KmQ8Va&T`41PKXs!$F#x02Rj$8OJW=5^}v!XmZb1@-Nb4J=sjWd;)7Z_X6kH^3ZW}|t^779)Ur)RrKj$ZQs(XAXH>@T$8`8C7 z^c{Y_*4^Cj+7r_UJG%8A z-P%E`S;Bn2^mM$iC9W%kZSpjaAT@n8qsl_FZMk)i!@(f+Ytyy&aL6X#wpHf)=f<`E zV5xG&Va_Q0nDrO(U&kxm!aTQ+i?$TgqM5EvPOUwUEuN>5)XLKlKKd#a9t&;db-0fb zW0DX6$d0)Tz?=yg9tA!3Wc6geMQegxP;ZA5LOq#&#P}G$Nv-@#tGOW~pU35Z#=zZr9_?6NfzhUwtt20)m5{8C7#dJe|OCQUV z`?L@ehNMDjhAsq(v!*(_vaRk!VUFqEv@rJ8kZ1!nh7s=l@2&F;^oVzY{R_lbfo9Pp zDY#c(ra?8sGe)p~Wh$&*-)F{8rEi?IQcLM-5{_Fq@Ek0sew^~FndY4YU0w>lGDjko zpm(dYn<1*Ho1=qS;q&1}MLoy^>TA|lIHeb%rcKAZuWN)Q^AQ%Tz3M{LOFFnCv~y}F ze*l7r`=ICEhGQ6fg2kS!yDaEBI-ON$&22PAY)!0fl5By{II-dMdj*!C%e82>k=tyO zOsWWIq|19GrwHP<;sB@P%!KXR+`&?u^xfy-=1L%?mkO#qudA%Qh<7HuE`#TeS0|Ka zJK{Ljolf9yykWr?Ct2QslI#hd-QMS;WtF1a_&)7k+y8nG z>p*Cs_lp4=#uk}4;h#kr~3*_<2<;2)Sf zb|JwpZ@dtLN);t7x<5cXXuL7KL9Pnly}8l0KQKy6<6hEi;lHG-H zjUaVLsVe1+YpZ*BH``fiMW6p0!b6yrqdZE~=_x8#>{7aXMLQ6a!Sbc&<<)WAuUMFA zB5T@dnDPz8SyL~GG9jg>fKdbLcP^L^KeW<5X2VakbX-2sh>mGfz)Pl*_gKW&ROcqF zPlQ`_AptLHoQ`-wNHX$$8VXTSU9Y-j`|+ zIY)N7=uvC8-XfkftQ!d*RV9)=HV+D(A(DzDZC(sK4>hkc?}N;4__nHKZNZ3WQpPdM zy#X_vW}49#64xIK6oRVeryX2x6L% zv^dz@guN1*E7m&!Y+oI*L|t-OiW-VO9+HB<@}Twl+#HKii%2LbMdPyMGN*Fw z@`7^7@@=JkETyb+BYD=gl0S{-=*|2d=bJ$eQZ!_?+0B6YS-LV8^h-7Rr zOtEy?0$WP;Sbg+6jXO;_%?zgIVunru9f>&xxVO7!t6TbH!^7p&nxj^+b+?&yTUWEW zrbc9@`AE?tPe_>@(IfK=>Txs#t&U`x_!vw|5&v!IO~UOJsWV(nyNG!+SSp9`Tt&W6 zexiMc32OfHFXjN2WF$6EKAz)%s6JprAfRSU;tu_ea8=@tT-Jgfm8+l2rFZnbe~N8L zy*kS$D<@zgpb@LK5XKlR-0&zj5xy+TX3xnkmQmubLbd!ed2oP({jZY*(gfez@uk2O)8fJvp36N^Q|R6OmZ>Wf0fG@ zb2!Uda~pHN$dtD!u=}+d$ta^Y@|%pA#g=`9u`j z3evij+AiM1WC!;JBL)izrURj1s9-c>Jz-B{_^>Krr^EQsOGNn(?+)7!s}JKChS^_s zCFNl7d9{=d-%n+z&L9lp4?D8CPmTC|8eX0vwscApj262wU0D4C-qaITTX`MZKd#ye|Ek7`ox~US<#YcC@}%GZhJZb zsuBGX7N&Ysf87vaW3y(*m}hZzy>9WIpa|NhZ~ub@>e(^3{9u9E<5e(Bs*UI|g`-LOq!a|>8{xZBI+1#}gedGqqNV;K z)lWJ-YB>W@MF1e-k`F7=I;EVy3RL<<`)ojM5nI~;ri6ZaaMyMF^{Xi3p6X}?{4lYG zaYn!#Sunw^L+!GoYS*rk_)U~}J!(mf;VmE)r>Th$!!&`s4TA5W{cV{Ddt08|(2(uo z@ppOsrR)}zN#Zm`R%J3hAPAb}I^zBK*It@3S%p5i<>WV+zz%U6x_E>!DLqs($$#Mw zEDv+WV#az>zG;1luH!)xX=n2u$UUI41nBZq=Pz?+n=U;;$HeRU`sj`LyfZ`y=`JfT zN?=rgJ=r&~VXx(UswyKdMP9Ms0eu91A|drVA2_fTV^?X&iA7J-VUgY}WbV}I$a*y^ zRP2RNMH^!Up7s1NMc$87j+`35mR1%*j8@5_!V)E&OL)YmF!~bID;-u6?rw=jJHo)9;dj6Tv8<72#%{6@_w{H>^4yLkqmHOV(d{Fhcb|e@4LcVAqJ@I zqzpTM0#Iq1bXE$1L3#5;B%NPp2Kg`^J?FJQ%x}@gvIAa^-A16JT*)2>(F8`K`*?cQ zPZn)zvGfH>1qe}#g`TfoZWG@?e#fueHI`qoDr_5h=|o}vIPhXoehLJEw6XB*gb=Z4 z%~q&J&ZyLl$^J+#i}MwphvK(Z}FV3kJe^ylT=JGJpcht}NM>QLmmfRw+caECu! zAWkeQ^~B+++ERIynIQ*bdhM$~ePIB%vw$lngqcyp;%k~! zwnwv6R(ADOrtYl86tx--Xm4lZ+W+PPH-EUmFb~6=0IqO4wG`3<*U!2=$T*$H#=zXJ zx4kSH^<2h{v9(nL7Mt+vTj>6O0o;a(pO`>fvkMF4PPaxHPcOIl%>t2b5ef3k{qD;K zxZJ=E@4UI8rEZfX=jCNS%{>f2w*w_7 z2yZLxSj6HM*;MB?6ioX3->68pjvscBqraszFm77)7AS9{`jJ0sJ zcy`9@-Id^+J2AOBasX1p+F9N-s>;S<954-@kt?Yo09RZYjZ4oQa! zo=H94N5{gF2@(NbFRANXrMJ7uCHHv(={AP=dbwYtrim>Dm9$kRa;Z~mpjiUSwFdNJrH>!kv(sh9t*_%Nkwj09qra&pCutac z@-bk;`yud)vu{$V`d*M1r&j6&^0LNa+sr0Wl&injfIe|J9ZMNv%OQ{{LLh1?d>cID zP5r{^Vs+!+tPrA~nT6NX+)EA)tghq3BOeErAT7&a#(D!hq@yZhXBrHC4?jxR+*hrQ zM}5D{Kh0Jr@B2*&5B!pp5=9wD(+;J$8FBu;X68z{R=BL+7<9Ut-NEiOlZSy+^HZ{V2b*tTTk8xwse4YY{Ad zC$-GhH8y+a0De1Ct6;zjC-c<2dNJ+Y$E>IeYYZNX&{z&MS*eB~o4Ep?6beE|Sgvc} zG+JA4pBVeaBMR#QrfEHWOC6&%u`}ZRHdFp2OXn5;!U6+KNT{a*?{XvuT^2>;J`J#< zH5UO7K5I3*tEpjBRMqlb4B4xwPS?XtnZ4PmOe8jBCRrk!))?hgCpyo@(_PmDO{kZC zqMZ2otZT$CUP5?8Zaz7Xu8bJ>VcGTfS#VwZovT@zV=yNr2Pb9r588pP@b94W6KeUu zR)blE;q`j&aP3eJl0z0THRxmapY$nE^;l!)zKd5?ZdqUxYqf=D`~^Ln%ZK&jl9U6Z zRFkS`N)CWm&+qY@t1^KEHdAe&E#m59X!a}|;^ZJ^(oOz4PHd=O=m(zaTmWWe;$h+P ztfP)uL_sUPH#WCu*2~l2(N&4nSDLtEQg9q3m@mqG6!w`F_zk`|oQ&39F)`|~`np}Q zHhSwaWNsTBIN$G(rNh9tHSmEtnlW4dsL=B_z`Rb855Jl9$?lttJHlD5W@cRaA3W^WU9Zbq-~`P%YV|FsKUNhhuVkd%Ac8e2Cop&+3MVq~ zhd$OnV;gyxabAVqRq$cCv_TN7M8Q_bE{7$%tg5u2p zjRLvLX5NEKlF8g#jOClK^fwDJ^FP7PUqpw8p(z9i$p48IS~-(E2=3PpsQMR!9?D+T zo!JkA0_?3}UUFyn>mlZd^VjRk?^zHXH6bw)2VNL&jsNYO3N&1(JhN_suv`lk#V#aD6lPk(cjJ21m zfossB?`_E;Rlzy0tAFT!J#zB_d)3W z4BvteK{cU1Hb6ukXSQKKSYY=L7C2uKXxnA-W+T)cKQUb^P>n)opdlQCMOP+?u zcMAY)r_^wlJn%m-kWOuSI`lk^*f=~56h8Tl!eX5v{KW0wNuUP6Iba)emd8wS2&NB@ z@^9A8kk}5ip-%JDm#jj~r?7X{cWHMCq0H>u&SQHwEj0{)s127cz9jcnqZ09m76(>G zzmBsYxaWBzAaKGUOKGTG56LkVInje-Kj9Zd7W~ipf`SCN!S{AdPuY>Tl5`n*`S77K z3zwMElm^=s>F*qWwDS+j=3)>}gF1&Zjh}_Am|g5$0OT)Cnx#S+yG4V`yQ6`1*{xp& z5iCm(TrOj)0Epbv6`~Qd-_6)_cExUl40r+MEv}D|`%<+`s4xnKL~sy?bVv(KHuaY9 zEvTB6VUJT0oElge7aXyRV)0?S2Lr4f@zK?m0)N(Bm1Z6 zYKn`Mt}_c&5?ow@&qc=PAYGmBdt0ma913^0;RHEnu!6ByCEUY z)x{BJq7H77L!u%=I#N^eJUdb_{>)i}uP(pqBT?T*hX>KSX0cVr~G z#+wn@cDkF^mq66s(7Gc>#{8WFajs1Jl&Fg?#}U1cDCR~2@NpV?qalN{d5;zG6$uBf3rhf)o-BE1sWQEA(->BC{sW%1sYa+dCyOBnnPyd2-&2 z#ZkJ0W!sF_^6Nh?a6!nz)N-H^!dbDY&E%%cCgByR#lXGl6yn}KYZ7qVQ2`>Gj*j2H zU6jOFt{aihire0ytlF0=yv$)GlMi*rc-%v_2yqs2-pBq)?H%2Hkf78mjF0FO3LABd{2Zz;>XAsYisN$=n<*v1$vfP~p1PVG(+bU?HHpoMuH$($6e#DwPYq1zC ztYyKFyQ9P8w->+(0|=*M&6iWM+7I0e2O8YO0tXG3fDl3u;)*=+^E;SGZ0Wgv{~DX( zdH&I)QZgK~k24(`PPY;M686VFK)pIoDh-3 z0S!ns4q`OS*i;4kV=O*&(Sja4Dr-!q!oE*J9>JPti#oglumT1*(T`xz zKnx)Nt(6SCJsB$kkqhi%p(A79%{ERNE&y zBZURNy}r%4;c>(A0q+Ue9_h<@;o2YpMj+!&GL-%0r6(FTy z>(DVT>df6RVo3$>rN+vH6hB+k-$v&krqcVn26Tm@=elYOlZ^qiC*nm;bcsj)Pq%uyazU!>E#_C{li<* zVp3sJZqP1QFIO)B+M8b$zRLGdC|DrCCj-kZsrYRg8Fc*#cKyj`#vVoJ2Hn{Rw1&DC zr9o8!L7QS!(&z`fM$7Qx&@m^VOKTQ#Y*!Kouz_xg(G1Nw?xmUDnOL-EZNv_p=plXC`ep85rQ?}#FQ#OA!^{^Tp zuoIqkA6;-fr+I>33XZyGlK|kNCUz;T@k-=kg{OiYSkmXJRk}cNVSOn^+LazoeZpQr zzNj3&J<~0y-HTCotcE;7TOaMs9oC?Th>K$^l8S|ie@2~mHD)<3YNM&QQSShb>?_&9 zfJ=~f<7~s985SVK>#ub~-U`A-CJvQ{Nfo9hwfiGn&B~hBf=Q)r7o|3R6sIW`jSyE= zi4GaCih7_l^<0c!=GIY=UI17bVz&$*3;A(4c{qB{Ep|$8lwPQoWYWt&eKFy7su1=BA)a-~l<*a((p5*rYlgSLc z)%(EUngR#sUc}KS7a6h=v`B54L~-F22{UeCMjfxfC|u|j(b(!Mh#QwX`* z7^GWLoO7&=@3m!GqYA(VA8gR z`|kg*M9NcjDgSaQ-Ci-4egS3w&s%qzX?ybeH13{5>&kYULUp+308kxn0&V#hS--J? z`)&891`Igi&3Ab|lG`}m9>o*$3Nm9&*#bBWMx=ICzutL#S<4DK z3{2AMgj`yTRobN9woD`c(P@2ti+Ed#vFeG2dIrqHIF6Wa5H{XF^g3hZV<@e!@BOT_ z)IKGzw(T%z05lnLH8>JX$F;}{QXZ(9q!k$)BFEr$;D9>iHykupSg@#g8}PqmW)qWG zXtCPx3;9v;^l@TXXtN-2m}-In;CQoSswH{7mOV^o4a91if+vf0lrAVc#dA9g466G- zt=DBar93EZxjcf}d3$YqWv)tmERo;_9#}j%U&%VM_z%z&l6ie$ov|IS4=itppYdP( z5zM`s=Yx|Y4HkdX#7MKn^#f$-(Nou@2B1Qam(bHO3TTI2Xt)(;U{h-GD)FJa4cUJ7 z2oO^b_4hLyVu4vat%B-`qgz(a4Inc&FzeM7Yh#yiOc`51F|#p4)6eG4SrAl3EeTsj zftjZ`5{OLLNmz$oFs2YxEW4Nq=Z5=V`}lDNAc}_Y~W(OvwsIl z%Y&0rMZSp7r1clE}qdk0(edZz^K9He2EvqG{1nc7}Beqt#cJWg6AlTSfl z)G$Qdtk%M&8#9^bwHRSPXc*<}`Y_3H`=)P*Xb@dGmJb%@)Dw3|r0NPvGMM|i7&+oJ zSoAONSR?rp=nofZ??Q@~3`8C9#%vgXFWI7zf))pny@aD%OP(YEz?hVnl;mpmCGzM^ zshlV+J71S~21d|~L|4U4*Jx`w>1B<+NL~I!7*-M)^loZ?xUaJUM!kprW#M)Flpkf} z%^V;Mk)E%p6Cw1l>GhQS!N^s!XSQ5l5$ijxoC3#{Yjw9Oq_<>JayRUm3e<^t z&@_>~NiX)rK@DgWr$*}(fn`ifLhb>7hqbkr3PnG>%{9?ec%vcTEfu@CsuNnZ(lH*= zN@tk%^p0Crp|q7GByY8(G!g+n(?t&jAoUjks>Op zk-{fc-~FzdOpiPTn+U%E+hUqRSGMhz|4e87md$rra=+pf%_C5eth7Er32lz;zQop9Z=)KlqF0MltqEEp zY?J>YoL1?Qj2}c%ED?cvPRI#ynA;ZPb$*qHBQ@8IdhTJ<84ky=%n=(fEy50Kw9v{k6ApZ4qi$YAV0S0ryGGx61_= zv1YGr2v>h%g0dn7UIv%idr@7-0fBi*nd%G#ecgp>vXo&#NW|~c^azQPn@ll7%vkO< z@k2ue*02aN)v`}?Ep+u$lqzK$ z6s9gHyt5a7gIR6Fo9kzDCE_0@V^x0PQN?29DaA);( zyCrv2!fr-gI3c=ffcnhk0i0uxT}rD19QQHY0Awo1hlGpO0s876wRS8K`{h$;JEjj; z$yEQoXy-EoSnA(_p}2eDZp3>*qZWbutVzZmUQ+FBTxe2z$IRxKQHg|$qj8&Ov5oUw z!B#A07hh6b%WERHUdtx%DF>ld@}=~d+KhCYrk&+3Qr?u>5jGd{VYo0Zy>q#vl;*IJRF-5uoM zvZ|4^wp8}z>R1SPSq;|<*9jprQ2PUH2&Ic#^+ zt)-Yn2SeX{dZBZebl)~)uS~oG?boos`!|T8EY87dpiK~$rH)` z_9=w1DNa}ge|qTDJ7s1)Dg7x2!j#g1#K|4ml{HmHyG~o2_=isU#N{^HHWO zoS*U^8;CW|el09Xdbgz%v~L{5+lhJ}WuNA(xn3V1)uiP&9S++sb)VZR4SnlhLX$Ye z-Z&t$x4>hayc^Sq_O!)8KANA;TeP}b6Ms4=8Fn3y<$tzH|CReQnXuwJYRN5S1&(d7 zy`lJq0VV{xJxV*_Bt9yStJ%0^j;Z->SVVQRu8EcaHzwez<7t4sIe{OQMmP$J*T@i& z#xC8=nl{$Cn0Ubpr)5H!s!<$uYD}#Lv2+<{Bs9b>NEGwP-68*lxZg|Q_r=%7y>-uv z6+sUlmEr1XkZO_zPA#gyQo`J5La17W*t?0PSBzWG6{^36V4b*hL*noDrPw}JrvbT9 zL=(T-pDSaMtDWq&u?%3|zc<187-q;!$Byzn3hsr!5vNZDnb-mR%tYaN;c`N3`vUkP zt1Ne~0^SilT0=0RM#+dBV{m7;Y*(3PE1Odry>FM7dY&Vz-}# zKIL~(ok`HA)Y2JKkKKU4TINj6h~E$&LoK4wWN5_|pO&EcxOE$;V*es;|R)PDZS0dC&^C%Hsc=;!M8)fj?Do-GKy^K}0 zQv8B04APXZzKf{rdEKxSUH|D&0OKVHCf~rqfFe#&Mlq=j)v14026sl4G%RigzZL_& zJ%@dAxPizSB0u-1IT(CIpT`lJ^|#;Y&gi0##!pzBrcmDjqwwS}t&v8iT!cEK5+sHG z7~DWU1CXyacY{;*0rITKzO?{Ie`jVLD)QwbRtzPFQ`Q}xk0jA` zL%O15`Y>FcjCA;~neRsTqtEBsN%w}klp$cMd$vSz98JOyMc?*S;<`+#^Uy|)c^?yh zsSnK`24|k57w@YS8jCeyKH@Nv<51%-L={PiEFp zRZdVc(xyg6Qc*Qf2}x>6?)4%oNm|GQ8v3+qZEFc8-)ajO4WfmcivH$o&@4iJ4vKMc zu`yw$&N)V)XGrs@8jxUcwfw${ddAisPf3#%UFStkmn>3I&3IM{|D-9G-UJ>OTzbW% znT#I`gf4YcU$3CvPGIbd7{{;Fe1BqtP{#Wv5)_f3;#=J(zxq2vTA(@|IU+D#?(GER z1L7ONwxi*tcRiYWG}9AJz;7mw)Bl3d+fw-3hN$JQT0g9`dQm&S-})d^ew>;VwgYJl zUYT((g~MWX1r13g@%msUvFN)Wmr>Yxc1P5K=sz(AEe0KX^J0UzJqY2-Ka{ZNNC68V z%bWG^*EL_-^5vxpN;9D6f41Cus#!{gO=c#AfPJndE=M*#tCm5hZD30RqZ%6DOZOMt zDL)@W)hSa$&=%qAmx7oflDd-M6<|OHVjJeDHu;&mqPF@ly|7`biZjexoC$APt2`9J z1FNCy0{WPT2OP9V9gajz*c=l%ralVNTpR9Nb&i0kT@_DzU7oO$0qO(iFOq0Wp;&0L zqTv&??VjZkIq8X9I!+LxzZb0*Q>z3Wc{3J6VAM}i(co-@(FOY)eF~!XxG!|0%+^o) zGIk$IWiFImCrDF7sEzhOITkv;n|4MuL$cb4U0e=$Uy6iN@d>8y^1sPY_*} z4zKedmmrG|B=)XPSdLN1f&0}0Qq!;6seokohkpXjt0~1CJiF&C;x?T%tuH8#2ZR}a zj$@693}=pmflzMh5}*H;9`e@Etn>CV2|eIn=*J<=fC*?cE|JjZ2W!egT-P#?;|QaiYdBGU0Mdxj8T(2hfzMS*%@yD~s`9Z~(xS$Q z7~Ers4an-H)oZDnbA~T-RU7#!I&6&R7`H^Zc^tPvlc_Y!)6TC5+9B|$d(rg6Crbfu2v_jh;ponAEN6U1#Y`t&+$z6QMA8( zmiZ*$*qq04t5ACWYjhSDNrK!vQ-FSbC|=Z9*D5h_S6Sa$9ub#~}(*)JjadIy#SR{*{NR(`Q0`2 zoV={Dbxnp+n{!zMZCe6M+#3R1y~cnwF1g3KHVL^$mXdSB$kVRnHKqQXWoh#|ji=CD1XXd@kg{!B>ajG2 zzT6E!uP*iYec@?%@+l!*?Pt?v4(3rEqD6HalK}CE_xtgkunVT0Iup6s1}zlF`|f$0 z1VAXl&AU5V^`yPfmg$#RIFrV0%I0YqYE{E{Lb*v&i&vvC&zn?WO4+PY6I;AKlZQWz zV&!hD5lf=4{HB>WF}hb8q3Q8_aFx-`^YL{u(EW(B|IKr9CXE$AocQq>j!KggkpWP9 z?@tZp!3Sr@^ep^6seeENLwu516^eSWbHtx7;^}P)LZDo%xsj{w`x6?)m*t?mAwwk_R7^WGcLAO_^7= zQt7#EnIpUXc6|`2U+*jleudL!4p4|P$1jx+^`);JO&&~HE+Pe@>+Xa(Il#hM^P}O0 z%LanTtq8V3LAcSSXOBJ}Sl$t}>-m$($Gxu!EQX+cnl%_UNMvL`nI#ko?um%*@$%q| z2yR9;$c6Cg@V_{F>zKO2XKj=g3KS?>+@0d?u!}nsDDLj1xU+G0_l-+&r?|UAaoD)K zZ(zg6?|eDA_ax_!o13g;W!|inwPxO#$@AJgM3U61sg3)dTz0w(6`z6+V|N&CNHJB1 zAH*?QnTX($ge2-g2uY0v#D&x$hK_+Y&eI&n;GZ7qh;`;koO@HUO5E;}gf)>Dj~LWv zq$;PT_AAgA&y5&G#q*)@I~~eG5NsXyXi)U)&y3Tk(}>f~i!T1oQ9$nLKx)a*9>`4W zQu&d}8#PC^ih!S_e_Q5J|4HM8{}m4y{1zPuvyd^JeC#;H>J;{ux>Xllf(rdP+}1d* z?}D}mGlTYOH)8PCC#MkbHw#W9UC-F-9($)RWq_|#PC5>{miL4W;o)o1Gil9d|8dn} z?5Pty&0Av@b^9lY@k!jHkxd|{3*TSHi(vEwCG8pQ6WwV`w^BuIlPt1-Ks#gg#c7es zl=Tf`^-Sc;zW-9aM5i8yj)rnvvwG_fLcwY>VCVVo6W2;sHDtPTv$F}7FBgq!ES=sy ztFj88SFc`(%~h|uvr2sPRFBrta?YO~&G+XrzFO4eR287jbxo%pmR#~x&HePvi^Ag1 z(DZ)sUYO|*`w*UoJKDQdCSSJ~TsKibgG^Di{Oa7+dYU9u`n~yV$*v~Co2w*Q-Og8$TSmog;#llq%2 zle_X!VToY#ABJp6oEGXub^Z>c7%P-=;>$6;GnNk`Z6$=)b!R$xP2rzQ@1@Sz&p=B8 zw(0YKdRq#u+Pf;{59!;g*SYmJ<~vQ4k2w{7xOO8dZr&;Oy8rfFb6bNyANh4ohoI;b zd4*J!VzcAPhy3WLam2foZ9fI^74|wR18YyR5R7f4{|G$PFKJI=(I&v9Zlm_2T&{r1U+QW zwMZ^YrDHJ1glV8%5!QO5CqNaXb_@xBg=s{Dbi@$SzI3|3rpI$Y@;@QuRqrUWvVQoL z+#Yj7@i7d$Uvk?*RK!m=Kg=+mKVJx58Jkr59!|dP!snoE-TZtn<8`f>ZTXSyagviPuju$*nq);G3zx55JM^!Z1vId}`Pk@vK`1Hf&z7e$L|EgmW87vSXz$kMCh$ z3mspZc_t3VHomaMfB11o(nzZ^>}}B{RcW4qf0>To8)Hmc>!gVwGRQn3DbRFOl!5CF2g@m;T%ONEG?jx$;f%M{9L3JwEl0{hBL4Y;k5CvEm9PV zRS6kWy~}4`$`Y#)U%Q?Noe4ja_|$$0r@rPYUm1iWf3VH#R;7uvP(SckQ3p1drop-& zQFKIxN@W|gBRzcDI56ov+3Y+P>1EYKQ3~wweq?AR+4$Us+0~u8>4+y7ikl=Z=NWut zeR5+atgcmQag3atSFRRLoRT}N(5>(<^Z6B+4{vwl5 zcitikQuZqRyZLzJs%2{}eOWsmK*g_|CZjZ@Q&;8dp;JSOCYRD=25Joh_b^-~?r48H zlgY3hcu*+r=Qd_%ON~RoiWe8Wnylch)v$CR{fP6hI#-R3WfzHsTQea(PFg$=ZvHtI zGdt9t@CVLD8VIJ7WXlSZPdh%fQ$yMBymj9u4{_D)y$F5rk^^Nfi-NDN&kwF1pR5uy z^TchlZOhApjr=Df*Q8ly4z687v_^S997^-C*x*X9DXwIYUF30MIMwp z;@R>PAqKZ_>a(PMgBo*Itiqjh^Gy$-y{WOdk2kOFw&}brJjt`558WYYnN?4Fl(lMD z-smu}V(1=JA6VK@q>ZQUD8Na0_d(d!Y51yxEwrOqnnZD#n~>ym@@CbyoI0=D)s_ZN zHq?y(21oYT+DGIJ9n)H9dcPNtEe`YWJ+~-2>!evw7R9zajRXzC7m?8p^PGi}3PMGJ z|ElrG+si_K9S2X{8clO_TsD$;R)^lcZ*yS%MnK-c=!>Yr)`S_Y}?6FCNzXEBrK+0*A)qVeb1IEjQ9^SpWS;?oz$>A6nK8h33eH(x60^`}Qc z(#g=OS@~>^H#lfjq%-MY=*g2_AEa(_x^^T z??r|bm@53r9Q#F#q&(mw=t?N(>dQ8-K1^=z_mr-Xv@WO$7mH!Ch(FA+o8BK(r1q-a zPm3Nsrkz8kuYBh+?ma>>KMdnr!FRu+CznaLKfS(i%_sP3aDjiA94hyL=5GQ#J8fAJ z2_CV+fT&Ksh+5_w7q`;C^~|!kyYi*=3SBwXBjzz13#k-;QkKQ@lKc!mroKy%3eRW7 zRmg9nlgTb*)(FpfnhoifX!;}mgCdDeEkgHcc3*BA=SVz)T|%oxvGAuFotg{1a$und zd+t|{m{U3ANP%1|kGmp?!J?CV@ww;Ouoj~1;YJpPb^+$anfmei(^}cWVYS^2U@?}$ zshj{I0@)0e?`BcUBLB>8X9MQz7pd^#+ajSDoj*8vK33Q+4vWbY;VQjd&hY|ZRa`eE zij0L+Zgh>p7@Q9NFCjEfV)^z@v#7E1Kjp+Zo@RA1ZKx>RE;<{m!tXF=#ydF~XSzfv zGXAAD(K$3~Q!#NQq{N$1^Z|rkAy?zTIXnrY&=ml|^`-y{uoSWW;?Vi1tf zQ@p+Q=60`L!*|-($9ea9-aBPqedlxXyrAU;?dn)C1`Q~nS_JhigSUwM*HJ7Iwd&y9rK&lUTEvq8;B{~c~E67L?mQNTA-iz z5{h`^@Xo%2wSMLHGPjC1=E7@V9{bfrnb^Zo5a;MtmRfsi#0EE%W4HdI>EqWhJ0R#eQJ_%%*EIky=crgQCRbKQ5dxysnFI&T4UbhgzoEz>cpM`$| z+{9ky^4IO9pYk>%SI7^a6lrzk3ohQ0TEGtdOzCTjfY(hWi52OG59Dszy3Ah2p;>+r z_V)RUl+RC2!ao2c{Rc*4Q$CcP6~e!tMBjc<*wqdgdC+D-AndO0$!~XKK>G8jT`yR# zOA^5Z4q@S5iu%MR2L-?|23lo>kxH-nCD@_>pV$PY+fhQhzy` z%Xp5yw_<0~BK!6WXXRPv-}m6_oz~YVDPOmqIdqW?<2wIvx7J!D zZ`aWLC0B54<{bHAmyif8uj|W1y7RRh=H~i(W>jW{l^&W?#1t9YOh+>B*o^sZ+95+v}#Y z69sP}#JIeqJgnn;?MM-O=#ES-9SFy?T3DP`#TH!0^W8hlSGx`__z>OQ3kaCBd9THT zmhc50J6}yY*YPt~fbvuQonjPgU}ec4vyARIib5xaCF~?NuXqF>(iAS~^s%z~QZEf2 z{*D~~R)-7a&1y<7RSBYH=+!Fi(^6fh91wzqXmF7Pr3JJ{@C`@Lqy% zOemiEe*1a29E#hGzE8?yf~1naPsWA=>Y+aW9F_$XZ=ogQe0!vM463&o23WV|bWT46 zah#n`y&diRK9;g7IMcrNK$E(XtIdDq$!B;T{OF2 zy4DAs0dMrUTp8rVAlHS8Gre#j{Cs|ui&b@=f;d*0B9p@5Vsa>#7|4H(crC?ISa#0A zuu;Q%$HA~4!PWQCBXkd7(!3i^lUPE7Uww~d2<+aBY0o*GtN4=+HL0m~$x2#%jK&xg zZm_5;QbK$ZM)WFN;49nKQN+scMs1PMbI{pe;D65fJ(I_JYK&5+H%VJ&Io?&U2NjP*IR|W zpM=?Oe0>T20s6jbzz|rO+2-2U;SDHb^y@nQz0a3bWQz~KHdz1q@}zwr6V+W?Q4);+ zKe1b2lTGSOfW3yS+G6K6RF5hWmHo9b&R~)6ao`cm20%6`ph0+M?9=qE+5nM`))t#V zf(;2C4@xRAV7sdI87&5`R|b@VN2w&8DvlndO};~l-o z(;M^>q)LcbYK9AXrK$L^n{;sC)ziwiXE!h?KbTbkCHdWMn2V%IQO*?n8N=rOCSxsq zMJJBOn{w2G_X?XZdEj_JFnmh?yCBJPJ|*qJ;BCKn%Ta%n=`=6wqHtDFUwvdNd*|`B zMBKupod1}h%#i+72LWlfHQM-6z8KUu3Pwep zUz&=WlJX$mof5r}IK(a3i1enq-uaP(0^vNI2=RqKHp};gKOGP0u&T*KdEVKyC|m_u z6F$}WOcGBfAaQzx2wSKRSVIqI2MEbUAm+)SU`L?acC#E_B> z{>@fUWU{1j@kzaK)WfSx)G zL=;V_QfF8Ua`}})V#v8szifQ#i_@~6?S;PR3?MO#T(d|%-pQ^}sv&Qg;lfB}NT-M( zmkq7L=Go5YYA;1J55Y}H-=Y>jtV01Lg9GONz&MOh|NVvW!Dz(p}YHS6yiUYs3%#(oDaa~Mf8{CJoXEfAR@CwbrNFO~- z?53W6AuHJMOvs(sH(!bkJvR$*8Sm;%@JfD_eELj`5S-5^nN-0QCGP+#ss><;v#D%R z5KZ&)!~iKgQ}j`zN{4drC8fM?BS$Ufp)5@?Q+%Y+hXUryhC-MJ_3aQ}fzCUUN*B2l#Rw|I78|I{n$RfxlGg z2W5Zc+rGC5!LW9+!6PTG#+%77rjl@-{yL&ct(55fXLpXOOYZt8VJgnMc(+!q_dfBjdy=pw9OQ^`<=ER1z3rwBhka6#NB8La z*qFYbtMi>SfUY4siT$5Bb%94_GdH;J)&fMEz=EJI14_VGmoX1f%Xq+0rxd4DkM#c= z-Ms;X38eN$UY+2n1K;3kegmdtZ=Yy=;l*WPja(Y`j9@y2zvABp>o4&l`ocljlQh2j z!h5PsS4(yf%vXnG(=Q3Ywqe&8zyqY#UdL;!UtTt9Wclp|=!*cOS6-Ue0GU^Vgg32g zN|K!y7WTLJT~axHdWiB!tjw#%NUWSb4Fo8s&kO-my@_9AC4g=(-_pl5q+i`elw^Fh zuVqI752Y9B<2ABAWi_p!6Zcz;iP}G=8ieZL1AdYd52p(H<$nQc89OTMee@&mkKLJ2 z>wj(zQX6OfizavQfiXH12BSku@M22xMw2^$R@4jrQE-=36yi7x3fka5td{CPUc73>uPzJ)De(l-dU();X47p4k8Uf*}mrWogI91~mjx8jV*QS8-rF_#{44nw3TQ@!&^3Gif8uJ#7-hGFDyqguw2Y5f60x|^eWU1Ap zQ|oZA2E{y6oCj%hoCiyNQ3rcI`zSB2Ffo@f6EIoM=bV0l8~2UK*nJFdxYz+Dibv<& zgM2~U>mtEu+)L-qEHFY&sJHN_hFB-6r^GbaPkW%W>w{~Ap*`_wRS4Akab#zAit!wO zb#3{_`7*A#CUthx(1<@{7DZuWpl4lm+zVZ)7~Hu$i)N708=y1XV=_}^^oV6E_%GhY zLp}Z&p)HRR!1_gRN9gs*d)fMhD>89D6{*~{_aj|8&(NQQM$+;X>K=M%M9?K~@_!oi z#xy(h4>f(nIF0W-9Y~uaonNuy2kmiWyj#K!cKR)a)uJ}}C{kG94T4|l>E5t+b@G~t zpX_rtT;uOOLAJTCl)FxT_0AQbWSU*uxJL9a=6VFl8#hWuxKU; zK!SXbl*7_Io}r$ybJ(AS&^&`*CJWg48rb|1;cWjxh9qRDz*T`F9B(sb{j6g911Gw-TcCiKK~qo0<<$hYy> zr@8a?l$>+Ro=s3x3C+^;H0M?DBuJbowy-`mNgZp6olSA6RW=ytqskjodD+GoO@*vWgh@H0cY{9Bt9*QQA@2^xo1SX$ppzu>*ll$nb+{xi}^4M%%g z_CEFW2AVJ4dmaDUPzLNj03;aJnAc;fqapFjYr#(mmzQ*N zL-Oc@BO;d@`QJ#AP#&fkNX*%upnXtV)VP{yXj_oPM9p{8eo-4dy}sLnCxp#-5<8VT z8{z_vpqLrx7d$37*K7YgK|?EH*5og?MX)9u{*CzEYx0IH#CYTII0vwa9^K}`tlEhB zK`%5(_JQ6Ax2|G9(&IBJs83Bj;k|43IrkD64j>Wm(uh!S3;Bw3)S9i?K7Ql1;(o{8 z^=8>)1~#z5A#JCE#Sy?;e6iE&!@rAO@)0#|9)@e9YQtRe(Kmj;#yU+nweDfoQ)TaC z9%(mf{%Oo3sk(%{LgY%a^ylLsy?n@1U1-qs1k2N>>(FVoZ6L zsECkT7>;KsTT$_a<_~}>;h_+1>qTBdY1Bq|{t0t#6$w8Tt)ki4-xB)tbyWhEoQH;w zw{%Hp{*q<1e`T!uf7$hEPdO3K61d8M!`3~+*C(}|-nEc5Ox+NoJiWiSOia*YT2gEN zQ~BUQJXf1pdbCvZaN>L_{863>uBpzyR;yc^fRzV-sSOrS9uDUMhK3e4)2jQB(U6NU zO|SG{ZcbvQ=&Myy7b3*`u;U|CL%AC6&LB8j!_c1Nv=i}Jsc(ut`dy{wqN{=$Ierae zRBxs!;J5S%|FRkDHNa6Q^+8 z*zZp5rG-#{L{){6w^SP*PhazdM{ptDg*_$H@2}xFMLpaYzi^1z7P-Y(f&Hb=1PU+F z<42+W&aXmKyDr`z-Xd7`EZoTf6OoW_`X4*dAYybZ!7o^cYTw_!9;;n$Y-5oy%X%d7 z_J@Z1co))bIpn9sVh3-^#T)ot%MX``v0y*#Rtw!w+=A6QE^=oZ@qjw3ZgiVZ8@)R1 zkj}sa=+X^o2Eyz{FUf{CSY)JXuMycO>7E#|z#2>*jQddl(*1*^hLbdH6uxQJzc#&l z1;y|eO-(8)iaBiLjsJ+CAqVMuV6ce#VvRP7bi#VJ<#nOm-50zHFD-xZ5-@ZlJhcx1 zQHZ>*1S;+e6K{geHxC(!?F_3U1#-doUUOj3Xyz#`%MVU&X$gxc}*FtO|2FlnK&(x^e` z85Q@&+eTVr?-}9tLq|Wtpmr3RIiA`xVo+$2E|`H#(EXf}ZaG=E4V}gc?6-38w{+R4f#R`M7ma^AoBG@ zzFy3El=3OuCekxl^UO0Y+tQKL_g(W;=**ngw)@7_ylCwc*q+xvE;_c6xxk|o?NHTw zOAMzifOlncA4Q(+>a$p}g_R4fe087W@N8obe;Q^EZ|nLyO5pb|Wqw85u5EhJ1cXT< zF^5-W3b+^io%t@8h~G43yl4BH?-79w>IJ-|3)ZpWqWvY_{bhpv<&ZF)ZsjM-{_RQFp)X>M&SYFNl4>{8#G7!nhd0l;6WbD?!x3iYpj8J^Tnv2Sw zy93|;Jbw>Z*=jk^89tqjl4cX;HlLbmvn~hD&J*uaDlm;-qxY!U-AznTxMogpOK#8H z%}W@woV(0#d(9#0ThmqIeEl}_SR~$cQ}C7MuimwA!b9>4{D^&0mN0<2_iNGbk+Lqj zbt=Az*Kc>JYmDgw0@yk~+Y1_*1_Y*DsuT3W-KA`FBHVIL^r_oYz~OoBQYZQmZr1D1 zY3SPiys_>-gIj)vKRRPN{6V=orrTEkw!Ns_U(4QKpBQG79A>W;=5&B@5wsw4QUS5b z)9$FTY0L-R{|%duiL^X}#V9!Y0FHQpPXc^`&|@G0vbnA;a4t$G{$q_C2yry6>=+eL z-VvJ%i3+Iem^^RnR)`J zK#A?r*9X)#ONl zZ@Ta>C=^sF3Z~6n+Ws4wYpUUh=D$ri@W~oD5v0pl8kAGo ze+!w5&pYn_0exA4okcD(njIn#()nIbR@V`Kl+w)R12=o>ckTS8gwe$f*pm>+Vma9?5IVG!SCa+uznUGFOj7?_vx|D76@=Ree_r#{F@ zI0MN*q1N)YRuMz0(|%SAg*3#E{9?Rec*(<*L}b4tmAOy-i0~vsh7MT+5hJdDWAyZ6 zqfS;aEK73>J1%TpxWDGx#9-m15WUe|8<1ciZjLf|VNOO39)`ttJe2=Qe|y=&1^>+-T!dsqbfH@}@-$j}p|GC+I20u(y3F=Y_nEZND0kLIv$E`fQ6 zB2}ku2ZgoGg%KFC*TAs(ULX`P-JQ1onH3Z_fPO=^f{rF;g6#7TIRpfjla!A=)Br*s=SQ3Y!y6|XZ)b#14D)bTT+IzG7&w=7t>k`i_KGgmAO6mM4eOoF+@fKs9% zZcV;Rfo4``*wm9SUDiTmHPWvt#F=Tg3+AQr$1j8=Fzg+wDD)xkonm@J#_|EK;Z69~ z3H@mm7JBaY+8jW{;wRWhws@T6y{~OJPTEQTJKE>=z&k-$zgSTBq>uVLtx7WRcI~f| zwe(N-UX%^yS^KZ!Gel`c9&zoQ4nHPLSr#Rwm%6Ecv^V2)l@wR6kG$3rLb`UNfvXFi z@dx;pa?%C{&0_5c+md0t=D>i*<_sQ>Iu4|QtEV@ZEmi(H% z!zsK#Jj6+^(ORT!q%cCVr#MK)K0Y4`8XFGnkRH0dzgmmm&p!I^g(*D|KWxTsWACmw zMuV+hqW(}QHqotZTc9YnNa%n4_3*I(k-_hJ)C8&7Yq{PT;1xh(j5f1Vk-%NoMz=-H z85>&={36MpfL227O;@=o;mkL!E!_nNS@r9}9rfwH$d7JM;Jyhx#3hO|6hdwwnH3-d zl3vD&{iUIMvnZw2WV)D4xzVo(IH<+Ur6kuwgm`q*{-}y4M&P^m7ZmtjC#s4ezY-GT9oy3}t`YyStg@4zyb{GPBaJt4P25gC5iYolXs2s;#T}iD`K^6s>sA|f zi(W5n??Ctsy*bW*d(||EjtZ-PaoBk#avl*>frYxvXnV~+EfgpG31Z<7UjIhrlWm{) zVUnqE>2EZOoB98Lr%`qvuo(~0V7%#aS0K^8@;sl|%wTudqf3ImxW!xPe5vNJJ>kqd zJ^1z?f#t8AklVSpZF!HyC}`1xCaqSSqy@5@iSC9OJb+YX`>iQdm~LgL)FswsbyW%- zs~B)wi;Gc&9A^wXBplj8S^cbUQzNfAq>Jppsyl>|3$9I4lgB|&JB7n?8w&%k$C_kwfMLJ7C{+-|e(MI1`>i|mH2S42f2u{?KM=|7Bd^C9 zOH?1n+t82}H2~!D_=QB8`K;QPAqw*4ay3 zlF@qAEp0mZ&8j>AhSC_%5OD+ufmyli& zBX1#)w~vuaDz;LBed^d6hdpC7P^eTta*JzA?-Q6j>*hxi?2NUm>dSHVIgDj9 zb?(Dj$-s-rWkgKe#bq{`2=Tj@R#OeMC!lN=75XVr2tnvXAuffVclk$ubkenFfO_#OWAFLe%<8Ul#n39N5m zV`^-3)78j=oOg!W*jUA$cZ^yOL&g6HyLCfii@-{;itd%ERxLV%uT@tBUwp5e+O9IW zg3hqHG4)h}3-38RAf`?1_0a#aMP+|2O3vQE(WIeApc~qPI%U6s*h@rm3~}Ex`_}rY z#|=~Cfdq(&u6|1|N~pT$uhyV4?@)U#X>=0-)UuzqDYOa8fiM_E~+__>^1-B;p8H#l^(_<2yna_xz;^}~$`^l+M_#UzD| zgXLqAUC_-O>9UtioA`e`RF(&C%^USfHjL%_pl;ae9WUV;p8kjMp)bZPqp^~^8Xq#I z6LXeBm@TUf&W;5O)9k%ZO4)ljRTv4!m5f2P%pr90kBIP|Pj*gwn`-w|#D|w8x_z&H#MjnGR50VO-4nls@1Fd- zN-(d8%H0SNIqlznZ0Kq_B|;s!r#a=D`IBGi(O+n)$Bj4njsi~>y+i|wu44YN3v{+o=Ncaiv#2|V6tzZ_rxkEyCoz#X*kn4+^b^*DBJ_4>!lkEcZdvU;xWyW|rO zasc+GuMOYq#X`6onMfHHFRi}YkU{Zw?Gpf7I=hkWBER<9b`?97NH!7ntb8?n;Ttj= zm2foMb`8r#HVp0JcPbSe>+aIB%>NCu+hk;!L?>e}aDBfapCEgS5?JOxjcML+wXY#q z>scuO<$qVVIw1KdOHAf@izA`p*OxZawV(>sq>#m;3gyC-HhG!j2+`(j5 zEz94dy6`X5;FTO!FNMG;PdUU_lsl7NT~WeT7z@qVuS7WBb4_pg)1Zy<|~QA zXRhDyEi@A1?`2hN7Zb|Q_^+9~Xy*k#-kL~1H!O8##WX}7@;;k;wh6^gHqg#ld(r#! zGZzv13!@z^lD#o)b|LI3ibBxxHnd8o`Y|Z_72|SI-@{)cp?(@1I(;E?_$Cy$!xVSG z6?fu6xF@%rNU>0GJu``?yK_*GcSqP3YwO19J|MZmT-F;bV50(cYyYnx{#xb0IDxmL z9Wz(fb3GU;Nyv|!hVxb5q~zp7-`Kubtdhs{p}Em0FDM$OHumHC&!u?xB%2@~Ea$M@fMN33fz+I)}FA-VFl%l6|u_Qqw04yN0 z{eJjChlvy`{hFL5)4SPU@ZUF~=qaar*fwD@>dg4QN9(07e7VAGhx?eP8w3Bp@B~lk z1-F*ph>ztorxa>ZwdH z1K#e9itJ-=rg1Ovpu?qhcF!$ zqUi;^g~k_sE7GW*YZdlzc&Sbn#@nwP4cD`o zk4PA?x46o5$Lv60jWMPUCwxoWmMkC8W0yK48uiG`S6~Ou?h)B0bI2rD<|JzMD65(l z!|j(dCGw#EFKmQw@!;~S8G)DM%(!HQkyo8=Alhhl&^4)ONBs{_ z_+l`6W-#T#hf4oj=V1`c3&u9p?HkGP70&Pt!SG#s^tHrM}@-@KfI@km=|VBhc>} zBr0?~FTI#f)5d4AM?Lx9sqp#R?K8sR8{O~~^Y9#ues)62FdK~zhP?}FGMeXfoKza+|qF=W-9bju*9b?7(ae_Tl(NC=hr zFQ9%&0=uHwKIE4+a6i3Ij#_Y;`!X*t6kpjzC!`GL(Dd{9-XVwYVT~m;f%uwVg+(fU z!Vz^V=nU3c(q444?9JbBOnD6{nLw-bsY16L<(yJgYalfqYv41R>%w3Df7bXRR4{#bs8Xk zao0Qt@g-(@02LR#5@>IOm870;j#z;>XK0Rh_sX4gSqRm&lwQ|sJAfta`&k0u$e-_) zi}fFYc{$juTY4Ruub4bB{JA-{(rQ0GEK6wcLc2ady7j zwYK=drd52dC?n%Cw|I=xM|v)lORwq(NkPLpq6xK+GUGM6*wT%SjD)pf=9W!i(b~Fq zC*xImj@0HUs2Es&l$cSq$jK~z^d(Svo)Gr$HFC~Dx^Uo@OViXeh@oqfI_HQ3Pu~$l z{&3-Qh?s*hR4N9)Tqz7&n>JNJGgTYoAym1p@BeS}OWkn?VdX(PrcI(-^IcN&xb}rz zL(c6h{-LWhY0wKtH?3>OV&AXJry&ww2pKc@9`v~Fr2#izP#?>r<<}9#+Yo7vjJA9H@KIb_3 zH2LKz&(SCV=5Hw#-b#;#-faZxWSHToaFzZM z<qJhC8zih*QA@n9c5kYu8$n)om1z-m3K*zEmW9c4vn>a@*b8(dvt-c zksS)tcY@>#;|qYdyOfMJvPH9_*k(s1m2vrG+gyO_ zJj&deQQQcB2$N-(x8E!d2)DmyhF-;In)&6ToQi}84a-TXiZSSLb1uSYBg&)a`L{C0 zox-JlMz|(z7On(^jH5 z$y*iOE@i*8KBMd-x^fRfGux};~=r1zB} z<&u=PNym+evdvUp>hDsbt?1V`eoDEzlmbYO?a0@he!4Cn_ab{;E(!W9ek((^*rIzG zQ)G;Rt60|}JKLa@xzQA*JVV`mV5p@5e8TO=ZzB4*E`c*>LEEE}(BJ<$?2Zu#`TrJC zbN#WNjPiKwiY7R~URen(xbWnHac8b*JMa@GpJlUgU-n)cuZOA&5H{GzTukI$L_Oe4 zSEGX6{?ps#(XoEeosLgQOy1z0OXhu!5V}4DZQ;d{Bs32h5F6uY4@Zck6afc*5oJ8C z;8^!FMJXu=W1q1v{GX5mKTSn>J%m6kw-^1;URd?weQzOm5cAg?{6TVK#DSI{c+jA; z=EaiXd{WQ2E^8Sp=(HrnbgLAnMxw#@4+4tMrpgdlcG&C^LZo{U)Oysj{FjydR~I;^ zL#sxx>RB>uATA9i`8*^tow&DX%O|?O+G}256k)`BgZ41k)KSML;$FB5>mahBgNI8!FkYns(Xg>`R5bH-I9E3_rUDA#FS^T=8!Z( zXw^xiSRF5Rq^Tud@*mV;64xaYQ=nt|*)Gm)Mz1d6JP~as@cOf-rttqVUln~iSv;a| zTQP332s07N8Ti6+4$1@59KP`m*P`t%e{lS#`i>51;i|7hh5O3!f_HVoHglSmMT;P4 z8olGU{%Ix9(3J1hWM{Xe@6>?EAyYg-x3I#wcmnp{+mNwPD(YR^;wUOYCj6ZxQ zAeq!5QY?vEYe&EYVOZuKL;Nqj$i7rF2v$c^%=S9M+{_yDb@?(9+O)`Q zLbq7jp)<#qv8{y^@;76W<8Z;qUs1!_Gq(7_TD#~-RH2-!a>hBn_`#}GbMBc-FaJnI zffVtsOTL73_SQ%t)uvT+E;*wL=^t9?`|OD(fq($+J}nx%uXVYP4LVQA19*9o;FRjl zBz_ypW{U>Z^!BAeV$tOJtPd}$i-yaN49B-g*NzgTR8Kc}H~Br_$}jN?%aT22dwb>L zj6lZu!u*=kM#0e=T4i$we}~c>G=pUNmM5dL;=Q%I%i2s1H;$Tu~Nvzr{J%4|#Kn4~z1?@^LP&Jq<4pMS1JzEB*t;58F? z3Uv(Y5JPP|+52*D&bP5aRh^@1y9ikPR~BLp_=ZLGr2xf-~g}uX{ZEOrc3H zj6-;7mip=k`cn$~2))DT=9>;e+oLaynIctln0t}42t79K(+~Np_)*oReFayY+t!{v z4+&sDY_2fW{M5eW5>MI9$f4ktj&H_KAHo>yT)NUD#=T}jLes+fZVeLGz>&Vw>JOR0 z!|k|s?%6)(DGY*?F;~J1^d)!tw_kf+NF!+MTY2}_nOwcN@_Tfsk2|8b`7R`CM{LlQ zJP`UlFQVLM>$C6l4Gz9lFD>szyX4#y^WN$Io!e9sXzM`8HrYTb8PVBe1qRQp0;i8I zGz9uy94#6E1K+NO_=|jNRnH$>_Me-as)3S>w`gHrPlP&w(DP@=3$HtCVexB=LV zx37c?6{*?~9!-Ut643#QJp92kp3HY@NEQ5d-m#nOCLjUm4~Ci4$Z!~_<>al#Iu^b) zq`e_&+;X9rbO1kb9O7Pxp}DbNC=+fBtThu<5(L}t;&-`cR@&a+N89b@Rf@MuZqY0m z`~aDru*iaPNkraYrdp$bHKWTcZ-dL^i~M(vf%RIi^UGw-yybh`7G-M}KS#gYb5zk29oiC&Z_b~g9KZ)lg`@A5M# zceXf$&k`Qmo6Qkw7O#8md^|#U*+6>LLzEqcEX1N#dhR3|{FnZoJ8=cxeqD|nP}sf` z+c!hdyJYpN7-;A|0u5C1O}BZ=rF54hDnMoH0)@D5Y_q?aHII1x6WZr$Y3pcShrg)N zxyW_^-K4GU?v0hS+w6MUS9s56!Z*Or#)`bsK(d-87QDXH^{i66c|Gs4UF6zcqsr}* zCUro4>bkn*P;Ur#ErF4YS3DysZ){rUi^GstPLFDUI`eJyhYU0;c!ibJ)fTR8G~b(<_b*5l3|;G$TbVgN*uK?sMkK?140pS6=S;j-Q^=v%(=wV;1+I zivOA|f!VH+!<Z)!bIUF?)=<%BRlVSNbK!r))+VPr+)h!ddV!y8Iq-23;A|b zypq=_Vc3#i*6UB-er%QevMmDYQp74}(D%{F-9hnf28j02ivNJ7w5<0UcU1xqAvC1Z z)V^#QdieT(TajG8FiH?Mrt$~vpTy@rMfC0Xc^)w$3#HVI5mB#y#dY?{$w)M|Cs|I* z=$SE#+#hG`I}rBz4tzWxfb&x)OV;HBXB-hTmT2b=~4EI`|3c{ zl`$}9Q3UV#6i_$t@2MX6Su;yc1-LGf=&}f6Tl~C7tMi~Z*m{A~<(n!Tc}!4O_3Szo zmUyia(Q#HGxQE-#pCeS#UTiISmo@qcSh7%;sy*USli=0UD#^J!kh}H3xY*P`kyc-I zIc9$3w4ZZ8RK{irhfJE|)u-XKG%VyR{C@zNKxMyks4Pyt)e(ymJ$pQ$)6{QPrH_o_ zN7XY|7}SY~!f^ed98o$g>bIq$5fsvCEtVtO>~Qb`aok>@SEha$sa2=Vo>R53diu=Z zY|tp(<`UHH-Ba4FHAm4)AXj}(h8vlIo38MSL+P_C_006t_cJrT2zYbR3e35xX(k!= zEn~PWU%NPUW%eAI6eTQ@$ZD*rJE_7jX&!Fu&&Gf6g!Y}cp&uKgT>Nwrlnr;~r zqx(*|xzK%|ySdVRc8l&Zzn_vm5{e)1od^9n^?NAc`awCObYIl(oJ5a)NBudQ+(2;L z_OaiwZ;+JkOH+5stxL;F`EhOOxuCC?D{bgyAg3$*)D%;8ujM& z^fuzoI6A7WIT=oHT(7tKkbWdWTPufkj+vN%N=PfM!5BavjbyTW@Y z+n(*@cX$>N^-jsfM7>LL2~qEmG-G5CI*@k+q5XMo5t`}mbcJ^frq7wVhV(sYk~wQ6 z>b-#<67{~oKN9txKr==Lp?6vxLFhy)_xlI)R=M<%X#f7fyd^GNKPX4kej)1ZZ_%S2 z+8>Y>eFz`tbSF2B(pu1jRxf5S!Erkxm!;nD1p9+BJ>cpf$D~fSo?@#%4BuDlUlQ=kl(9UFSUZvnkh{+S6rRaRLS=W>qT-MXS~<`se48p zIiLAJ*EQ!eMeRDDiC#Csd+6%DN$GPEo`Y7q8Y~w?UDSJ!vZJguXpbY{Nvb~M??PCq z=K_np38I+d**2iCWk;@H*pf`4Eo$c-L}Kpuj)}Kj5l4$ z`mnsfKD}pZ&T45Nne6GF-0k{W*S>idN_g$Z)iYyY>YI0)gzIO+O4K_`qQ^N7J7#z@ zqmlJmg5#Da=31SDjrN=-;;uJ+tg>74sP|RmcB~vdR&~yh)IGAau~H{}#-6t^R>2d# zT-)@n@P2~yc@5k2oIE9X6BbtWTlXj{?T+kG9<(p?(>Gn=JLqlCnR&LPC z55G*8L&O?u9`x3}{ND;qpR=%+p6k0KNbK9smFUcW-iJ zFJ*0FWn^S&ZfA2ZZ){~xVrpe$bTuwwX>ROUX;@R&x?XE%WO8R9Vbp|45tT5iSnCc{ z6f2q_ptV)Q6crf)A=KG`(h63L130v{QLFV-trVQ628XuV;iR@=TZaZj5r~@2ARt5b zT^sB<=bm%!J;VKT@AKS`=MC9AYp?NJ-}`=R|6u0V@2~c30Domu00Y=52f+S|d+;R& zjRyes^4d;O)bF>c|F{2BHh{oL(M~Fa(L>P}S0bwFS6c`%QE9n1ZgjWIW+W-&jqztq@^6@(qWA8f}wq=14CSV?B)H3%pneTU=H!??{o5B zeRt*!#jLp?6BHn55c_FBIw(Z3P(8>5|D1WF)_;#Dv=98F^0cuKT{h9c0jGyjr{Yys}+LeJxvqt~EHC^a%59k#gT%!^=$dxApoZ-8R0oVwK z^bP<|vs>F3o-OUD(G7s=YNtB-NDl0217$_6ZDMAT7-++FH=m)I@>rzyBVb zIB~)T-hcmnA5a7qQ;QdaS+iz=a4I|;czSvQo8D#vy}iAFe3?@>(0KYdAdw~|yl zdD==&Z0?Zvc5-kJyrH+Nt+magv1h>9N=g@gH~cX0tF1n7}+V zu#8op;D{XadfX|R&5-M`S(GMMqg$D$mR2Qdw7O!(n8%$iy|WoWhb6AeNz*X0L1dDZ zB$_*ZAlfM=`%Y_{w7Fxx+!Oh_2OI}N(Y_RU4))61N_Mq%cPDXhd(Z2SJDFB;sufKJ zND76`9qnzg_&i+M-d5i?4z{)}ecXv#$xEna3?!jJP5!~szZq_ZP0!&rw3Cw=K}?pF zWTWS;WHQ%EI#@|ZE9rzT&>&r`uSMDp%9-=?$w&h|>(YYtwTR#3l-P+psvXSady%=i`QiZ;9BEY^u*m|#Cp#$ZbnyE(Q&~>W-a>VS0Gdhtz^7x z(a60~!bSe|gmksEowV(bZg$TSE*iN-2=|P|eD{oH>=_jxD|cnTjcz4y^dr9#trs~I ziycGxtbHrF^+4!^pJTYVt`~tKkM$zy==GxGl3K4}gf#4ki@>Q^>|88%DHabqQY^-c z#k^uMzgR4Iy;v*^jJVijW?U>QE|wh^ z%ZZb3-AB|+`I%^$loG~^iQylKixtGh3gcp3<6=eVLL3+C78mOtCol2;4pqw!P(%qn z{sDc1(|~Y7v|_&e6&K-m(Qd+^g1K11T*i0Nhi@^Lhz`alWz9eH3o6$QgwjyyJn{Fc zd15ZtqUu*+y>3#G*eB(LU!|L2c$T1$8IjGZ^k%1VE5<7eQ?dl@^R@^bBC;JLvJW{` zx?Q@5YN-yGPfCbVVX1HuF>Zm2ThPaTKF`Krf;9|*Zsy{kur;i(HSDl8oUrAarjP!j z9zBr&TruzD4oxY~>l0qmEU1pXtBh%JBwXKaR&@1OR@w!pPgHTeBQCqf`NCp2!$cXTyb=xPNY00<{fk69dnl+46ocG z^w@gXMc`R1_7a5hw?9E`e%P(QSM{u_9+(6sf75tVV?SE|+*&3C;vxB_CBZ_FVk7Dw8(~gs>yFCx59F3}ntDGKtj@>*H6ED_ysRb^F~oy=n*d z$cjpb6_t)FDxFqTI=ime>iBWUPVusf%(qRYrcI4Km5n{uYtAf^efyQqtnlBibeNWX zwd^Z6eroAv*+;U^Wp@9Bki6fn5l}cQW7*e>(wBX`Il@(R#XaV`=&ZL+uQP8oKk6!N z>;eAsvywyk0q_DV6t`QL59C*&;OVprb)pj1I2gE%BhBLHxqSHR1&0oYU?34lxc;u? zJ0m^D#YnKs6nER1n42DczYTBpww;L@{lPRDP*M+Gye%u04YNosU&smx`NlBaWvywi zWv_YW>BO^U5Q*P2Kl?h2*LP#{goc|dzASobIe_zRezx)M@<&Un{lToq&kEyDEv^2> z;ZfZNTcr&gww<$mYrAi=*&rpP0x0m?txa{?@NUU~phJX@J|_W_TMYanQt@a{@u{ai?f9CYlTxBGiqz zh89bwq#D19gHW5I#yerzW#z-fhPZRy3E+d3%Uj|ayrXrpcV)X}^CbdB&Gak&surL> zRPKpK1b^EH)0hF%+zv=iN|;|sCidlDk=^nm@ZGPq%Z`){V6*Qr7TsfB$v;@Mv^mzt zL7p)>mvIkUT>+&n+UKFlYDTDooFkDPRfObM-qx*6D5-$cDqmMqT57a%24)vR{>|5a_nx$>OG8!=8se53QI}7YUTU6$upyZ(UaSDt%7w z&)*K-y1Yqya{tNw&$OHJH|77dUivKmS^kv+KNbH}j4P|J>@T{qf8K+E)Pd9qH|IW> z*C1eiSvTRPP+9Gt@1Or>b%+)V%@--F-$Z{I)vs%3=F2r{8R+YmVVYD;s)w?gcR4bt zdYx9E@1?A6xcO@+m`5n;wZuAND{+8m#rtvYFwYfVzKD{sr@6=l3jf!6FHSBjs))YF zn()n?lQYi8$?MPMZ`|-h27h;Ko@n(-4+s}16HGN}hI^A&d zXzC~WvwCn~=dYokfB0BG**4pjVgv7j2*b28T#;euJGHJ%pTEV>d3yTwvhaLYg;(>* zjm5LG8VyV04}22Un*e@RSgV@+cgJv4pS|&0=S*c0*XU^+Ydm=VgM)KE>OFW7+&nYt zYQ^dLAC*2EjeCr28E}`Gjldu<_#3>c;tWI;$TqAqd=mG8A^+R8u~!Xs25`LUg<-O3 zwkgFFZA>((jbQnn^NF>lA59NUT_&#C(+tL%rn-b@#^N8rw-jVE*d@8vh`Cej?fHnz3CX$Vk zMagE$7Rod-P%PUaJ0v?JyCJ(Pdn%)3fKT`lt9CY~#t=!wVqygWHW7P?<3ug-Bk_>v zBDjhePet-T+CbU_#q9+A?pzS8Rw?oo8x)m_!-{i?Zx!Ia!lr<#<$JQApDI!{T?OW= zvQ%qT2GxGm3DuXXpH=ZKsy6Ss&>@zuU6GnfyY%Zs+>&2O)^uoDbb`hf#s$$(+4KU#ivb=jdx5>Ou%m9 znSIR>=4s|P%*)Jc%wUW8$BM(|bLMZ&_suplv;d(cz%s!y!}69T$D+49D*)AJ?0R|E z%~RJbw=K<2o6zas2kK>>M4@8eB2M`M&i@)uqfBX_zHXzz843_@ml;x{2|_jb0waVv67f6 z-Z7(I`;EWzmM}}y=hwFne9cR10INz+RvCCm;-B1)Dg1M6iKvQK1ZW#7o|$)3po0R+UpFCI)FCO6H#&dVY61fzu5 z_0t~0Bz`JCL);*wcZsJ2Mer4V@#1hrtOCqaWGYrG$`wXMwc@IxPQiPuc%hIGTW))d zQf0f(4r9Tj2JzI}z$Cu;`EJz_)kW1GRrSf$HQc8v-P-%bZmybJIZE4OZ3WHNs>UB1QbIQi4BTQ5wh^EQKEv)ml?qu}OS z=A6q6;|6o5<%Z>*@Xl*=%o~Mb9%ga9&2xOPt*Ozzk$?SKgGCwU{dIZ%FZIWWpKUEo zmJI}TZb9&_Zy!A%G$_fafA+a8I{%lXlZ`D+_LC2we?R8m4;lX5qbyODnU;ljHa*<> z#g8oyFMfLFSbyjB^@6c5+W^7+q9YLf=69UA30S4US%>YMGOi82o`gj z9M@(@P~J;Xm;^)v88!i*fxm@^K!%n4ly4({8xTM0k~cjsO)TZ^#Tt6&XhHmP=#vpP zED$bZg);9i;aYJpfa4M$Nw_3dGEb5zSuL@rI{gAb%m(#F9DaA_e3|5|MuZm`f}rRuP+to{xyeo`byP z#F&r}7&>oWx)k`8cLHrI43Ae z9hws<^>R+M6q*x2SL_uMdJm=C$}4yJtE@%U8s5tLV0$?c6j8`xNKPIPkzSN?%ZE%6!au4zVd2T7RNOmLYyAO4KD z;C=XN4#oJfwe3tX63>cC29FntrWQ?`n8}Qlq)8O;Do__QOQ83goc&DhaEbd?R?~CG zN(KvcDvl+vPDh!iw1Wg7%5H0~q~?+w6x`|oePE!c&#;Jo03#5RG3IlivWnloW;oTIlk?O|CQZ3+Yq!_Yp zuAw9alr4wvfTx@?(9L}Z;F~Cl%{5RIso&I1vGR&h%mR?Za4iK;O|FKeyMdMg^Oi6tX96L=1l*Rh!plS-GWv$Hev3MjR7d8TfuR7a^-=%lH+8B!`;lbfqf*JYyIUz(@M%+*l^!O}#X zR;?RIX90~gLtT(bY1J80b$YtGFIU$^X6jZ31CW!}*wb!m?r0`DJM9c+vuT*_j{Gp? z(r)r@K3P0_f>$%)$|SsoGx|ESKy8E*+1E*$D&q$If@$qUo-vg^BVe?&x3}?KJZ+r^ z{Tv>4LyY+=cal4OdHl+?z>jXjrgq5%eyk%#nrMjUm) z@E{ zFhaN)!2zMGZHk+NqmLj>zLOI?+4uVJw>vDL)5HXyE@qR~YMuILzKOeI& zL@>@-=)l1Eyy4k7 z&It5*KmGMwh>jw6o#-6!r z-?YV{NWm^%48Siw1mnBWj}|=kLv=1l^$u8&skzH2*6h%LE?la*njRmpG%GKzRnwmd z)+(g>tLbr(OEqt;P1CdV;J9YJc0p<^>(kZul~@N@$3h0)+eS8_0_ z*h-GKk`t_C@Vu2rY%)KW@~I7+RRrc%-Zoi;Ud$x=!u)o7*Zg<1*)B`Hl= z6yhw6HWuI9$sz*HKL=%;%lVUr)WGL<+Lph@5vXY!3igJKcB`%1eXx19!s8bGT z&kb>uztYnGlB29717jYd53FCZRoQQYY$Zo*MZ9^zg}llF-~zM0jzcNk$4yP@^LcT} zBnPk*8&?J~eIW`HDTR()9>si^h5=5Y%Xu!}$wka4;@<~$wQm_@i{07oMS{`%1h+EG zNx&8`_xv_J`?Zyx@@oU7*hDa09*~THTASg&<0qwfhHU_7b zRT$6Z?J16ifG02B4Ce#+Df>|YV?<1$gv%@X<655Fj>tn4;{YOSXTApKyasD)h3f^p z5x$!RBYf`&nb9$)-CcdBeub`BcU@2OPI&|d3r{Hqd8MLePCS0gnnmwZ~4N6#6dX zJ>aJbwvL~;CLw6wExgI0>G^5bmC}$;G~^$4O|8w0kHO~5WTZ~ZD=K3~!!Ht2nZleK zeh<5ktZ(e8P6%q^9@4kAJ)NUf9@1QN+l6h0j+*J^D5wfD()wjXm#{VM)$BFk}3AFIRR;>m0vD>aF=Y-s$wlnMG_L1uny<@3d9SUqHy7ftO+ILHHQ*+ed zg0f~?RHP>4vhk;iPbNOvX0HsxQidtDnhdHiT_>Ggn3J}k?}Ii0O!4qKoVW5I%q>1} zLFsU~W?Ok=Q}K){^45;1$OjcF^pum*FIBtl@M(UrQvCiOsQtWSN14ZJww;Y!`(4$| zoykjDA8sGsx&R|9=;!4 zP@sN$T@|puT?12uUWZ*$@hdZ?)*M$R&n>o?P_yNt2Neo5aF;}*ntsJ7G>;WRMvA-F z;ZsV(f8dlaIeE! zR=vFD_-EUHF@6938qUM*uB|(|9jB^?SLdj6b%CSL#mI{iL|%t8mN(TL*`Ha-Tx7^I zH1 zn$jK@kkcC#nbQl((Z^YYa#2pNGC8MLUiW2AZ!9aPcMU72EBD81T>4OTOX;UuN}Hn> zyrD|ytX{k@Eo4zRNO20Vuh^HT*6L8Iyc*cwLt_X@!2uVy8 z|JJpwcLycr0MaWbmpen5_}P<=L4En%0hgoDHWL&heQ71*DV;OSLyBkd7pd&MdCu^* z2Eb>C3u%SPEOY)A{;UCkw8C8Q-p|gE(t!Pw08*I!Rx;2^UcW~*^kS`6((!R8(wvh= zIh)`VBpG*F$;3Wz!%AKo=?rh39|MF|G6oPxdQnKR9YYFl{BfkP07?lnW`h1J3J{)% zW#X%GUpO_N;|%xtf?$Rbr(djKt!?bLPmR=d?oJ0*o5?1>d^b=Q;4>umjSO7*3xgE z2M;3#HY~!DXV4OrqLuLsq)|WNgN;ku{&gKv0`QhZxL+7Y!JT16kkHTTnkdJ!D%h+EPI3CbIdAW zwF!2x3R6L!ho%_~(0kvPoOgMJUYp!o+i0D400-bTi`VuozHeIIdbZ&b7sSkq}WF@D!w0{}*gvk<(&%*Bd zAYC#nbh4nRf zc=Kt|tw^x^7b4-ILlN}61UoQY&wp@j=HXEFf8+n08EUM@*rI3#ky6IkiprTGs=H;Z zsoWWaqGhb@HA_hAAX@M4Nl99C2&MHPDpZpq?o*&yyB7>-oRuzI>UP$a$Lmu zTD{_Hbxz+5R?0z>cwm6f1{o+_liq64Y5~2mEbxR5w_2REfVAk$0`askR4A=GTzt z)0!t#7>>+FI7+XTL7VQYwOZ(|^i%3tJ=9L^_>t$GcV$61bl`-?VfMj;CWe0=r;R%S zkkLk&s};MU%-CY(i0fycV0ut=1T<_JS)|U~XtF`R;Dt%l2DLP=sZO(MCTfOv{Gc;= zE`}{@TP>71$>A~5<9@*e6w%L2AjQW7>GI7&?D?#%tPkpbyAJZ=l^=`L5Bu}gB(&Rn z`%qFIXTYm-xvq$Q%hN zXRZ%-RVetNk<8uCU0hZHUF6>4)^b5Bx0g$B&+x5yJH=VN)jY6~SB;Hhxx5ly4X>GZ z7_0cnOFnGIKQ1mvKZQbM?Z^AnaER>959Z_2U3@SHXGufwhy3~YY5W3CM1of9>edmg z6Ym~qKj7mvc!W?UELFZrf_PfTw%3?S3MsCV#ga7=)+WhbNrvRSq*T(=Z(Nrnc!&JY zPI(Z!%OB-&wxa3RpQVwWrGak)#i(l5@_W?shy;Fg+hZ&&U;TCa@2)w>#`3XpEFFJ~ zX~~vK^`&5%)RGoLTO-{hRgJs9z@Sw65O0yrlzH)IFwAAk8Ng)1)5O%dY2L6q+-5-`U>Y zYx;ue#2)WWY=7O}Fi~^NLVb$CsGC}a1;JuX8~GI&eJZ{mB46SyG^fIlH5mUWcYuJ6uUws}D&uss zr?AahKl!$8p2Ch-H>Y0dGht}+z$H$)$31Ac>&=Jfq%PTanHn{dfP<#6jA+I71M%}| z^r+hjRpGn%QjzDY8!8q3oDeQ)r%3il-D8Ml3K?q^$Jx(8|`}M)`)H7lH=;#P(@?SAL&~DI<#S zj?g(Xx3wp>JF&NY)!N^Y*baJ;7D;K(O7H0$H_lSdT8<%)BV0zYRp`x1-;;xg^`3Cv zEOi@Zd^fcHuD<40B?}4qdOn%!67UsW&W(HBuE)wn{M>KaU}QJ!H1`^p@__q-+rbTc z-Ts3sT6(mAVEK;baYplG%0E1$NS#tqkJu_o8G!Qe?%D|gKi)bXNRA=7ewq#;`Mh#o z9d9=T#!bKS6uh>Y#C9{jyJVRJ`18MDsc1U?H2)g^0sjRbbnu7x6U0YpyU-)3YM%ux zm8FQ4aVoweeky)1?iT}+#86@*nSh(%e@f=#0?E)}#C*}z%kz@Ab0Z&=J94)~ZBIHi z1af1RZjo+aaOMH<-%4D!Ok^*dNI*;zRgx!ztfimYoQKh>6FAe7lvgLs;r_) zDsz-s#ZaG|w8(d=0bi9>lu5wpa;u6$tdPw|1W zDv|e?A8As>TBqHvUy&fz*^;hq^|wo{n=BsX)iz#|2GYDU7^Kjl5go)2CWseIUIQyP zCtPi-1;0@*M>{9#?{|y*uH%%W)N1_o`xd9R9(*G*plkFwdOBT$JnZRQp#Hs8Fn8{t z4!t{V4oSYqZxJ*L-1$C}c~WB$bfME$-O9&y?TW)A%5)MPvp$*st3AAh?i+?A{Yoy@ zq-D9l5_VSGU*iP_!Rz52@F6(tER4dHaCPR!Jb`H&EEs^*XvQ>KnmcWosRp@8!KKk_ zR;bc^YUd6!mjg6Q$6XD1CM0lt zx*0ziItUGsalDY{+#3kkfgD0;l2wvQuJaz^@9_p)^aRHS zZR6^3an318DGH3ZEvOrJ40Zn2nd<#BK=K5^wkbbac=7_*FO1^^@<@bk{^n$6-hYrcx(c)K`WkSszQ&UGxj*T{}Pf-C{ zq9QaYa3uHPfTH-MgW)ecuf=Z2)4|z|>2-8$U~;CJN!PSwFogvj0yFN2;-*50)%wlQ zEhV6J+jctXwka&NUqbr$G<;n`+@92h4S7+o{cv(5zV&^cK|0%GU5OIVB$9E;ssMN6 zETcO@t)vyPdQ%~Tor%nbH!ro@UX}^FU-jz?zeP<=EqSiuGob>siqC~hoZ6(wjLrsT z7iNl7*XWS|*iFTvhTG|H_+fz;X}1hV_FPKape;NdT{Ql!2m*F5?g_#esun@4I54EP zbE8FnQ0Ex-t(YQ=yOUBz7(OP()31!DGpMp_($ojwws0V-d0oAzgEz#xFB}s?K{NXG zB^}ql%iScg!Pqj2tmTUS=jV9zP}Mcz&ZQ| zu8ionF4XS#UCnB;u?=O%Y*}~TD(GJPsKH;1)?sa|N5~65XnEg7{+=!fmc__+eNOe6 ztY01w>qByg_4z%g27y!bA9BE$tZP{;+k#Nl5y6BE0S6gJ2nkV4JS3WkPs9%bXu*dh z0!G%%6>u1w0H?s;Bn+BSxbBYej%k8i^BvZ4H{J3t75Lpe zphWiSTqUwK&xKV}{}-|?vdBrVGVG512ibFTE`#YOb{^}Rn**^=EIzc)nV<1_ZiXxS zSx`WVg8$6lQ`nI!jPxow4*iuY%w|JcI>|l_$H@yDGV8On-!qT8?5`OM6@5~s#D>Hj zH(<%9;)diub~#|@%<$b?8%^@#T`8u$;tE1cl~_q!B#R`g zPw9E!BW?TOzD*K(4fcz`ooRo$=pB~~o)`EPpCkpmzLC`}0Y4==C=F$zUZ{zsqDlFp zC7~u7xop*L(jD|E`W^-S=wYcLwhiBh{ek_7Wy9bZoQ36KH?b$!pYT`grxfT(kK@eu z2_wtjCXBdB7fS(3+a#UH(4*Ng>}xl6=`w&Wp3E4yu@SF^ix|0h2KZrd3$JbdW%jU& zz^$)>+n>4{ZY`fyOBlG`lRTubf zlp=ozHsl9kh?{qsxd?y*na*A2LJk3}fc?M(u$;w7zTE2Np>Mnw#=?7z+A3 zY(E@qzbXDYj7OPqDE3R3##`2J9$R_GDbVmM|BvWxwVdr~J4vh0yPBtnHYCiMnR0XB z4s?gSInHg`!j!@YcfXV-|9PP)ryLgtZ1(VeMR_n*hx0UM=e~C@c3zv#qqy(u!nvrhg=R82s)bcjJ z(508ZeU0^Pz9)wgxU|dq>j}fI6MUaj%rBLc+w6#Z83zhS6VXPm`)s|N^4s3!=g*f2=*l3=Lfqyn`ETlfRvKU0rfMQ}7+(slY2Lsz_=bEN{#^ba{6G0% z3tzwov|-6a^a=kBzXk^izMj}p>@4;XuM)?KcZ^K;~n>5B#cu8&VDt~*tj!}#O!hR*5Njm>QRm>L4;Iki~{ z+DS!WZkcon@f);=`ZL8}Byv)3MLyYZ9`_e$OVq)6on*8`9lAF$V?xX*$ZRVCSBpKH z#5)MjmwU6m`Rk3Xv^R$AsT!3~rj|x!Z^<%avPPwkzV_ma4Mae!F?6xP$6)H%N>REo zluq?A2q=ze8gm(Y{?C9CzRL#tY*cPMsiZ4j{p~BGrZYG5yc#W}fe(jG7$lcGr5pc4 z5QHwXyK++T8Y)mwe^lF3=^AUO%YM+CdMov8abv0n?)$!_&Nm8oE)0A!DRjc;zDe}V z!Qr}Tz(GB}wZe9n&gp-2=j?9ptprjzX~+b+VL{iB%F!~xY_&DilI}=9I9U^y6$8yf~j}Gdk_MDe8Q4YuROO-us zx4{BZl9FP!tfxE7AWhGLFA=xDkNQ0(X7PFm!8ifK7O)fS4bQT1$%Xin`+FjoanOV3{X;&sNZr1ml4m{#{fBuON?!tdd6O^I|3mF2q+YP6fZ$S5Y@_L zviJf5%8Pl5-Hf{xO03PF#x?a4!pf zm@5m?fM2+#sc@YDML7C-wUbDjG#?Cvl+k!h-sI-dF3?-KI%;h@)*ci2@SRut+75x~ z+mzzoN;mn*m}|)oHnIpRi^bj_@yPMj`vFS1-NO##WT^sse^slk9dnnb*MqY^w%1)p zFAD_vyR({ZMC++o{X`^>dng|-{p+!+pRuy~+)k(JbBg0t&|lJ&>r#0{+@z@Im9j^z zQgUOT6ui%SlFrtDNCAtKtjBb)I5}aw{c(KXr_GC2N=srQA$ZsK zvf9Xzw8aB~nxcMJHAl5Q19K3yyo9NlQ;&)Im$-pIIrzB{EPWiyX;26?^fU>UN|1p$u!HG*UOGSRJh$#@mpB|6Uq|!-vud^IV;6kC&sG`#273*#kEq)r1%6%!1kk5UQV%J~#35`2RVmKIzd%=fye=Xq zc?in)DaDzg0(wmvK7$2`OH= zI>40^*p%$pCn1R*_3#OzJYK;DCQK5$F;h2?*GpoD?x;7Sdk7bBZ8HP7QK2pPi=EtQ z-_XNHKUbfSrwpIV^GpS4A;$}j96?uiUdjyj5VprdvjVrJ=Qeo*$KP6ot z)nrMaLJ>J2YvEZzacHujapQgTvUI}M(Byz5*VH8nkL2HUj+vySGnQa*n_s^zwgLRZ z{|g`I!(!DIziNqp^2A13$TWf8PVr&!-{Srvah14H{PBRE$4FbV(wu*Y>4!QDT0IT^ zaSNmT`f)c>~CJ_Olxj$kXtWXzp5einL8__+esWcZ=!}YLc z6m+BJ$`_A@F)=SJ2Wrd~B7T@Jf3+U#*t5L3AbU;RSKVdQt?BJ!0Pq_%{a@K*ChLcO&Z&&3rJ0i4|5 zP}`Ic-WVR7)UsA;0TLp{wTCSF8;seMZnI(Q595>LQ&{>e*4!4_JB)zh#>|EbJ^r^e&sIsZRbYdiQkoyQq+Lk>z&P`M)0OxZnM#`HNtU1d z)_W-;YU0r8Ncwm`9Rk)Y_kXQ<0}I4{n<(51#`^M&%-u61O`O27@;XS0YY!k@5W4 zp7phj8`8=n0yPK1G+fUo^+wD=ljf1UATM&(K$u%?l4FGX>!kZH=g~hTO?7O`Y|Fgn zzhFEm$Wh%xGesjbh~t+V|CBUj0jzi~yv7XzERyHKI5;z*0W`9=;DW|R_947kjN+9z z{S{us`;KeK{x<*S(d8^-`N&quVr9E!M`f2zpjTxK`qX{(Qyjs!FN9#ho#2FE!QCwc z*Wm61cbCN_xVt;S3GVLh&H_ttSafmralcjf{qXAk0ryQ!SIu-ySNHUsKIe2#pU+3G zeL60>BC~d8)Zca|?EE_Ks~(>Kd1j8yAHMv5U(bA7Imw-|y$xs6iJs|b@<@^@t>@Np z0wf-A5<;TNzuh5bil=XCyj}Y{2zFntTZyKTkBtWN^!x1Co_*H%5KN@iBhDqb{~ z-biJFX@(no9Zgh%m<&GB2;+g{oOLbXxQvK4RS}RBGoQuBmxdmF0bK5JYN^=P%11TYXIk zvr}?FD0WVkp+rfNtsLw@`!!Cfb*N`p!w*YqH2SF9_g9xjR zc#7BREk%07r#I;0c#y}NxRSV`@0{k;^Or~-<(&1&yzMk29!{t9FQ~>reDu1I+ac8- zH^11{AlEj2ufihB<%cssWEx1}9L8?fik=;bPG@&!H;z$*-`8;IdhPyO@trej*{wY3 zK%do?8Pp&2GP-x5iV#hF2-8nAz&Ak3-I*9MVrZI3V+e773hJQwqU-tfvyC}_ns1mc zVJ1)eQb>%~>j^ys%zEp7y;6&vm|W0SRoElj%Hb4D&h_0#<%fir0ATCWsFP>R6M?zV zvyYGB=ggMm`p?8P68&F@X>5P@$LDP)h4y*xZ|3R$N(vojg@H7e`vf*94wNLAIW&D+ zey}YWIgh>Esv#)-H07&B=qZvtj3Mp!GUhV#ddUl{4nt+rJ^ST6G!;^xjwtN-O(=}= z**Lf_;z<=5@w*+O8#5CfqdmM`MR*r$hUAQb-^`TnF$_x^*bHyfBIOwaIFPTcWpQD!R(f!mqa7+ z8M@InMOII@AH68JO*kj>N^MdIYvR=UZ*p3m&?N6>6oGWqB$m;6N3!Uz%~GUL_cdyw zy4HOC*kmT4Mf_t>Dn4cxwoZiYkKj--1Yfdg0+Ary9loW&wgJ|3X^xV$IsDnuD63Hw zCp<*g7yfF71izHG?kc#2&mF~bpKUP7iWCcvZ=+!yhvl-PLskX$O8=s6xT50ffa8Ul z*In6?opiBjCUQ&hqt2Nr!EJ1dS)n`g-?R^L_O0)d(alDGeh%6+jG;+PJ4Qq%6n2tV zl(hMMTu$7Ms~@G}S&nIrew^E;<#R*y1Y_qD=$TrbgA*`oTxSPXT@%%Bl{C2dRCpTdAR%1EHYhY zcPc*pq~VTmBoC|)(a`MrV|M19ov<^u?(Bz6o~dYd_Nih5#E)dLT4VN9>d_v*C#UpC z)hM|B9%t9GFhx{HBTauZvAv1-Is_)%td&oeLSPYf&#)Ng?6X|X_u%^}C7)-SW?se3 zirUEmSe02@v~bVZ*5&zd7&7~&Y9f^?vxRlL zN-jt1E2&of;yA(UMn^mACFDwggljxrSM(P*6@nGdF%zC970Eypi$1eKo-;~Sa;0cL zhSFm0L-IoEp0MDZvq_*u6sk)%aPdu9v*{YHf85E(v(T7Afq(?&B>|+@VJbx1O=@84PWMhI*I|h-~okY3VhTvVS<<Pg=c%hMu?Sv!}t!$0P|Pd8Nn;cqn}18Nr8`z6S_F$Vi75dPdbp@_Tb`4 zsd&J{yY6l*X>8)$hMIz9cQSd`-{kuXJ}PL4idD*piix7_VphvyV=*Cx!1QU@nSKmf zwHBrPq#pbsvU44`uP9Hdc<*Pif?dgSVd?t1hFwg#mfcdhXIW-IkY}%712P?50f`O} ze18}C@^?mX<0s* z9jGoAmM!zTW711Vr;+!R4w5S*AAKiarTF|$z|O;n(enZz@>opkUj82H2TkE5*)7vUfj$%k9*-JVc0c*M5sP!7f1xfNDuDvd6qb2J;mU7!d4QUh zj;@xszlqZJlH==j(*2LO#oFh1%d03GA)MUba*5O?9xG@n>1^qDG?ly>3FK)M9*RZ@ zV*IE1x5>Aew|!N#CgWdDz1(W9Be;m9F2ZEoi3B}-o1Dh+an9VL2d=u<`)Vg4PkYgi z*Lsg(jr5_^Ed*%MzqT%}^1h=wKlVF3q!X;EX7n%5u49V!F5@L?@_rQ>{H9FOfM zwbbb>xZUTo$bP-@qGjFvetXwC9pMl%(YSo2va;E<`Hbwa6d)%3-&4cB7bHd!L!kj$ zRp|5vZ;!lfhO%=Y$HQ*p=`FuhRte%+8jES`ygk7%>U#UFDL$i$Z;`ZHpSK<9i;-1H zkX|L7`Rn9|L#_6L7L+UFXh9#vwcY%<{Y{~4!!OejBR;$Eur1ec_BngJsd$~|ZoE)L z#mT?g*}Km1JYUi>Uk3z4c;iQOTwBI;mKlr}vIel1I__)(ODtkhA7ayBSrktZZmB8K`jId;V zr#YWpi$IzKu+nJ(?2oU?SlCt+({gHZzDJ8t{lobe;et3gW`0wXY%vzt%YIu=X096*RR%FW~8g;7NL?sYQ)nS!)p})n>fgC$gtH@e`U*zt}s9HVCObE zF2Em9t@O8qHpwFe}ScN##4ryxJhl4Xq!zY#Mfq~bQ z_AE;+XKyUMnIa>Q=S_N8+hL8BJ~7)lm?8GBW|rUAET)fWV8zV4C9IV@ay>6jU8F+= z{;4CUM6nAeOZP!WOE$WIuK3LBs}c9Si#{{v7Ly&aGr|zU66|rkD(X7y{?#{ixUOc@ z#Q0~;zT;-}S(jN4`3YKh)46>h|D~Z*Jv#!wl_EhnnNIOOx}TVo8=y-%_T( z2({{r(C>g+(??b#yx7er@AlYz*4LCja}4l5L=K>B+j+Oa)On)`Or)@HYIPxa4NHG* zT+f2a)R~1KEPpLN3i1^M<^a2Lxt9Qkou)sdzJ)J7-y}g%f9+$(-7zM!sY(s2A9v9l z;yQmnkjIV(mb>SrbbQnSR0Zs-K|&UXiiSI113x53%bU+gMoaqXCUEKzH1f$N%osDI zS!`kVrAxDWVtXBKsWrPP4?Zz^C9(@xteSQKVt5 zwGrDo{z#+UBl)kC{7+uFzotX{3Bf5apWT$5m08p8v_ASLTfRZZMFM!mBX&!$n_d4_ zJHJ%#1IuYqa;jUg{e3lu<))t}>U3Y!QavvRz0t(7j~h@=J&C-$XV5DAa^b&mLHa~j&|gpz%RPKoKLd@`wn%G5;(&<&!*#T7N=7W78DXw zeWPRT!7iW}iGQpP%6L-)ud}~ihoq7qqLy%`ENVR7n+G55B?GS=g+DKp$ z*kaF}w034@&$CYM(cF$x64I*2%nozpfF3!=f|BEDAL?CeICa}KGQ~ci$7%OSivbf- zE}L&78q2aN?^7*K0=t|$-qNo<9%&xauDM9i5cSel#_Vt%=(a(Qc)sS@A6ivh{sJuk znSih!rTPSaG31wId|4u(>TPKvi?2 zuZH^jW118H>N;P_ z2M*pYyGA@gMWm{Q_YsTvX6O6cZ6$}Fr)d_f@%6JSEiK%!krUWp{OQvx`LzAM@EvSZzozoWzszNxWP)b?Ix7M8r^ zzwYQ}CN*4qRUNF67(<+7s;HT9bGc=GGVlQIY?ms2#WCxi?pOA9_-<%-&G=vG2%W>W zb)o35fG1QyJK4Sq_puCR`lf{a_eU6-^f-%q$v$^qmfS+UibW87k~;%!1x%%GT`TT( zzHlR5DE_TjCMt+Z@D{-w8-Sd1vJpP{k1UGG={38iG;M+5*p188X8EcAa`$lWx-3M$ zhsNTzNu-TB03O1?DF;5-!g#rqf08Mb`cNo`EyRAqux=dLO^fKc`7P8Sn`Sf)>l>>! zr@EWiVxE$ypRM?UDQgIMZ`_X3;mk72zgiH%O<&##NQ~Ut0YQI6rf|lo3yB0X^f%!H zopqS>4<>AL6%IeF*V43~aX+mhqC8Hi8~BFRC_;r!?a7c56(ggZ9Tz<c3U1x4>qjxvRs_wg@7F_kQ{;V%hO!w=&m+X?(%l-@qpTM0li8c~n|8FA1E=8pBYlL51_U!A_Z2 z%qveBZz6Hf2*uu1rm+@Yyw$8%)4ly!VGtL9G7)!oHsTfhDSi=Rt$Bw8io0o8GQ4a9 z-b5`*EPFhz6;?hYY-z$(=7rO2*!;N0%EX*fn?jm1+T)RXTJn<#wwB`(v0=_xIO4n` zsz~*Uz1vcFErWh`-_V@kox&`bkdG7JJuGs~j$a(DFH3FJkD=H9BuB7-nlUFRx$~!F z-_oGly%HaDWl*Haqw%>cymi zj%z;@T{-#^2_r&f+7N)Uujq*A*FL%=f5;Z!P8?5~!$QAdmc2DxBp22kNV&0G=Pu4% zBB4AgHbMOUeBfuHg#Hcb@c0K`5nd5K9BZIE>p<5PxG>>5;?ZpGo1SyZ%IC2?6uj%4 z$!!`dDAz4%FROQeH~Ukh+sLBlAh5HeFwdmCFt!Mj4xgFE{_&S$!FEoy(e03u{g`&& zt!`PlHka0=8@kNH!Uc7JXJlH1**8G4*!b%LIXOa;cgFng?ojY)^3XV793<-~qh0+X zy``H{I;Ig?v8@^?$T`8NY^MsS>K?4b?Z~km- z%0d>6JlQT-v_{aeJ>R~XMw$Le9m#C@o2Pg#gx%DG#O zWIV&m!bkEYTvK!5q;`azLhJA@6n4s-K7Y4U_=!x3?nzY(>azUe0lrFY#(}~bf=OQMki5Tq{yA9yIQhf0!5E{v7rxRz7;5mJJ zsMX_^Fih!Ab2RIZE|3!{liC_M!uH#ITb@v7vcfHv`w;*n9gcinDWooC9`6h( zOCXq16egi`zCft?EN#`4fcCRC(X#n+7bT$dVab`tu#-hq=6(uor|JUaxLb6gNRT>3 ztau0n4&`95-zb-Ut;cI(M(vT9}`HS!Hlan&hQFLzZ^v#duehFD79+M z8Rk?FziGUk8sU|Z&^&NxV(ie%5~JANMCyK+!tm*ZZz>uU=hP|gIBSe5$))^|@chp1 z`}GSM#v4}5n570p+Khpe;w~{ZMN*D>{!v9a4)}BDlgY=e+CM4oNx|LMl-Dp`A-?T` z65VRuin(ltIhDf9(C_+8{_>iwe1@*K@;NM#9>jE+=W&10$KB$!bQ?u=FB4K9g0pBZ-%bsV3+f%kcRgvOO?wImQ&Wb?{yfM)Yra>sE{3Gz{P`f%hUUMn>(tg0 zn<&T5V~0=d$A{wqURP0?4L8m9-6R^z;N|!Jb-vN$uKJQY`tpnySI4XBql_Fea%Anx ze&Z+-)u#bb`%8S+{#Vheh=|zzLv187`CmYxY%sA)K2q;m=$mSkb@j3w${R<-q* zh-2y!$)0~fJ6Ymzvp*)`CNaW24g5WgeV{g9g_tXm4=teT$rH-Son8Q0P?~j6$t#&2 zQmU-26w6tcya@|~*@{>kB#jveM5Vp($_TXoav6`5odwjc%F{|&@0w1#>8nO~qTXu@ zUiHL^dSIKs3X5qVyG}ZLCv zS&4k?IRiP6kX&9W;upQ|hV$3^*FFYo-i|K|%DN9nwP|7fMTXT*f;AtDbl#c_T`Quz ze+#xs`1aFBbf+$lAcflGKRVB!D@fZ7&MS~a<9ur4dSk0P5a@5oMcqYNHZkC zNO@OX*jg<6BE1o`vwRiLnWW(4v`t6(&jdG3AA5ewIWlU$F64(ko{Qr5lcsOOcd;R6 zE#DBvr6<1^A5%~*q0lj3u&ZD@AM#5|I_uz3#V0uKFL9C4S4YYuC6J|rul+MS_^G!% zF1_N3o0h{q-#+nMeL}jX(nN7gdgx+dj4|S$G%^W2B|*51QF;htswkU@3N@?xcLV0Y z39N>nA9!R(JH9#+n4rt;7$dh~Pi14eNPPfHk18V+L<=s9A&{CWeIcK5OG_9T4RMT) zW{ejLj3$Xo_x#MKfUsb;pYnHRM@Rcs=UVaeCOs8FrMWT_RbFDBD!cRjL)%Z_J?I^z!-kHN2uw7?hc}SDs(98e$jHRUwD$OgSk8=w-QsV(>R4G~ zjtkplrrngHvB|`B>OX}Rronw>bWP^r;Z!zTu!eeMeJ}ek=-Y^r>L!Zsf~%F?M{t zVJ)N)WLC!F=meDT1LMj$!*&XMVwGXKpn}lVS@z*bebz!uP#PP$=ufg?2%{-Uu%azv zRkRa+tdiVt{{-R6&bV3huz{GVA;>t09RVFDgHQl_iQpU#UwzoIU%D)@9=;P^P&YBQ z3{*YLIvI03ESM0E6+a=V=ZM3K)Eh$UppGL-&Hq7EYt_(#FrS0f6@kVi+YNyr$2fnQ z6^X5eKRD|uRMlh9^9VJUe*iPbn9f9tKR9jLsQ(Mr(v*CYgn1q*YB~@y-S}x z-w_A~KN8JF1iOY^{mwA*?c&z5KoXNrpT?}=xO59c8bopplg^=(wfMdKIlm5h%Ir>+ zGwI~p>g>l~{k_C}@_Nmgz4FnR-AdJp4V!+HyfX;TSrjTE@QSER!zbjHAZ z?P^q_fE6vQZI=o*ND>pllN3LlFxM!bi4!Cs|XYLghj#mzq++wn8CL%th=C7$$H&o>R5m zjs2OAQP24sQy8vL(nn}jE6e_S^A)w8jhf?4E;+wz^P!U{O#pjjOzm8u__)Uf_eDH| z4Dax7sC*~w08)LjYtQX!jwjqo8*vCD&QpT~ky}Q}$aw`I#Vxl>=KE)LDWM&Ns%jYC z#4WpCY|TU?x=mBi$DAEyq#a!{e5yaeVw`uR0<`I~{x=pe7h&h zLm$&P=O;!#X2LWSyJSXm$`?w8ZDn8CPd{*)u1?wg-?KAo!;`1w%HAHdwK-hd7Gvl_ z+;hO~H)Z#dwGqwAW;*18qrmiQ^Ghn-@1$vt+zv5GN4AAb>X7eLB9r?)5jU`0=)>IT z3fZJ_#6R_v1?Ed!!mw->VH)PKeo<5DK9Fw*-8l3W4A&gL7UG~g&#(3>ajhM2y-r1T zN;SG(lL*XL6~wi8<=JU@W#~*sudcWz=aC_pkmjfUxOJa*NnI|N!;#{sLLLn2tt&q^ zSF`$N6(9>m(vtpo`ZQEFf;@7vqZP_A2VO^fB6X ziS)On>fBPa{oNqx;%ywf(6I)jK#lG;y6JNwvgF`;^u)*he66fM85HcxH1KSNT3L7* zJHc96Uj~bZ*3TwB%9+|JS_DPDXl3=V_Z?^vN7c!wJ$O}35x+HpP3%It-x~8w?Y_TS zm0{r=Ds_<9H1y`V^DLJZ_U1u&mrDopme051TQ54c0tXD%VNP$}I4J=vXhpiOM@l#A za=MmsEq~F<`saMCDw&Qn;7+YJyBZ&ti^!K2`p*UTeyH&_d9qa3&VDP&r3of0BY$L_ ze-%RLRtkk5dLx^=Yh*bS{B&aV9CUE~$rWQ&I~%y>mt zmU^D^j?_6$=!k~qrDWd}d%o&{uQY|u<*$l)5LG(g(r*tiBUdB_ag{r3h0_T0L5 zNT}AIq#`v3_=Zb_4>6QG%fG-pMLWN5aSE-$=xT0D(QB=_#3Y}66G!WO(O#Dmw- zbs56wV!ms$p2}$e6{HnsacPp_?a5u~B)rX5`eZf*e9~0?-i$tWfG>3Ww9LerRmof$ zGHC7+r}`b=%!LKF7Go`bs#=%)FH4pUZMMd~AAi=V9?rEU_dR7t*mHH3-B-LY{FM+? z*QoS8POog;5XO8Ovv-7n2AfuV`+i#7+JAdvs#{+)mP3$KH@|T6Dlj`ntp=xKxBSkt z{_f`*RqmL{*m;6)T#oFuhab2SQhr`kURU-&>;aS(?9X9j>yIs*o5(o}{!6i-g5wSM z*>&Obycb+PLDleARukdP;oAfK{#j2;+VD1hxOSQXjveHV_HktMa0hmBbvDn^Xmx3a zah;#@PZ=noR`NO#*-fqVR@BbRM}n4<+Tq8jbT)W}jd&$Lx7ARRHIegzjtgGg+^EtG z{P!*7>yhPVlDo;c4r&8+DRQ)EO*>UL7bfo-A|fmei56B&Oy9n-mek_k#1kiX@!z(q zpDSx0I&D+qEjE|_P?}8`pKnFT$rJ~fralTA!b#x`=OwxXC)8>3e+;)D(@=u7gBe}@yL~cH^7TUvLNSmd#Lycx!fbsQoAfK9s}c6a=j<8KN3ssk z#$q^{CONPB#FyIQ?apwF8syg2ldE+Gk_)2|Ld7@5)&y@o^SgRz1m`02siNksr}C*I zNSNQmX}V|Q`(oKPwew5p5nX8eYJU@eG1Be~->7j4y^HiX{SYj)W5F+8G3|)W_#?+- zR8*IDCwKQlQow~6N8?esklJXQ#lsjk%xO5pWoTE_XbmSBc3!ABfCg6Jatjo|g%=Q@ zBm65+G3JAZE*(+q0lCa_*DT}r4E8~M`-w8X^)||zp0Kt4JlbHN9nw>SU9Dv`-PRL+KkbuuPgc=$j59WGqp|O+IhLX`@X@66eFdjK91#a45m0D_NybAu}h?Tm*BJY6tPg$ z`GD0Sbefj2L9={LLSm*96{RH-Lzf4nqmtf!uqx~pf^ifjmsDQ${GNqnRv}<4Rn;L` zwQKd&WJDQ2m5@*Sb(skT;pfo+;$VA$O90^{T!axf>jez&5Y;j=lcl?)5IIlL)c{Nn z_j?R{^HyWk3gQt5;Zi|>rRxO;`5^xvg^gKH*Y(0^$$!z3S*RuptOT zZ0M2P4wC;u5bR%QW7bD9oBxX2Qx6BE9+6ftZ~_5WMN`(LG~_3fg=KTPMTLWMC82h{qJz-{)$vwHSjPfQlSamC{QYBYR&FfEQf5-yt7Zj#KozP;}E7+;b@1%5VU;qax+?pnMg zy@xmtc&u*8p#y*Vvk3Sil6K!-lHNRA4|t6JV?Y98<<)+9sdQS0ed6(ZK2l$5$ni7O z=4hI^vJsYrN_*b?*$~0%ju|^*Km+1JLa`fOCa9C{@!50`e1~G79#@9%<1p)?w)3=! z6uNZpd-GZga`|&uu8*E8$)P!W{Hbw9tf9*eB7q90U)9Uez)i~O@bD{U>7Veb0V300 zPwQQ_?DL;4GJ|L<+fe)^usrSA(HoDSh|n98I=?}%Vq;In-tZ^4MueH$jYdvd-z#P% zDVxh6I2WCR%$?BPA~-HfPWzFEg(kL(&sUw_&jTqc<#3^iCi zBIGI&^e?kWH3$l3M(@`5xV-;XoBS37^*#nZ)TT|2fL#q1ew;fSX>5Kya)oc-&)04e z5NK`ryX0qWzt9x5;j%HKdioc?L_Zdo8V;R)sO@;oBIsFvvrv54dub?I6h=HKp^+&Y zXjur0&C zZ-PyQz|v`(RVZ^a^z_1iDw-eg)>Svh_T=%l_1NsMeG4Q8o=MLdSf{}??9qsoG0X}h z{wjv9zKrO-^k`aTt)J$*X-Ca7kAPdlS4g35E&exqD+|!vDSv+A9sv3zibA5IB<=fm zoQ#BK?1CR3l#(i^5iZK%iq=KQniARxo1)O)a-faVjcJ+0Zku9hAdfjQxf_im@IDJy zH(zygxy?DW{TXNz2Xu|`xsY}bXg}K_&x2xm(m`Uj(w18ljLI-qnLaWeZI+GF{-6(} z!lud1!WJ1(v+w`?S(Ib92yghX-vBnQos$7Ddr-H)Nm^6o4+U&<$oeGA%H7d z@1+&@B2R6M20j+?b>y~A`$21%pZvj#=NlZ8je?9yPN#;3TkUSA8m|@hOF7(MzDz86 zR6tSg%HnXnzS4K-+jdL)hDm#`-`@QAGVfkC+AfRr)k?+RPIzm1;Kwp50Uz_1>(l;Y zzLl32b?3o9DeO|r@llI2i>-hDY8}XR=rOH~sXw}ol-Ft+GRzwfED?1p$5mtL>6@85 zIItXUMM#6@RXJxWxioc4Ux$`#*Jp(_AdQod6fHu){x(+BC=TZCSuB;`dvhq)*Xw4 z!%gXhONH%G%I&oblolWjio_#YXmR6Zq2k`By#9?;#KMeuBj82=pm1bCS}Dr&oJXP) zV)%5H_jtCz&XN~8kag{= za2}vzM)=!F6HBD`xxVnTl<@N$m%(!)Q7s^4=IzgnpKtYwpG);h_pn7A>diXsd-C6D zsJ&SWAL@iOm>4|2mzfauDV(;+(>ZLi06;VW5b={P$bon5D`g%aYDT!d&!(rf&&Gez z0(jCuzH-zwV|Tlr2U%-*JOgEQLQ-jJU$yhPHZ1Z8@7HT%e9l=O&N_S{^vJbcOuJ1` zmwo|Z)+o;^LI$|Q>47GL3sDY_{0xEFNm9!LbWRt<%r>~G&A?y(d$YaMW~Tqsz_eh2 z3YQkx3`vhXu2?Cj&A)&o_?7iB*{Yjwj@6hE+Z3@BwtF+5N`8C zjp4?)wEU;)_2zFD*YvQ`beVW=5*fi`*Flq;z4<}cH&t@(oXy0k6yrrmapN(cRAsFq zt;eAQD2o&QxO*ijnWtUsJcyy0C4Z{X^oOu-o%gdmZj1Pfe4?|#f{+|=R*<8J4ZFno z`76rMMbhn?VI?j^MtQ|O&+JCBK?%FZS%UdoAtBjSaix8U6UkxWNsGUDOpmT9A& z+ zX$LtYmk$d^Gmoism_9Vt?-usCs+vwa{fA(c)F_{z@e0-fL~0(n*e-zXoO7ZH8tz?^ zpLV_czYC+2`Gj58i4UOLo$J}#o7VW%ieLM!fd5T*zrH!fG+Y_KZkOubPf`FRt-$J0S&`L!jgoQZ>o%5=3jK+%wW<{OVVQ zo?Ui&HB6KMKD28F4lL8`zWP;6Ca$sTLltn~rk4(R4F`Dk-{IH!VsUPaZ|w*$FfE=1 z_jaEbz)hp(R;IO9x%$Dg!|p=7w|2H(5>}=Yh&rkGc82;G>D(IQt3glbr}sbCFtn>K zNbKqwob{?Ek27;h{nF5sytSfTNgEojds7nRiH>hG1iw5A-!}hA%eUya!n){WWyk{< z)wt-kSh2iW1U)e?gEcG2i8)^17Wk!l;xhbdXE^GBMS!W)sC}EoLlzYgL#Y>sh~tr? zLovYCFh*k@Lru_z+Wo8_*yWfUeTq~`I`(aC>(A)G3K_p9BfJJQ7&uM5Mft!i}< z1)z}99H`m}@H)C-+Ywx??)E)sgY@ipi^-vaadz$7uM8A5r{@N{=_P~_X`t6H|8=VQ z=tMoA$+pNBtl0I6!xyh3?+;i$H5-1} zhJ-Sq4TCRGd}hOXT!$Z@sYO8fei59TEWxYmd2NE-#~YgIJYG_PUWtd~91b=u9#z|o zU96@4EoH2wwRa`DNnEDJ*SeFFML)T-0RTTx0 z|8zOn#P(bzPuZ#DNwY}_%D89FU>o%FT*`{f*%(%rL=&_$d0O(DLMPA`UD-_OHZqwk}D7by?z81SzjmAh=q3<}xN^-2y`&y

W0|04xv`ph6 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%0D5(9wH+#_ki~d(Q;MXVil$ied2qSjhU=Ju^#I# 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 z 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

~XLv+=|%Ntp0t&$DAeM-L}5EE{aDX9KPc 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

l;LS%BUANvaNt%A}_8)?!TV05DRi-)f20&dhCr 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ⅆs1*@W%VX|Zic1s( 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 Iy!p`o4>No$N&o-= literal 0 HcmV?d00001 diff --git a/ports/cortex_m0/gnu/CMakeLists.txt b/ports/cortex_m0/gnu/CMakeLists.txt new file mode 100644 index 0000000..75c7953 --- /dev/null +++ b/ports/cortex_m0/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/cortex_m0/gnu/inc/ux_port.h b/ports/cortex_m0/gnu/inc/ux_port.h new file mode 100644 index 0000000..ca1b561 --- /dev/null +++ b/ports/cortex_m0/gnu/inc/ux_port.h @@ -0,0 +1,215 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* ux_port.h Cortex-M0/GNU */ +/* 6.0 */ +/* */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make USBX function */ +/* identically on a variety of different processor architectures. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_PORT_H +#define UX_PORT_H + + +/* Determine if the optional USBX user define file should be used. */ + +#ifdef UX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in ux_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "ux_user.h" +#endif + + +/* Include library header files. */ + +#include +#include + + +/* CPU definition for X86 systems without preemptive timer function. + This will make USBX uses the controller for the timer. */ + +#undef THREADX_X86_NO_PTIMER + + +/* For X86 systems, the define #define UX_USE_IO_INSTRUCTIONS should be used. */ + + +/* Define additional generic USBX types. */ + +typedef long SLONG; + + +/* Generic USBX Project constants follow. */ + +#ifndef UX_PERIODIC_RATE +#define UX_PERIODIC_RATE 100 +#endif + +#ifndef UX_MAX_CLASS_DRIVER +#define UX_MAX_CLASS_DRIVER 1 +#endif + +#ifndef UX_MAX_SLAVE_CLASS_DRIVER +#define UX_MAX_SLAVE_CLASS_DRIVER 1 +#endif + +#ifndef UX_MAX_HCD +#define UX_MAX_HCD 1 +#endif + +#ifndef UX_MAX_DEVICES +#define UX_MAX_DEVICES 1 +#endif + +#ifndef UX_MAX_ED +#define UX_MAX_ED 70 +#endif + +#ifndef UX_MAX_TD +#define UX_MAX_TD 16 +#endif + +#ifndef UX_MAX_ISO_TD +#define UX_MAX_ISO_TD 0 +#endif + +#ifndef UX_HOST_ENUM_THREAD_STACK_SIZE +#define UX_HOST_ENUM_THREAD_STACK_SIZE (1*1024) +#endif + +#ifndef UX_THREAD_STACK_SIZE +#define UX_THREAD_STACK_SIZE (1*1024) +#endif + +#ifndef UX_THREAD_PRIORITY_ENUM +#define UX_THREAD_PRIORITY_ENUM 20 +#endif + +#ifndef UX_THREAD_PRIORITY_CLASS +#define UX_THREAD_PRIORITY_CLASS 20 +#endif + +#ifndef UX_THREAD_PRIORITY_KEYBOARD +#define UX_THREAD_PRIORITY_KEYBOARD 20 +#endif + +#ifndef UX_THREAD_PRIORITY_HCD +#define UX_THREAD_PRIORITY_HCD 2 +#endif + +#ifndef UX_THREAD_PRIORITY_DCD +#define UX_THREAD_PRIORITY_DCD 2 +#endif + +#ifndef UX_NO_TIME_SLICE +#define UX_NO_TIME_SLICE 0 +#endif + +#ifndef UX_MAX_SLAVE_LUN +#define UX_MAX_SLAVE_LUN 1 +#endif + +#ifndef UX_MAX_HOST_LUN +#define UX_MAX_HOST_LUN 1 +#endif + +#ifndef UX_HOST_CLASS_STORAGE_MAX_MEDIA +#define UX_HOST_CLASS_STORAGE_MAX_MEDIA 1 +#endif + +#ifndef UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH +#define UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH 256 +#endif + + +#ifndef UX_SLAVE_REQUEST_DATA_MAX_LENGTH +#define UX_SLAVE_REQUEST_DATA_MAX_LENGTH 2048 +#endif + +#ifndef UX_USE_IO_INSTRUCTIONS + +/* Don't use IO instructions if this define is not set. Default to memory mapped. */ + +#define inpb(a) *((UCHAR *) (a)) +#define inpw(a) *((USHORT *) (a)) +#define inpl(a) *((ULONG *) (a)) +#define outpb(a, b) *((UCHAR *) (a)) = ((UCHAR) (b)) +#define outpw(a, b) *((USHORT *) (a)) = ((USHORT) (b)) +#define outpl(a, b) *((ULONG *) (a)) = ((ULONG) (b)) +#else + + +/* Define simple prototypes for non-memory mapped hardware access. */ + +UCHAR inpb(ULONG); +USHORT inpw(ULONG); +ULONG inpl(ULONG); + +VOID outpb(ULONG,UCHAR); +VOID outpw(ULONG,USHORT); +VOID outpl(ULONG,ULONG); + +#endif + + +/* Define interrupt lockout constructs to protect the memory allocation/release which could happen + under ISR in the device stack. */ + +#define UX_INT_SAVE_AREA unsigned int old_interrupt_posture; +#define UX_DISABLE_INTS old_interrupt_posture = tx_interrupt_control(TX_INT_DISABLE); +#define UX_RESTORE_INTS tx_interrupt_control(old_interrupt_posture); + + +/* Define the version ID of USBX. This may be utilized by the application. */ + +#ifdef UX_SYSTEM_INIT +CHAR _ux_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * USBX Cortex-M0/GNU Version 6.0 *"; +#else +extern CHAR _ux_version_id[]; +#endif + +#endif + diff --git a/ports/cortex_m3/gnu/CMakeLists.txt b/ports/cortex_m3/gnu/CMakeLists.txt new file mode 100644 index 0000000..75c7953 --- /dev/null +++ b/ports/cortex_m3/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/cortex_m3/gnu/inc/ux_port.h b/ports/cortex_m3/gnu/inc/ux_port.h new file mode 100644 index 0000000..db505f8 --- /dev/null +++ b/ports/cortex_m3/gnu/inc/ux_port.h @@ -0,0 +1,215 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* ux_port.h Cortex-M3/GNU */ +/* 6.0 */ +/* */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make USBX function */ +/* identically on a variety of different processor architectures. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_PORT_H +#define UX_PORT_H + + +/* Determine if the optional USBX user define file should be used. */ + +#ifdef UX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in ux_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "ux_user.h" +#endif + + +/* Include library header files. */ + +#include +#include + + +/* CPU definition for X86 systems without preemptive timer function. + This will make USBX uses the controller for the timer. */ + +#undef THREADX_X86_NO_PTIMER + + +/* For X86 systems, the define #define UX_USE_IO_INSTRUCTIONS should be used. */ + + +/* Define additional generic USBX types. */ + +typedef long SLONG; + + +/* Generic USBX Project constants follow. */ + +#ifndef UX_PERIODIC_RATE +#define UX_PERIODIC_RATE 100 +#endif + +#ifndef UX_MAX_CLASS_DRIVER +#define UX_MAX_CLASS_DRIVER 2 +#endif + +#ifndef UX_MAX_SLAVE_CLASS_DRIVER +#define UX_MAX_SLAVE_CLASS_DRIVER 2 +#endif + +#ifndef UX_MAX_HCD +#define UX_MAX_HCD 1 +#endif + +#ifndef UX_MAX_DEVICES +#define UX_MAX_DEVICES 2 +#endif + +#ifndef UX_MAX_ED +#define UX_MAX_ED 80 +#endif + +#ifndef UX_MAX_TD +#define UX_MAX_TD 32 +#endif + +#ifndef UX_MAX_ISO_TD +#define UX_MAX_ISO_TD 2 +#endif + +#ifndef UX_HOST_ENUM_THREAD_STACK_SIZE +#define UX_HOST_ENUM_THREAD_STACK_SIZE (2*1024) +#endif + +#ifndef UX_THREAD_STACK_SIZE +#define UX_THREAD_STACK_SIZE (1*1024) +#endif + +#ifndef UX_THREAD_PRIORITY_ENUM +#define UX_THREAD_PRIORITY_ENUM 20 +#endif + +#ifndef UX_THREAD_PRIORITY_CLASS +#define UX_THREAD_PRIORITY_CLASS 20 +#endif + +#ifndef UX_THREAD_PRIORITY_KEYBOARD +#define UX_THREAD_PRIORITY_KEYBOARD 20 +#endif + +#ifndef UX_THREAD_PRIORITY_HCD +#define UX_THREAD_PRIORITY_HCD 2 +#endif + +#ifndef UX_THREAD_PRIORITY_DCD +#define UX_THREAD_PRIORITY_DCD 2 +#endif + +#ifndef UX_NO_TIME_SLICE +#define UX_NO_TIME_SLICE 0 +#endif + +#ifndef UX_MAX_SLAVE_LUN +#define UX_MAX_SLAVE_LUN 1 +#endif + +#ifndef UX_MAX_HOST_LUN +#define UX_MAX_HOST_LUN 1 +#endif + +#ifndef UX_HOST_CLASS_STORAGE_MAX_MEDIA +#define UX_HOST_CLASS_STORAGE_MAX_MEDIA 1 +#endif + +#ifndef UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH +#define UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH 256 +#endif + + +#ifndef UX_SLAVE_REQUEST_DATA_MAX_LENGTH +#define UX_SLAVE_REQUEST_DATA_MAX_LENGTH 2048 +#endif + +#ifndef UX_USE_IO_INSTRUCTIONS + +/* Don't use IO instructions if this define is not set. Default to memory mapped. */ + +#define inpb(a) *((UCHAR *) (a)) +#define inpw(a) *((USHORT *) (a)) +#define inpl(a) *((ULONG *) (a)) +#define outpb(a, b) *((UCHAR *) (a)) = ((UCHAR) (b)) +#define outpw(a, b) *((USHORT *) (a)) = ((USHORT) (b)) +#define outpl(a, b) *((ULONG *) (a)) = ((ULONG) (b)) +#else + + +/* Define simple prototypes for non-memory mapped hardware access. */ + +UCHAR inpb(ULONG); +USHORT inpw(ULONG); +ULONG inpl(ULONG); + +VOID outpb(ULONG,UCHAR); +VOID outpw(ULONG,USHORT); +VOID outpl(ULONG,ULONG); + +#endif + + +/* Define interrupt lockout constructs to protect the memory allocation/release which could happen + under ISR in the device stack. */ + +#define UX_INT_SAVE_AREA unsigned int old_interrupt_posture; +#define UX_DISABLE_INTS old_interrupt_posture = tx_interrupt_control(TX_INT_DISABLE); +#define UX_RESTORE_INTS tx_interrupt_control(old_interrupt_posture); + + +/* Define the version ID of USBX. This may be utilized by the application. */ + +#ifdef UX_SYSTEM_INIT +CHAR _ux_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * USBX Cortex-M3/GNU Version 6.0 *"; +#else +extern CHAR _ux_version_id[]; +#endif + +#endif + diff --git a/ports/cortex_m4/gnu/CMakeLists.txt b/ports/cortex_m4/gnu/CMakeLists.txt new file mode 100644 index 0000000..75c7953 --- /dev/null +++ b/ports/cortex_m4/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/cortex_m4/gnu/inc/ux_port.h b/ports/cortex_m4/gnu/inc/ux_port.h new file mode 100644 index 0000000..b9a8656 --- /dev/null +++ b/ports/cortex_m4/gnu/inc/ux_port.h @@ -0,0 +1,215 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* ux_port.h Cortex-M4/GNU */ +/* 6.0 */ +/* */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make USBX function */ +/* identically on a variety of different processor architectures. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_PORT_H +#define UX_PORT_H + + +/* Determine if the optional USBX user define file should be used. */ + +#ifdef UX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in ux_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "ux_user.h" +#endif + + +/* Include library header files. */ + +#include +#include + + +/* CPU definition for X86 systems without preemptive timer function. + This will make USBX uses the controller for the timer. */ + +#undef THREADX_X86_NO_PTIMER + + +/* For X86 systems, the define #define UX_USE_IO_INSTRUCTIONS should be used. */ + + +/* Define additional generic USBX types. */ + +typedef long SLONG; + + +/* Generic USBX Project constants follow. */ + +#ifndef UX_PERIODIC_RATE +#define UX_PERIODIC_RATE 100 +#endif + +#ifndef UX_MAX_CLASS_DRIVER +#define UX_MAX_CLASS_DRIVER 2 +#endif + +#ifndef UX_MAX_SLAVE_CLASS_DRIVER +#define UX_MAX_SLAVE_CLASS_DRIVER 2 +#endif + +#ifndef UX_MAX_HCD +#define UX_MAX_HCD 2 +#endif + +#ifndef UX_MAX_DEVICES +#define UX_MAX_DEVICES 8 +#endif + +#ifndef UX_MAX_ED +#define UX_MAX_ED 80 +#endif + +#ifndef UX_MAX_TD +#define UX_MAX_TD 32 +#endif + +#ifndef UX_MAX_ISO_TD +#define UX_MAX_ISO_TD 8 +#endif + +#ifndef UX_HOST_ENUM_THREAD_STACK_SIZE +#define UX_HOST_ENUM_THREAD_STACK_SIZE (2*1024) +#endif + +#ifndef UX_THREAD_STACK_SIZE +#define UX_THREAD_STACK_SIZE (1*1024) +#endif + +#ifndef UX_THREAD_PRIORITY_ENUM +#define UX_THREAD_PRIORITY_ENUM 20 +#endif + +#ifndef UX_THREAD_PRIORITY_CLASS +#define UX_THREAD_PRIORITY_CLASS 20 +#endif + +#ifndef UX_THREAD_PRIORITY_KEYBOARD +#define UX_THREAD_PRIORITY_KEYBOARD 20 +#endif + +#ifndef UX_THREAD_PRIORITY_HCD +#define UX_THREAD_PRIORITY_HCD 2 +#endif + +#ifndef UX_THREAD_PRIORITY_DCD +#define UX_THREAD_PRIORITY_DCD 2 +#endif + +#ifndef UX_NO_TIME_SLICE +#define UX_NO_TIME_SLICE 0 +#endif + +#ifndef UX_MAX_SLAVE_LUN +#define UX_MAX_SLAVE_LUN 2 +#endif + +#ifndef UX_MAX_HOST_LUN +#define UX_MAX_HOST_LUN 2 +#endif + +#ifndef UX_HOST_CLASS_STORAGE_MAX_MEDIA +#define UX_HOST_CLASS_STORAGE_MAX_MEDIA 2 +#endif + +#ifndef UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH +#define UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH 256 +#endif + + +#ifndef UX_SLAVE_REQUEST_DATA_MAX_LENGTH +#define UX_SLAVE_REQUEST_DATA_MAX_LENGTH 2048 +#endif + +#ifndef UX_USE_IO_INSTRUCTIONS + +/* Don't use IO instructions if this define is not set. Default to memory mapped. */ + +#define inpb(a) *((UCHAR *) (a)) +#define inpw(a) *((USHORT *) (a)) +#define inpl(a) *((ULONG *) (a)) +#define outpb(a, b) *((UCHAR *) (a)) = ((UCHAR) (b)) +#define outpw(a, b) *((USHORT *) (a)) = ((USHORT) (b)) +#define outpl(a, b) *((ULONG *) (a)) = ((ULONG) (b)) +#else + + +/* Define simple prototypes for non-memory mapped hardware access. */ + +UCHAR inpb(ULONG); +USHORT inpw(ULONG); +ULONG inpl(ULONG); + +VOID outpb(ULONG,UCHAR); +VOID outpw(ULONG,USHORT); +VOID outpl(ULONG,ULONG); + +#endif + + +/* Define interrupt lockout constructs to protect the memory allocation/release which could happen + under ISR in the device stack. */ + +#define UX_INT_SAVE_AREA unsigned int old_interrupt_posture; +#define UX_DISABLE_INTS old_interrupt_posture = tx_interrupt_control(TX_INT_DISABLE); +#define UX_RESTORE_INTS tx_interrupt_control(old_interrupt_posture); + + +/* Define the version ID of USBX. This may be utilized by the application. */ + +#ifdef UX_SYSTEM_INIT +CHAR _ux_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * USBX Cortex-M4/GNU Version 6.0 *"; +#else +extern CHAR _ux_version_id[]; +#endif + +#endif + diff --git a/ports/cortex_m7/gnu/CMakeLists.txt b/ports/cortex_m7/gnu/CMakeLists.txt new file mode 100644 index 0000000..75c7953 --- /dev/null +++ b/ports/cortex_m7/gnu/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/ports/cortex_m7/gnu/inc/ux_port.h b/ports/cortex_m7/gnu/inc/ux_port.h new file mode 100644 index 0000000..3411fed --- /dev/null +++ b/ports/cortex_m7/gnu/inc/ux_port.h @@ -0,0 +1,215 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** USBX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* ux_port.h Cortex-M7/GNU */ +/* 6.0 */ +/* */ +/* AUTHOR */ +/* */ +/* Chaoqiong Xiao, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make USBX function */ +/* identically on a variety of different processor architectures. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef UX_PORT_H +#define UX_PORT_H + + +/* Determine if the optional USBX user define file should be used. */ + +#ifdef UX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in ux_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "ux_user.h" +#endif + + +/* Include library header files. */ + +#include +#include + + +/* CPU definition for X86 systems without preemptive timer function. + This will make USBX uses the controller for the timer. */ + +#undef THREADX_X86_NO_PTIMER + + +/* For X86 systems, the define #define UX_USE_IO_INSTRUCTIONS should be used. */ + + +/* Define additional generic USBX types. */ + +typedef long SLONG; + + +/* Generic USBX Project constants follow. */ + +#ifndef UX_PERIODIC_RATE +#define UX_PERIODIC_RATE 100 +#endif + +#ifndef UX_MAX_CLASS_DRIVER +#define UX_MAX_CLASS_DRIVER 8 +#endif + +#ifndef UX_MAX_SLAVE_CLASS_DRIVER +#define UX_MAX_SLAVE_CLASS_DRIVER 4 +#endif + +#ifndef UX_MAX_HCD +#define UX_MAX_HCD 2 +#endif + +#ifndef UX_MAX_DEVICES +#define UX_MAX_DEVICES 8 +#endif + +#ifndef UX_MAX_ED +#define UX_MAX_ED 80 +#endif + +#ifndef UX_MAX_TD +#define UX_MAX_TD 32 +#endif + +#ifndef UX_MAX_ISO_TD +#define UX_MAX_ISO_TD 16 +#endif + +#ifndef UX_HOST_ENUM_THREAD_STACK_SIZE +#define UX_HOST_ENUM_THREAD_STACK_SIZE (2*1024) +#endif + +#ifndef UX_THREAD_STACK_SIZE +#define UX_THREAD_STACK_SIZE (1*1024) +#endif + +#ifndef UX_THREAD_PRIORITY_ENUM +#define UX_THREAD_PRIORITY_ENUM 20 +#endif + +#ifndef UX_THREAD_PRIORITY_CLASS +#define UX_THREAD_PRIORITY_CLASS 20 +#endif + +#ifndef UX_THREAD_PRIORITY_KEYBOARD +#define UX_THREAD_PRIORITY_KEYBOARD 20 +#endif + +#ifndef UX_THREAD_PRIORITY_HCD +#define UX_THREAD_PRIORITY_HCD 2 +#endif + +#ifndef UX_THREAD_PRIORITY_DCD +#define UX_THREAD_PRIORITY_DCD 2 +#endif + +#ifndef UX_NO_TIME_SLICE +#define UX_NO_TIME_SLICE 0 +#endif + +#ifndef UX_MAX_SLAVE_LUN +#define UX_MAX_SLAVE_LUN 2 +#endif + +#ifndef UX_MAX_HOST_LUN +#define UX_MAX_HOST_LUN 2 +#endif + +#ifndef UX_HOST_CLASS_STORAGE_MAX_MEDIA +#define UX_HOST_CLASS_STORAGE_MAX_MEDIA 2 +#endif + +#ifndef UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH +#define UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH 256 +#endif + + +#ifndef UX_SLAVE_REQUEST_DATA_MAX_LENGTH +#define UX_SLAVE_REQUEST_DATA_MAX_LENGTH 4096 +#endif + +#ifndef UX_USE_IO_INSTRUCTIONS + +/* Don't use IO instructions if this define is not set. Default to memory mapped. */ + +#define inpb(a) *((UCHAR *) (a)) +#define inpw(a) *((USHORT *) (a)) +#define inpl(a) *((ULONG *) (a)) +#define outpb(a, b) *((UCHAR *) (a)) = ((UCHAR) (b)) +#define outpw(a, b) *((USHORT *) (a)) = ((USHORT) (b)) +#define outpl(a, b) *((ULONG *) (a)) = ((ULONG) (b)) +#else + + +/* Define simple prototypes for non-memory mapped hardware access. */ + +UCHAR inpb(ULONG); +USHORT inpw(ULONG); +ULONG inpl(ULONG); + +VOID outpb(ULONG,UCHAR); +VOID outpw(ULONG,USHORT); +VOID outpl(ULONG,ULONG); + +#endif + + +/* Define interrupt lockout constructs to protect the memory allocation/release which could happen + under ISR in the device stack. */ + +#define UX_INT_SAVE_AREA unsigned int old_interrupt_posture; +#define UX_DISABLE_INTS old_interrupt_posture = tx_interrupt_control(TX_INT_DISABLE); +#define UX_RESTORE_INTS tx_interrupt_control(old_interrupt_posture); + + +/* Define the version ID of USBX. This may be utilized by the application. */ + +#ifdef UX_SYSTEM_INIT +CHAR _ux_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * USBX Cortex-M7/GNU Version 6.0 *"; +#else +extern CHAR _ux_version_id[]; +#endif + +#endif + diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 0000000..539f3dd --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Use paths relative to this script's location +SCRIPT=$(readlink -f "$0") +BASEDIR=$(dirname "$SCRIPT") + +# If you want to build into a different directory, change this variable +BUILDDIR="$BASEDIR/build" + +# Create our build folder if required and clear it +mkdir -p $BUILDDIR +rm -rf $BUILDDIR/* + +# Generate the build system using Ninja +cmake -B"$BUILDDIR" -GNinja -DCMAKE_TOOLCHAIN_FILE=$BASEDIR/cmake/arm-gcc-toolchain.cmake $BASEDIR +# Generate the build system using the system default +# cmake -B"$BUILDDIR" -DCMAKE_TOOLCHAIN_FILE=$BASEDIR/cmake/arm-gcc-toolchain.cmake $BASEDIR + +# And then do the build +cmake --build $BUILDDIR diff --git a/samples/demo_usbx.c b/samples/demo_usbx.c new file mode 100644 index 0000000..1d7de2b --- /dev/null +++ b/samples/demo_usbx.c @@ -0,0 +1,395 @@ +/* This is a small demo of the USBX */ + +#include "ux_api.h" +#include "ux_system.h" +#include "ux_utility.h" +#include "ux_host_class_dpump.h" +#include "ux_device_class_dpump.h" + + +/* Define USBX demo constants. */ + +#define UX_DEMO_STACK_SIZE 4096 +#define UX_DEMO_BUFFER_SIZE 2048 +#define UX_DEMO_RUN 1 +#define UX_DEMO_MEMORY_SIZE (64*1024) + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG error_counter; + + +/* Define USBX demo global variables. */ + +unsigned char host_out_buffer[UX_HOST_CLASS_DPUMP_PACKET_SIZE]; +unsigned char host_in_buffer[UX_HOST_CLASS_DPUMP_PACKET_SIZE]; +unsigned char slave_buffer[UX_HOST_CLASS_DPUMP_PACKET_SIZE]; + +UX_HOST_CLASS *class_driver; +UX_HOST_CLASS_DPUMP *dpump; +UX_SLAVE_CLASS_DPUMP *dpump_slave; + + +#define DEVICE_FRAMEWORK_LENGTH_FULL_SPEED 50 +UCHAR device_framework_full_speed[] = { + + /* Device descriptor */ + 0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, + 0xec, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + + /* Configuration descriptor */ + 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0xc0, + 0x32, + + /* Interface descriptor */ + 0x09, 0x04, 0x00, 0x00, 0x02, 0x99, 0x99, 0x99, + 0x00, + + /* Endpoint descriptor (Bulk Out) */ + 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, + + /* Endpoint descriptor (Bulk In) */ + 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00 + }; + + +#define DEVICE_FRAMEWORK_LENGTH_HIGH_SPEED 60 +UCHAR device_framework_high_speed[] = { + + /* Device descriptor */ + 0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, + 0x0a, 0x07, 0x25, 0x40, 0x01, 0x00, 0x01, 0x02, + 0x03, 0x01, + + /* Device qualifier descriptor */ + 0x0a, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, + 0x01, 0x00, + + /* Configuration descriptor */ + 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0xc0, + 0x32, + + /* Interface descriptor */ + 0x09, 0x04, 0x00, 0x00, 0x02, 0x99, 0x99, 0x99, + 0x00, + + /* Endpoint descriptor (Bulk Out) */ + 0x07, 0x05, 0x01, 0x02, 0x00, 0x02, 0x00, + + /* Endpoint descriptor (Bulk In) */ + 0x07, 0x05, 0x82, 0x02, 0x00, 0x02, 0x00 + }; + + /* String Device Framework : + Byte 0 and 1 : Word containing the language ID : 0x0904 for US + Byte 2 : Byte containing the index of the descriptor + Byte 3 : Byte containing the length of the descriptor string + */ + +#define STRING_FRAMEWORK_LENGTH 38 +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, 0x0c, + 0x44, 0x61, 0x74, 0x61, 0x50, 0x75, 0x6d, 0x70, + 0x44, 0x65, 0x6d, 0x6f, + + /* Serial Number string descriptor : Index 3 */ + 0x09, 0x04, 0x03, 0x04, + 0x30, 0x30, 0x30, 0x31 + }; + + + /* 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. */ +#define LANGUAGE_ID_FRAMEWORK_LENGTH 2 +UCHAR language_id_framework[] = { + + /* English. */ + 0x09, 0x04 + }; + + +/* Define prototypes for external Host Controller's (HCDs), classes and clients. */ + +VOID tx_demo_instance_activate(VOID *dpump_instance); +VOID tx_demo_instance_deactivate(VOID *dpump_instance); + +UINT _ux_host_class_dpump_entry(UX_HOST_CLASS_COMMAND *command); +UINT ux_hcd_sim_initialize(UX_HCD *hcd); +UINT _ux_host_class_dpump_write(UX_HOST_CLASS_DPUMP *dpump, UCHAR * data_pointer, + ULONG requested_length, ULONG *actual_length); +UINT _ux_host_class_dpump_read (UX_HOST_CLASS_DPUMP *dpump, UCHAR *data_pointer, + ULONG requested_length, ULONG *actual_length); + +TX_THREAD tx_demo_thread_host_simulation; +TX_THREAD tx_demo_thread_slave_simulation; +void tx_demo_thread_host_simulation_entry(ULONG); +void tx_demo_thread_slave_simulation_entry(ULONG); +VOID error_handler(void); + + +/* Define the entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); + + return(0); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *stack_pointer; +CHAR *memory_pointer; +UINT status; +UX_SLAVE_CLASS_DPUMP_PARAMETER parameter; + + + /* Initialize the free memory pointer. */ + stack_pointer = (CHAR *) first_unused_memory; + memory_pointer = stack_pointer + (UX_DEMO_STACK_SIZE * 2); + + /* Initialize USBX Memory. */ + status = ux_system_initialize(memory_pointer, UX_DEMO_MEMORY_SIZE, UX_NULL, 0); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* The code below is required for installing the host portion of USBX. */ + status = ux_host_stack_initialize(UX_NULL); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* Register all the host class drivers for this USBX implementation. */ + status = ux_host_stack_class_register(_ux_system_host_class_dpump_name, ux_host_class_dpump_entry); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* Register all the USB host controllers available in this system */ + status = ux_host_stack_hcd_register(_ux_system_host_hcd_simulator_name, ux_hcd_sim_host_initialize,0,0); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* The code below is required for installing the device portion of USBX */ + 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); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* Set the parameters for callback when insertion/extraction of a Data Pump device. */ + parameter.ux_slave_class_dpump_instance_activate = tx_demo_instance_activate; + parameter.ux_slave_class_dpump_instance_deactivate = tx_demo_instance_deactivate; + + /* Initialize the device dpump class. The class is connected with interface 0 */ + status = _ux_device_stack_class_register(_ux_system_slave_class_dpump_name, _ux_device_class_dpump_entry, + 1, 0, ¶meter); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* Initialize the simulated device controller. */ + status = _ux_dcd_sim_slave_initialize(); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* Create the main host simulation thread. */ + status = tx_thread_create(&tx_demo_thread_host_simulation, "tx demo host simulation", tx_demo_thread_host_simulation_entry, 0, + stack_pointer, UX_DEMO_STACK_SIZE, + 20, 20, 1, TX_AUTO_START); + + /* Check for error. */ + if (status != TX_SUCCESS) + error_handler(); + + /* Create the main demo thread. */ + status = tx_thread_create(&tx_demo_thread_slave_simulation, "tx demo slave simulation", tx_demo_thread_slave_simulation_entry, 0, + stack_pointer + UX_DEMO_STACK_SIZE, UX_DEMO_STACK_SIZE, + 20, 20, 1, TX_AUTO_START); + + /* Check for error. */ + if (status != TX_SUCCESS) + error_handler(); +} + + +void tx_demo_thread_host_simulation_entry(ULONG arg) +{ + +UINT status; +ULONG actual_length; +UCHAR current_char; +UX_HOST_CLASS *class; + + + UX_PARAMETER_NOT_USED(arg); + + /* Find the main data pump container. */ + status = ux_host_stack_class_get(_ux_system_host_class_dpump_name, &class); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* We get the first instance of the data pump device. */ + do + { + + status = ux_host_stack_class_instance_get(class, 0, (VOID **) &dpump); + tx_thread_relinquish(); + } while (status != UX_SUCCESS); + + /* We still need to wait for the data pump status to be live. */ + while (dpump -> ux_host_class_dpump_state != UX_HOST_CLASS_INSTANCE_LIVE) + { + + tx_thread_relinquish(); + } + + /* At this point, the data pump class has been found. Now use the + data pump to send and receive data between the host and device. */ + + /* We start with a 'A' in buffer. */ + current_char = 'A'; + + while(1) + { + + /* Increment thread counter. */ + thread_0_counter++; + + /* Initialize the write buffer. */ + _ux_utility_memory_set(host_out_buffer, current_char, UX_HOST_CLASS_DPUMP_PACKET_SIZE); + + /* Increment the character in buffer. */ + current_char++; + + /* Check for upper alphabet limit. */ + if (current_char > 'Z') + current_char = 'A'; + + /* Write to the host Data Pump Bulk out endpoint. */ + status = _ux_host_class_dpump_write (dpump, host_out_buffer, UX_HOST_CLASS_DPUMP_PACKET_SIZE, &actual_length); + + /* Check for error. */ + if (status != UX_SUCCESS) + error_handler(); + + /* Verify that the status and the amount of data is correct. */ + if ((status != UX_SUCCESS) || actual_length != UX_HOST_CLASS_DPUMP_PACKET_SIZE) + return; + + /* Read to the Data Pump Bulk out endpoint. */ + status = _ux_host_class_dpump_read (dpump, host_in_buffer, UX_HOST_CLASS_DPUMP_PACKET_SIZE, &actual_length); + + /* Verify that the status and the amount of data is correct. */ + if ((status != UX_SUCCESS) || actual_length != UX_HOST_CLASS_DPUMP_PACKET_SIZE) + error_handler(); + + /* Relinquish to other thread. */ + tx_thread_relinquish(); + } +} + + +void tx_demo_thread_slave_simulation_entry(ULONG arg) +{ + +UINT status; +ULONG actual_length; + + + UX_PARAMETER_NOT_USED(arg); + + while(1) + { + + /* Ensure the dpump class on the device is still alive. */ + while (dpump_slave != UX_NULL) + { + + /* Increment thread counter. */ + thread_1_counter++; + + /* Read from the device data pump. */ + status = _ux_device_class_dpump_read(dpump_slave, slave_buffer, UX_HOST_CLASS_DPUMP_PACKET_SIZE, &actual_length); + + /* Verify that the status and the amount of data is correct. */ + if ((status != UX_SUCCESS) || actual_length != UX_HOST_CLASS_DPUMP_PACKET_SIZE) + error_handler(); + + /* Now write to the device data pump. */ + status = _ux_device_class_dpump_write(dpump_slave, slave_buffer, UX_HOST_CLASS_DPUMP_PACKET_SIZE, &actual_length); + + /* Verify that the status and the amount of data is correct. */ + if ((status != UX_SUCCESS) || actual_length != UX_HOST_CLASS_DPUMP_PACKET_SIZE) + error_handler(); + } + + /* Relinquish to other thread. */ + tx_thread_relinquish(); + } +} + +VOID tx_demo_instance_activate(VOID *dpump_instance) +{ + + /* Save the DPUMP instance. */ + dpump_slave = (UX_SLAVE_CLASS_DPUMP *) dpump_instance; +} + +VOID tx_demo_instance_deactivate(VOID *dpump_instance) +{ + + UX_PARAMETER_NOT_USED(dpump_instance); + + /* Reset the DPUMP instance. */ + dpump_slave = UX_NULL; +} + + +VOID error_handler(void) +{ + + /* Increment error counter. */ + error_counter++; + + while(1) + { + + /* Error - just spin here! Look at call tree in debugger + to see where the error occurred. */ + } +} + diff --git a/support/usbx_windows_host_files/CDC_ACM_Template.inf b/support/usbx_windows_host_files/CDC_ACM_Template.inf new file mode 100755 index 0000000..0d6a32a --- /dev/null +++ b/support/usbx_windows_host_files/CDC_ACM_Template.inf @@ -0,0 +1,65 @@ +; CDC_ACM.inf +; +; INF file for ExpressLogic simple CDC/ACM class +; +; 1) Replace VID/PID to your own in [MYCORP] section +; VID_vvvv&PID_pppp +; vvvv, pppp: four digit hex number of VID and PID, respectively +; +; 2) Replace 'MYCORP' to your own abbreviated one (without space) +; ex ExpressLogic +; - Replace all MYCORP in this inf file +; +; 3) Replace 'MYDEV000' to your device model number (without space) +; ex CDC ACM Example +; - Replace all MYDEV000 in this inf file +; +; 4) Edit the strings in [Strings] section +; + +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} + +Provider=%MYCORP% +LayoutFile=layout.inf +DriverVer=08/04/2004,5.1.2600.2180 + +[Manufacturer] +%MYCORP%=MYCORP + +[MYCORP] +%MYDEV000%= MYDEV000,USB\VID_9191&PID_0000 + + +[DestinationDirs] +FakeModemCopyFileSection=12 +DefaultDestDir = 12 + +[MYDEV000.NT] +include=mdmcpq.inf +CopyFiles=FakeModemCopyFileSection +AddReg=MYDEV000.NT.AddReg + +[MYDEV000.NT.Services] +AddService = usbser, 0x00000002, Service_Inst + +[Service_Inst] +DisplayName = %Serial.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\usbser.sys +LoadOrderGroup = Base + +[MYDEV000.NT.AddReg] +HKR,,NTMPDriver,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" +HKR,,PortSubClass,1,01 + +[Strings] +MYCORP = "ExpressLogic" ; Your company name +MYDEV000 = "CDC ACM example device" ; Device description +Serial.SvcDesc = "CDC ACM Driver" ; Device driver description \ No newline at end of file diff --git a/support/usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf b/support/usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf new file mode 100755 index 0000000..f30b28b --- /dev/null +++ b/support/usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf @@ -0,0 +1,66 @@ +; +; This INF file is for linking a USB device that has a specific VID/PID to Windows serial device driver (usbser.sys). +; Where to modify the VID/PID values and various strings is commented below. +; For just getting a device to work, you should only need to modify the VID/PID values. +; + +; +; Modify these strings to change the names displayed for the device. +; + +[Strings] +DriverPackageDisplayName="Express Logic Drivers" +ManufacturerName="Express Logic" +ServiceName="USB RS-232 Emulation Driver" +DeviceName="Express Logic CDC-ACM Device" + +[DefaultInstall] +CopyINF=cdc_acm_elogic.inf + +[Version] +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Signature="$Windows NT$" +Provider=%ManufacturerName% +DriverVer=04/01/2014,1.3.0.0 +DriverPackageDisplayName=%DriverPackageDisplayName% + +[Manufacturer] +%ManufacturerName%=DeviceList, NTamd64 + +[DestinationDirs] +FakeModemCopyFileSection=12 +DefaultDestDir=12 + +; +; Change the VID/PID in the following strings 'USB\VID_xxxx&PID_yyyyy&MI_00' to match your USB device. +; For example, if the VID is '8484' and the PID is '0000', the string should be: USB\VID_8484&PID_0000&MI_00 +; + +[DeviceList] +%DeviceName%=DriverInstall, USB\VID_xxxx&PID_yyyy&MI_00 + +[DeviceList.NTamd64] +%DeviceName%=DriverInstall, USB\VID_xxxx&PID_yyyy&MI_00 + +[DriverInstall] +include=mdmcpq.inf,usb.inf +CopyFiles = FakeModemCopyFileSection +AddReg=DriverAddReg + +[DriverAddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.Services] +include=mdmcpq.inf +AddService=usbser, 0x00000002, DriverService + +[DriverService] +DisplayName=%ServiceName% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys +LoadOrderGroup=Base diff --git a/support/usbx_windows_host_files/CDC_Composite_Template.inf b/support/usbx_windows_host_files/CDC_Composite_Template.inf new file mode 100755 index 0000000..1114cef --- /dev/null +++ b/support/usbx_windows_host_files/CDC_Composite_Template.inf @@ -0,0 +1,68 @@ +; +; Template INF for a USB CDC Device +; Copyright (c) ExpressLogic +; +[Version] +DriverVer =10/06/1999,5.00.2157.0 +LayoutFile=Layout.inf +Signature="$CHICAGO$" +Class=Modem +ClassGUID={4D36E96D-E325-11CE-BFC1-08002BE10318} +Provider=%Mfg% + + + +[Manufacturer] +%Mfg% = Models + +[ControlFlags] +ExcludeFromSelect=USB\VID_8080&PID_0000&MI_00 + +[DestinationDirs] +FakeModemCopyFileSection=12 +DefaultDestDir=12 + +[Models] +%ELOGIC% = ELOGIC,USB\VID_8080&PID_0000&MI_00 + +[ELOGIC.NT] +include=usb.inf +CopyFiles=FakeModemCopyFileSection +AddReg=USB,ATMEL.resp,ELOGIC.AddReg + +[ELOGIC.NT.Services] +AddService=usbser, 0x00000000, LowerFilter_Service_Inst + +[ELOGIC.NT.HW] +AddReg=LowerFilterAddReg + +[LowerFilterAddReg] +HKR,,"LowerFilters",0x00010000,"usbser" + +[LowerFilter_Service_Inst] +DisplayName=%USBFilterString% +ServiceType= 1 +StartType = 3 +ErrorControl = 0 +ServiceBinary = %12%\usbser.sys + +[FakeModemCopyFileSection] +usbser.sys,,,0x20 + +[Strings] +Mfg = "Express Logic, Inc." +ELOGIC = "Express Logic USB serial emulation" +USBFilterString ="Express Logic USB serial emulation" + +[USB] +HKR,,FriendlyDriver,,Unimodem.vxd +HKR,,DevLoader,,*vcomm +HKR,,ConfigDialog,,serialui.dll +HKR,,AttachedTo,,COM5 +HKR,,EnumPropPages,,"serialui.dll,EnumPropPages" +HKR,,DeviceType, 0, 01 ; +HKR,,PortSubClass,1,02 + +[ELOGIC.AddReg] ;Express Logic USB serial emulation +HKR,, Properties, 1, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,c2,01,00, 00,C2,01,00 + diff --git a/support/usbx_windows_host_files/CDC_ECM_Template.inf b/support/usbx_windows_host_files/CDC_ECM_Template.inf new file mode 100755 index 0000000..2cf7066 --- /dev/null +++ b/support/usbx_windows_host_files/CDC_ECM_Template.inf @@ -0,0 +1,182 @@ +; +; Copyright (c) 2009-2010 Thesycon GmbH +; +; USB CDC ECM Driver setup information file DEMO! +; +; This file supports: +; Windows XP +; Windows Server 2003 +; Windows Vista +; + +;****************************************************************************** +; Version Section +;------------------------------------------------------------------------------ +[Version] +Signature="$Windows NT$" +DriverVer=03/19/2010,1.10.0.0 +Provider=%S_Provider% +CatalogFile=%S_DriverName%.cat + +; private setup class +Class=net +ClassGUID={4d36e972-e325-11ce-bfc1-08002be10318} + + + +;****************************************************************************** +; Manufacturer +;------------------------------------------------------------------------------ +[Manufacturer] +%S_Mfg%=_Models + + +;****************************************************************************** +; Models Section +;------------------------------------------------------------------------------ +[_Models] + +; enter your VID (VVVV) and PID (PPPP) here +%S_DeviceDesc%=_Install,USB\VID_04b4&PID_1128 +; if a multi interface driver is installed enter also the interface number (II) +; %S_DeviceDesc1%=Install,USB\VID_VVVV&PID_PPPP&MI_II +; for more information have a look at the documentation + + +;****************************************************************************** +; Control Flags Sections +;------------------------------------------------------------------------------ +[ControlFlags] +ExcludeFromSelect=* + + +;****************************************************************************** +; Install Sections +;------------------------------------------------------------------------------ +[_Install.NT] +; NCF_PHYSICAL + NCF_HAS_UI +Characteristics = 0x84 +; bus type = PnP Bus: 15 +BusType = 15 +; driver version under XP +DriverVer = 03/19/2010,1.10.0.0 +AddReg = _AddReg_SW +CopyFiles=_CopyFiles_sys + +[_Install.NT.Services] +AddService = %S_ServiceName%, 0x00000002, _AddService, _EventLog + + +[_AddService] +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; MANUAL +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\%S_DriverName%.sys +AddReg=_AddReg_Service + +[_EventLog] +AddReg=_EventLog_AddReg + +[_EventLog_AddReg] +HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\%S_DriverName%.sys" +HKR,,TypesSupported, %REG_DWORD%, 7 + + +;****************************************************************************** +; Registry sections +;------------------------------------------------------------------------------ +[_AddReg_SW] +HKR, Ndi, Service, 0, %S_ServiceName% +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +; turn of the power management feature +; HKR,,PnPCapabilities,%REG_DWORD%, 0x38 +HKR,,RxBuffers,%REG_DWORD%, 4 +HKR,,TxBuffers,%REG_DWORD%, 4 + +; use this if the device does not report an MAC address +; the device overwrites this value +;HKR,,NetworkAddress,%REG_SZ%, "ACDE48020100" +; set the default media state, 0 is disconnected, 1 is connected +HKR,,DefaultMediaState,%REG_DWORD%, 1 + +; set this key to one of the device ned a one byte termination instead of +; a zero length packet +HKR,,OneByteTermination,%REG_DWORD%, 0 + +; the configuration index for the device +; the Linux gadget may expose the ECM interface on index 1 +HKR,,ConfigurationIndex,%REG_DWORD%, 0 + +; set this to 1 to force the driver to send a clear feature endpoint halt on start +HKR,,SendClearEpHaltOnStart,%REG_DWORD%, 0 + +[_AddReg_Service] +HKR,Parameters,,, + + +;****************************************************************************** +; Copy file sections +;------------------------------------------------------------------------------ +[_CopyFiles_sys] +; Note: no string variable possible in this section ! +; Use COPYFLG_NOVERSIONCHECK for each file to suppress pop-up dialogs if newer files are overwritten. +cdcecm_demo.sys,,,0x00000004 + + +;****************************************************************************** +; Destination Directories +;------------------------------------------------------------------------------ +[DestinationDirs] +DefaultDestDir = 12 ; %windir%\system32\drivers +_CopyFiles_sys = 12 + +;****************************************************************************** +; Disk Layout +;------------------------------------------------------------------------------ +[SourceDisksNames.x86] +1=%S_DiskName%,,0 + +[SourceDisksFiles.x86] +cdcecm_demo.sys=1 +; note: no string variable possible in this section ! + + +;****************************************************************************** +; Strings +;------------------------------------------------------------------------------ +[Strings] +; +; Non-Localizable Strings, DO NOT MODIFY! +; +REG_SZ = 0x00000000 +REG_MULTI_SZ = 0x00010000 +REG_EXPAND_SZ = 0x00020000 +REG_BINARY = 0x00000001 +REG_DWORD = 0x00010001 + + +; +; Localizable Strings, modify as required +; +; provider +S_Provider="ExpressLogic" +S_Mfg="ExpressLogic" + + +; disk name +S_DiskName="CdcEcm driver disk" + +; device description +S_DeviceDesc="ExpressLogic CDC ECM" + +; driver name +S_DriverName="cdcecm_demo" + +; service name +S_ServiceName="cdcecm" + + + +;;;;;;;;;;;;;;;;;;;;;; EOF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/support/usbx_windows_host_files/RNDIS_Template.inf b/support/usbx_windows_host_files/RNDIS_Template.inf new file mode 100755 index 0000000..2d89a7a --- /dev/null +++ b/support/usbx_windows_host_files/RNDIS_Template.inf @@ -0,0 +1,124 @@ +; +; Template INF for a USB Remote NDIS Device +; Copyright (c) ExpressLogic +; + +[Version] +Signature = "$Windows NT$" +Class = Net +ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} +Provider = %ELogic% +DriverVer = mm/dd/yyyy,x.y.v.z +CatalogFile = ELogic.cat + +[Manufacturer] +%ELogic% = ELogicDevices,NT.5.1 + +[ELogicDevices] +%ELogicDevice% = RNDIS, USB\VID_3939&PID_0000 + +[ELogicDevices.NT.5.1] +%ELogicDevice% = RNDIS.NT.5.1, USB\VID_3939&PID_0000 + +[ControlFlags] +ExcludeFromSelect=* + +; Windows 2000 specific sections --------------------------------- + +[RNDIS.NT] +Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI +BusType = 15 +DriverVer = 05/17/2008,0.0.0.0 +AddReg = RNDIS_AddReg_NT, RNDIS_AddReg_WIN2K +CopyFiles = RNDIS_CopyFiles_NT + +; DO NOT MODIFY THE SERVICE NAME +[RNDIS.NT.Services] +AddService = USB_RNDISY, 2, RNDIS_ServiceInst_NT, RNDIS_EventLog + +[RNDIS_CopyFiles_NT] +; no rename of files on Windows 2000, use the 'y' names as is +usb8023y.sys, , , 0 +rndismpy.sys, , , 0 + +[RNDIS_ServiceInst_NT] +DisplayName = %ServiceDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\usb8023y.sys +LoadOrderGroup = NDIS +AddReg = RNDIS_WMI_AddReg_NT + +[RNDIS_WMI_AddReg_NT] +HKR, , MofImagePath, 0x00020000, "System32\drivers\rndismpy.sys" + +; Windows XP specific sections ----------------------------------- + +[RNDIS.NT.5.1] +Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI +BusType = 15 +DriverVer = 05/17/2008,0.0.0.0 +AddReg = RNDIS_AddReg_XP +include = netrndis.inf +needs = Usb_Rndis.ndi + +; no copyfiles - the files are already in place + +[RNDIS.NT.5.1.Services] +include = netrndis.inf +needs = Usb_Rndis.ndi.Services + +; Windows 2000 sections + +; DO NOT MODIFY ServiceName +[RNDIS_AddReg_NT] +HKR, Ndi, Service, 0, "USB_RNDISY" +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +[RNDIS_AddReg_WIN2K] +HKR, , ReclaimRecv, 0x00010001, 1 +HKR, NDI\params\NetworkAddress, ParamDesc, 0, %NetworkAddress% +HKR, NDI\params\NetworkAddress, type, 0, "edit" +HKR, NDI\params\NetworkAddress, LimitText, 0, "12" +HKR, NDI\params\NetworkAddress, UpperCase, 0, "1" +HKR, NDI\params\NetworkAddress, default, 0, " " +HKR, NDI\params\NetworkAddress, optional, 0, "1" + +[RNDIS_EventLog] +AddReg = RNDIS_EventLog_AddReg + +[RNDIS_EventLog_AddReg] +HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll" +HKR, , TypesSupported, 0x00010001, 7 + +; An optional Property to demonstrate adding advanced properties on Windows XP +[RNDIS_AddReg_XP] +HKR, NDI\params\XPProperty, ParamDesc, 0, %Sample_Property% +HKR, NDI\params\XPProperty, type, 0, "edit" +HKR, NDI\params\XPProperty, LimitText, 0, "12" +HKR, NDI\params\XPProperty, UpperCase, 0, "1" +HKR, NDI\params\XPProperty, default, 0, " " +HKR, NDI\params\XPProperty, optional, 0, "1" + + +[SourceDisksNames] +1=%SourceDisk%,,1 + +[SourceDisksFiles] +usb8023y.sys=1 +rndismpy.sys=1 + +[DestinationDirs] +RNDIS_CopyFiles_NT = 12 + +; DO NOT CHANGE ServiceDisplayName +[Strings] +ServiceDisplayName = "USB Remote NDIS Y Network Device Driver" +Sample_Property = "Sample XP property" +NetworkAddress = "Network Address" +ELogic = "ExpressLogic Inc." +ELogicDevice = "ExpressLogic USB Remote NDIS Network Device" +SourceDisk = "ExpressLogic USB Network Driver Install Disk" + diff --git a/usbx_windows_host_files/CDC_ACM_Template.inf b/usbx_windows_host_files/CDC_ACM_Template.inf new file mode 100644 index 0000000..0d6a32a --- /dev/null +++ b/usbx_windows_host_files/CDC_ACM_Template.inf @@ -0,0 +1,65 @@ +; CDC_ACM.inf +; +; INF file for ExpressLogic simple CDC/ACM class +; +; 1) Replace VID/PID to your own in [MYCORP] section +; VID_vvvv&PID_pppp +; vvvv, pppp: four digit hex number of VID and PID, respectively +; +; 2) Replace 'MYCORP' to your own abbreviated one (without space) +; ex ExpressLogic +; - Replace all MYCORP in this inf file +; +; 3) Replace 'MYDEV000' to your device model number (without space) +; ex CDC ACM Example +; - Replace all MYDEV000 in this inf file +; +; 4) Edit the strings in [Strings] section +; + +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} + +Provider=%MYCORP% +LayoutFile=layout.inf +DriverVer=08/04/2004,5.1.2600.2180 + +[Manufacturer] +%MYCORP%=MYCORP + +[MYCORP] +%MYDEV000%= MYDEV000,USB\VID_9191&PID_0000 + + +[DestinationDirs] +FakeModemCopyFileSection=12 +DefaultDestDir = 12 + +[MYDEV000.NT] +include=mdmcpq.inf +CopyFiles=FakeModemCopyFileSection +AddReg=MYDEV000.NT.AddReg + +[MYDEV000.NT.Services] +AddService = usbser, 0x00000002, Service_Inst + +[Service_Inst] +DisplayName = %Serial.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\usbser.sys +LoadOrderGroup = Base + +[MYDEV000.NT.AddReg] +HKR,,NTMPDriver,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" +HKR,,PortSubClass,1,01 + +[Strings] +MYCORP = "ExpressLogic" ; Your company name +MYDEV000 = "CDC ACM example device" ; Device description +Serial.SvcDesc = "CDC ACM Driver" ; Device driver description \ No newline at end of file diff --git a/usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf b/usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf new file mode 100644 index 0000000..f30b28b --- /dev/null +++ b/usbx_windows_host_files/CDC_ACM_Template_Win7_64bit.inf @@ -0,0 +1,66 @@ +; +; This INF file is for linking a USB device that has a specific VID/PID to Windows serial device driver (usbser.sys). +; Where to modify the VID/PID values and various strings is commented below. +; For just getting a device to work, you should only need to modify the VID/PID values. +; + +; +; Modify these strings to change the names displayed for the device. +; + +[Strings] +DriverPackageDisplayName="Express Logic Drivers" +ManufacturerName="Express Logic" +ServiceName="USB RS-232 Emulation Driver" +DeviceName="Express Logic CDC-ACM Device" + +[DefaultInstall] +CopyINF=cdc_acm_elogic.inf + +[Version] +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Signature="$Windows NT$" +Provider=%ManufacturerName% +DriverVer=04/01/2014,1.3.0.0 +DriverPackageDisplayName=%DriverPackageDisplayName% + +[Manufacturer] +%ManufacturerName%=DeviceList, NTamd64 + +[DestinationDirs] +FakeModemCopyFileSection=12 +DefaultDestDir=12 + +; +; Change the VID/PID in the following strings 'USB\VID_xxxx&PID_yyyyy&MI_00' to match your USB device. +; For example, if the VID is '8484' and the PID is '0000', the string should be: USB\VID_8484&PID_0000&MI_00 +; + +[DeviceList] +%DeviceName%=DriverInstall, USB\VID_xxxx&PID_yyyy&MI_00 + +[DeviceList.NTamd64] +%DeviceName%=DriverInstall, USB\VID_xxxx&PID_yyyy&MI_00 + +[DriverInstall] +include=mdmcpq.inf,usb.inf +CopyFiles = FakeModemCopyFileSection +AddReg=DriverAddReg + +[DriverAddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.Services] +include=mdmcpq.inf +AddService=usbser, 0x00000002, DriverService + +[DriverService] +DisplayName=%ServiceName% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys +LoadOrderGroup=Base diff --git a/usbx_windows_host_files/CDC_Composite_Template.inf b/usbx_windows_host_files/CDC_Composite_Template.inf new file mode 100644 index 0000000..1114cef --- /dev/null +++ b/usbx_windows_host_files/CDC_Composite_Template.inf @@ -0,0 +1,68 @@ +; +; Template INF for a USB CDC Device +; Copyright (c) ExpressLogic +; +[Version] +DriverVer =10/06/1999,5.00.2157.0 +LayoutFile=Layout.inf +Signature="$CHICAGO$" +Class=Modem +ClassGUID={4D36E96D-E325-11CE-BFC1-08002BE10318} +Provider=%Mfg% + + + +[Manufacturer] +%Mfg% = Models + +[ControlFlags] +ExcludeFromSelect=USB\VID_8080&PID_0000&MI_00 + +[DestinationDirs] +FakeModemCopyFileSection=12 +DefaultDestDir=12 + +[Models] +%ELOGIC% = ELOGIC,USB\VID_8080&PID_0000&MI_00 + +[ELOGIC.NT] +include=usb.inf +CopyFiles=FakeModemCopyFileSection +AddReg=USB,ATMEL.resp,ELOGIC.AddReg + +[ELOGIC.NT.Services] +AddService=usbser, 0x00000000, LowerFilter_Service_Inst + +[ELOGIC.NT.HW] +AddReg=LowerFilterAddReg + +[LowerFilterAddReg] +HKR,,"LowerFilters",0x00010000,"usbser" + +[LowerFilter_Service_Inst] +DisplayName=%USBFilterString% +ServiceType= 1 +StartType = 3 +ErrorControl = 0 +ServiceBinary = %12%\usbser.sys + +[FakeModemCopyFileSection] +usbser.sys,,,0x20 + +[Strings] +Mfg = "Express Logic, Inc." +ELOGIC = "Express Logic USB serial emulation" +USBFilterString ="Express Logic USB serial emulation" + +[USB] +HKR,,FriendlyDriver,,Unimodem.vxd +HKR,,DevLoader,,*vcomm +HKR,,ConfigDialog,,serialui.dll +HKR,,AttachedTo,,COM5 +HKR,,EnumPropPages,,"serialui.dll,EnumPropPages" +HKR,,DeviceType, 0, 01 ; +HKR,,PortSubClass,1,02 + +[ELOGIC.AddReg] ;Express Logic USB serial emulation +HKR,, Properties, 1, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,c2,01,00, 00,C2,01,00 + diff --git a/usbx_windows_host_files/CDC_ECM_Template.inf b/usbx_windows_host_files/CDC_ECM_Template.inf new file mode 100644 index 0000000..2cf7066 --- /dev/null +++ b/usbx_windows_host_files/CDC_ECM_Template.inf @@ -0,0 +1,182 @@ +; +; Copyright (c) 2009-2010 Thesycon GmbH +; +; USB CDC ECM Driver setup information file DEMO! +; +; This file supports: +; Windows XP +; Windows Server 2003 +; Windows Vista +; + +;****************************************************************************** +; Version Section +;------------------------------------------------------------------------------ +[Version] +Signature="$Windows NT$" +DriverVer=03/19/2010,1.10.0.0 +Provider=%S_Provider% +CatalogFile=%S_DriverName%.cat + +; private setup class +Class=net +ClassGUID={4d36e972-e325-11ce-bfc1-08002be10318} + + + +;****************************************************************************** +; Manufacturer +;------------------------------------------------------------------------------ +[Manufacturer] +%S_Mfg%=_Models + + +;****************************************************************************** +; Models Section +;------------------------------------------------------------------------------ +[_Models] + +; enter your VID (VVVV) and PID (PPPP) here +%S_DeviceDesc%=_Install,USB\VID_04b4&PID_1128 +; if a multi interface driver is installed enter also the interface number (II) +; %S_DeviceDesc1%=Install,USB\VID_VVVV&PID_PPPP&MI_II +; for more information have a look at the documentation + + +;****************************************************************************** +; Control Flags Sections +;------------------------------------------------------------------------------ +[ControlFlags] +ExcludeFromSelect=* + + +;****************************************************************************** +; Install Sections +;------------------------------------------------------------------------------ +[_Install.NT] +; NCF_PHYSICAL + NCF_HAS_UI +Characteristics = 0x84 +; bus type = PnP Bus: 15 +BusType = 15 +; driver version under XP +DriverVer = 03/19/2010,1.10.0.0 +AddReg = _AddReg_SW +CopyFiles=_CopyFiles_sys + +[_Install.NT.Services] +AddService = %S_ServiceName%, 0x00000002, _AddService, _EventLog + + +[_AddService] +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; MANUAL +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\%S_DriverName%.sys +AddReg=_AddReg_Service + +[_EventLog] +AddReg=_EventLog_AddReg + +[_EventLog_AddReg] +HKR,,EventMessageFile,%REG_EXPAND_SZ%,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\%S_DriverName%.sys" +HKR,,TypesSupported, %REG_DWORD%, 7 + + +;****************************************************************************** +; Registry sections +;------------------------------------------------------------------------------ +[_AddReg_SW] +HKR, Ndi, Service, 0, %S_ServiceName% +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +; turn of the power management feature +; HKR,,PnPCapabilities,%REG_DWORD%, 0x38 +HKR,,RxBuffers,%REG_DWORD%, 4 +HKR,,TxBuffers,%REG_DWORD%, 4 + +; use this if the device does not report an MAC address +; the device overwrites this value +;HKR,,NetworkAddress,%REG_SZ%, "ACDE48020100" +; set the default media state, 0 is disconnected, 1 is connected +HKR,,DefaultMediaState,%REG_DWORD%, 1 + +; set this key to one of the device ned a one byte termination instead of +; a zero length packet +HKR,,OneByteTermination,%REG_DWORD%, 0 + +; the configuration index for the device +; the Linux gadget may expose the ECM interface on index 1 +HKR,,ConfigurationIndex,%REG_DWORD%, 0 + +; set this to 1 to force the driver to send a clear feature endpoint halt on start +HKR,,SendClearEpHaltOnStart,%REG_DWORD%, 0 + +[_AddReg_Service] +HKR,Parameters,,, + + +;****************************************************************************** +; Copy file sections +;------------------------------------------------------------------------------ +[_CopyFiles_sys] +; Note: no string variable possible in this section ! +; Use COPYFLG_NOVERSIONCHECK for each file to suppress pop-up dialogs if newer files are overwritten. +cdcecm_demo.sys,,,0x00000004 + + +;****************************************************************************** +; Destination Directories +;------------------------------------------------------------------------------ +[DestinationDirs] +DefaultDestDir = 12 ; %windir%\system32\drivers +_CopyFiles_sys = 12 + +;****************************************************************************** +; Disk Layout +;------------------------------------------------------------------------------ +[SourceDisksNames.x86] +1=%S_DiskName%,,0 + +[SourceDisksFiles.x86] +cdcecm_demo.sys=1 +; note: no string variable possible in this section ! + + +;****************************************************************************** +; Strings +;------------------------------------------------------------------------------ +[Strings] +; +; Non-Localizable Strings, DO NOT MODIFY! +; +REG_SZ = 0x00000000 +REG_MULTI_SZ = 0x00010000 +REG_EXPAND_SZ = 0x00020000 +REG_BINARY = 0x00000001 +REG_DWORD = 0x00010001 + + +; +; Localizable Strings, modify as required +; +; provider +S_Provider="ExpressLogic" +S_Mfg="ExpressLogic" + + +; disk name +S_DiskName="CdcEcm driver disk" + +; device description +S_DeviceDesc="ExpressLogic CDC ECM" + +; driver name +S_DriverName="cdcecm_demo" + +; service name +S_ServiceName="cdcecm" + + + +;;;;;;;;;;;;;;;;;;;;;; EOF ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/usbx_windows_host_files/RNDIS_Template.inf b/usbx_windows_host_files/RNDIS_Template.inf new file mode 100644 index 0000000..2d89a7a --- /dev/null +++ b/usbx_windows_host_files/RNDIS_Template.inf @@ -0,0 +1,124 @@ +; +; Template INF for a USB Remote NDIS Device +; Copyright (c) ExpressLogic +; + +[Version] +Signature = "$Windows NT$" +Class = Net +ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} +Provider = %ELogic% +DriverVer = mm/dd/yyyy,x.y.v.z +CatalogFile = ELogic.cat + +[Manufacturer] +%ELogic% = ELogicDevices,NT.5.1 + +[ELogicDevices] +%ELogicDevice% = RNDIS, USB\VID_3939&PID_0000 + +[ELogicDevices.NT.5.1] +%ELogicDevice% = RNDIS.NT.5.1, USB\VID_3939&PID_0000 + +[ControlFlags] +ExcludeFromSelect=* + +; Windows 2000 specific sections --------------------------------- + +[RNDIS.NT] +Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI +BusType = 15 +DriverVer = 05/17/2008,0.0.0.0 +AddReg = RNDIS_AddReg_NT, RNDIS_AddReg_WIN2K +CopyFiles = RNDIS_CopyFiles_NT + +; DO NOT MODIFY THE SERVICE NAME +[RNDIS.NT.Services] +AddService = USB_RNDISY, 2, RNDIS_ServiceInst_NT, RNDIS_EventLog + +[RNDIS_CopyFiles_NT] +; no rename of files on Windows 2000, use the 'y' names as is +usb8023y.sys, , , 0 +rndismpy.sys, , , 0 + +[RNDIS_ServiceInst_NT] +DisplayName = %ServiceDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\usb8023y.sys +LoadOrderGroup = NDIS +AddReg = RNDIS_WMI_AddReg_NT + +[RNDIS_WMI_AddReg_NT] +HKR, , MofImagePath, 0x00020000, "System32\drivers\rndismpy.sys" + +; Windows XP specific sections ----------------------------------- + +[RNDIS.NT.5.1] +Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI +BusType = 15 +DriverVer = 05/17/2008,0.0.0.0 +AddReg = RNDIS_AddReg_XP +include = netrndis.inf +needs = Usb_Rndis.ndi + +; no copyfiles - the files are already in place + +[RNDIS.NT.5.1.Services] +include = netrndis.inf +needs = Usb_Rndis.ndi.Services + +; Windows 2000 sections + +; DO NOT MODIFY ServiceName +[RNDIS_AddReg_NT] +HKR, Ndi, Service, 0, "USB_RNDISY" +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +[RNDIS_AddReg_WIN2K] +HKR, , ReclaimRecv, 0x00010001, 1 +HKR, NDI\params\NetworkAddress, ParamDesc, 0, %NetworkAddress% +HKR, NDI\params\NetworkAddress, type, 0, "edit" +HKR, NDI\params\NetworkAddress, LimitText, 0, "12" +HKR, NDI\params\NetworkAddress, UpperCase, 0, "1" +HKR, NDI\params\NetworkAddress, default, 0, " " +HKR, NDI\params\NetworkAddress, optional, 0, "1" + +[RNDIS_EventLog] +AddReg = RNDIS_EventLog_AddReg + +[RNDIS_EventLog_AddReg] +HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll" +HKR, , TypesSupported, 0x00010001, 7 + +; An optional Property to demonstrate adding advanced properties on Windows XP +[RNDIS_AddReg_XP] +HKR, NDI\params\XPProperty, ParamDesc, 0, %Sample_Property% +HKR, NDI\params\XPProperty, type, 0, "edit" +HKR, NDI\params\XPProperty, LimitText, 0, "12" +HKR, NDI\params\XPProperty, UpperCase, 0, "1" +HKR, NDI\params\XPProperty, default, 0, " " +HKR, NDI\params\XPProperty, optional, 0, "1" + + +[SourceDisksNames] +1=%SourceDisk%,,1 + +[SourceDisksFiles] +usb8023y.sys=1 +rndismpy.sys=1 + +[DestinationDirs] +RNDIS_CopyFiles_NT = 12 + +; DO NOT CHANGE ServiceDisplayName +[Strings] +ServiceDisplayName = "USB Remote NDIS Y Network Device Driver" +Sample_Property = "Sample XP property" +NetworkAddress = "Network Address" +ELogic = "ExpressLogic Inc." +ELogicDevice = "ExpressLogic USB Remote NDIS Network Device" +SourceDisk = "ExpressLogic USB Network Driver Install Disk" +