mirror of
https://github.com/Architeuthis-Flux/Jumperless.git
synced 2024-11-27 17:00:55 +01:00
Added Rotary Encoder Mode and multiple saved slots
This commit is contained in:
parent
54f73c173a
commit
bbd5d2bc1f
@ -6,14 +6,14 @@
|
|||||||
"apply_defaults_to_fp_fields": false,
|
"apply_defaults_to_fp_fields": false,
|
||||||
"apply_defaults_to_fp_shapes": false,
|
"apply_defaults_to_fp_shapes": false,
|
||||||
"apply_defaults_to_fp_text": false,
|
"apply_defaults_to_fp_text": false,
|
||||||
"board_outline_line_width": 0.09999999999999999,
|
"board_outline_line_width": 0.1,
|
||||||
"copper_line_width": 0.19999999999999998,
|
"copper_line_width": 0.2,
|
||||||
"copper_text_italic": false,
|
"copper_text_italic": false,
|
||||||
"copper_text_size_h": 1.5,
|
"copper_text_size_h": 1.5,
|
||||||
"copper_text_size_v": 1.5,
|
"copper_text_size_v": 1.5,
|
||||||
"copper_text_thickness": 0.3,
|
"copper_text_thickness": 0.3,
|
||||||
"copper_text_upright": false,
|
"copper_text_upright": false,
|
||||||
"courtyard_line_width": 0.049999999999999996,
|
"courtyard_line_width": 0.05,
|
||||||
"dimension_precision": 4,
|
"dimension_precision": 4,
|
||||||
"dimension_units": 3,
|
"dimension_units": 3,
|
||||||
"dimensions": {
|
"dimensions": {
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"text_position": 0,
|
"text_position": 0,
|
||||||
"units_format": 1
|
"units_format": 1
|
||||||
},
|
},
|
||||||
"fab_line_width": 0.09999999999999999,
|
"fab_line_width": 0.1,
|
||||||
"fab_text_italic": false,
|
"fab_text_italic": false,
|
||||||
"fab_text_size_h": 1.0,
|
"fab_text_size_h": 1.0,
|
||||||
"fab_text_size_v": 1.0,
|
"fab_text_size_v": 1.0,
|
||||||
@ -120,16 +120,16 @@
|
|||||||
"min_copper_edge_clearance": 0.25,
|
"min_copper_edge_clearance": 0.25,
|
||||||
"min_hole_clearance": 0.15,
|
"min_hole_clearance": 0.15,
|
||||||
"min_hole_to_hole": 0.25,
|
"min_hole_to_hole": 0.25,
|
||||||
"min_microvia_diameter": 0.19999999999999998,
|
"min_microvia_diameter": 0.2,
|
||||||
"min_microvia_drill": 0.09999999999999999,
|
"min_microvia_drill": 0.1,
|
||||||
"min_resolved_spokes": 1,
|
"min_resolved_spokes": 1,
|
||||||
"min_silk_clearance": 0.0,
|
"min_silk_clearance": 0.0,
|
||||||
"min_text_height": 0.7999999999999999,
|
"min_text_height": 0.8,
|
||||||
"min_text_thickness": 0.12,
|
"min_text_thickness": 0.12,
|
||||||
"min_through_hole_diameter": 0.15,
|
"min_through_hole_diameter": 0.15,
|
||||||
"min_track_width": 0.08,
|
"min_track_width": 0.08,
|
||||||
"min_via_annular_width": 0.075,
|
"min_via_annular_width": 0.075,
|
||||||
"min_via_diameter": 0.19999999999999998,
|
"min_via_diameter": 0.2,
|
||||||
"solder_mask_clearance": 0.0,
|
"solder_mask_clearance": 0.0,
|
||||||
"solder_mask_min_width": 0.0,
|
"solder_mask_min_width": 0.0,
|
||||||
"solder_mask_to_copper_clearance": 0.005,
|
"solder_mask_to_copper_clearance": 0.005,
|
||||||
|
@ -1 +0,0 @@
|
|||||||
{"hostname":"Kevins-MBP-2","username":"kevinsanto"}
|
|
@ -0,0 +1 @@
|
|||||||
|
{"hostname":"Kevins-MBP","username":"kevinsanto"}
|
@ -37,7 +37,6 @@
|
|||||||
8,
|
8,
|
||||||
9,
|
9,
|
||||||
10,
|
10,
|
||||||
11,
|
|
||||||
12,
|
12,
|
||||||
13,
|
13,
|
||||||
15,
|
15,
|
||||||
@ -67,6 +66,12 @@
|
|||||||
"visible_layers": "0001030_80000001",
|
"visible_layers": "0001030_80000001",
|
||||||
"zone_display_mode": 0
|
"zone_display_mode": 0
|
||||||
},
|
},
|
||||||
|
"git": {
|
||||||
|
"repo_password": "",
|
||||||
|
"repo_type": "",
|
||||||
|
"repo_username": "",
|
||||||
|
"ssh_key": ""
|
||||||
|
},
|
||||||
"meta": {
|
"meta": {
|
||||||
"filename": "LEDboardRev3.kicad_prl",
|
"filename": "LEDboardRev3.kicad_prl",
|
||||||
"version": 3
|
"version": 3
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
"3dviewports": [],
|
"3dviewports": [],
|
||||||
"design_settings": {
|
"design_settings": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"apply_defaults_to_fp_fields": false,
|
||||||
|
"apply_defaults_to_fp_shapes": false,
|
||||||
|
"apply_defaults_to_fp_text": false,
|
||||||
"board_outline_line_width": 0.09999999999999999,
|
"board_outline_line_width": 0.09999999999999999,
|
||||||
"copper_line_width": 0.19999999999999998,
|
"copper_line_width": 0.19999999999999998,
|
||||||
"copper_text_italic": false,
|
"copper_text_italic": false,
|
||||||
@ -72,6 +75,7 @@
|
|||||||
"duplicate_footprints": "warning",
|
"duplicate_footprints": "warning",
|
||||||
"extra_footprint": "warning",
|
"extra_footprint": "warning",
|
||||||
"footprint": "error",
|
"footprint": "error",
|
||||||
|
"footprint_symbol_mismatch": "warning",
|
||||||
"footprint_type_mismatch": "ignore",
|
"footprint_type_mismatch": "ignore",
|
||||||
"hole_clearance": "error",
|
"hole_clearance": "error",
|
||||||
"hole_near_hole": "error",
|
"hole_near_hole": "error",
|
||||||
@ -133,9 +137,6 @@
|
|||||||
},
|
},
|
||||||
"teardrop_options": [
|
"teardrop_options": [
|
||||||
{
|
{
|
||||||
"td_allow_use_two_tracks": true,
|
|
||||||
"td_curve_segcount": 5,
|
|
||||||
"td_on_pad_in_zone": false,
|
|
||||||
"td_onpadsmd": true,
|
"td_onpadsmd": true,
|
||||||
"td_onroundshapesonly": false,
|
"td_onroundshapesonly": false,
|
||||||
"td_ontrackend": false,
|
"td_ontrackend": false,
|
||||||
@ -144,29 +145,35 @@
|
|||||||
],
|
],
|
||||||
"teardrop_parameters": [
|
"teardrop_parameters": [
|
||||||
{
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
"td_curve_segcount": 0,
|
"td_curve_segcount": 0,
|
||||||
"td_height_ratio": 1.0,
|
"td_height_ratio": 1.0,
|
||||||
"td_length_ratio": 0.5,
|
"td_length_ratio": 0.5,
|
||||||
"td_maxheight": 2.0,
|
"td_maxheight": 2.0,
|
||||||
"td_maxlen": 1.0,
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
"td_target_name": "td_round_shape",
|
"td_target_name": "td_round_shape",
|
||||||
"td_width_to_size_filter_ratio": 0.9
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
"td_curve_segcount": 0,
|
"td_curve_segcount": 0,
|
||||||
"td_height_ratio": 1.0,
|
"td_height_ratio": 1.0,
|
||||||
"td_length_ratio": 0.5,
|
"td_length_ratio": 0.5,
|
||||||
"td_maxheight": 2.0,
|
"td_maxheight": 2.0,
|
||||||
"td_maxlen": 1.0,
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
"td_target_name": "td_rect_shape",
|
"td_target_name": "td_rect_shape",
|
||||||
"td_width_to_size_filter_ratio": 0.9
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
"td_curve_segcount": 0,
|
"td_curve_segcount": 0,
|
||||||
"td_height_ratio": 1.0,
|
"td_height_ratio": 1.0,
|
||||||
"td_length_ratio": 0.5,
|
"td_length_ratio": 0.5,
|
||||||
"td_maxheight": 2.0,
|
"td_maxheight": 2.0,
|
||||||
"td_maxlen": 1.0,
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
"td_target_name": "td_track_end",
|
"td_target_name": "td_track_end",
|
||||||
"td_width_to_size_filter_ratio": 0.9
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
}
|
}
|
||||||
@ -174,6 +181,32 @@
|
|||||||
"track_widths": [
|
"track_widths": [
|
||||||
0.0
|
0.0
|
||||||
],
|
],
|
||||||
|
"tuning_pattern_settings": {
|
||||||
|
"diff_pair_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 1.0
|
||||||
|
},
|
||||||
|
"diff_pair_skew_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
},
|
||||||
|
"single_track_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
}
|
||||||
|
},
|
||||||
"via_dimensions": [
|
"via_dimensions": [
|
||||||
{
|
{
|
||||||
"diameter": 0.0,
|
"diameter": 0.0,
|
||||||
@ -186,6 +219,13 @@
|
|||||||
],
|
],
|
||||||
"zones_allow_external_fillets": false
|
"zones_allow_external_fillets": false
|
||||||
},
|
},
|
||||||
|
"ipc2581": {
|
||||||
|
"dist": "",
|
||||||
|
"distpn": "",
|
||||||
|
"internal_id": "",
|
||||||
|
"mfg": "",
|
||||||
|
"mpn": ""
|
||||||
|
},
|
||||||
"layer_presets": [],
|
"layer_presets": [],
|
||||||
"viewports": []
|
"viewports": []
|
||||||
},
|
},
|
||||||
@ -444,8 +484,11 @@
|
|||||||
"gencad": "",
|
"gencad": "",
|
||||||
"idf": "",
|
"idf": "",
|
||||||
"netlist": "",
|
"netlist": "",
|
||||||
|
"plot": "",
|
||||||
|
"pos_files": "",
|
||||||
"specctra_dsn": "",
|
"specctra_dsn": "",
|
||||||
"step": "../../Jumpeledardflipped.step",
|
"step": "../../Jumpeledardflipped.step",
|
||||||
|
"svg": "",
|
||||||
"vrml": ""
|
"vrml": ""
|
||||||
},
|
},
|
||||||
"page_layout_descr_file": ""
|
"page_layout_descr_file": ""
|
||||||
|
BIN
Hardware/ProductionFiles/JumperlessBOMWithLCSCPNs 2.xls
Normal file
BIN
Hardware/ProductionFiles/JumperlessBOMWithLCSCPNs 2.xls
Normal file
Binary file not shown.
BIN
Hardware/ProductionFiles/LED Board 2.zip
Normal file
BIN
Hardware/ProductionFiles/LED Board 2.zip
Normal file
Binary file not shown.
BIN
Hardware/ProductionFiles/Main Board 3point1 2.zip
Normal file
BIN
Hardware/ProductionFiles/Main Board 3point1 2.zip
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 710 KiB |
Binary file not shown.
After Width: | Height: | Size: 582 KiB |
Binary file not shown.
@ -0,0 +1,104 @@
|
|||||||
|
Ref,Val,Package,PosX,PosY,Rot,Side
|
||||||
|
"A2","CH446Q","LQFP44_Tight",41.390000,-150.718800,180.000000,bottom
|
||||||
|
"B1","CH446Q","LQFP44_Tight",55.067200,-150.690800,180.000000,bottom
|
||||||
|
"C1","100uF","C_1206_3216Metric_Pad1.33x1.80mm_HandSolder",88.062000,-118.579500,-90.000000,bottom
|
||||||
|
"C4","100uF","C_1206_3216Metric_Pad1.33x1.80mm_HandSolder",85.522000,-118.579500,90.000000,bottom
|
||||||
|
"C6","100uF","C_1206_3216Metric_Pad1.33x1.80mm_HandSolder",82.982000,-118.579500,90.000000,bottom
|
||||||
|
"C7","10u","C_0603_1608Metric",70.917000,-180.454000,-90.000000,bottom
|
||||||
|
"C8","10u","C_0603_1608Metric",70.917000,-184.330000,-90.000000,bottom
|
||||||
|
"C9","1u","C_0402_1005Metric",100.762000,-117.475000,-167.500000,bottom
|
||||||
|
"C10",".1uF","C_0402_1005Metric",52.959200,-123.190000,90.000000,bottom
|
||||||
|
"C11","1u","C_0402_1005Metric",104.699000,-132.588000,-60.000000,bottom
|
||||||
|
"C12","1u","C_0402_1005Metric",98.984000,-121.158000,55.000000,bottom
|
||||||
|
"C13","27p","C_0402_1005Metric",107.620000,-122.174000,-110.000000,bottom
|
||||||
|
"C14","27p","C_0402_1005Metric",111.049000,-120.777000,80.000000,bottom
|
||||||
|
"C15",".1uF","C_0402_1005Metric",52.959200,-120.904000,90.000000,bottom
|
||||||
|
"C16","10uF","C_0603_1608Metric",115.367000,-160.033000,-90.000000,bottom
|
||||||
|
"C17","27p","C_0402_1005Metric",41.834000,-136.398000,90.000000,bottom
|
||||||
|
"C18","1u","C_0402_1005Metric",98.730000,-130.302000,-122.500000,bottom
|
||||||
|
"C19","1u","C_0402_1005Metric",103.429000,-121.031000,110.000000,bottom
|
||||||
|
"C20","1u","C_0402_1005Metric",104.953000,-124.079000,-147.500000,bottom
|
||||||
|
"C21","1u","C_0402_1005Metric",111.544807,-129.434317,35.000000,bottom
|
||||||
|
"C22","27p","C_0402_1005Metric",43.358000,-136.370000,90.000000,bottom
|
||||||
|
"C23","27p","C_0402_1005Metric",44.882000,-136.398000,90.000000,bottom
|
||||||
|
"C24","27p","C_0402_1005Metric",48.438000,-134.112000,90.000000,bottom
|
||||||
|
"C25","10uF","C_0603_1608Metric",36.500000,-160.020000,90.000000,bottom
|
||||||
|
"C26","CH446Q","LQFP44_Tight",96.022600,-150.688800,180.000000,bottom
|
||||||
|
"D61","1N5819","D_SOD-323",72.060000,-115.824000,-90.000000,bottom
|
||||||
|
"D62","1N5819","D_SOD-323",69.520000,-115.790000,90.000000,bottom
|
||||||
|
"D63","1N4148","D_SOD-323",63.424000,-115.790000,-90.000000,bottom
|
||||||
|
"D65","1N5819","D_SOD-323",66.980000,-120.650000,-90.000000,bottom
|
||||||
|
"D66","1N4148","D_SOD-323",63.551000,-120.650000,90.000000,bottom
|
||||||
|
"D67","1N5819","D_SOD-323",69.520000,-120.650000,90.000000,bottom
|
||||||
|
"D68","1N5819","D_SOD-323",72.060000,-120.650000,-90.000000,bottom
|
||||||
|
"D70","CH446Q","LQFP44_Tight",109.689600,-150.688800,180.000000,bottom
|
||||||
|
"D91","LED","LED_PLCC2_reversemount",49.403200,-115.138200,0.000000,bottom
|
||||||
|
"E1","CH446Q","LQFP44_Tight",41.465600,-168.494200,180.000000,bottom
|
||||||
|
"F5","CH446Q","LQFP44_Tight",55.079600,-168.494200,180.000000,bottom
|
||||||
|
"FID2","Fiducial","Fiducial_1mm_Mask2mm",47.930000,-179.070000,180.000000,bottom
|
||||||
|
"FID3","Fiducial","Fiducial_1mm_Mask2mm",103.810000,-179.070000,180.000000,bottom
|
||||||
|
"FID4","Fiducial","Fiducial_1mm_Mask2mm",42.850000,-111.760000,180.000000,bottom
|
||||||
|
"G1","CH446Q","LQFP44_Tight",95.999600,-168.494200,180.000000,bottom
|
||||||
|
"H1","CH446Q","LQFP44_Tight",109.713200,-168.494200,180.000000,bottom
|
||||||
|
"I1","CH446Q","LQFP44_Tight",68.717600,-150.690800,180.000000,bottom
|
||||||
|
"J6","CH446Q","LQFP44_Tight",68.669600,-168.494200,180.000000,bottom
|
||||||
|
"JP1","NANO 3V3","SolderJumper_2_Bridged_Small",67.386400,-132.791200,90.000000,bottom
|
||||||
|
"JP2","NANO 5V","SolderJumper_2_Bridged_Small",68.885000,-132.791200,90.000000,bottom
|
||||||
|
"JP13","Jumper_2_Bridged","SolderJumper_2_Bridged_Small",84.353600,-132.969000,90.000000,bottom
|
||||||
|
"JP14","Jumper_2_Bridged","SolderJumper_2_Bridged_Small",82.855000,-132.969000,90.000000,bottom
|
||||||
|
"K1","CH446Q","LQFP44_Tight",82.359600,-168.494200,180.000000,bottom
|
||||||
|
"L1","CH446Q","LQFP44_Tight",82.321600,-150.690800,180.000000,bottom
|
||||||
|
"R1","2Ω","R_0603_1608Metric",60.757000,-179.070000,0.000000,bottom
|
||||||
|
"R2","22R","R_0402_1005Metric",105.588000,-118.745000,-12.500000,bottom
|
||||||
|
"R3","22R","R_0402_1005Metric",108.763000,-117.602000,-80.000000,bottom
|
||||||
|
"R4","2Ω","R_0603_1608Metric",91.110000,-179.070000,0.000000,bottom
|
||||||
|
"R5","1k","R_0402_1005Metric",104.826000,-114.808000,115.000000,bottom
|
||||||
|
"R6","1K","R_0402_1005Metric",41.834000,-134.110000,-90.000000,bottom
|
||||||
|
"R7","1K","R_0402_1005Metric",101.143000,-129.159000,-80.000000,bottom
|
||||||
|
"R8","1K","R_0402_1005Metric",43.358000,-134.110000,-90.000000,bottom
|
||||||
|
"R9","1K","R_0402_1005Metric",108.255000,-132.969000,-120.000000,bottom
|
||||||
|
"R10","1K","R_0402_1005Metric",44.882000,-134.108000,-90.000000,bottom
|
||||||
|
"R11","1K","R_0402_1005Metric",111.557000,-133.223000,-104.000000,bottom
|
||||||
|
"R12","1k","R_0402_1005Metric",110.795000,-124.968000,-7.500000,bottom
|
||||||
|
"R13","47K","R_0402_1005Metric",49.962000,-134.110000,90.000000,bottom
|
||||||
|
"R14","47K","R_0402_1005Metric",46.914000,-134.110000,-90.000000,bottom
|
||||||
|
"R15","10K","R_0402_1005Metric",49.962000,-136.396000,90.000000,bottom
|
||||||
|
"R16","47K","R_0402_1005Metric",48.438000,-136.396000,90.000000,bottom
|
||||||
|
"R17","68K","R_0402_1005Metric",46.914000,-138.682000,90.000000,bottom
|
||||||
|
"R18","56.2K","R_0402_1005Metric",49.962000,-138.682000,90.000000,bottom
|
||||||
|
"R19","47K","R_0402_1005Metric",46.914000,-136.396000,90.000000,bottom
|
||||||
|
"R20","56.2K","R_0402_1005Metric",48.438000,-138.682000,90.000000,bottom
|
||||||
|
"R21","2K","R_0402_1005Metric",41.834000,-138.682000,-90.000000,bottom
|
||||||
|
"R22","2K","R_0402_1005Metric",43.358000,-138.682000,-90.000000,bottom
|
||||||
|
"R23","2K","R_0402_1005Metric",44.882000,-138.682000,-90.000000,bottom
|
||||||
|
"R24","1K","R_0402_1005Metric",40.564000,-122.174000,90.000000,bottom
|
||||||
|
"R26","2K","R_0402_1005Metric",97.079000,-118.745000,106.000000,bottom
|
||||||
|
"R27","2K","R_0402_1005Metric",95.628822,-122.818683,130.000000,bottom
|
||||||
|
"R29","68K","R_0402_1005Metric",46.152000,-134.110000,-90.000000,bottom
|
||||||
|
"SW1","USB BOOT","SW_SPST_TL3342",110.007600,-111.937800,135.000000,bottom
|
||||||
|
"TP5","FB/SHDN","TestPoint_D0.75mm",80.442000,-119.634000,180.000000,bottom
|
||||||
|
"TP6","VREF","TestPoint_D0.75mm",80.442000,-117.602000,180.000000,bottom
|
||||||
|
"TP7","0-5V DAC","TestPoint_D0.75mm",43.866000,-105.664000,180.000000,bottom
|
||||||
|
"TP8","+-8V DAC","TestPoint_D0.75mm",45.644000,-105.664000,180.000000,bottom
|
||||||
|
"TP9","ADC 0 IN","TestPoint_D0.75mm",52.883000,-105.664000,180.000000,bottom
|
||||||
|
"TP10","ADC 1 IN","TestPoint_D0.75mm",51.359000,-105.664000,180.000000,bottom
|
||||||
|
"TP11","ADC 2 IN","TestPoint_D0.75mm",49.835000,-105.664000,180.000000,bottom
|
||||||
|
"TP12","ADC 3 IN","TestPoint_D0.75mm",48.311000,-105.664000,180.000000,bottom
|
||||||
|
"TP13","-8V","TestPoint_D0.75mm",35.738000,-118.872000,-90.000000,bottom
|
||||||
|
"TP14","+8V","TestPoint_D0.75mm",35.738000,-117.094000,0.000000,bottom
|
||||||
|
"TP15","LED OUT","TestPoint_Pad_D1.0mm",116.510000,-144.780000,90.000000,bottom
|
||||||
|
"TP16","GPIO 0","TestPoint_Pad_D1.0mm",35.738000,-114.554000,0.000000,bottom
|
||||||
|
"TP17","LED IN","TestPoint_Pad_D1.0mm",35.230000,-144.780000,0.000000,bottom
|
||||||
|
"TP19","GND","TestPoint_Pad_D1.0mm",116.510000,-142.240000,0.000000,bottom
|
||||||
|
"TP22","TestPoint","BackMaskWindows",58.090000,-128.270000,0.000000,bottom
|
||||||
|
"TP23","TestPoint","BackMaskWindows",58.090000,-106.542937,0.000000,bottom
|
||||||
|
"TP25","GND","TestPoint_Pad_D1.0mm",35.230000,-142.240000,0.000000,bottom
|
||||||
|
"U2","NCP1117-3.3_SOT223","SOT-223-3_TabPin2",76.784000,-182.372000,0.000000,bottom
|
||||||
|
"U4","INA219","SOIC-8_3.9x4.9mm_P1.27mm",60.630000,-182.626000,180.000000,bottom
|
||||||
|
"U6","INA219","SOIC-8_3.9x4.9mm_P1.27mm",91.110000,-182.753000,180.000000,bottom
|
||||||
|
"U7","LM324","SOIC-14_3.9x8.7mm_P1.27mm",60.376000,-134.620000,-90.000000,bottom
|
||||||
|
"U8","W25Q128JVS","SOIC-8_5.23x5.23mm_P1.27mm",100.635000,-110.490000,-90.000000,bottom
|
||||||
|
"U10","L272D","SOIC-16_3.9x9.9mm_P1.27mm",46.660000,-122.377200,-90.000000,bottom
|
||||||
|
"U11","LM324","SOIC-14_3.9x8.7mm_P1.27mm",75.870000,-134.685000,-90.000000,bottom
|
||||||
|
"U12","MCP4822","SOIC-8_3.9x4.9mm_P1.27mm",91.364000,-134.682000,-90.000000,bottom
|
||||||
|
"Y1","ABLS-12.000MHZ-B4-T","Crystal_SMD_3225-4Pin_3.2x2.5mm",114.947000,-120.439000,90.000000,bottom
|
|
@ -0,0 +1,99 @@
|
|||||||
|
Ref,Val,Package,PosX,PosY,Rot,Side
|
||||||
|
"A1","Arduino_Nano_v3.x","Arduino_Nano",93.396000,-110.911000,-90.000000,top
|
||||||
|
"C2","10uF","CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder",72.314000,-118.110000,-90.000000,top
|
||||||
|
"C3","10uF","CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder",67.996000,-118.110000,-90.000000,top
|
||||||
|
"C5","10uF","CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder",76.759000,-118.110000,-90.000000,top
|
||||||
|
"D1","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",39.031200,-151.130000,90.000000,top
|
||||||
|
"D2","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",41.571200,-151.130000,90.000000,top
|
||||||
|
"D3","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",44.111200,-151.130000,90.000000,top
|
||||||
|
"D4","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",46.651200,-151.130000,90.000000,top
|
||||||
|
"D5","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",49.191200,-151.130000,90.000000,top
|
||||||
|
"D6","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",51.731200,-151.130000,90.000000,top
|
||||||
|
"D7","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",54.271200,-151.130000,90.000000,top
|
||||||
|
"D8","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",56.811200,-151.130000,90.000000,top
|
||||||
|
"D9","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",59.351200,-151.130000,90.000000,top
|
||||||
|
"D10","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",61.891200,-151.130000,90.000000,top
|
||||||
|
"D11","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",64.440000,-151.130600,90.000000,top
|
||||||
|
"D12","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",66.987800,-151.130600,90.000000,top
|
||||||
|
"D13","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",69.527800,-151.130600,90.000000,top
|
||||||
|
"D14","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",72.067800,-151.130600,90.000000,top
|
||||||
|
"D15","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",74.607800,-151.130600,90.000000,top
|
||||||
|
"D16","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",77.147800,-151.130600,90.000000,top
|
||||||
|
"D17","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",79.687800,-151.130600,90.000000,top
|
||||||
|
"D18","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",82.227800,-151.130600,90.000000,top
|
||||||
|
"D19","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",84.767800,-151.130600,90.000000,top
|
||||||
|
"D20","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",87.307800,-151.130600,90.000000,top
|
||||||
|
"D21","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",89.847800,-151.129400,90.000000,top
|
||||||
|
"D22","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",92.395600,-151.129400,90.000000,top
|
||||||
|
"D23","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",94.935600,-151.129400,90.000000,top
|
||||||
|
"D24","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",97.475600,-151.129400,90.000000,top
|
||||||
|
"D25","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",100.015600,-151.129400,90.000000,top
|
||||||
|
"D26","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",102.555600,-151.129400,90.000000,top
|
||||||
|
"D27","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",105.095600,-151.129400,90.000000,top
|
||||||
|
"D28","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",107.635600,-151.129400,90.000000,top
|
||||||
|
"D29","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",110.175600,-151.129400,90.000000,top
|
||||||
|
"D30","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",112.715600,-151.129400,90.000000,top
|
||||||
|
"D31","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",38.973600,-168.908800,90.000000,top
|
||||||
|
"D32","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",41.521400,-168.908800,90.000000,top
|
||||||
|
"D33","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",44.061400,-168.908800,90.000000,top
|
||||||
|
"D34","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",46.601400,-168.908800,90.000000,top
|
||||||
|
"D35","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",49.141400,-168.908800,90.000000,top
|
||||||
|
"D36","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",51.681400,-168.908800,90.000000,top
|
||||||
|
"D37","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",54.221400,-168.908800,90.000000,top
|
||||||
|
"D38","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",56.761400,-168.908800,90.000000,top
|
||||||
|
"D39","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",59.301400,-168.908800,90.000000,top
|
||||||
|
"D40","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",61.841400,-168.908800,90.000000,top
|
||||||
|
"D41","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",64.389200,-168.909400,90.000000,top
|
||||||
|
"D42","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",66.938000,-168.910600,90.000000,top
|
||||||
|
"D43","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",69.478000,-168.910600,90.000000,top
|
||||||
|
"D44","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",72.018000,-168.910600,90.000000,top
|
||||||
|
"D45","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",74.558000,-168.910600,90.000000,top
|
||||||
|
"D46","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",77.098000,-168.910600,90.000000,top
|
||||||
|
"D47","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",79.638000,-168.910600,90.000000,top
|
||||||
|
"D48","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",82.178000,-168.910600,90.000000,top
|
||||||
|
"D49","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",84.718000,-168.910600,90.000000,top
|
||||||
|
"D50","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",87.265800,-168.910600,90.000000,top
|
||||||
|
"D51","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",89.814600,-168.909400,90.000000,top
|
||||||
|
"D52","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",92.362400,-168.909400,90.000000,top
|
||||||
|
"D53","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",94.902400,-168.909400,90.000000,top
|
||||||
|
"D54","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",97.442400,-168.909400,90.000000,top
|
||||||
|
"D55","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",99.982400,-168.909400,90.000000,top
|
||||||
|
"D56","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",102.522400,-168.909400,90.000000,top
|
||||||
|
"D57","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",105.062400,-168.909400,90.000000,top
|
||||||
|
"D58","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",107.602400,-168.909400,90.000000,top
|
||||||
|
"D59","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",110.142400,-168.909400,90.000000,top
|
||||||
|
"D60","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",112.682400,-168.909400,90.000000,top
|
||||||
|
"D71","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",106.350000,-184.150000,180.000000,top
|
||||||
|
"D72","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",106.350000,-181.610000,180.000000,top
|
||||||
|
"D73","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",91.110000,-181.610000,180.000000,top
|
||||||
|
"D74","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",91.110000,-184.150000,180.000000,top
|
||||||
|
"D75","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",75.870000,-184.150000,180.000000,top
|
||||||
|
"D76","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",75.870000,-181.610000,180.000000,top
|
||||||
|
"D77","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",60.630000,-181.610000,180.000000,top
|
||||||
|
"D78","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",60.630000,-184.150000,180.000000,top
|
||||||
|
"D79","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",45.390000,-184.150000,180.000000,top
|
||||||
|
"D80","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",45.389400,-181.610000,180.000000,top
|
||||||
|
"D81","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",45.390000,-135.890000,180.000000,top
|
||||||
|
"D82","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",45.390000,-138.430000,180.000000,top
|
||||||
|
"D83","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",60.630000,-138.430000,180.000000,top
|
||||||
|
"D84","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",60.630000,-135.890000,180.000000,top
|
||||||
|
"D85","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",75.870000,-135.890000,180.000000,top
|
||||||
|
"D86","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",75.870000,-138.430000,180.000000,top
|
||||||
|
"D87","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",91.110000,-138.430000,180.000000,top
|
||||||
|
"D88","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",91.110000,-135.890000,180.000000,top
|
||||||
|
"D89","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",106.350000,-135.890000,180.000000,top
|
||||||
|
"D90","WS2812B","LED_WS2812B-2020_PLCC4_2.0x2.0mm",106.350000,-138.430000,180.000000,top
|
||||||
|
"FID1","Fiducial","Fiducial_1mm_Mask2mm",47.930000,-179.070000,0.000000,top
|
||||||
|
"FID5","Fiducial","Fiducial_1mm_Mask2mm",103.810000,-179.070000,0.000000,top
|
||||||
|
"FID6","Fiducial","Fiducial_1mm_Mask2mm",104.572000,-107.696000,0.000000,top
|
||||||
|
"J1","Debug","PinHeader_1x03_P2.54mm_Vertical",92.380000,-121.285000,180.000000,top
|
||||||
|
"J4","Breadboard","Breadboard_CustomClips_Fallback",75.870000,-160.020000,0.000000,top
|
||||||
|
"J5","USB_B_Mini","USB_Mini-B_Wuerth_65100516121_Horizontal",110.001500,-111.943500,135.000000,top
|
||||||
|
"J7","Conn_01x04_Pin","GPIO_header",38.903974,-109.169200,-135.000000,top
|
||||||
|
"J14","5V","PINHEADER-1x1_vertical",55.296000,-106.578400,0.000000,top
|
||||||
|
"SW2","SW_DP3T","Slide_Switch_SSSS223600",37.388000,-120.936000,0.000000,top
|
||||||
|
"TP18","GPIO 0","PINHEADER-1x1_vertical",115.113000,-116.840000,0.000000,top
|
||||||
|
"TP20","LED OUT","PINHEADER-1x1_vertical",95.936000,-106.426000,0.000000,top
|
||||||
|
"TP21","LED IN","PINHEADER-1x1_vertical",55.296000,-110.998000,0.000000,top
|
||||||
|
"U1","LT1054","DIP-8_W7.62mm",80.442000,-122.428000,90.000000,top
|
||||||
|
"U9","RP2040","RP2040-QFN-56",105.837000,-126.492000,0.000000,top
|
|
Binary file not shown.
@ -0,0 +1,64 @@
|
|||||||
|
Designator,Footprint,Quantity,Value,LCSC Part #
|
||||||
|
A1,Arduino_Nano,1,Arduino_Nano_v3.x,
|
||||||
|
"A2, B1, C26, D70, E1, F5, G1, H1, I1, J6, K1, L1",LQFP44_Tight,12,CH446Q,
|
||||||
|
"C1, C4, C6",1206,3,100uF,
|
||||||
|
"C10, C15",402,2,.1uF,
|
||||||
|
"C11, C12, C18, C19, C20, C21, C9",402,7,1u,
|
||||||
|
"C13, C14, C17, C22, C23, C24",402,6,27p,
|
||||||
|
"C16, C25",603,2,10uF,
|
||||||
|
"C2, C3, C5",CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder,3,10uF,
|
||||||
|
"C7, C8",603,2,10u,
|
||||||
|
"D1, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D2, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, D3, D30, D31, D32, D33, D34, D35, D36, D37, D38, D39, D4, D40, D41, D42, D43, D44, D45, D46, D47, D48, D49, D5, D50, D51, D52, D53, D54, D55, D56, D57, D58, D59, D6, D60, D7, D71, D72, D73, D74, D75, D76, D77, D78, D79, D8, D80, D81, D82, D83, D84, D85, D86, D87, D88, D89, D9, D90",LED_WS2812B-2020_PLCC4_2.0x2.0mm,80,WS2812B,
|
||||||
|
"D61, D62, D65, D67, D68",D_SOD-323,5,1N5819,
|
||||||
|
"D63, D66",D_SOD-323,2,1N4148,
|
||||||
|
D91,LED_PLCC2_reversemount,1,LED,
|
||||||
|
"FID1, FID2, FID3, FID4, FID5, FID6",Fiducial_1mm_Mask2mm,6,Fiducial,
|
||||||
|
J1,PinHeader_1x03_P2.54mm_Vertical,1,Debug,
|
||||||
|
J14,PINHEADER-1x1_vertical,1,5V,
|
||||||
|
J4,Breadboard_CustomClips_Fallback,1,Breadboard,
|
||||||
|
J5,USB_Mini-B_Wuerth_65100516121_Horizontal,1,USB_B_Mini,
|
||||||
|
J7,GPIO_header,1,Conn_01x04_Pin,
|
||||||
|
JP1,SolderJumper_2_Bridged_Small,1,NANO 3V3,
|
||||||
|
"JP13, JP14",SolderJumper_2_Bridged_Small,2,Jumper_2_Bridged,
|
||||||
|
JP2,SolderJumper_2_Bridged_Small,1,NANO 5V,
|
||||||
|
"R1, R4",603,2,2Ω,
|
||||||
|
"R10, R11, R12, R24, R5, R6, R7, R8, R9",402,9,1K,
|
||||||
|
"R13, R14, R16, R19",402,4,47K,
|
||||||
|
R15,402,1,10K,
|
||||||
|
"R17, R29",402,2,68K,
|
||||||
|
"R18, R20",402,2,56.2K,
|
||||||
|
"R2, R3",402,2,22R,
|
||||||
|
"R21, R22, R23, R26, R27",402,5,2K,
|
||||||
|
SW1,SW_SPST_TL3342,1,USB BOOT,
|
||||||
|
SW2,Slide_Switch_SSSS223600,1,SW_DP3T,
|
||||||
|
TP1,TestPoint_Pad_D1.0mm,1,GPIO 16,
|
||||||
|
TP10,TestPoint_D0.75mm,1,ADC 1 IN,
|
||||||
|
TP11,TestPoint_D0.75mm,1,ADC 2 IN,
|
||||||
|
TP12,TestPoint_D0.75mm,1,ADC 3 IN,
|
||||||
|
TP13,TestPoint_D0.75mm,1,-8V,
|
||||||
|
TP14,TestPoint_D0.75mm,1,+8V,
|
||||||
|
TP15,TestPoint_Pad_D1.0mm,1,LED OUT,
|
||||||
|
TP16,TestPoint_Pad_D1.0mm,1,GPIO 0,
|
||||||
|
TP17,TestPoint_Pad_D1.0mm,1,LED IN,
|
||||||
|
TP18,PINHEADER-1x1_vertical,1,GPIO 0,
|
||||||
|
"TP19, TP25",TestPoint_Pad_D1.0mm,2,GND,
|
||||||
|
TP2,TestPoint_Pad_D1.0mm,1,GPIO 17,
|
||||||
|
TP20,PINHEADER-1x1_vertical,1,LED OUT,
|
||||||
|
TP21,PINHEADER-1x1_vertical,1,LED IN,
|
||||||
|
"TP22, TP23",BackMaskWindows,2,TestPoint,
|
||||||
|
TP3,TestPoint_Pad_D1.0mm,1,GPIO 18,
|
||||||
|
TP4,TestPoint_Pad_D1.0mm,1,GPIO 19,
|
||||||
|
TP5,TestPoint_D0.75mm,1,FB/SHDN,
|
||||||
|
TP6,TestPoint_D0.75mm,1,VREF,
|
||||||
|
TP7,TestPoint_D0.75mm,1,0-5V DAC,
|
||||||
|
TP8,TestPoint_D0.75mm,1,+-8V DAC,
|
||||||
|
TP9,TestPoint_D0.75mm,1,ADC 0 IN,
|
||||||
|
U1,DIP-8_W7.62mm,1,LT1054,
|
||||||
|
U10,SOIC-16_3.9x9.9mm_P1.27mm,1,L272D,
|
||||||
|
"U11, U7",SOIC-14_3.9x8.7mm_P1.27mm,2,LM324,
|
||||||
|
U12,SOIC-8_3.9x4.9mm_P1.27mm,1,MCP4822,
|
||||||
|
U2,SOT-223-3_TabPin2,1,NCP1117-3.3_SOT223,
|
||||||
|
"U4, U6",SOIC-8_3.9x4.9mm_P1.27mm,2,INA219,
|
||||||
|
U8,SOIC-8_5.23x5.23mm_P1.27mm,1,W25Q128JVS,
|
||||||
|
U9,RP2040-QFN-56,1,RP2040,
|
||||||
|
Y1,Crystal_SMD_3225-4Pin_3.2x2.5mm,1,ABLS-12.000MHZ-B4-T,
|
|
@ -0,0 +1,205 @@
|
|||||||
|
A1:1
|
||||||
|
A2:1
|
||||||
|
B1:1
|
||||||
|
C1:1
|
||||||
|
C10:1
|
||||||
|
C11:1
|
||||||
|
C12:1
|
||||||
|
C13:1
|
||||||
|
C14:1
|
||||||
|
C15:1
|
||||||
|
C16:1
|
||||||
|
C17:1
|
||||||
|
C18:1
|
||||||
|
C19:1
|
||||||
|
C2:1
|
||||||
|
C20:1
|
||||||
|
C21:1
|
||||||
|
C22:1
|
||||||
|
C23:1
|
||||||
|
C24:1
|
||||||
|
C25:1
|
||||||
|
C26:1
|
||||||
|
C3:1
|
||||||
|
C4:1
|
||||||
|
C5:1
|
||||||
|
C6:1
|
||||||
|
C7:1
|
||||||
|
C8:1
|
||||||
|
C9:1
|
||||||
|
D1:1
|
||||||
|
D10:1
|
||||||
|
D11:1
|
||||||
|
D12:1
|
||||||
|
D13:1
|
||||||
|
D14:1
|
||||||
|
D15:1
|
||||||
|
D16:1
|
||||||
|
D17:1
|
||||||
|
D18:1
|
||||||
|
D19:1
|
||||||
|
D2:1
|
||||||
|
D20:1
|
||||||
|
D21:1
|
||||||
|
D22:1
|
||||||
|
D23:1
|
||||||
|
D24:1
|
||||||
|
D25:1
|
||||||
|
D26:1
|
||||||
|
D27:1
|
||||||
|
D28:1
|
||||||
|
D29:1
|
||||||
|
D3:1
|
||||||
|
D30:1
|
||||||
|
D31:1
|
||||||
|
D32:1
|
||||||
|
D33:1
|
||||||
|
D34:1
|
||||||
|
D35:1
|
||||||
|
D36:1
|
||||||
|
D37:1
|
||||||
|
D38:1
|
||||||
|
D39:1
|
||||||
|
D4:1
|
||||||
|
D40:1
|
||||||
|
D41:1
|
||||||
|
D42:1
|
||||||
|
D43:1
|
||||||
|
D44:1
|
||||||
|
D45:1
|
||||||
|
D46:1
|
||||||
|
D47:1
|
||||||
|
D48:1
|
||||||
|
D49:1
|
||||||
|
D5:1
|
||||||
|
D50:1
|
||||||
|
D51:1
|
||||||
|
D52:1
|
||||||
|
D53:1
|
||||||
|
D54:1
|
||||||
|
D55:1
|
||||||
|
D56:1
|
||||||
|
D57:1
|
||||||
|
D58:1
|
||||||
|
D59:1
|
||||||
|
D6:1
|
||||||
|
D60:1
|
||||||
|
D61:1
|
||||||
|
D62:1
|
||||||
|
D63:1
|
||||||
|
D65:1
|
||||||
|
D66:1
|
||||||
|
D67:1
|
||||||
|
D68:1
|
||||||
|
D7:1
|
||||||
|
D70:1
|
||||||
|
D71:1
|
||||||
|
D72:1
|
||||||
|
D73:1
|
||||||
|
D74:1
|
||||||
|
D75:1
|
||||||
|
D76:1
|
||||||
|
D77:1
|
||||||
|
D78:1
|
||||||
|
D79:1
|
||||||
|
D8:1
|
||||||
|
D80:1
|
||||||
|
D81:1
|
||||||
|
D82:1
|
||||||
|
D83:1
|
||||||
|
D84:1
|
||||||
|
D85:1
|
||||||
|
D86:1
|
||||||
|
D87:1
|
||||||
|
D88:1
|
||||||
|
D89:1
|
||||||
|
D9:1
|
||||||
|
D90:1
|
||||||
|
D91:1
|
||||||
|
E1:1
|
||||||
|
F5:1
|
||||||
|
FID1:1
|
||||||
|
FID2:1
|
||||||
|
FID3:1
|
||||||
|
FID4:1
|
||||||
|
FID5:1
|
||||||
|
FID6:1
|
||||||
|
G1:1
|
||||||
|
H1:1
|
||||||
|
I1:1
|
||||||
|
J1:1
|
||||||
|
J14:1
|
||||||
|
J4:1
|
||||||
|
J5:1
|
||||||
|
J6:1
|
||||||
|
J7:1
|
||||||
|
JP1:1
|
||||||
|
JP13:1
|
||||||
|
JP14:1
|
||||||
|
JP2:1
|
||||||
|
K1:1
|
||||||
|
L1:1
|
||||||
|
R1:1
|
||||||
|
R10:1
|
||||||
|
R11:1
|
||||||
|
R12:1
|
||||||
|
R13:1
|
||||||
|
R14:1
|
||||||
|
R15:1
|
||||||
|
R16:1
|
||||||
|
R17:1
|
||||||
|
R18:1
|
||||||
|
R19:1
|
||||||
|
R2:1
|
||||||
|
R20:1
|
||||||
|
R21:1
|
||||||
|
R22:1
|
||||||
|
R23:1
|
||||||
|
R24:1
|
||||||
|
R26:1
|
||||||
|
R27:1
|
||||||
|
R29:1
|
||||||
|
R3:1
|
||||||
|
R4:1
|
||||||
|
R5:1
|
||||||
|
R6:1
|
||||||
|
R7:1
|
||||||
|
R8:1
|
||||||
|
R9:1
|
||||||
|
SW1:1
|
||||||
|
SW2:1
|
||||||
|
TP1:1
|
||||||
|
TP10:1
|
||||||
|
TP11:1
|
||||||
|
TP12:1
|
||||||
|
TP13:1
|
||||||
|
TP14:1
|
||||||
|
TP15:1
|
||||||
|
TP16:1
|
||||||
|
TP17:1
|
||||||
|
TP18:1
|
||||||
|
TP19:1
|
||||||
|
TP2:1
|
||||||
|
TP20:1
|
||||||
|
TP21:1
|
||||||
|
TP22:1
|
||||||
|
TP23:1
|
||||||
|
TP25:1
|
||||||
|
TP3:1
|
||||||
|
TP4:1
|
||||||
|
TP5:1
|
||||||
|
TP6:1
|
||||||
|
TP7:1
|
||||||
|
TP8:1
|
||||||
|
TP9:1
|
||||||
|
U1:1
|
||||||
|
U10:1
|
||||||
|
U11:1
|
||||||
|
U12:1
|
||||||
|
U2:1
|
||||||
|
U4:1
|
||||||
|
U6:1
|
||||||
|
U7:1
|
||||||
|
U8:1
|
||||||
|
U9:1
|
||||||
|
Y1:1
|
|
File diff suppressed because it is too large
Load Diff
64
Hardware/Rev3Plots/Main Board 3point1/bom 2.csv
Normal file
64
Hardware/Rev3Plots/Main Board 3point1/bom 2.csv
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
Designator,Footprint,Quantity,Value,LCSC Part #
|
||||||
|
A1,Arduino_Nano,1,Arduino_Nano_v3.x,
|
||||||
|
"A2, B1, C26, D70, E1, F5, G1, H1, I1, J6, K1, L1",LQFP44_Tight,12,CH446Q,
|
||||||
|
"C1, C4, C6",1206,3,100uF,
|
||||||
|
"C10, C15",0402,2,.1uF,
|
||||||
|
"C11, C12, C18, C19, C20, C21, C9",0402,7,1u,
|
||||||
|
"C13, C14, C17, C22, C23, C24",0402,6,27p,
|
||||||
|
"C16, C25",0603,2,10uF,
|
||||||
|
"C2, C3, C5",CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder,3,10uF,
|
||||||
|
"C7, C8",0603,2,10u,
|
||||||
|
"D1, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D2, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, D3, D30, D31, D32, D33, D34, D35, D36, D37, D38, D39, D4, D40, D41, D42, D43, D44, D45, D46, D47, D48, D49, D5, D50, D51, D52, D53, D54, D55, D56, D57, D58, D59, D6, D60, D7, D71, D72, D73, D74, D75, D76, D77, D78, D79, D8, D80, D81, D82, D83, D84, D85, D86, D87, D88, D89, D9, D90",LED_WS2812B-2020_PLCC4_2.0x2.0mm,80,WS2812B,
|
||||||
|
"D61, D62, D65, D67, D68",D_SOD-323,5,1N5819,
|
||||||
|
"D63, D66",D_SOD-323,2,1N4148,
|
||||||
|
D91,LED_PLCC2_reversemount,1,LED,
|
||||||
|
"FID1, FID2, FID3, FID4, FID5, FID6",Fiducial_1mm_Mask2mm,6,Fiducial,
|
||||||
|
J1,PinHeader_1x03_P2.54mm_Vertical,1,Debug,
|
||||||
|
J14,PINHEADER-1x1_vertical,1,5V,
|
||||||
|
J4,Breadboard_CustomClips_Fallback,1,Breadboard,
|
||||||
|
J5,USB_Mini-B_Wuerth_65100516121_Horizontal,1,USB_B_Mini,
|
||||||
|
J7,GPIO_header,1,Conn_01x04_Pin,
|
||||||
|
JP1,SolderJumper_2_Bridged_Small,1,NANO 3V3,
|
||||||
|
"JP13, JP14",SolderJumper_2_Bridged_Small,2,Jumper_2_Bridged,
|
||||||
|
JP2,SolderJumper_2_Bridged_Small,1,NANO 5V,
|
||||||
|
"R1, R4",0603,2,2Ω,
|
||||||
|
"R10, R11, R12, R24, R5, R6, R7, R8, R9",0402,9,1K,
|
||||||
|
"R13, R14, R16, R19",0402,4,47K,
|
||||||
|
R15,0402,1,10K,
|
||||||
|
"R17, R29",0402,2,68K,
|
||||||
|
"R18, R20",0402,2,56.2K,
|
||||||
|
"R2, R3",0402,2,22R,
|
||||||
|
"R21, R22, R23, R26, R27",0402,5,2K,
|
||||||
|
SW1,SW_SPST_TL3342,1,USB BOOT,
|
||||||
|
SW2,Slide_Switch_SSSS223600,1,SW_DP3T,
|
||||||
|
TP1,TestPoint_Pad_D1.0mm,1,GPIO 16,
|
||||||
|
TP10,TestPoint_D0.75mm,1,ADC 1 IN,
|
||||||
|
TP11,TestPoint_D0.75mm,1,ADC 2 IN,
|
||||||
|
TP12,TestPoint_D0.75mm,1,ADC 3 IN,
|
||||||
|
TP13,TestPoint_D0.75mm,1,-8V,
|
||||||
|
TP14,TestPoint_D0.75mm,1,+8V,
|
||||||
|
TP15,TestPoint_Pad_D1.0mm,1,LED OUT,
|
||||||
|
TP16,TestPoint_Pad_D1.0mm,1,GPIO 0,
|
||||||
|
TP17,TestPoint_Pad_D1.0mm,1,LED IN,
|
||||||
|
TP18,PINHEADER-1x1_vertical,1,GPIO 0,
|
||||||
|
"TP19, TP25",TestPoint_Pad_D1.0mm,2,GND,
|
||||||
|
TP2,TestPoint_Pad_D1.0mm,1,GPIO 17,
|
||||||
|
TP20,PINHEADER-1x1_vertical,1,LED OUT,
|
||||||
|
TP21,PINHEADER-1x1_vertical,1,LED IN,
|
||||||
|
"TP22, TP23",BackMaskWindows,2,TestPoint,
|
||||||
|
TP3,TestPoint_Pad_D1.0mm,1,GPIO 18,
|
||||||
|
TP4,TestPoint_Pad_D1.0mm,1,GPIO 19,
|
||||||
|
TP5,TestPoint_D0.75mm,1,FB/SHDN,
|
||||||
|
TP6,TestPoint_D0.75mm,1,VREF,
|
||||||
|
TP7,TestPoint_D0.75mm,1,0-5V DAC,
|
||||||
|
TP8,TestPoint_D0.75mm,1,+-8V DAC,
|
||||||
|
TP9,TestPoint_D0.75mm,1,ADC 0 IN,
|
||||||
|
U1,DIP-8_W7.62mm,1,LT1054,
|
||||||
|
U10,SOIC-16_3.9x9.9mm_P1.27mm,1,L272D,
|
||||||
|
"U11, U7",SOIC-14_3.9x8.7mm_P1.27mm,2,LM324,
|
||||||
|
U12,SOIC-8_3.9x4.9mm_P1.27mm,1,MCP4822,
|
||||||
|
U2,SOT-223-3_TabPin2,1,NCP1117-3.3_SOT223,
|
||||||
|
"U4, U6",SOIC-8_3.9x4.9mm_P1.27mm,2,INA219,
|
||||||
|
U8,SOIC-8_5.23x5.23mm_P1.27mm,1,W25Q128JVS,
|
||||||
|
U9,RP2040-QFN-56,1,RP2040,
|
||||||
|
Y1,Crystal_SMD_3225-4Pin_3.2x2.5mm,1,ABLS-12.000MHZ-B4-T,
|
|
205
Hardware/Rev3Plots/Main Board 3point1/designators 2.csv
Normal file
205
Hardware/Rev3Plots/Main Board 3point1/designators 2.csv
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
A1:1
|
||||||
|
A2:1
|
||||||
|
B1:1
|
||||||
|
C1:1
|
||||||
|
C10:1
|
||||||
|
C11:1
|
||||||
|
C12:1
|
||||||
|
C13:1
|
||||||
|
C14:1
|
||||||
|
C15:1
|
||||||
|
C16:1
|
||||||
|
C17:1
|
||||||
|
C18:1
|
||||||
|
C19:1
|
||||||
|
C2:1
|
||||||
|
C20:1
|
||||||
|
C21:1
|
||||||
|
C22:1
|
||||||
|
C23:1
|
||||||
|
C24:1
|
||||||
|
C25:1
|
||||||
|
C26:1
|
||||||
|
C3:1
|
||||||
|
C4:1
|
||||||
|
C5:1
|
||||||
|
C6:1
|
||||||
|
C7:1
|
||||||
|
C8:1
|
||||||
|
C9:1
|
||||||
|
D1:1
|
||||||
|
D10:1
|
||||||
|
D11:1
|
||||||
|
D12:1
|
||||||
|
D13:1
|
||||||
|
D14:1
|
||||||
|
D15:1
|
||||||
|
D16:1
|
||||||
|
D17:1
|
||||||
|
D18:1
|
||||||
|
D19:1
|
||||||
|
D2:1
|
||||||
|
D20:1
|
||||||
|
D21:1
|
||||||
|
D22:1
|
||||||
|
D23:1
|
||||||
|
D24:1
|
||||||
|
D25:1
|
||||||
|
D26:1
|
||||||
|
D27:1
|
||||||
|
D28:1
|
||||||
|
D29:1
|
||||||
|
D3:1
|
||||||
|
D30:1
|
||||||
|
D31:1
|
||||||
|
D32:1
|
||||||
|
D33:1
|
||||||
|
D34:1
|
||||||
|
D35:1
|
||||||
|
D36:1
|
||||||
|
D37:1
|
||||||
|
D38:1
|
||||||
|
D39:1
|
||||||
|
D4:1
|
||||||
|
D40:1
|
||||||
|
D41:1
|
||||||
|
D42:1
|
||||||
|
D43:1
|
||||||
|
D44:1
|
||||||
|
D45:1
|
||||||
|
D46:1
|
||||||
|
D47:1
|
||||||
|
D48:1
|
||||||
|
D49:1
|
||||||
|
D5:1
|
||||||
|
D50:1
|
||||||
|
D51:1
|
||||||
|
D52:1
|
||||||
|
D53:1
|
||||||
|
D54:1
|
||||||
|
D55:1
|
||||||
|
D56:1
|
||||||
|
D57:1
|
||||||
|
D58:1
|
||||||
|
D59:1
|
||||||
|
D6:1
|
||||||
|
D60:1
|
||||||
|
D61:1
|
||||||
|
D62:1
|
||||||
|
D63:1
|
||||||
|
D65:1
|
||||||
|
D66:1
|
||||||
|
D67:1
|
||||||
|
D68:1
|
||||||
|
D7:1
|
||||||
|
D70:1
|
||||||
|
D71:1
|
||||||
|
D72:1
|
||||||
|
D73:1
|
||||||
|
D74:1
|
||||||
|
D75:1
|
||||||
|
D76:1
|
||||||
|
D77:1
|
||||||
|
D78:1
|
||||||
|
D79:1
|
||||||
|
D8:1
|
||||||
|
D80:1
|
||||||
|
D81:1
|
||||||
|
D82:1
|
||||||
|
D83:1
|
||||||
|
D84:1
|
||||||
|
D85:1
|
||||||
|
D86:1
|
||||||
|
D87:1
|
||||||
|
D88:1
|
||||||
|
D89:1
|
||||||
|
D9:1
|
||||||
|
D90:1
|
||||||
|
D91:1
|
||||||
|
E1:1
|
||||||
|
F5:1
|
||||||
|
FID1:1
|
||||||
|
FID2:1
|
||||||
|
FID3:1
|
||||||
|
FID4:1
|
||||||
|
FID5:1
|
||||||
|
FID6:1
|
||||||
|
G1:1
|
||||||
|
H1:1
|
||||||
|
I1:1
|
||||||
|
J1:1
|
||||||
|
J14:1
|
||||||
|
J4:1
|
||||||
|
J5:1
|
||||||
|
J6:1
|
||||||
|
J7:1
|
||||||
|
JP1:1
|
||||||
|
JP13:1
|
||||||
|
JP14:1
|
||||||
|
JP2:1
|
||||||
|
K1:1
|
||||||
|
L1:1
|
||||||
|
R1:1
|
||||||
|
R10:1
|
||||||
|
R11:1
|
||||||
|
R12:1
|
||||||
|
R13:1
|
||||||
|
R14:1
|
||||||
|
R15:1
|
||||||
|
R16:1
|
||||||
|
R17:1
|
||||||
|
R18:1
|
||||||
|
R19:1
|
||||||
|
R2:1
|
||||||
|
R20:1
|
||||||
|
R21:1
|
||||||
|
R22:1
|
||||||
|
R23:1
|
||||||
|
R24:1
|
||||||
|
R26:1
|
||||||
|
R27:1
|
||||||
|
R29:1
|
||||||
|
R3:1
|
||||||
|
R4:1
|
||||||
|
R5:1
|
||||||
|
R6:1
|
||||||
|
R7:1
|
||||||
|
R8:1
|
||||||
|
R9:1
|
||||||
|
SW1:1
|
||||||
|
SW2:1
|
||||||
|
TP1:1
|
||||||
|
TP10:1
|
||||||
|
TP11:1
|
||||||
|
TP12:1
|
||||||
|
TP13:1
|
||||||
|
TP14:1
|
||||||
|
TP15:1
|
||||||
|
TP16:1
|
||||||
|
TP17:1
|
||||||
|
TP18:1
|
||||||
|
TP19:1
|
||||||
|
TP2:1
|
||||||
|
TP20:1
|
||||||
|
TP21:1
|
||||||
|
TP22:1
|
||||||
|
TP23:1
|
||||||
|
TP25:1
|
||||||
|
TP3:1
|
||||||
|
TP4:1
|
||||||
|
TP5:1
|
||||||
|
TP6:1
|
||||||
|
TP7:1
|
||||||
|
TP8:1
|
||||||
|
TP9:1
|
||||||
|
U1:1
|
||||||
|
U10:1
|
||||||
|
U11:1
|
||||||
|
U12:1
|
||||||
|
U2:1
|
||||||
|
U4:1
|
||||||
|
U6:1
|
||||||
|
U7:1
|
||||||
|
U8:1
|
||||||
|
U9:1
|
||||||
|
Y1:1
|
|
2278
Hardware/Rev3Plots/Main Board 3point1/netlist 2.ipc
Normal file
2278
Hardware/Rev3Plots/Main Board 3point1/netlist 2.ipc
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Ha Thach (tinyusb.org) for Adafruit Industries
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if CFG_TUH_ENABLED && CFG_TUH_MSC
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
#include "Adafruit_USBH_CDC.h"
|
||||||
|
|
||||||
|
Adafruit_USBH_CDC::Adafruit_USBH_CDC(void) { _idx = TUSB_INDEX_INVALID_8; }
|
||||||
|
|
||||||
|
void Adafruit_USBH_CDC::begin(unsigned long baudrate) {
|
||||||
|
// default to index 0 when begin
|
||||||
|
if (_idx == TUSB_INDEX_INVALID_8) {
|
||||||
|
_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_baud = baudrate;
|
||||||
|
if (_baud == 0) {
|
||||||
|
_baud = 115200; // default, backward compatible with previous API begin(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if already mounted, this will set baudrate
|
||||||
|
if (mounted()) {
|
||||||
|
setBaudrate(_baud);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_USBH_CDC::begin(unsigned long baudrate, uint16_t config) {
|
||||||
|
(void)config; // TODO support line coding later
|
||||||
|
begin(baudrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_USBH_CDC::end(void) { _idx = TUSB_INDEX_INVALID_8; }
|
||||||
|
|
||||||
|
bool Adafruit_USBH_CDC::mount(uint8_t idx) {
|
||||||
|
_idx = idx;
|
||||||
|
|
||||||
|
uint32_t local_baud = baud();
|
||||||
|
if (local_baud != _baud) {
|
||||||
|
return setBaudrate(_baud);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_USBH_CDC::umount(uint8_t idx) {
|
||||||
|
if (_idx == idx) {
|
||||||
|
_idx = TUSB_INDEX_INVALID_8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_USBH_CDC::connected(void) { return tuh_cdc_connected(_idx); }
|
||||||
|
|
||||||
|
bool Adafruit_USBH_CDC::mounted(void) { return tuh_cdc_mounted(_idx); }
|
||||||
|
uint32_t Adafruit_USBH_CDC::baud() {
|
||||||
|
cdc_line_coding_t line_coding;
|
||||||
|
if (!tuh_cdc_get_local_line_coding(_idx, &line_coding)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return line_coding.bit_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Control API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
bool Adafruit_USBH_CDC::setDtrRts(bool dtr, bool rts, tuh_xfer_cb_t complete_cb,
|
||||||
|
uintptr_t user_data) {
|
||||||
|
if (!tuh_cdc_mounted(_idx)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t const line_state = (dtr ? CDC_CONTROL_LINE_STATE_DTR : 0) |
|
||||||
|
(rts ? CDC_CONTROL_LINE_STATE_RTS : 0);
|
||||||
|
|
||||||
|
return tuh_cdc_set_control_line_state(_idx, line_state, complete_cb,
|
||||||
|
user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_USBH_CDC::setBaudrate(uint32_t baudrate,
|
||||||
|
tuh_xfer_cb_t complete_cb,
|
||||||
|
uintptr_t user_data) {
|
||||||
|
if (!tuh_cdc_mounted(_idx)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baud() == baudrate) {
|
||||||
|
// skip if already matched
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tuh_cdc_set_baudrate(_idx, baudrate, complete_cb, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Stream API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
int Adafruit_USBH_CDC::available(void) {
|
||||||
|
return (int)tuh_cdc_read_available(_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Adafruit_USBH_CDC::peek(void) {
|
||||||
|
uint8_t ch;
|
||||||
|
return tuh_cdc_peek(_idx, &ch) ? (int)ch : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Adafruit_USBH_CDC::read(void) {
|
||||||
|
uint8_t ch;
|
||||||
|
return read(&ch, 1) ? (int)ch : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Adafruit_USBH_CDC::read(uint8_t *buffer, size_t size) {
|
||||||
|
return tuh_cdc_read(_idx, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_USBH_CDC::flush(void) { (void)tuh_cdc_write_flush(_idx); }
|
||||||
|
|
||||||
|
size_t Adafruit_USBH_CDC::write(uint8_t ch) { return write(&ch, 1); }
|
||||||
|
|
||||||
|
size_t Adafruit_USBH_CDC::write(const uint8_t *buffer, size_t size) {
|
||||||
|
size_t remain = size;
|
||||||
|
while (remain && tuh_cdc_mounted(_idx)) {
|
||||||
|
size_t wrcount = tuh_cdc_write(_idx, buffer, remain);
|
||||||
|
remain -= wrcount;
|
||||||
|
buffer += wrcount;
|
||||||
|
|
||||||
|
// Write FIFO is full, run host task while wait for space become available
|
||||||
|
if (remain) {
|
||||||
|
tuh_task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size - remain;
|
||||||
|
|
||||||
|
return tuh_cdc_write(_idx, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Adafruit_USBH_CDC::availableForWrite(void) {
|
||||||
|
return (int)tuh_cdc_write_available(_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Ha Thach (tinyusb.org) for Adafruit Industries
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADAFRUIT_USBH_CDC_H_
|
||||||
|
#define ADAFRUIT_USBH_CDC_H_
|
||||||
|
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
|
||||||
|
class Adafruit_USBH_CDC : public HardwareSerial {
|
||||||
|
public:
|
||||||
|
Adafruit_USBH_CDC(void);
|
||||||
|
|
||||||
|
// Set/Get index of cdc interface
|
||||||
|
void setInterfaceIndex(uint8_t idx) { _idx = idx; }
|
||||||
|
uint8_t getInterfaceIndex(void) { return _idx; }
|
||||||
|
|
||||||
|
void begin(unsigned long baudrate);
|
||||||
|
void begin(unsigned long baudrate, uint16_t config);
|
||||||
|
|
||||||
|
bool mount(uint8_t idx);
|
||||||
|
void umount(uint8_t idx);
|
||||||
|
|
||||||
|
// unbind cdc interface
|
||||||
|
void end(void);
|
||||||
|
|
||||||
|
// If cdc is mounted
|
||||||
|
bool mounted(void);
|
||||||
|
operator bool() { return mounted(); }
|
||||||
|
|
||||||
|
// if cdc's DTR is asserted
|
||||||
|
bool connected(void);
|
||||||
|
|
||||||
|
// Line encoding
|
||||||
|
uint32_t baud();
|
||||||
|
|
||||||
|
//------------- Control API -------------//
|
||||||
|
bool setDtrRts(bool dtr, bool rts, tuh_xfer_cb_t complete_cb = NULL,
|
||||||
|
uintptr_t user_data = 0);
|
||||||
|
bool setBaudrate(uint32_t baudrate, tuh_xfer_cb_t complete_cb = NULL,
|
||||||
|
uintptr_t user_data = 0);
|
||||||
|
|
||||||
|
//------------- Stream API -------------//
|
||||||
|
virtual int available(void);
|
||||||
|
virtual int peek(void);
|
||||||
|
|
||||||
|
virtual int read(void);
|
||||||
|
size_t read(uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
virtual void flush(void);
|
||||||
|
virtual size_t write(uint8_t ch);
|
||||||
|
|
||||||
|
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||||
|
size_t write(const char *buffer, size_t size) {
|
||||||
|
return write((const uint8_t *)buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int availableForWrite(void);
|
||||||
|
using Print::write; // pull in write(str) from Print
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t _idx; // TinyUSB CDC Interface Index
|
||||||
|
uint32_t _baud;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,352 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 hathach for Adafruit Industries
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if CFG_TUD_ENABLED && CFG_TUD_VENDOR
|
||||||
|
|
||||||
|
#include "Adafruit_USBD_WebUSB.h"
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#include "USB.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
#define EPOUT 0x00
|
||||||
|
#define EPIN 0x80
|
||||||
|
#define EPSIZE 64
|
||||||
|
|
||||||
|
enum { VENDOR_REQUEST_WEBUSB = 1, VENDOR_REQUEST_MICROSOFT = 2 };
|
||||||
|
|
||||||
|
// TODO multiple instances
|
||||||
|
static Adafruit_USBD_WebUSB *_webusb_dev = NULL;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// BOS Descriptor
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/* Microsoft OS 2.0 registry property descriptor
|
||||||
|
Per MS requirements
|
||||||
|
https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
|
||||||
|
device should create DeviceInterfaceGUIDs. It can be done by driver and
|
||||||
|
in case of real PnP solution device should expose MS "Microsoft OS 2.0
|
||||||
|
registry property descriptor". Such descriptor can insert any record
|
||||||
|
into Windows registry per device/configuration/interface. In our case it
|
||||||
|
will insert "DeviceInterfaceGUIDs" multistring property.
|
||||||
|
|
||||||
|
GUID is freshly generated and should be OK to use.
|
||||||
|
|
||||||
|
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
|
||||||
|
(Section Microsoft OS compatibility descriptors)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define BOS_TOTAL_LEN \
|
||||||
|
(TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
|
||||||
|
|
||||||
|
#define MS_OS_20_DESC_LEN 0xB2
|
||||||
|
|
||||||
|
// BOS Descriptor is required for webUSB
|
||||||
|
uint8_t const desc_bos[] = {
|
||||||
|
// total length, number of device caps
|
||||||
|
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
|
||||||
|
|
||||||
|
// Vendor Code, iLandingPage
|
||||||
|
TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
|
||||||
|
|
||||||
|
// Microsoft OS 2.0 descriptor
|
||||||
|
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)};
|
||||||
|
|
||||||
|
uint8_t desc_ms_os_20[] = {
|
||||||
|
// Set header: length, type, windows version, total length
|
||||||
|
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR),
|
||||||
|
U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
|
||||||
|
|
||||||
|
// Configuration subset header: length, type, configuration index, reserved,
|
||||||
|
// configuration total length
|
||||||
|
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION),
|
||||||
|
0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A),
|
||||||
|
|
||||||
|
// Function Subset header: length, type, first interface, reserved, subset
|
||||||
|
// length
|
||||||
|
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION),
|
||||||
|
0 /*itf num*/, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08),
|
||||||
|
|
||||||
|
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub
|
||||||
|
// compatible ID
|
||||||
|
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W',
|
||||||
|
'I', 'N', 'U', 'S', 'B', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, // sub-compatible
|
||||||
|
|
||||||
|
// MS OS 2.0 Registry property descriptor: length, type
|
||||||
|
U16_TO_U8S_LE(MS_OS_20_DESC_LEN - 0x0A - 0x08 - 0x08 - 0x14),
|
||||||
|
U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), U16_TO_U8S_LE(0x0007),
|
||||||
|
U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and
|
||||||
|
// PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
|
||||||
|
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00,
|
||||||
|
'n', 0x00, 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00,
|
||||||
|
'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00,
|
||||||
|
0x00,
|
||||||
|
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
|
||||||
|
// bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
|
||||||
|
'{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00,
|
||||||
|
'D', 0x00, '9', 0x00, '-', 0x00, '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00,
|
||||||
|
'-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, '8', 0x00,
|
||||||
|
'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00,
|
||||||
|
'C', 0x00, 'A', 0x00, '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00,
|
||||||
|
'9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// IMPLEMENTATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
static uint16_t webusb_load_descriptor(uint8_t *dst, uint8_t *itf) {
|
||||||
|
// uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC");
|
||||||
|
|
||||||
|
uint8_t ep_in = tinyusb_get_free_in_endpoint();
|
||||||
|
uint8_t ep_out = tinyusb_get_free_out_endpoint();
|
||||||
|
TU_VERIFY(ep_in && ep_out);
|
||||||
|
ep_in |= 0x80;
|
||||||
|
|
||||||
|
uint16_t desc_len = _webusb_dev->getInterfaceDescriptor(0, NULL, 0);
|
||||||
|
|
||||||
|
desc_len = _webusb_dev->makeItfDesc(*itf, dst, desc_len, ep_in, ep_out);
|
||||||
|
|
||||||
|
*itf += 1;
|
||||||
|
return desc_len;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Adafruit_USBD_WebUSB::Adafruit_USBD_WebUSB(const void *url) {
|
||||||
|
_connected = false;
|
||||||
|
_url = (const uint8_t *)url;
|
||||||
|
_linestate_cb = NULL;
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
// ESP32 requires setup configuration descriptor within constructor
|
||||||
|
|
||||||
|
// WebUSB requires USB version at least 2.1 (or 3.x)
|
||||||
|
USB.usbVersion(0x0210);
|
||||||
|
|
||||||
|
_webusb_dev = this;
|
||||||
|
uint16_t const desc_len = getInterfaceDescriptor(0, NULL, 0);
|
||||||
|
tinyusb_enable_interface(USB_INTERFACE_VENDOR, desc_len,
|
||||||
|
webusb_load_descriptor);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_USBD_WebUSB::begin(void) {
|
||||||
|
if (!TinyUSBDevice.addInterface(*this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebUSB requires USB version at least 2.1 (or 3.x)
|
||||||
|
TinyUSBDevice.setVersion(0x0210);
|
||||||
|
|
||||||
|
_webusb_dev = this;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_USBD_WebUSB::setLandingPage(const void *url) {
|
||||||
|
_url = (const uint8_t *)url;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_USBD_WebUSB::setLineStateCallback(linestate_callback_t fp) {
|
||||||
|
_linestate_cb = fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Adafruit_USBD_WebUSB::makeItfDesc(uint8_t itfnum, uint8_t *buf,
|
||||||
|
uint16_t bufsize, uint8_t ep_in,
|
||||||
|
uint8_t ep_out) {
|
||||||
|
uint8_t desc[] = {TUD_VENDOR_DESCRIPTOR(itfnum, 0, ep_out, ep_in, EPSIZE)};
|
||||||
|
uint16_t const len = sizeof(desc);
|
||||||
|
|
||||||
|
// null buffer for length only
|
||||||
|
if (buf) {
|
||||||
|
if (bufsize < len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf, desc, len);
|
||||||
|
|
||||||
|
// update the bFirstInterface in MS OS 2.0 descriptor
|
||||||
|
// that is binded to WinUSB driver
|
||||||
|
desc_ms_os_20[0x0a + 0x08 + 4] = itfnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Adafruit_USBD_WebUSB::getInterfaceDescriptor(uint8_t itfnum,
|
||||||
|
uint8_t *buf,
|
||||||
|
uint16_t bufsize) {
|
||||||
|
// usb core will automatically update endpoint number
|
||||||
|
return makeItfDesc(itfnum, buf, bufsize, EPIN, EPOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adafruit_USBD_WebUSB::connected(void) {
|
||||||
|
return tud_vendor_mounted() && _connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
Adafruit_USBD_WebUSB::operator bool() {
|
||||||
|
// Add an yield to run usb background in case sketch block wait as follows
|
||||||
|
// while( !webusb ) {}
|
||||||
|
if (!connected()) {
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
return connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Adafruit_USBD_WebUSB::available(void) {
|
||||||
|
uint32_t count = tud_vendor_available();
|
||||||
|
|
||||||
|
// Add an yield to run usb background in case sketch block wait as follows
|
||||||
|
// while( !webusb.available() ) {}
|
||||||
|
if (!count) {
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Adafruit_USBD_WebUSB::read(void) {
|
||||||
|
uint8_t ch;
|
||||||
|
return tud_vendor_read(&ch, 1) ? (int)ch : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Adafruit_USBD_WebUSB::read(uint8_t *buffer, size_t size) {
|
||||||
|
return tud_vendor_read(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Adafruit_USBD_WebUSB::write(uint8_t b) { return this->write(&b, 1); }
|
||||||
|
|
||||||
|
size_t Adafruit_USBD_WebUSB::write(const uint8_t *buffer, size_t size) {
|
||||||
|
size_t remain = size;
|
||||||
|
while (remain && _connected) {
|
||||||
|
size_t wrcount = tud_vendor_write(buffer, remain);
|
||||||
|
remain -= wrcount;
|
||||||
|
buffer += wrcount;
|
||||||
|
|
||||||
|
// Write FIFO is full, run usb background to flush
|
||||||
|
if (remain) {
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return size - remain;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Adafruit_USBD_WebUSB::peek(void) {
|
||||||
|
uint8_t ch;
|
||||||
|
return tud_vendor_peek(&ch) ? (int)ch : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adafruit_USBD_WebUSB::flush(void) { tud_vendor_flush(); }
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// TinyUSB stack callbacks
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
uint8_t const *tud_descriptor_bos_cb(void) { return desc_bos; }
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage
|
||||||
|
// (setup/data/ack) return false to stall control endpoint (e.g unsupported
|
||||||
|
// request)
|
||||||
|
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
||||||
|
tusb_control_request_t const *request) {
|
||||||
|
if (!_webusb_dev) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing to with DATA & ACK stage
|
||||||
|
if (stage != CONTROL_STAGE_SETUP) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (request->bmRequestType_bit.type) {
|
||||||
|
case TUSB_REQ_TYPE_VENDOR:
|
||||||
|
switch (request->bRequest) {
|
||||||
|
case VENDOR_REQUEST_WEBUSB:
|
||||||
|
// match vendor request in BOS descriptor
|
||||||
|
// Get landing page url
|
||||||
|
if (!_webusb_dev->_url) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return tud_control_xfer(rhport, request, (void *)_webusb_dev->_url,
|
||||||
|
_webusb_dev->_url[0]);
|
||||||
|
|
||||||
|
case VENDOR_REQUEST_MICROSOFT:
|
||||||
|
if (request->wIndex == 7) {
|
||||||
|
// Get Microsoft OS 2.0 compatible descriptor
|
||||||
|
uint16_t total_len;
|
||||||
|
memcpy(&total_len, desc_ms_os_20 + 8, 2);
|
||||||
|
|
||||||
|
return tud_control_xfer(rhport, request, (void *)desc_ms_os_20,
|
||||||
|
total_len);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TUSB_REQ_TYPE_CLASS:
|
||||||
|
if (request->bRequest == 0x22) {
|
||||||
|
// Webserial simulate the CDC_REQUEST_SET_CONTROL_LINE_STATE (0x22) to
|
||||||
|
// connect and disconnect.
|
||||||
|
_webusb_dev->_connected = (request->wValue != 0);
|
||||||
|
|
||||||
|
// response with status OK
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
|
||||||
|
// invoked callback if any (TODO should be done at ACK stage)
|
||||||
|
if (_webusb_dev->_linestate_cb) {
|
||||||
|
_webusb_dev->_linestate_cb(_webusb_dev->_connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// stall unknown request
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CFG_TUD_ENABLED
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 hathach for Adafruit Industries
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADAFRUIT_USBD_WEBUSB_H_
|
||||||
|
#define ADAFRUIT_USBD_WEBUSB_H_
|
||||||
|
|
||||||
|
#include "Stream.h"
|
||||||
|
#include "arduino/Adafruit_USBD_Device.h"
|
||||||
|
|
||||||
|
#define WEBUSB_URL_DEF(_name, _scheme, _url) \
|
||||||
|
struct TU_ATTR_PACKED { \
|
||||||
|
uint8_t bLength; \
|
||||||
|
uint8_t bDescriptorType; \
|
||||||
|
uint8_t bScheme; \
|
||||||
|
char url[3 + sizeof(_url)]; \
|
||||||
|
} const _name = {3 + sizeof(_url) - 1, 3, _scheme, _url}
|
||||||
|
|
||||||
|
class Adafruit_USBD_WebUSB : public Stream, public Adafruit_USBD_Interface {
|
||||||
|
public:
|
||||||
|
typedef void (*linestate_callback_t)(bool connected);
|
||||||
|
Adafruit_USBD_WebUSB(const void *url = NULL);
|
||||||
|
|
||||||
|
bool begin(void);
|
||||||
|
|
||||||
|
bool setLandingPage(const void *url);
|
||||||
|
void setLineStateCallback(linestate_callback_t fp);
|
||||||
|
|
||||||
|
// Stream API
|
||||||
|
virtual int available(void);
|
||||||
|
virtual int peek(void);
|
||||||
|
|
||||||
|
virtual int read(void);
|
||||||
|
size_t read(uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
virtual void flush(void);
|
||||||
|
virtual size_t write(uint8_t b);
|
||||||
|
|
||||||
|
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||||
|
size_t write(const char *buffer, size_t size) {
|
||||||
|
return write((const uint8_t *)buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connected(void);
|
||||||
|
operator bool();
|
||||||
|
|
||||||
|
// from Adafruit_USBD_Interface
|
||||||
|
virtual uint16_t getInterfaceDescriptor(uint8_t itfnum, uint8_t *buf,
|
||||||
|
uint16_t bufsize);
|
||||||
|
|
||||||
|
// internal use only
|
||||||
|
uint16_t makeItfDesc(uint8_t itfnum, uint8_t *buf, uint16_t bufsize,
|
||||||
|
uint8_t ep_in, uint8_t ep_out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _connected;
|
||||||
|
const uint8_t *_url;
|
||||||
|
linestate_callback_t _linestate_cb;
|
||||||
|
|
||||||
|
// Make all tinyusb callback friend to access private data
|
||||||
|
friend bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage,
|
||||||
|
tusb_control_request_t const *request);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ADAFRUIT_USBD_WEBUSB_H_ */
|
260
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/bth/bth_device 2.c
Executable file
260
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/bth/bth_device 2.c
Executable file
@ -0,0 +1,260 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Jerzy Kasenberg
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_BTH)
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INCLUDE
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
#include "bth_device.h"
|
||||||
|
#include <device/usbd_pvt.h>
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t itf_num;
|
||||||
|
uint8_t ep_ev;
|
||||||
|
uint8_t ep_acl_in;
|
||||||
|
uint8_t ep_acl_out;
|
||||||
|
uint8_t ep_voice[2]; // Not used yet
|
||||||
|
uint8_t ep_voice_size[2][CFG_TUD_BTH_ISO_ALT_COUNT];
|
||||||
|
|
||||||
|
// Endpoint Transfer buffer
|
||||||
|
CFG_TUSB_MEM_ALIGN bt_hci_cmd_t hci_cmd;
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_BTH_DATA_EPSIZE];
|
||||||
|
|
||||||
|
} btd_interface_t;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
CFG_TUSB_MEM_SECTION btd_interface_t _btd_itf;
|
||||||
|
|
||||||
|
static bool bt_tx_data(uint8_t ep, void *data, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
|
||||||
|
// skip if previous transfer not complete
|
||||||
|
TU_VERIFY(!usbd_edpt_busy(rhport, ep));
|
||||||
|
|
||||||
|
TU_ASSERT(usbd_edpt_xfer(rhport, ep, data, len));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// READ API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// WRITE API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
bool tud_bt_event_send(void *event, uint16_t event_len)
|
||||||
|
{
|
||||||
|
return bt_tx_data(_btd_itf.ep_ev, event, event_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_bt_acl_data_send(void *event, uint16_t event_len)
|
||||||
|
{
|
||||||
|
return bt_tx_data(_btd_itf.ep_acl_in, event, event_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void btd_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(&_btd_itf, sizeof(_btd_itf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void btd_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void)rhport;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||||
|
{
|
||||||
|
tusb_desc_endpoint_t const *desc_ep;
|
||||||
|
uint16_t drv_len = 0;
|
||||||
|
// Size of single alternative of ISO interface
|
||||||
|
const uint16_t iso_alt_itf_size = sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t);
|
||||||
|
// Size of hci interface
|
||||||
|
const uint16_t hci_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t);
|
||||||
|
// Ensure this is BT Primary Controller
|
||||||
|
TU_VERIFY(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass &&
|
||||||
|
TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||||
|
TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0);
|
||||||
|
|
||||||
|
TU_ASSERT(itf_desc->bNumEndpoints == 3 && max_len >= hci_itf_size);
|
||||||
|
|
||||||
|
_btd_itf.itf_num = itf_desc->bInterfaceNumber;
|
||||||
|
|
||||||
|
desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
|
||||||
|
|
||||||
|
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
|
||||||
|
TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
|
||||||
|
_btd_itf.ep_ev = desc_ep->bEndpointAddress;
|
||||||
|
|
||||||
|
// Open endpoint pair
|
||||||
|
TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(desc_ep), 2, TUSB_XFER_BULK, &_btd_itf.ep_acl_out,
|
||||||
|
&_btd_itf.ep_acl_in), 0);
|
||||||
|
|
||||||
|
itf_desc = (tusb_desc_interface_t const *)tu_desc_next(tu_desc_next(tu_desc_next(desc_ep)));
|
||||||
|
|
||||||
|
// Prepare for incoming data from host
|
||||||
|
TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0);
|
||||||
|
|
||||||
|
drv_len = hci_itf_size;
|
||||||
|
|
||||||
|
// Ensure this is still BT Primary Controller
|
||||||
|
TU_ASSERT(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass &&
|
||||||
|
TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||||
|
TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0);
|
||||||
|
TU_ASSERT(itf_desc->bNumEndpoints == 2 && max_len >= iso_alt_itf_size + drv_len);
|
||||||
|
|
||||||
|
uint8_t dir;
|
||||||
|
|
||||||
|
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
|
||||||
|
TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
|
||||||
|
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
|
||||||
|
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
|
||||||
|
_btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
|
||||||
|
// Store endpoint size for alternative
|
||||||
|
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t) tu_edpt_packet_size(desc_ep);
|
||||||
|
|
||||||
|
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
|
||||||
|
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0);
|
||||||
|
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
|
||||||
|
_btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress;
|
||||||
|
// Store endpoint size for alternative
|
||||||
|
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t) tu_edpt_packet_size(desc_ep);
|
||||||
|
drv_len += iso_alt_itf_size;
|
||||||
|
|
||||||
|
for (int i = 1; i < CFG_TUD_BTH_ISO_ALT_COUNT && drv_len + iso_alt_itf_size <= max_len; ++i) {
|
||||||
|
// Make sure rest of alternatives matches
|
||||||
|
itf_desc = (tusb_desc_interface_t const *)tu_desc_next(desc_ep);
|
||||||
|
if (itf_desc->bDescriptorType != TUSB_DESC_INTERFACE ||
|
||||||
|
TUSB_CLASS_WIRELESS_CONTROLLER != itf_desc->bInterfaceClass ||
|
||||||
|
TUD_BT_APP_SUBCLASS != itf_desc->bInterfaceSubClass ||
|
||||||
|
TUD_BT_PROTOCOL_PRIMARY_CONTROLLER != itf_desc->bInterfaceProtocol)
|
||||||
|
{
|
||||||
|
// Not an Iso interface instance
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0);
|
||||||
|
|
||||||
|
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc);
|
||||||
|
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
|
||||||
|
// Verify that alternative endpoint are same as first ones
|
||||||
|
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
|
||||||
|
_btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
|
||||||
|
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t) tu_edpt_packet_size(desc_ep);
|
||||||
|
|
||||||
|
desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep);
|
||||||
|
dir = tu_edpt_dir(desc_ep->bEndpointAddress);
|
||||||
|
// Verify that alternative endpoint are same as first ones
|
||||||
|
TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT &&
|
||||||
|
_btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0);
|
||||||
|
_btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t) tu_edpt_packet_size(desc_ep);
|
||||||
|
drv_len += iso_alt_itf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool btd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||||
|
{
|
||||||
|
(void)rhport;
|
||||||
|
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
|
||||||
|
request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE)
|
||||||
|
{
|
||||||
|
// HCI command packet addressing for single function Primary Controllers
|
||||||
|
TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0);
|
||||||
|
}
|
||||||
|
else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE)
|
||||||
|
{
|
||||||
|
if (request->bRequest == TUSB_REQ_SET_INTERFACE && _btd_itf.itf_num + 1 == request->wIndex)
|
||||||
|
{
|
||||||
|
// TODO: Set interface it would involve changing size of endpoint size
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// HCI command packet for Primary Controller function in a composite device
|
||||||
|
TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == _btd_itf.itf_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, sizeof(_btd_itf.hci_cmd));
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_DATA )
|
||||||
|
{
|
||||||
|
// Handle class request only
|
||||||
|
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||||
|
|
||||||
|
if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, tu_min16(request->wLength, sizeof(_btd_itf.hci_cmd)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void)result;
|
||||||
|
|
||||||
|
// received new data from host
|
||||||
|
if (ep_addr == _btd_itf.ep_acl_out)
|
||||||
|
{
|
||||||
|
if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_itf.epout_buf, xferred_bytes);
|
||||||
|
|
||||||
|
// prepare for next data
|
||||||
|
TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE));
|
||||||
|
}
|
||||||
|
else if (ep_addr == _btd_itf.ep_ev)
|
||||||
|
{
|
||||||
|
if (tud_bt_event_sent_cb) tud_bt_event_sent_cb((uint16_t)xferred_bytes);
|
||||||
|
}
|
||||||
|
else if (ep_addr == _btd_itf.ep_acl_in)
|
||||||
|
{
|
||||||
|
if (tud_bt_acl_data_sent_cb) tud_bt_acl_data_sent_cb((uint16_t)xferred_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
109
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/bth/bth_device 2.h
Executable file
109
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/bth/bth_device 2.h
Executable file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Jerzy Kasenberg
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_BTH_DEVICE_H_
|
||||||
|
#define _TUSB_BTH_DEVICE_H_
|
||||||
|
|
||||||
|
#include <common/tusb_common.h>
|
||||||
|
#include <device/usbd.h>
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Driver Configuration
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
#ifndef CFG_TUD_BTH_EVENT_EPSIZE
|
||||||
|
#define CFG_TUD_BTH_EVENT_EPSIZE 16
|
||||||
|
#endif
|
||||||
|
#ifndef CFG_TUD_BTH_DATA_EPSIZE
|
||||||
|
#define CFG_TUD_BTH_DATA_EPSIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint16_t op_code;
|
||||||
|
uint8_t param_length;
|
||||||
|
uint8_t param[255];
|
||||||
|
} bt_hci_cmd_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callback API (weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Invoked when HCI command was received over USB from Bluetooth host.
|
||||||
|
// Detailed format is described in Bluetooth core specification Vol 2,
|
||||||
|
// Part E, 5.4.1.
|
||||||
|
// Length of the command is from 3 bytes (2 bytes for OpCode,
|
||||||
|
// 1 byte for parameter total length) to 258.
|
||||||
|
TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len);
|
||||||
|
|
||||||
|
// Invoked when ACL data was received over USB from Bluetooth host.
|
||||||
|
// Detailed format is described in Bluetooth core specification Vol 2,
|
||||||
|
// Part E, 5.4.2.
|
||||||
|
// Length is from 4 bytes, (12 bits for Handle, 4 bits for flags
|
||||||
|
// and 16 bits for data total length) to endpoint size.
|
||||||
|
TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len);
|
||||||
|
|
||||||
|
// Called when event sent with tud_bt_event_send() was delivered to BT stack.
|
||||||
|
// Controller can release/reuse buffer with Event packet at this point.
|
||||||
|
TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes);
|
||||||
|
|
||||||
|
// Called when ACL data that was sent with tud_bt_acl_data_send()
|
||||||
|
// was delivered to BT stack.
|
||||||
|
// Controller can release/reuse buffer with ACL packet at this point.
|
||||||
|
TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes);
|
||||||
|
|
||||||
|
// Bluetooth controller calls this function when it wants to send even packet
|
||||||
|
// as described in Bluetooth core specification Vol 2, Part E, 5.4.4.
|
||||||
|
// Event has at least 2 bytes, first is Event code second contains parameter
|
||||||
|
// total length. Controller can release/reuse event memory after
|
||||||
|
// tud_bt_event_sent_cb() is called.
|
||||||
|
bool tud_bt_event_send(void *event, uint16_t event_len);
|
||||||
|
|
||||||
|
// Bluetooth controller calls this to send ACL data packet
|
||||||
|
// as described in Bluetooth core specification Vol 2, Part E, 5.4.2
|
||||||
|
// Minimum length is 4 bytes, (12 bits for Handle, 4 bits for flags
|
||||||
|
// and 16 bits for data total length). Upper limit is not limited
|
||||||
|
// to endpoint size since buffer is allocate by controller
|
||||||
|
// and must not be reused till tud_bt_acl_data_sent_cb() is called.
|
||||||
|
bool tud_bt_acl_data_send(void *acl_data, uint16_t data_len);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void btd_init (void);
|
||||||
|
void btd_reset (uint8_t rhport);
|
||||||
|
uint16_t btd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool btd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||||
|
bool btd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_BTH_DEVICE_H_ */
|
119
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/dfu/dfu 2.h
Normal file
119
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/dfu/dfu 2.h
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 XMOS LIMITED
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_DFU_H_
|
||||||
|
#define _TUSB_DFU_H_
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Common Definitions
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// DFU Protocol
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DFU_PROTOCOL_RT = 0x01,
|
||||||
|
DFU_PROTOCOL_DFU = 0x02,
|
||||||
|
} dfu_protocol_type_t;
|
||||||
|
|
||||||
|
// DFU Descriptor Type
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DFU_DESC_FUNCTIONAL = 0x21,
|
||||||
|
} dfu_descriptor_type_t;
|
||||||
|
|
||||||
|
// DFU Requests
|
||||||
|
typedef enum {
|
||||||
|
DFU_REQUEST_DETACH = 0,
|
||||||
|
DFU_REQUEST_DNLOAD = 1,
|
||||||
|
DFU_REQUEST_UPLOAD = 2,
|
||||||
|
DFU_REQUEST_GETSTATUS = 3,
|
||||||
|
DFU_REQUEST_CLRSTATUS = 4,
|
||||||
|
DFU_REQUEST_GETSTATE = 5,
|
||||||
|
DFU_REQUEST_ABORT = 6,
|
||||||
|
} dfu_requests_t;
|
||||||
|
|
||||||
|
// DFU States
|
||||||
|
typedef enum {
|
||||||
|
APP_IDLE = 0,
|
||||||
|
APP_DETACH = 1,
|
||||||
|
DFU_IDLE = 2,
|
||||||
|
DFU_DNLOAD_SYNC = 3,
|
||||||
|
DFU_DNBUSY = 4,
|
||||||
|
DFU_DNLOAD_IDLE = 5,
|
||||||
|
DFU_MANIFEST_SYNC = 6,
|
||||||
|
DFU_MANIFEST = 7,
|
||||||
|
DFU_MANIFEST_WAIT_RESET = 8,
|
||||||
|
DFU_UPLOAD_IDLE = 9,
|
||||||
|
DFU_ERROR = 10,
|
||||||
|
} dfu_state_t;
|
||||||
|
|
||||||
|
// DFU Status
|
||||||
|
typedef enum {
|
||||||
|
DFU_STATUS_OK = 0x00,
|
||||||
|
DFU_STATUS_ERR_TARGET = 0x01,
|
||||||
|
DFU_STATUS_ERR_FILE = 0x02,
|
||||||
|
DFU_STATUS_ERR_WRITE = 0x03,
|
||||||
|
DFU_STATUS_ERR_ERASE = 0x04,
|
||||||
|
DFU_STATUS_ERR_CHECK_ERASED = 0x05,
|
||||||
|
DFU_STATUS_ERR_PROG = 0x06,
|
||||||
|
DFU_STATUS_ERR_VERIFY = 0x07,
|
||||||
|
DFU_STATUS_ERR_ADDRESS = 0x08,
|
||||||
|
DFU_STATUS_ERR_NOTDONE = 0x09,
|
||||||
|
DFU_STATUS_ERR_FIRMWARE = 0x0A,
|
||||||
|
DFU_STATUS_ERR_VENDOR = 0x0B,
|
||||||
|
DFU_STATUS_ERR_USBR = 0x0C,
|
||||||
|
DFU_STATUS_ERR_POR = 0x0D,
|
||||||
|
DFU_STATUS_ERR_UNKNOWN = 0x0E,
|
||||||
|
DFU_STATUS_ERR_STALLEDPKT = 0x0F,
|
||||||
|
} dfu_status_t;
|
||||||
|
|
||||||
|
#define DFU_ATTR_CAN_DOWNLOAD (1u << 0)
|
||||||
|
#define DFU_ATTR_CAN_UPLOAD (1u << 1)
|
||||||
|
#define DFU_ATTR_MANIFESTATION_TOLERANT (1u << 2)
|
||||||
|
#define DFU_ATTR_WILL_DETACH (1u << 3)
|
||||||
|
|
||||||
|
// DFU Status Request Payload
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t bStatus;
|
||||||
|
uint8_t bwPollTimeout[3];
|
||||||
|
uint8_t bState;
|
||||||
|
uint8_t iString;
|
||||||
|
} dfu_status_response_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(dfu_status_response_t) == 6, "size is not correct");
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_DFU_H_ */
|
@ -0,0 +1,460 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 XMOS LIMITED
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_DFU)
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "dfu_device.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t attrs;
|
||||||
|
uint8_t alt;
|
||||||
|
|
||||||
|
dfu_state_t state;
|
||||||
|
dfu_status_t status;
|
||||||
|
|
||||||
|
bool flashing_in_progress;
|
||||||
|
uint16_t block;
|
||||||
|
uint16_t length;
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
|
||||||
|
} dfu_state_ctx_t;
|
||||||
|
|
||||||
|
// Only a single dfu state is allowed
|
||||||
|
CFG_TUSB_MEM_SECTION tu_static dfu_state_ctx_t _dfu_ctx;
|
||||||
|
|
||||||
|
static void reset_state(void)
|
||||||
|
{
|
||||||
|
_dfu_ctx.state = DFU_IDLE;
|
||||||
|
_dfu_ctx.status = DFU_STATUS_OK;
|
||||||
|
_dfu_ctx.flashing_in_progress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
|
||||||
|
static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Debug
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
#if CFG_TUSB_DEBUG >= 2
|
||||||
|
|
||||||
|
tu_static tu_lookup_entry_t const _dfu_request_lookup[] =
|
||||||
|
{
|
||||||
|
{ .key = DFU_REQUEST_DETACH , .data = "DETACH" },
|
||||||
|
{ .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
|
||||||
|
{ .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
|
||||||
|
{ .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" },
|
||||||
|
{ .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" },
|
||||||
|
{ .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
|
||||||
|
{ .key = DFU_REQUEST_ABORT , .data = "ABORT" },
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static tu_lookup_table_t const _dfu_request_table =
|
||||||
|
{
|
||||||
|
.count = TU_ARRAY_SIZE(_dfu_request_lookup),
|
||||||
|
.items = _dfu_request_lookup
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static tu_lookup_entry_t const _dfu_state_lookup[] =
|
||||||
|
{
|
||||||
|
{ .key = APP_IDLE , .data = "APP_IDLE" },
|
||||||
|
{ .key = APP_DETACH , .data = "APP_DETACH" },
|
||||||
|
{ .key = DFU_IDLE , .data = "IDLE" },
|
||||||
|
{ .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
|
||||||
|
{ .key = DFU_DNBUSY , .data = "DNBUSY" },
|
||||||
|
{ .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
|
||||||
|
{ .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
|
||||||
|
{ .key = DFU_MANIFEST , .data = "MANIFEST" },
|
||||||
|
{ .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" },
|
||||||
|
{ .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
|
||||||
|
{ .key = DFU_ERROR , .data = "ERROR" },
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static tu_lookup_table_t const _dfu_state_table =
|
||||||
|
{
|
||||||
|
.count = TU_ARRAY_SIZE(_dfu_state_lookup),
|
||||||
|
.items = _dfu_state_lookup
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static tu_lookup_entry_t const _dfu_status_lookup[] =
|
||||||
|
{
|
||||||
|
{ .key = DFU_STATUS_OK , .data = "OK" },
|
||||||
|
{ .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" },
|
||||||
|
{ .key = DFU_STATUS_ERR_FILE , .data = "errFILE" },
|
||||||
|
{ .key = DFU_STATUS_ERR_WRITE , .data = "errWRITE" },
|
||||||
|
{ .key = DFU_STATUS_ERR_ERASE , .data = "errERASE" },
|
||||||
|
{ .key = DFU_STATUS_ERR_CHECK_ERASED , .data = "errCHECK_ERASED" },
|
||||||
|
{ .key = DFU_STATUS_ERR_PROG , .data = "errPROG" },
|
||||||
|
{ .key = DFU_STATUS_ERR_VERIFY , .data = "errVERIFY" },
|
||||||
|
{ .key = DFU_STATUS_ERR_ADDRESS , .data = "errADDRESS" },
|
||||||
|
{ .key = DFU_STATUS_ERR_NOTDONE , .data = "errNOTDONE" },
|
||||||
|
{ .key = DFU_STATUS_ERR_FIRMWARE , .data = "errFIRMWARE" },
|
||||||
|
{ .key = DFU_STATUS_ERR_VENDOR , .data = "errVENDOR" },
|
||||||
|
{ .key = DFU_STATUS_ERR_USBR , .data = "errUSBR" },
|
||||||
|
{ .key = DFU_STATUS_ERR_POR , .data = "errPOR" },
|
||||||
|
{ .key = DFU_STATUS_ERR_UNKNOWN , .data = "errUNKNOWN" },
|
||||||
|
{ .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" },
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static tu_lookup_table_t const _dfu_status_table =
|
||||||
|
{
|
||||||
|
.count = TU_ARRAY_SIZE(_dfu_status_lookup),
|
||||||
|
.items = _dfu_status_lookup
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void dfu_moded_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
_dfu_ctx.attrs = 0;
|
||||||
|
_dfu_ctx.alt = 0;
|
||||||
|
|
||||||
|
reset_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dfu_moded_init(void)
|
||||||
|
{
|
||||||
|
dfu_moded_reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
//------------- Interface (with Alt) descriptor -------------//
|
||||||
|
uint8_t const itf_num = itf_desc->bInterfaceNumber;
|
||||||
|
uint8_t alt_count = 0;
|
||||||
|
|
||||||
|
uint16_t drv_len = 0;
|
||||||
|
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU, 0);
|
||||||
|
|
||||||
|
while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU)
|
||||||
|
{
|
||||||
|
TU_ASSERT(max_len > drv_len, 0);
|
||||||
|
|
||||||
|
// Alternate must have the same interface number
|
||||||
|
TU_ASSERT(itf_desc->bInterfaceNumber == itf_num, 0);
|
||||||
|
|
||||||
|
// Alt should increase by one every time
|
||||||
|
TU_ASSERT(itf_desc->bAlternateSetting == alt_count, 0);
|
||||||
|
alt_count++;
|
||||||
|
|
||||||
|
drv_len += tu_desc_len(itf_desc);
|
||||||
|
itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------- DFU Functional descriptor -------------//
|
||||||
|
tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc;
|
||||||
|
TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0);
|
||||||
|
drv_len += sizeof(tusb_desc_dfu_functional_t);
|
||||||
|
|
||||||
|
_dfu_ctx.attrs = func_desc->bAttributes;
|
||||||
|
|
||||||
|
// CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR
|
||||||
|
uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t const*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
|
||||||
|
TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len);
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||||
|
|
||||||
|
TU_LOG2(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
|
||||||
|
|
||||||
|
if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
|
||||||
|
{
|
||||||
|
// Standard request include GET/SET_INTERFACE
|
||||||
|
switch ( request->bRequest )
|
||||||
|
{
|
||||||
|
case TUSB_REQ_SET_INTERFACE:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
// Switch Alt interface and reset state machine
|
||||||
|
_dfu_ctx.alt = (uint8_t) request->wValue;
|
||||||
|
reset_state();
|
||||||
|
return tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TUSB_REQ_GET_INTERFACE:
|
||||||
|
if(stage == CONTROL_STAGE_SETUP)
|
||||||
|
{
|
||||||
|
return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unsupported request
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS )
|
||||||
|
{
|
||||||
|
TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
|
||||||
|
|
||||||
|
// Class request
|
||||||
|
switch ( request->bRequest )
|
||||||
|
{
|
||||||
|
case DFU_REQUEST_DETACH:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_ACK )
|
||||||
|
{
|
||||||
|
if ( tud_dfu_detach_cb ) tud_dfu_detach_cb();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_REQUEST_CLRSTATUS:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
reset_state();
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_REQUEST_GETSTATE:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
tud_control_xfer(rhport, request, &_dfu_ctx.state, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_REQUEST_ABORT:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
reset_state();
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_ACK )
|
||||||
|
{
|
||||||
|
if ( tud_dfu_abort_cb ) tud_dfu_abort_cb(_dfu_ctx.alt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_REQUEST_UPLOAD:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
|
||||||
|
TU_VERIFY(tud_dfu_upload_cb);
|
||||||
|
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
|
||||||
|
|
||||||
|
uint16_t const xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_ctx.transfer_buf, request->wLength);
|
||||||
|
|
||||||
|
return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_REQUEST_DNLOAD:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD);
|
||||||
|
TU_VERIFY(_dfu_ctx.state == DFU_IDLE || _dfu_ctx.state == DFU_DNLOAD_IDLE);
|
||||||
|
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
|
||||||
|
|
||||||
|
// set to true for both download and manifest
|
||||||
|
_dfu_ctx.flashing_in_progress = true;
|
||||||
|
|
||||||
|
// save block and length for flashing
|
||||||
|
_dfu_ctx.block = request->wValue;
|
||||||
|
_dfu_ctx.length = request->wLength;
|
||||||
|
|
||||||
|
if ( request->wLength )
|
||||||
|
{
|
||||||
|
// Download with payload -> transition to DOWNLOAD SYNC
|
||||||
|
_dfu_ctx.state = DFU_DNLOAD_SYNC;
|
||||||
|
return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, request->wLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Download is complete -> transition to MANIFEST SYNC
|
||||||
|
_dfu_ctx.state = DFU_MANIFEST_SYNC;
|
||||||
|
return tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_REQUEST_GETSTATUS:
|
||||||
|
switch ( _dfu_ctx.state )
|
||||||
|
{
|
||||||
|
case DFU_DNLOAD_SYNC:
|
||||||
|
return process_download_get_status(rhport, stage, request);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_MANIFEST_SYNC:
|
||||||
|
return process_manifest_get_status(rhport, stage, request);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP ) return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: return false; // stall unsupported request
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
return false; // unsupported request
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tud_dfu_finish_flashing(uint8_t status)
|
||||||
|
{
|
||||||
|
_dfu_ctx.flashing_in_progress = false;
|
||||||
|
|
||||||
|
if ( status == DFU_STATUS_OK )
|
||||||
|
{
|
||||||
|
if (_dfu_ctx.state == DFU_DNBUSY)
|
||||||
|
{
|
||||||
|
_dfu_ctx.state = DFU_DNLOAD_SYNC;
|
||||||
|
}
|
||||||
|
else if (_dfu_ctx.state == DFU_MANIFEST)
|
||||||
|
{
|
||||||
|
_dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||||
|
? DFU_MANIFEST_SYNC : DFU_MANIFEST_WAIT_RESET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// failed while flashing, move to dfuError
|
||||||
|
_dfu_ctx.state = DFU_ERROR;
|
||||||
|
_dfu_ctx.status = (dfu_status_t)status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
// only transition to next state on CONTROL_STAGE_ACK
|
||||||
|
dfu_state_t next_state;
|
||||||
|
uint32_t timeout;
|
||||||
|
|
||||||
|
if ( _dfu_ctx.flashing_in_progress )
|
||||||
|
{
|
||||||
|
next_state = DFU_DNBUSY;
|
||||||
|
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_state = DFU_DNLOAD_IDLE;
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_ACK )
|
||||||
|
{
|
||||||
|
if ( _dfu_ctx.flashing_in_progress )
|
||||||
|
{
|
||||||
|
_dfu_ctx.state = DFU_DNBUSY;
|
||||||
|
tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_ctx.transfer_buf, _dfu_ctx.length);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
_dfu_ctx.state = DFU_DNLOAD_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
// only transition to next state on CONTROL_STAGE_ACK
|
||||||
|
dfu_state_t next_state;
|
||||||
|
uint32_t timeout;
|
||||||
|
|
||||||
|
if ( _dfu_ctx.flashing_in_progress )
|
||||||
|
{
|
||||||
|
next_state = DFU_MANIFEST;
|
||||||
|
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next_state = DFU_IDLE;
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_ACK )
|
||||||
|
{
|
||||||
|
if ( _dfu_ctx.flashing_in_progress )
|
||||||
|
{
|
||||||
|
_dfu_ctx.state = DFU_MANIFEST;
|
||||||
|
tud_dfu_manifest_cb(_dfu_ctx.alt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dfu_ctx.state = DFU_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
|
||||||
|
{
|
||||||
|
dfu_status_response_t resp;
|
||||||
|
resp.bStatus = (uint8_t) status;
|
||||||
|
resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout);
|
||||||
|
resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout);
|
||||||
|
resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout);
|
||||||
|
resp.bState = (uint8_t) state;
|
||||||
|
resp.iString = 0;
|
||||||
|
|
||||||
|
return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 XMOS LIMITED
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_DFU_DEVICE_H_
|
||||||
|
#define _TUSB_DFU_DEVICE_H_
|
||||||
|
|
||||||
|
#include "dfu.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Driver Default Configure & Validation
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#if !defined(CFG_TUD_DFU_XFER_BUFSIZE)
|
||||||
|
#error "CFG_TUD_DFU_XFER_BUFSIZE must be defined, it has to be set to the buffer size used in TUD_DFU_DESCRIPTOR"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Must be called when the application is done with flashing started by
|
||||||
|
// tud_dfu_download_cb() and tud_dfu_manifest_cb().
|
||||||
|
// status is DFU_STATUS_OK if successful, any other error status will cause state to enter dfuError
|
||||||
|
void tud_dfu_finish_flashing(uint8_t status);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callback API (weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
|
||||||
|
|
||||||
|
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
|
||||||
|
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
|
||||||
|
// During this period, USB host won't try to communicate with us.
|
||||||
|
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state);
|
||||||
|
|
||||||
|
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
|
||||||
|
// This callback could be returned before flashing op is complete (async).
|
||||||
|
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||||
|
void tud_dfu_download_cb (uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length);
|
||||||
|
|
||||||
|
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
|
||||||
|
// Application can do checksum, or actual flashing if buffered entire image previously.
|
||||||
|
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||||
|
void tud_dfu_manifest_cb(uint8_t alt);
|
||||||
|
|
||||||
|
// Invoked when received DFU_UPLOAD request
|
||||||
|
// Application must populate data with up to length bytes and
|
||||||
|
// Return the number of written bytes
|
||||||
|
TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length);
|
||||||
|
|
||||||
|
// Invoked when a DFU_DETACH request is received
|
||||||
|
TU_ATTR_WEAK void tud_dfu_detach_cb(void);
|
||||||
|
|
||||||
|
// Invoked when the Host has terminated a download or upload transfer
|
||||||
|
TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void dfu_moded_init(void);
|
||||||
|
void dfu_moded_reset(uint8_t rhport);
|
||||||
|
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_DFU_MODE_DEVICE_H_ */
|
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_DFU_RUNTIME)
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "dfu_rt_device.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void dfu_rtd_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void dfu_rtd_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
(void) max_len;
|
||||||
|
|
||||||
|
// Ensure this is DFU Runtime
|
||||||
|
TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) &&
|
||||||
|
(itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT), 0);
|
||||||
|
|
||||||
|
uint8_t const * p_desc = tu_desc_next( itf_desc );
|
||||||
|
uint16_t drv_len = sizeof(tusb_desc_interface_t);
|
||||||
|
|
||||||
|
if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
|
||||||
|
{
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
// nothing to do with DATA or ACK stage
|
||||||
|
if ( stage != CONTROL_STAGE_SETUP ) return true;
|
||||||
|
|
||||||
|
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||||
|
|
||||||
|
// dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request
|
||||||
|
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
|
||||||
|
TUSB_REQ_SET_INTERFACE == request->bRequest )
|
||||||
|
{
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle class request only from here
|
||||||
|
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||||
|
|
||||||
|
switch (request->bRequest)
|
||||||
|
{
|
||||||
|
case DFU_REQUEST_DETACH:
|
||||||
|
{
|
||||||
|
TU_LOG2(" DFU RT Request: DETACH\r\n");
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
tud_dfu_runtime_reboot_to_dfu_cb();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DFU_REQUEST_GETSTATUS:
|
||||||
|
{
|
||||||
|
TU_LOG2(" DFU RT Request: GETSTATUS\r\n");
|
||||||
|
dfu_status_response_t resp;
|
||||||
|
// Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0
|
||||||
|
TU_VERIFY(tu_memset_s(&resp, sizeof(resp), 0x00, sizeof(resp))==0);
|
||||||
|
tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
TU_LOG2(" DFU RT Unexpected Request: %d\r\n", request->bRequest);
|
||||||
|
return false; // stall unsupported request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_DFU_RT_DEVICE_H_
|
||||||
|
#define _TUSB_DFU_RT_DEVICE_H_
|
||||||
|
|
||||||
|
#include "dfu.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callback API (weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Invoked when a DFU_DETACH request is received and bitWillDetach is set
|
||||||
|
void tud_dfu_runtime_reboot_to_dfu_cb(void);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void dfu_rtd_init(void);
|
||||||
|
void dfu_rtd_reset(uint8_t rhport);
|
||||||
|
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_DFU_RT_DEVICE_H_ */
|
1131
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/hid/hid 2.h
Normal file
1131
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/hid/hid 2.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,415 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_HID)
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INCLUDE
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "hid_device.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t itf_num;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out; // optional Out endpoint
|
||||||
|
uint8_t itf_protocol; // Boot mouse or keyboard
|
||||||
|
|
||||||
|
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||||
|
uint8_t idle_rate; // up to application to handle idle rate
|
||||||
|
uint16_t report_desc_len;
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
|
||||||
|
|
||||||
|
// TODO save hid descriptor since host can specifically request this after enumeration
|
||||||
|
// Note: HID descriptor may be not available from application after enumeration
|
||||||
|
tusb_hid_descriptor_hid_t const * hid_descriptor;
|
||||||
|
} hidd_interface_t;
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION tu_static hidd_interface_t _hidd_itf[CFG_TUD_HID];
|
||||||
|
|
||||||
|
/*------------- Helpers -------------*/
|
||||||
|
static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
|
||||||
|
{
|
||||||
|
for (uint8_t i=0; i < CFG_TUD_HID; i++ )
|
||||||
|
{
|
||||||
|
if ( itf_num == _hidd_itf[i].itf_num ) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// APPLICATION API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
bool tud_hid_n_ready(uint8_t instance)
|
||||||
|
{
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
uint8_t const ep_in = _hidd_itf[instance].ep_in;
|
||||||
|
return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
hidd_interface_t * p_hid = &_hidd_itf[instance];
|
||||||
|
|
||||||
|
// claim endpoint
|
||||||
|
TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
|
||||||
|
|
||||||
|
// prepare data
|
||||||
|
if (report_id)
|
||||||
|
{
|
||||||
|
p_hid->epin_buf[0] = report_id;
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf+1, CFG_TUD_HID_EP_BUFSIZE-1, report, len));
|
||||||
|
len++;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf, CFG_TUD_HID_EP_BUFSIZE, report, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tud_hid_n_interface_protocol(uint8_t instance)
|
||||||
|
{
|
||||||
|
return _hidd_itf[instance].itf_protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tud_hid_n_get_protocol(uint8_t instance)
|
||||||
|
{
|
||||||
|
return _hidd_itf[instance].protocol_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
|
||||||
|
{
|
||||||
|
hid_keyboard_report_t report;
|
||||||
|
|
||||||
|
report.modifier = modifier;
|
||||||
|
report.reserved = 0;
|
||||||
|
|
||||||
|
if ( keycode )
|
||||||
|
{
|
||||||
|
memcpy(report.keycode, keycode, sizeof(report.keycode));
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
tu_memclr(report.keycode, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
|
||||||
|
uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
|
||||||
|
{
|
||||||
|
hid_mouse_report_t report =
|
||||||
|
{
|
||||||
|
.buttons = buttons,
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.wheel = vertical,
|
||||||
|
.pan = horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
|
||||||
|
int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
|
||||||
|
hid_gamepad_report_t report =
|
||||||
|
{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.z = z,
|
||||||
|
.rz = rz,
|
||||||
|
.rx = rx,
|
||||||
|
.ry = ry,
|
||||||
|
.hat = hat,
|
||||||
|
.buttons = buttons,
|
||||||
|
};
|
||||||
|
|
||||||
|
return tud_hid_n_report(instance, report_id, &report, sizeof(report));
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD-CLASS API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void hidd_init(void)
|
||||||
|
{
|
||||||
|
hidd_reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hidd_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
|
||||||
|
{
|
||||||
|
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
|
||||||
|
|
||||||
|
// len = interface + hid + n*endpoints
|
||||||
|
uint16_t const drv_len =
|
||||||
|
(uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
|
||||||
|
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||||
|
TU_ASSERT(max_len >= drv_len, 0);
|
||||||
|
|
||||||
|
// Find available interface
|
||||||
|
hidd_interface_t * p_hid = NULL;
|
||||||
|
uint8_t hid_id;
|
||||||
|
for(hid_id=0; hid_id<CFG_TUD_HID; hid_id++)
|
||||||
|
{
|
||||||
|
if ( _hidd_itf[hid_id].ep_in == 0 )
|
||||||
|
{
|
||||||
|
p_hid = &_hidd_itf[hid_id];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TU_ASSERT(p_hid, 0);
|
||||||
|
|
||||||
|
uint8_t const *p_desc = (uint8_t const *) desc_itf;
|
||||||
|
|
||||||
|
//------------- HID descriptor -------------//
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
TU_ASSERT(HID_DESC_TYPE_HID == tu_desc_type(p_desc), 0);
|
||||||
|
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
|
||||||
|
|
||||||
|
//------------- Endpoint Descriptor -------------//
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
|
||||||
|
|
||||||
|
if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||||
|
|
||||||
|
p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
|
||||||
|
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||||
|
|
||||||
|
// Use offsetof to avoid pointer to the odd/misaligned address
|
||||||
|
p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
|
||||||
|
|
||||||
|
// Prepare for output endpoint
|
||||||
|
if (p_hid->ep_out)
|
||||||
|
{
|
||||||
|
if ( !usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)) )
|
||||||
|
{
|
||||||
|
TU_LOG_FAILED();
|
||||||
|
TU_BREAKPOINT();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||||
|
|
||||||
|
uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
|
||||||
|
TU_VERIFY(hid_itf < CFG_TUD_HID);
|
||||||
|
|
||||||
|
hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
|
||||||
|
|
||||||
|
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
|
||||||
|
{
|
||||||
|
//------------- STD Request -------------//
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
uint8_t const desc_type = tu_u16_high(request->wValue);
|
||||||
|
//uint8_t const desc_index = tu_u16_low (request->wValue);
|
||||||
|
|
||||||
|
if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
|
||||||
|
{
|
||||||
|
TU_VERIFY(p_hid->hid_descriptor);
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)(uintptr_t) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
|
||||||
|
}
|
||||||
|
else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
|
||||||
|
{
|
||||||
|
uint8_t const * desc_report = tud_hid_descriptor_report_cb(hid_itf);
|
||||||
|
tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_report, p_hid->report_desc_len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false; // stall unsupported request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
|
||||||
|
{
|
||||||
|
//------------- Class Specific Request -------------//
|
||||||
|
switch( request->bRequest )
|
||||||
|
{
|
||||||
|
case HID_REQ_CONTROL_GET_REPORT:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
uint8_t const report_type = tu_u16_high(request->wValue);
|
||||||
|
uint8_t const report_id = tu_u16_low(request->wValue);
|
||||||
|
|
||||||
|
uint8_t* report_buf = p_hid->epin_buf;
|
||||||
|
uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||||
|
|
||||||
|
uint16_t xferlen = 0;
|
||||||
|
|
||||||
|
// If host request a specific Report ID, add ID to as 1 byte of response
|
||||||
|
if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) )
|
||||||
|
{
|
||||||
|
*report_buf++ = report_id;
|
||||||
|
req_len--;
|
||||||
|
|
||||||
|
xferlen++;
|
||||||
|
}
|
||||||
|
|
||||||
|
xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
|
||||||
|
TU_ASSERT( xferlen > 0 );
|
||||||
|
|
||||||
|
tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_CONTROL_SET_REPORT:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
|
||||||
|
tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_ACK )
|
||||||
|
{
|
||||||
|
uint8_t const report_type = tu_u16_high(request->wValue);
|
||||||
|
uint8_t const report_id = tu_u16_low(request->wValue);
|
||||||
|
|
||||||
|
uint8_t const* report_buf = p_hid->epout_buf;
|
||||||
|
uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
|
||||||
|
|
||||||
|
// If host request a specific Report ID, extract report ID in buffer before invoking callback
|
||||||
|
if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) )
|
||||||
|
{
|
||||||
|
report_buf++;
|
||||||
|
report_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_CONTROL_SET_IDLE:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
p_hid->idle_rate = tu_u16_high(request->wValue);
|
||||||
|
if ( tud_hid_set_idle_cb )
|
||||||
|
{
|
||||||
|
// stall request if callback return false
|
||||||
|
TU_VERIFY( tud_hid_set_idle_cb( hid_itf, p_hid->idle_rate) );
|
||||||
|
}
|
||||||
|
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_CONTROL_GET_IDLE:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
// TODO idle rate of report
|
||||||
|
tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_CONTROL_GET_PROTOCOL:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_CONTROL_SET_PROTOCOL:
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_ACK )
|
||||||
|
{
|
||||||
|
p_hid->protocol_mode = (uint8_t) request->wValue;
|
||||||
|
if (tud_hid_set_protocol_cb)
|
||||||
|
{
|
||||||
|
tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: return false; // stall unsupported request
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
return false; // stall unsupported request
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void) result;
|
||||||
|
|
||||||
|
uint8_t instance = 0;
|
||||||
|
hidd_interface_t * p_hid = _hidd_itf;
|
||||||
|
|
||||||
|
// Identify which interface to use
|
||||||
|
for (instance = 0; instance < CFG_TUD_HID; instance++)
|
||||||
|
{
|
||||||
|
p_hid = &_hidd_itf[instance];
|
||||||
|
if ( (ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in) ) break;
|
||||||
|
}
|
||||||
|
TU_ASSERT(instance < CFG_TUD_HID);
|
||||||
|
|
||||||
|
// Sent report successfully
|
||||||
|
if (ep_addr == p_hid->ep_in)
|
||||||
|
{
|
||||||
|
if (tud_hid_report_complete_cb)
|
||||||
|
{
|
||||||
|
tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint16_t) xferred_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Received report
|
||||||
|
else if (ep_addr == p_hid->ep_out)
|
||||||
|
{
|
||||||
|
tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, (uint16_t) xferred_bytes);
|
||||||
|
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,418 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_HID_DEVICE_H_
|
||||||
|
#define _TUSB_HID_DEVICE_H_
|
||||||
|
|
||||||
|
#include "hid.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Driver Default Configure & Validation
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#if !defined(CFG_TUD_HID_EP_BUFSIZE) & defined(CFG_TUD_HID_BUFSIZE)
|
||||||
|
// TODO warn user to use new name later on
|
||||||
|
// #warning CFG_TUD_HID_BUFSIZE is renamed to CFG_TUD_HID_EP_BUFSIZE, please update to use the new name
|
||||||
|
#define CFG_TUD_HID_EP_BUFSIZE CFG_TUD_HID_BUFSIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_HID_EP_BUFSIZE
|
||||||
|
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API (Multiple Instances)
|
||||||
|
// CFG_TUD_HID > 1
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Check if the interface is ready to use
|
||||||
|
bool tud_hid_n_ready(uint8_t instance);
|
||||||
|
|
||||||
|
// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
|
||||||
|
uint8_t tud_hid_n_interface_protocol(uint8_t instance);
|
||||||
|
|
||||||
|
// Get current active protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
|
||||||
|
uint8_t tud_hid_n_get_protocol(uint8_t instance);
|
||||||
|
|
||||||
|
// Send report to host
|
||||||
|
bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len);
|
||||||
|
|
||||||
|
// KEYBOARD: convenient helper to send keyboard report if application
|
||||||
|
// use template layout report as defined by hid_keyboard_report_t
|
||||||
|
bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
|
||||||
|
|
||||||
|
// MOUSE: convenient helper to send mouse report if application
|
||||||
|
// use template layout report as defined by hid_mouse_report_t
|
||||||
|
bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
|
||||||
|
|
||||||
|
// Gamepad: convenient helper to send gamepad report if application
|
||||||
|
// use template layout report TUD_HID_REPORT_DESC_GAMEPAD
|
||||||
|
bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API (Single Port)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
static inline bool tud_hid_ready(void);
|
||||||
|
static inline uint8_t tud_hid_interface_protocol(void);
|
||||||
|
static inline uint8_t tud_hid_get_protocol(void);
|
||||||
|
static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len);
|
||||||
|
static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
|
||||||
|
static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
|
||||||
|
static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Callbacks (Weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Invoked when received GET HID REPORT DESCRIPTOR request
|
||||||
|
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||||
|
uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance);
|
||||||
|
|
||||||
|
// Invoked when received GET_REPORT control request
|
||||||
|
// Application must fill buffer report's content and return its length.
|
||||||
|
// Return zero will cause the stack to STALL request
|
||||||
|
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
|
||||||
|
|
||||||
|
// Invoked when received SET_REPORT control request or
|
||||||
|
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
|
||||||
|
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
|
||||||
|
|
||||||
|
// Invoked when received SET_PROTOCOL request
|
||||||
|
// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
|
||||||
|
TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
|
||||||
|
|
||||||
|
// Invoked when received SET_IDLE request. return false will stall the request
|
||||||
|
// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
|
||||||
|
// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
|
||||||
|
TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
|
||||||
|
|
||||||
|
// Invoked when sent REPORT successfully to host
|
||||||
|
// Application can use this to send the next report
|
||||||
|
// Note: For composite reports, report[0] is report ID
|
||||||
|
TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Inline Functions
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
static inline bool tud_hid_ready(void)
|
||||||
|
{
|
||||||
|
return tud_hid_n_ready(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t tud_hid_interface_protocol(void)
|
||||||
|
{
|
||||||
|
return tud_hid_n_interface_protocol(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t tud_hid_get_protocol(void)
|
||||||
|
{
|
||||||
|
return tud_hid_n_get_protocol(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len)
|
||||||
|
{
|
||||||
|
return tud_hid_n_report(0, report_id, report, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
|
||||||
|
{
|
||||||
|
return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
|
||||||
|
{
|
||||||
|
return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
|
||||||
|
{
|
||||||
|
return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------+
|
||||||
|
* HID Report Descriptor Template
|
||||||
|
*
|
||||||
|
* Convenient for declaring popular HID device (keyboard, mouse, consumer,
|
||||||
|
* gamepad etc...). Templates take "HID_REPORT_ID(n)" as input, leave
|
||||||
|
* empty if multiple reports is not used
|
||||||
|
*
|
||||||
|
* - Only 1 report: no parameter
|
||||||
|
* uint8_t const report_desc[] = { TUD_HID_REPORT_DESC_KEYBOARD() };
|
||||||
|
*
|
||||||
|
* - Multiple Reports: "HID_REPORT_ID(ID)" must be passed to template
|
||||||
|
* uint8_t const report_desc[] =
|
||||||
|
* {
|
||||||
|
* TUD_HID_REPORT_DESC_KEYBOARD( HID_REPORT_ID(1) ) ,
|
||||||
|
* TUD_HID_REPORT_DESC_MOUSE ( HID_REPORT_ID(2) )
|
||||||
|
* };
|
||||||
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// Keyboard Report Descriptor Template
|
||||||
|
#define TUD_HID_REPORT_DESC_KEYBOARD(...) \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ) ,\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||||
|
/* Report ID if any */\
|
||||||
|
__VA_ARGS__ \
|
||||||
|
/* 8 bits Modifier Keys (Shift, Control, Alt) */ \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
|
||||||
|
HID_USAGE_MIN ( 224 ) ,\
|
||||||
|
HID_USAGE_MAX ( 231 ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 1 ) ,\
|
||||||
|
HID_REPORT_COUNT ( 8 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 1 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
/* 8 bit reserved */ \
|
||||||
|
HID_REPORT_COUNT ( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_INPUT ( HID_CONSTANT ) ,\
|
||||||
|
/* Output 5-bit LED Indicator Kana | Compose | ScrollLock | CapsLock | NumLock */ \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ) ,\
|
||||||
|
HID_USAGE_MIN ( 1 ) ,\
|
||||||
|
HID_USAGE_MAX ( 5 ) ,\
|
||||||
|
HID_REPORT_COUNT ( 5 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 1 ) ,\
|
||||||
|
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
/* led padding */ \
|
||||||
|
HID_REPORT_COUNT ( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 3 ) ,\
|
||||||
|
HID_OUTPUT ( HID_CONSTANT ) ,\
|
||||||
|
/* 6-byte Keycodes */ \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ) ,\
|
||||||
|
HID_USAGE_MIN ( 0 ) ,\
|
||||||
|
HID_USAGE_MAX_N ( 255, 2 ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0 ) ,\
|
||||||
|
HID_LOGICAL_MAX_N( 255, 2 ) ,\
|
||||||
|
HID_REPORT_COUNT ( 6 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
|
||||||
|
HID_COLLECTION_END \
|
||||||
|
|
||||||
|
// Mouse Report Descriptor Template
|
||||||
|
#define TUD_HID_REPORT_DESC_MOUSE(...) \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||||
|
/* Report ID if any */\
|
||||||
|
__VA_ARGS__ \
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_POINTER ) ,\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||||
|
HID_USAGE_MIN ( 1 ) ,\
|
||||||
|
HID_USAGE_MAX ( 5 ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 1 ) ,\
|
||||||
|
/* Left, Right, Middle, Backward, Forward buttons */ \
|
||||||
|
HID_REPORT_COUNT( 5 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 1 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
/* 3 bit padding */ \
|
||||||
|
HID_REPORT_COUNT( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 3 ) ,\
|
||||||
|
HID_INPUT ( HID_CONSTANT ) ,\
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
|
/* X, Y position [-127, 127] */ \
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||||
|
HID_REPORT_COUNT( 2 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||||
|
/* Verital wheel scroll [-127, 127] */ \
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||||
|
HID_REPORT_COUNT( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
|
||||||
|
/* Horizontal wheel scroll [-127, 127] */ \
|
||||||
|
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
|
||||||
|
HID_LOGICAL_MIN ( 0x81 ), \
|
||||||
|
HID_LOGICAL_MAX ( 0x7f ), \
|
||||||
|
HID_REPORT_COUNT( 1 ), \
|
||||||
|
HID_REPORT_SIZE ( 8 ), \
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \
|
||||||
|
HID_COLLECTION_END , \
|
||||||
|
HID_COLLECTION_END \
|
||||||
|
|
||||||
|
// Consumer Control Report Descriptor Template
|
||||||
|
#define TUD_HID_REPORT_DESC_CONSUMER(...) \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||||
|
/* Report ID if any */\
|
||||||
|
__VA_ARGS__ \
|
||||||
|
HID_LOGICAL_MIN ( 0x00 ) ,\
|
||||||
|
HID_LOGICAL_MAX_N( 0x03FF, 2 ) ,\
|
||||||
|
HID_USAGE_MIN ( 0x00 ) ,\
|
||||||
|
HID_USAGE_MAX_N ( 0x03FF, 2 ) ,\
|
||||||
|
HID_REPORT_COUNT ( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 16 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
|
||||||
|
HID_COLLECTION_END \
|
||||||
|
|
||||||
|
/* System Control Report Descriptor Template
|
||||||
|
* 0x00 - do nothing
|
||||||
|
* 0x01 - Power Off
|
||||||
|
* 0x02 - Standby
|
||||||
|
* 0x03 - Wake Host
|
||||||
|
*/
|
||||||
|
#define TUD_HID_REPORT_DESC_SYSTEM_CONTROL(...) \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_CONTROL ) ,\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||||
|
/* Report ID if any */\
|
||||||
|
__VA_ARGS__ \
|
||||||
|
/* 2 bit system power control */ \
|
||||||
|
HID_LOGICAL_MIN ( 1 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 3 ) ,\
|
||||||
|
HID_REPORT_COUNT ( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 2 ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_SLEEP ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_SYSTEM_WAKE_UP ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
|
||||||
|
/* 6 bit padding */ \
|
||||||
|
HID_REPORT_COUNT ( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 6 ) ,\
|
||||||
|
HID_INPUT ( HID_CONSTANT ) ,\
|
||||||
|
HID_COLLECTION_END \
|
||||||
|
|
||||||
|
// Gamepad Report Descriptor Template
|
||||||
|
// with 32 buttons, 2 joysticks and 1 hat/dpad with following layout
|
||||||
|
// | X | Y | Z | Rz | Rx | Ry (1 byte each) | hat/DPAD (1 byte) | Button Map (4 bytes) |
|
||||||
|
#define TUD_HID_REPORT_DESC_GAMEPAD(...) \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ) ,\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||||
|
/* Report ID if any */\
|
||||||
|
__VA_ARGS__ \
|
||||||
|
/* 8 bit X, Y, Z, Rz, Rx, Ry (min -127, max 127 ) */ \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_X ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_Y ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_Z ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_RZ ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_RX ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_RY ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 0x7f ) ,\
|
||||||
|
HID_REPORT_COUNT ( 6 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
/* 8 bit DPad/Hat Button Map */ \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_DESKTOP_HAT_SWITCH ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 1 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 8 ) ,\
|
||||||
|
HID_PHYSICAL_MIN ( 0 ) ,\
|
||||||
|
HID_PHYSICAL_MAX_N ( 315, 2 ) ,\
|
||||||
|
HID_REPORT_COUNT ( 1 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
/* 32 bit Button Map */ \
|
||||||
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
|
||||||
|
HID_USAGE_MIN ( 1 ) ,\
|
||||||
|
HID_USAGE_MAX ( 32 ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0 ) ,\
|
||||||
|
HID_LOGICAL_MAX ( 1 ) ,\
|
||||||
|
HID_REPORT_COUNT ( 32 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 1 ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
HID_COLLECTION_END \
|
||||||
|
|
||||||
|
// FIDO U2F Authenticator Descriptor Template
|
||||||
|
// - 1st parameter is report size, which is 64 bytes maximum in U2F
|
||||||
|
// - 2nd parameter is HID_REPORT_ID(n) (optional)
|
||||||
|
#define TUD_HID_REPORT_DESC_FIDO_U2F(report_size, ...) \
|
||||||
|
HID_USAGE_PAGE_N ( HID_USAGE_PAGE_FIDO, 2 ) ,\
|
||||||
|
HID_USAGE ( HID_USAGE_FIDO_U2FHID ) ,\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
|
||||||
|
/* Report ID if any */ \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
/* Usage Data In */ \
|
||||||
|
HID_USAGE ( HID_USAGE_FIDO_DATA_IN ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0 ) ,\
|
||||||
|
HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_REPORT_COUNT ( report_size ) ,\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
/* Usage Data Out */ \
|
||||||
|
HID_USAGE ( HID_USAGE_FIDO_DATA_OUT ) ,\
|
||||||
|
HID_LOGICAL_MIN ( 0 ) ,\
|
||||||
|
HID_LOGICAL_MAX_N ( 0xff, 2 ) ,\
|
||||||
|
HID_REPORT_SIZE ( 8 ) ,\
|
||||||
|
HID_REPORT_COUNT ( report_size ) ,\
|
||||||
|
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
|
HID_COLLECTION_END \
|
||||||
|
|
||||||
|
// HID Generic Input & Output
|
||||||
|
// - 1st parameter is report size (mandatory)
|
||||||
|
// - 2nd parameter is report id HID_REPORT_ID(n) (optional)
|
||||||
|
#define TUD_HID_REPORT_DESC_GENERIC_INOUT(report_size, ...) \
|
||||||
|
HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\
|
||||||
|
HID_USAGE ( 0x01 ),\
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\
|
||||||
|
/* Report ID if any */\
|
||||||
|
__VA_ARGS__ \
|
||||||
|
/* Input */ \
|
||||||
|
HID_USAGE ( 0x02 ),\
|
||||||
|
HID_LOGICAL_MIN ( 0x00 ),\
|
||||||
|
HID_LOGICAL_MAX_N ( 0xff, 2 ),\
|
||||||
|
HID_REPORT_SIZE ( 8 ),\
|
||||||
|
HID_REPORT_COUNT( report_size ),\
|
||||||
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||||
|
/* Output */ \
|
||||||
|
HID_USAGE ( 0x03 ),\
|
||||||
|
HID_LOGICAL_MIN ( 0x00 ),\
|
||||||
|
HID_LOGICAL_MAX_N ( 0xff, 2 ),\
|
||||||
|
HID_REPORT_SIZE ( 8 ),\
|
||||||
|
HID_REPORT_COUNT( report_size ),\
|
||||||
|
HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
|
||||||
|
HID_COLLECTION_END \
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void hidd_init (void);
|
||||||
|
void hidd_reset (uint8_t rhport);
|
||||||
|
uint16_t hidd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
bool hidd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_HID_DEVICE_H_ */
|
@ -0,0 +1,772 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUH_ENABLED && CFG_TUH_HID)
|
||||||
|
|
||||||
|
#include "host/usbh.h"
|
||||||
|
#include "host/usbh_classdriver.h"
|
||||||
|
|
||||||
|
#include "hid_host.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t daddr;
|
||||||
|
|
||||||
|
uint8_t itf_num;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
|
||||||
|
uint8_t itf_protocol; // None, Keyboard, Mouse
|
||||||
|
uint8_t protocol_mode; // Boot (0) or Report protocol (1)
|
||||||
|
|
||||||
|
uint8_t report_desc_type;
|
||||||
|
uint16_t report_desc_len;
|
||||||
|
|
||||||
|
uint16_t epin_size;
|
||||||
|
uint16_t epout_size;
|
||||||
|
|
||||||
|
CFG_TUH_MEM_ALIGN uint8_t epin_buf[CFG_TUH_HID_EPIN_BUFSIZE];
|
||||||
|
CFG_TUH_MEM_ALIGN uint8_t epout_buf[CFG_TUH_HID_EPOUT_BUFSIZE];
|
||||||
|
} hidh_interface_t;
|
||||||
|
|
||||||
|
CFG_TUH_MEM_SECTION
|
||||||
|
tu_static hidh_interface_t _hidh_itf[CFG_TUH_HID];
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Helper
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
TU_ATTR_ALWAYS_INLINE static inline
|
||||||
|
hidh_interface_t* get_hid_itf(uint8_t daddr, uint8_t idx)
|
||||||
|
{
|
||||||
|
TU_ASSERT(daddr && idx < CFG_TUH_HID, NULL);
|
||||||
|
hidh_interface_t* p_hid = &_hidh_itf[idx];
|
||||||
|
return (p_hid->daddr == daddr) ? p_hid : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get instance ID by endpoint address
|
||||||
|
static uint8_t get_idx_by_epaddr(uint8_t daddr, uint8_t ep_addr)
|
||||||
|
{
|
||||||
|
for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ )
|
||||||
|
{
|
||||||
|
hidh_interface_t const * p_hid = &_hidh_itf[idx];
|
||||||
|
|
||||||
|
if ( p_hid->daddr == daddr &&
|
||||||
|
(p_hid->ep_in == ep_addr || p_hid->ep_out == ep_addr) )
|
||||||
|
{
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TUSB_INDEX_INVALID_8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static hidh_interface_t* find_new_itf(void)
|
||||||
|
{
|
||||||
|
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||||
|
{
|
||||||
|
if (_hidh_itf[i].daddr == 0) return &_hidh_itf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Interface API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
uint8_t tuh_hid_itf_get_count(uint8_t daddr)
|
||||||
|
{
|
||||||
|
uint8_t count = 0;
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||||
|
{
|
||||||
|
if (_hidh_itf[i].daddr == daddr) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tuh_hid_itf_get_total_count(void)
|
||||||
|
{
|
||||||
|
uint8_t count = 0;
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||||
|
{
|
||||||
|
if (_hidh_itf[i].daddr != 0) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_hid_mounted(uint8_t daddr, uint8_t idx)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
return p_hid != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* info)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid && info);
|
||||||
|
|
||||||
|
info->daddr = daddr;
|
||||||
|
|
||||||
|
// re-construct descriptor
|
||||||
|
tusb_desc_interface_t* desc = &info->desc;
|
||||||
|
desc->bLength = sizeof(tusb_desc_interface_t);
|
||||||
|
desc->bDescriptorType = TUSB_DESC_INTERFACE;
|
||||||
|
|
||||||
|
desc->bInterfaceNumber = p_hid->itf_num;
|
||||||
|
desc->bAlternateSetting = 0;
|
||||||
|
desc->bNumEndpoints = (uint8_t) ((p_hid->ep_in ? 1u : 0u) + (p_hid->ep_out ? 1u : 0u));
|
||||||
|
desc->bInterfaceClass = TUSB_CLASS_HID;
|
||||||
|
desc->bInterfaceSubClass = (p_hid->itf_protocol ? HID_SUBCLASS_BOOT : HID_SUBCLASS_NONE);
|
||||||
|
desc->bInterfaceProtocol = p_hid->itf_protocol;
|
||||||
|
desc->iInterface = 0; // not used yet
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num)
|
||||||
|
{
|
||||||
|
for ( uint8_t idx = 0; idx < CFG_TUH_HID; idx++ )
|
||||||
|
{
|
||||||
|
hidh_interface_t const * p_hid = &_hidh_itf[idx];
|
||||||
|
|
||||||
|
if ( p_hid->daddr == daddr && p_hid->itf_num == itf_num) return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TUSB_INDEX_INVALID_8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tuh_hid_interface_protocol(uint8_t daddr, uint8_t idx)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
return p_hid ? p_hid->itf_protocol : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Control Endpoint API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
uint8_t tuh_hid_get_protocol(uint8_t daddr, uint8_t idx)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
return p_hid ? p_hid->protocol_mode : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_protocol_complete(tuh_xfer_t* xfer)
|
||||||
|
{
|
||||||
|
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||||
|
uint8_t const daddr = xfer->daddr;
|
||||||
|
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||||
|
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid, );
|
||||||
|
|
||||||
|
if (XFER_RESULT_SUCCESS == xfer->result)
|
||||||
|
{
|
||||||
|
p_hid->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tuh_hid_set_protocol_complete_cb)
|
||||||
|
{
|
||||||
|
tuh_hid_set_protocol_complete_cb(daddr, idx, p_hid->protocol_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _hidh_set_protocol(uint8_t daddr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||||
|
{
|
||||||
|
TU_LOG2("HID Set Protocol = %d\r\n", protocol);
|
||||||
|
|
||||||
|
tusb_control_request_t const request =
|
||||||
|
{
|
||||||
|
.bmRequestType_bit =
|
||||||
|
{
|
||||||
|
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||||
|
.type = TUSB_REQ_TYPE_CLASS,
|
||||||
|
.direction = TUSB_DIR_OUT
|
||||||
|
},
|
||||||
|
.bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
|
||||||
|
.wValue = protocol,
|
||||||
|
.wIndex = itf_num,
|
||||||
|
.wLength = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
tuh_xfer_t xfer =
|
||||||
|
{
|
||||||
|
.daddr = daddr,
|
||||||
|
.ep_addr = 0,
|
||||||
|
.setup = &request,
|
||||||
|
.buffer = NULL,
|
||||||
|
.complete_cb = complete_cb,
|
||||||
|
.user_data = user_data
|
||||||
|
};
|
||||||
|
|
||||||
|
return tuh_control_xfer(&xfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid && p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE);
|
||||||
|
|
||||||
|
return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_report_complete(tuh_xfer_t* xfer)
|
||||||
|
{
|
||||||
|
TU_LOG2("HID Set Report complete\r\n");
|
||||||
|
|
||||||
|
if (tuh_hid_set_report_complete_cb)
|
||||||
|
{
|
||||||
|
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||||
|
uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
|
||||||
|
|
||||||
|
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
|
||||||
|
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||||
|
|
||||||
|
tuh_hid_set_report_complete_cb(xfer->daddr, idx, report_id, report_type,
|
||||||
|
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_hid_set_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid);
|
||||||
|
|
||||||
|
TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||||
|
|
||||||
|
tusb_control_request_t const request =
|
||||||
|
{
|
||||||
|
.bmRequestType_bit =
|
||||||
|
{
|
||||||
|
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||||
|
.type = TUSB_REQ_TYPE_CLASS,
|
||||||
|
.direction = TUSB_DIR_OUT
|
||||||
|
},
|
||||||
|
.bRequest = HID_REQ_CONTROL_SET_REPORT,
|
||||||
|
.wValue = tu_htole16(tu_u16(report_type, report_id)),
|
||||||
|
.wIndex = tu_htole16((uint16_t)p_hid->itf_num),
|
||||||
|
.wLength = len
|
||||||
|
};
|
||||||
|
|
||||||
|
tuh_xfer_t xfer =
|
||||||
|
{
|
||||||
|
.daddr = daddr,
|
||||||
|
.ep_addr = 0,
|
||||||
|
.setup = &request,
|
||||||
|
.buffer = report,
|
||||||
|
.complete_cb = set_report_complete,
|
||||||
|
.user_data = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return tuh_control_xfer(&xfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _hidh_set_idle(uint8_t daddr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||||
|
{
|
||||||
|
// SET IDLE request, device can stall if not support this request
|
||||||
|
TU_LOG2("HID Set Idle \r\n");
|
||||||
|
|
||||||
|
tusb_control_request_t const request =
|
||||||
|
{
|
||||||
|
.bmRequestType_bit =
|
||||||
|
{
|
||||||
|
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||||
|
.type = TUSB_REQ_TYPE_CLASS,
|
||||||
|
.direction = TUSB_DIR_OUT
|
||||||
|
},
|
||||||
|
.bRequest = HID_REQ_CONTROL_SET_IDLE,
|
||||||
|
.wValue = tu_htole16(idle_rate),
|
||||||
|
.wIndex = tu_htole16((uint16_t)itf_num),
|
||||||
|
.wLength = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
tuh_xfer_t xfer =
|
||||||
|
{
|
||||||
|
.daddr = daddr,
|
||||||
|
.ep_addr = 0,
|
||||||
|
.setup = &request,
|
||||||
|
.buffer = NULL,
|
||||||
|
.complete_cb = complete_cb,
|
||||||
|
.user_data = user_data
|
||||||
|
};
|
||||||
|
|
||||||
|
return tuh_control_xfer(&xfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Interrupt Endpoint API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Check if HID interface is ready to receive report
|
||||||
|
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||||
|
TU_VERIFY(p_hid);
|
||||||
|
|
||||||
|
return !usbh_edpt_busy(dev_addr, p_hid->ep_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_hid_receive_report(uint8_t daddr, uint8_t idx)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid);
|
||||||
|
|
||||||
|
// claim endpoint
|
||||||
|
TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_in) );
|
||||||
|
|
||||||
|
if ( !usbh_edpt_xfer(daddr, p_hid->ep_in, p_hid->epin_buf, p_hid->epin_size) )
|
||||||
|
{
|
||||||
|
usbh_edpt_release(daddr, p_hid->ep_in);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(dev_addr, idx);
|
||||||
|
TU_VERIFY(p_hid);
|
||||||
|
|
||||||
|
return !usbh_edpt_busy(dev_addr, p_hid->ep_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_hid_send_report(uint8_t daddr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len)
|
||||||
|
{
|
||||||
|
TU_LOG2("HID Send Report %d\r\n", report_id);
|
||||||
|
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid);
|
||||||
|
|
||||||
|
if (p_hid->ep_out == 0)
|
||||||
|
{
|
||||||
|
// This HID does not have an out endpoint (other than control)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (len > CFG_TUH_HID_EPOUT_BUFSIZE ||
|
||||||
|
(report_id != 0 && len > (CFG_TUH_HID_EPOUT_BUFSIZE - 1)))
|
||||||
|
{
|
||||||
|
// ep_out buffer is not large enough to hold contents
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// claim endpoint
|
||||||
|
TU_VERIFY( usbh_edpt_claim(daddr, p_hid->ep_out) );
|
||||||
|
|
||||||
|
if (report_id == 0)
|
||||||
|
{
|
||||||
|
// No report ID in transmission
|
||||||
|
memcpy(&p_hid->epout_buf[0], report, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_hid->epout_buf[0] = report_id;
|
||||||
|
memcpy(&p_hid->epout_buf[1], report, len);
|
||||||
|
++len; // 1 more byte for report_id
|
||||||
|
}
|
||||||
|
|
||||||
|
TU_LOG3_MEM(p_hid->epout_buf, len, 2);
|
||||||
|
|
||||||
|
if ( !usbh_edpt_xfer(daddr, p_hid->ep_out, p_hid->epout_buf, len) )
|
||||||
|
{
|
||||||
|
usbh_edpt_release(daddr, p_hid->ep_out);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBH API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void hidh_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(_hidh_itf, sizeof(_hidh_itf));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void) result;
|
||||||
|
|
||||||
|
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||||
|
uint8_t const idx = get_idx_by_epaddr(daddr, ep_addr);
|
||||||
|
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid);
|
||||||
|
|
||||||
|
if ( dir == TUSB_DIR_IN )
|
||||||
|
{
|
||||||
|
TU_LOG2(" Get Report callback (%u, %u)\r\n", daddr, idx);
|
||||||
|
TU_LOG3_MEM(p_hid->epin_buf, xferred_bytes, 2);
|
||||||
|
tuh_hid_report_received_cb(daddr, idx, p_hid->epin_buf, (uint16_t) xferred_bytes);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
if (tuh_hid_report_sent_cb) tuh_hid_report_sent_cb(daddr, idx, p_hid->epout_buf, (uint16_t) xferred_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hidh_close(uint8_t daddr)
|
||||||
|
{
|
||||||
|
for(uint8_t i=0; i<CFG_TUH_HID; i++)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = &_hidh_itf[i];
|
||||||
|
if (p_hid->daddr == daddr)
|
||||||
|
{
|
||||||
|
if(tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
|
||||||
|
p_hid->daddr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Enumeration
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
bool hidh_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
(void) max_len;
|
||||||
|
|
||||||
|
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
|
||||||
|
|
||||||
|
TU_LOG2("[%u] HID opening Interface %u\r\n", daddr, desc_itf->bInterfaceNumber);
|
||||||
|
|
||||||
|
// len = interface + hid + n*endpoints
|
||||||
|
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
|
||||||
|
desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||||
|
TU_ASSERT(max_len >= drv_len);
|
||||||
|
|
||||||
|
uint8_t const *p_desc = (uint8_t const *) desc_itf;
|
||||||
|
|
||||||
|
//------------- HID descriptor -------------//
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
|
||||||
|
TU_ASSERT(HID_DESC_TYPE_HID == desc_hid->bDescriptorType);
|
||||||
|
|
||||||
|
hidh_interface_t* p_hid = find_new_itf();
|
||||||
|
TU_ASSERT(p_hid); // not enough interface, try to increase CFG_TUH_HID
|
||||||
|
p_hid->daddr = daddr;
|
||||||
|
|
||||||
|
//------------- Endpoint Descriptors -------------//
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||||
|
|
||||||
|
for(int i = 0; i < desc_itf->bNumEndpoints; i++)
|
||||||
|
{
|
||||||
|
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
|
||||||
|
TU_ASSERT( tuh_edpt_open(daddr, desc_ep) );
|
||||||
|
|
||||||
|
if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
|
||||||
|
{
|
||||||
|
p_hid->ep_in = desc_ep->bEndpointAddress;
|
||||||
|
p_hid->epin_size = tu_edpt_packet_size(desc_ep);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_hid->ep_out = desc_ep->bEndpointAddress;
|
||||||
|
p_hid->epout_size = tu_edpt_packet_size(desc_ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_hid->itf_num = desc_itf->bInterfaceNumber;
|
||||||
|
|
||||||
|
// Assume bNumDescriptors = 1
|
||||||
|
p_hid->report_desc_type = desc_hid->bReportType;
|
||||||
|
p_hid->report_desc_len = tu_unaligned_read16(&desc_hid->wReportLength);
|
||||||
|
|
||||||
|
// Per HID Specs: default is Report protocol, though we will force Boot protocol when set_config
|
||||||
|
p_hid->protocol_mode = HID_PROTOCOL_BOOT;
|
||||||
|
if ( HID_SUBCLASS_BOOT == desc_itf->bInterfaceSubClass )
|
||||||
|
{
|
||||||
|
p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Set Configure
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CONFG_SET_IDLE,
|
||||||
|
CONFIG_SET_PROTOCOL,
|
||||||
|
CONFIG_GET_REPORT_DESC,
|
||||||
|
CONFIG_COMPLETE
|
||||||
|
};
|
||||||
|
|
||||||
|
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len);
|
||||||
|
static void process_set_config(tuh_xfer_t* xfer);
|
||||||
|
|
||||||
|
bool hidh_set_config(uint8_t daddr, uint8_t itf_num)
|
||||||
|
{
|
||||||
|
tusb_control_request_t request;
|
||||||
|
request.wIndex = tu_htole16((uint16_t) itf_num);
|
||||||
|
|
||||||
|
tuh_xfer_t xfer;
|
||||||
|
xfer.daddr = daddr;
|
||||||
|
xfer.result = XFER_RESULT_SUCCESS;
|
||||||
|
xfer.setup = &request;
|
||||||
|
xfer.user_data = CONFG_SET_IDLE;
|
||||||
|
|
||||||
|
// fake request to kick-off the set config process
|
||||||
|
process_set_config(&xfer);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_set_config(tuh_xfer_t* xfer)
|
||||||
|
{
|
||||||
|
// Stall is a valid response for SET_IDLE, sometime SET_PROTOCOL as well
|
||||||
|
// therefore we could ignore its result
|
||||||
|
if ( !(xfer->setup->bRequest == HID_REQ_CONTROL_SET_IDLE ||
|
||||||
|
xfer->setup->bRequest == HID_REQ_CONTROL_SET_PROTOCOL) )
|
||||||
|
{
|
||||||
|
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t const state = xfer->user_data;
|
||||||
|
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||||
|
uint8_t const daddr = xfer->daddr;
|
||||||
|
|
||||||
|
uint8_t const idx = tuh_hid_itf_get_index(daddr, itf_num);
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid, );
|
||||||
|
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case CONFG_SET_IDLE:
|
||||||
|
{
|
||||||
|
// Idle rate = 0 mean only report when there is changes
|
||||||
|
const uint16_t idle_rate = 0;
|
||||||
|
const uintptr_t next_state = (p_hid->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
|
||||||
|
_hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONFIG_SET_PROTOCOL:
|
||||||
|
_hidh_set_protocol(daddr, p_hid->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONFIG_GET_REPORT_DESC:
|
||||||
|
// Get Report Descriptor if possible
|
||||||
|
// using usbh enumeration buffer since report descriptor can be very long
|
||||||
|
if( p_hid->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
|
||||||
|
{
|
||||||
|
TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", p_hid->report_desc_len);
|
||||||
|
|
||||||
|
// Driver is mounted without report descriptor
|
||||||
|
config_driver_mount_complete(daddr, idx, NULL, 0);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
tuh_descriptor_get_hid_report(daddr, itf_num, p_hid->report_desc_type, 0, usbh_get_enum_buf(), p_hid->report_desc_len, process_set_config, CONFIG_COMPLETE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONFIG_COMPLETE:
|
||||||
|
{
|
||||||
|
uint8_t const* desc_report = usbh_get_enum_buf();
|
||||||
|
uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
|
||||||
|
|
||||||
|
config_driver_mount_complete(daddr, idx, desc_report, desc_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_driver_mount_complete(uint8_t daddr, uint8_t idx, uint8_t const* desc_report, uint16_t desc_len)
|
||||||
|
{
|
||||||
|
hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
|
||||||
|
TU_VERIFY(p_hid, );
|
||||||
|
|
||||||
|
// enumeration is complete
|
||||||
|
if (tuh_hid_mount_cb) tuh_hid_mount_cb(daddr, idx, desc_report, desc_len);
|
||||||
|
|
||||||
|
// notify usbh that driver enumeration is complete
|
||||||
|
usbh_driver_set_config_complete(daddr, p_hid->itf_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Report Descriptor Parser
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len)
|
||||||
|
{
|
||||||
|
// Report Item 6.2.2.2 USB HID 1.11
|
||||||
|
union TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t byte;
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t size : 2;
|
||||||
|
uint8_t type : 2;
|
||||||
|
uint8_t tag : 4;
|
||||||
|
};
|
||||||
|
} header;
|
||||||
|
|
||||||
|
tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t));
|
||||||
|
|
||||||
|
uint8_t report_num = 0;
|
||||||
|
tuh_hid_report_info_t* info = report_info_arr;
|
||||||
|
|
||||||
|
// current parsed report count & size from descriptor
|
||||||
|
// uint8_t ri_report_count = 0;
|
||||||
|
// uint8_t ri_report_size = 0;
|
||||||
|
|
||||||
|
uint8_t ri_collection_depth = 0;
|
||||||
|
|
||||||
|
while(desc_len && report_num < arr_count)
|
||||||
|
{
|
||||||
|
header.byte = *desc_report++;
|
||||||
|
desc_len--;
|
||||||
|
|
||||||
|
uint8_t const tag = header.tag;
|
||||||
|
uint8_t const type = header.type;
|
||||||
|
uint8_t const size = header.size;
|
||||||
|
|
||||||
|
uint8_t const data8 = desc_report[0];
|
||||||
|
|
||||||
|
TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
|
||||||
|
for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
|
||||||
|
TU_LOG(3, "\r\n");
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case RI_TYPE_MAIN:
|
||||||
|
switch (tag)
|
||||||
|
{
|
||||||
|
case RI_MAIN_INPUT: break;
|
||||||
|
case RI_MAIN_OUTPUT: break;
|
||||||
|
case RI_MAIN_FEATURE: break;
|
||||||
|
|
||||||
|
case RI_MAIN_COLLECTION:
|
||||||
|
ri_collection_depth++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_MAIN_COLLECTION_END:
|
||||||
|
ri_collection_depth--;
|
||||||
|
if (ri_collection_depth == 0)
|
||||||
|
{
|
||||||
|
info++;
|
||||||
|
report_num++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_TYPE_GLOBAL:
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case RI_GLOBAL_USAGE_PAGE:
|
||||||
|
// only take in account the "usage page" before REPORT ID
|
||||||
|
if ( ri_collection_depth == 0 ) memcpy(&info->usage_page, desc_report, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_GLOBAL_LOGICAL_MIN : break;
|
||||||
|
case RI_GLOBAL_LOGICAL_MAX : break;
|
||||||
|
case RI_GLOBAL_PHYSICAL_MIN : break;
|
||||||
|
case RI_GLOBAL_PHYSICAL_MAX : break;
|
||||||
|
|
||||||
|
case RI_GLOBAL_REPORT_ID:
|
||||||
|
info->report_id = data8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_GLOBAL_REPORT_SIZE:
|
||||||
|
// ri_report_size = data8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_GLOBAL_REPORT_COUNT:
|
||||||
|
// ri_report_count = data8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_GLOBAL_UNIT_EXPONENT : break;
|
||||||
|
case RI_GLOBAL_UNIT : break;
|
||||||
|
case RI_GLOBAL_PUSH : break;
|
||||||
|
case RI_GLOBAL_POP : break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_TYPE_LOCAL:
|
||||||
|
switch(tag)
|
||||||
|
{
|
||||||
|
case RI_LOCAL_USAGE:
|
||||||
|
// only take in account the "usage" before starting REPORT ID
|
||||||
|
if ( ri_collection_depth == 0 ) info->usage = data8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RI_LOCAL_USAGE_MIN : break;
|
||||||
|
case RI_LOCAL_USAGE_MAX : break;
|
||||||
|
case RI_LOCAL_DESIGNATOR_INDEX : break;
|
||||||
|
case RI_LOCAL_DESIGNATOR_MIN : break;
|
||||||
|
case RI_LOCAL_DESIGNATOR_MAX : break;
|
||||||
|
case RI_LOCAL_STRING_INDEX : break;
|
||||||
|
case RI_LOCAL_STRING_MIN : break;
|
||||||
|
case RI_LOCAL_STRING_MAX : break;
|
||||||
|
case RI_LOCAL_DELIMITER : break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// error
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc_report += size;
|
||||||
|
desc_len -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( uint8_t i = 0; i < report_num; i++ )
|
||||||
|
{
|
||||||
|
info = report_info_arr+i;
|
||||||
|
TU_LOG2("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return report_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_HID_HOST_H_
|
||||||
|
#define _TUSB_HID_HOST_H_
|
||||||
|
|
||||||
|
#include "hid.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Driver Configuration
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// TODO Highspeed interrupt can be up to 512 bytes
|
||||||
|
#ifndef CFG_TUH_HID_EPIN_BUFSIZE
|
||||||
|
#define CFG_TUH_HID_EPIN_BUFSIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUH_HID_EPOUT_BUFSIZE
|
||||||
|
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t report_id;
|
||||||
|
uint8_t usage;
|
||||||
|
uint16_t usage_page;
|
||||||
|
|
||||||
|
// TODO still use the endpoint size for now
|
||||||
|
// uint8_t in_len; // length of IN report
|
||||||
|
// uint8_t out_len; // length of OUT report
|
||||||
|
} tuh_hid_report_info_t;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Interface API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Get the total number of mounted HID interfaces of a device
|
||||||
|
uint8_t tuh_hid_itf_get_count(uint8_t dev_addr);
|
||||||
|
|
||||||
|
// Get all mounted interfaces across devices
|
||||||
|
uint8_t tuh_hid_itf_get_total_count(void);
|
||||||
|
|
||||||
|
// backward compatible rename
|
||||||
|
#define tuh_hid_instance_count tuh_hid_itf_get_count
|
||||||
|
|
||||||
|
// Get Interface information
|
||||||
|
bool tuh_hid_itf_get_info(uint8_t daddr, uint8_t idx, tuh_itf_info_t* itf_info);
|
||||||
|
|
||||||
|
// Get Interface index from device address + interface number
|
||||||
|
// return TUSB_INDEX_INVALID_8 (0xFF) if not found
|
||||||
|
uint8_t tuh_hid_itf_get_index(uint8_t daddr, uint8_t itf_num);
|
||||||
|
|
||||||
|
// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
|
||||||
|
uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t idx);
|
||||||
|
|
||||||
|
// Check if HID interface is mounted
|
||||||
|
bool tuh_hid_mounted(uint8_t dev_addr, uint8_t idx);
|
||||||
|
|
||||||
|
// Parse report descriptor into array of report_info struct and return number of reports.
|
||||||
|
// For complicated report, application should write its own parser.
|
||||||
|
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Control Endpoint API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Get current protocol: HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
|
||||||
|
// Note: Device will be initialized in Boot protocol for simplicity.
|
||||||
|
// Application can use set_protocol() to switch back to Report protocol.
|
||||||
|
uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t idx);
|
||||||
|
|
||||||
|
// Set protocol to HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
|
||||||
|
// This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
|
||||||
|
bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
|
||||||
|
|
||||||
|
// Set Report using control endpoint
|
||||||
|
// report_type is either Input, Output or Feature, (value from hid_report_type_t)
|
||||||
|
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Interrupt Endpoint API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Check if HID interface is ready to receive report
|
||||||
|
bool tuh_hid_receive_ready(uint8_t dev_addr, uint8_t idx);
|
||||||
|
|
||||||
|
// Try to receive next report on Interrupt Endpoint. Immediately return
|
||||||
|
// - true If succeeded, tuh_hid_report_received_cb() callback will be invoked when report is available
|
||||||
|
// - false if failed to queue the transfer e.g endpoint is busy
|
||||||
|
bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t idx);
|
||||||
|
|
||||||
|
// Check if HID interface is ready to send report
|
||||||
|
bool tuh_hid_send_ready(uint8_t dev_addr, uint8_t idx);
|
||||||
|
|
||||||
|
// Send report using interrupt endpoint
|
||||||
|
// If report_id > 0 (composite), it will be sent as 1st byte, then report contents. Otherwise only report content is sent.
|
||||||
|
bool tuh_hid_send_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, const void* report, uint16_t len);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Callbacks (Weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Invoked when device with hid interface is mounted
|
||||||
|
// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
|
||||||
|
// can be used to parse common/simple enough descriptor.
|
||||||
|
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
|
||||||
|
// therefore report_desc = NULL, desc_len = 0
|
||||||
|
TU_ATTR_WEAK void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report_desc, uint16_t desc_len);
|
||||||
|
|
||||||
|
// Invoked when device with hid interface is un-mounted
|
||||||
|
TU_ATTR_WEAK void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx);
|
||||||
|
|
||||||
|
// Invoked when received report from device via interrupt endpoint
|
||||||
|
// Note: if there is report ID (composite), it is 1st byte of report
|
||||||
|
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
|
||||||
|
|
||||||
|
// Invoked when sent report to device successfully via interrupt endpoint
|
||||||
|
TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
|
||||||
|
|
||||||
|
// Invoked when Sent Report to device via either control endpoint
|
||||||
|
// len = 0 indicate there is error in the transfer e.g stalled response
|
||||||
|
TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
|
||||||
|
|
||||||
|
// Invoked when Set Protocol request is complete
|
||||||
|
TU_ATTR_WEAK void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void hidh_init (void);
|
||||||
|
bool hidh_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||||
|
bool hidh_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||||
|
bool hidh_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||||
|
void hidh_close (uint8_t dev_addr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_HID_HOST_H_ */
|
212
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/midi/midi 2.h
Normal file
212
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/midi/midi 2.h
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \ingroup group_class
|
||||||
|
* \defgroup ClassDriver_CDC Communication Device Class (CDC)
|
||||||
|
* Currently only Abstract Control Model subclass is supported
|
||||||
|
* @{ */
|
||||||
|
|
||||||
|
#ifndef _TUSB_MIDI_H__
|
||||||
|
#define _TUSB_MIDI_H__
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Specific Descriptor
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MIDI_CS_INTERFACE_HEADER = 0x01,
|
||||||
|
MIDI_CS_INTERFACE_IN_JACK = 0x02,
|
||||||
|
MIDI_CS_INTERFACE_OUT_JACK = 0x03,
|
||||||
|
MIDI_CS_INTERFACE_ELEMENT = 0x04,
|
||||||
|
} midi_cs_interface_subtype_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MIDI_CS_ENDPOINT_GENERAL = 0x01
|
||||||
|
} midi_cs_endpoint_subtype_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MIDI_JACK_EMBEDDED = 0x01,
|
||||||
|
MIDI_JACK_EXTERNAL = 0x02
|
||||||
|
} midi_jack_type_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MIDI_CIN_MISC = 0,
|
||||||
|
MIDI_CIN_CABLE_EVENT = 1,
|
||||||
|
MIDI_CIN_SYSCOM_2BYTE = 2, // 2 byte system common message e.g MTC, SongSelect
|
||||||
|
MIDI_CIN_SYSCOM_3BYTE = 3, // 3 byte system common message e.g SPP
|
||||||
|
MIDI_CIN_SYSEX_START = 4, // SysEx starts or continue
|
||||||
|
MIDI_CIN_SYSEX_END_1BYTE = 5, // SysEx ends with 1 data, or 1 byte system common message
|
||||||
|
MIDI_CIN_SYSEX_END_2BYTE = 6, // SysEx ends with 2 data
|
||||||
|
MIDI_CIN_SYSEX_END_3BYTE = 7, // SysEx ends with 3 data
|
||||||
|
MIDI_CIN_NOTE_OFF = 8,
|
||||||
|
MIDI_CIN_NOTE_ON = 9,
|
||||||
|
MIDI_CIN_POLY_KEYPRESS = 10,
|
||||||
|
MIDI_CIN_CONTROL_CHANGE = 11,
|
||||||
|
MIDI_CIN_PROGRAM_CHANGE = 12,
|
||||||
|
MIDI_CIN_CHANNEL_PRESSURE = 13,
|
||||||
|
MIDI_CIN_PITCH_BEND_CHANGE = 14,
|
||||||
|
MIDI_CIN_1BYTE_DATA = 15
|
||||||
|
} midi_code_index_number_t;
|
||||||
|
|
||||||
|
// MIDI 1.0 status byte
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
//------------- System Exclusive -------------//
|
||||||
|
MIDI_STATUS_SYSEX_START = 0xF0,
|
||||||
|
MIDI_STATUS_SYSEX_END = 0xF7,
|
||||||
|
|
||||||
|
//------------- System Common -------------//
|
||||||
|
MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME = 0xF1,
|
||||||
|
MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER = 0xF2,
|
||||||
|
MIDI_STATUS_SYSCOM_SONG_SELECT = 0xF3,
|
||||||
|
// F4, F5 is undefined
|
||||||
|
MIDI_STATUS_SYSCOM_TUNE_REQUEST = 0xF6,
|
||||||
|
|
||||||
|
//------------- System RealTime -------------//
|
||||||
|
MIDI_STATUS_SYSREAL_TIMING_CLOCK = 0xF8,
|
||||||
|
// 0xF9 is undefined
|
||||||
|
MIDI_STATUS_SYSREAL_START = 0xFA,
|
||||||
|
MIDI_STATUS_SYSREAL_CONTINUE = 0xFB,
|
||||||
|
MIDI_STATUS_SYSREAL_STOP = 0xFC,
|
||||||
|
// 0xFD is undefined
|
||||||
|
MIDI_STATUS_SYSREAL_ACTIVE_SENSING = 0xFE,
|
||||||
|
MIDI_STATUS_SYSREAL_SYSTEM_RESET = 0xFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// MIDI Interface Header Descriptor
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||||
|
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||||
|
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||||
|
uint16_t bcdMSC ; ///< MidiStreaming SubClass release number in Binary-Coded Decimal
|
||||||
|
uint16_t wTotalLength ;
|
||||||
|
} midi_desc_header_t;
|
||||||
|
|
||||||
|
/// MIDI In Jack Descriptor
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||||
|
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||||
|
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||||
|
uint8_t bJackType ; ///< Embedded or External
|
||||||
|
uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
|
||||||
|
uint8_t iJack ; ///< string descriptor
|
||||||
|
} midi_desc_in_jack_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// MIDI Out Jack Descriptor with single pin
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||||
|
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||||
|
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||||
|
uint8_t bJackType ; ///< Embedded or External
|
||||||
|
uint8_t bJackID ; ///< Unique ID for MIDI IN Jack
|
||||||
|
uint8_t bNrInputPins;
|
||||||
|
|
||||||
|
uint8_t baSourceID;
|
||||||
|
uint8_t baSourcePin;
|
||||||
|
|
||||||
|
uint8_t iJack ; ///< string descriptor
|
||||||
|
} midi_desc_out_jack_t ;
|
||||||
|
|
||||||
|
/// MIDI Out Jack Descriptor with multiple pins
|
||||||
|
#define midi_desc_out_jack_n_t(input_num) \
|
||||||
|
struct TU_ATTR_PACKED { \
|
||||||
|
uint8_t bLength ; \
|
||||||
|
uint8_t bDescriptorType ; \
|
||||||
|
uint8_t bDescriptorSubType ; \
|
||||||
|
uint8_t bJackType ; \
|
||||||
|
uint8_t bJackID ; \
|
||||||
|
uint8_t bNrInputPins ; \
|
||||||
|
struct TU_ATTR_PACKED { \
|
||||||
|
uint8_t baSourceID; \
|
||||||
|
uint8_t baSourcePin; \
|
||||||
|
} pins[input_num]; \
|
||||||
|
uint8_t iJack ; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MIDI Element Descriptor
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t bLength ; ///< Size of this descriptor in bytes.
|
||||||
|
uint8_t bDescriptorType ; ///< Descriptor Type, must be Class-Specific
|
||||||
|
uint8_t bDescriptorSubType ; ///< Descriptor SubType
|
||||||
|
uint8_t bElementID;
|
||||||
|
|
||||||
|
uint8_t bNrInputPins;
|
||||||
|
uint8_t baSourceID;
|
||||||
|
uint8_t baSourcePin;
|
||||||
|
|
||||||
|
uint8_t bNrOutputPins;
|
||||||
|
uint8_t bInTerminalLink;
|
||||||
|
uint8_t bOutTerminalLink;
|
||||||
|
uint8_t bElCapsSize;
|
||||||
|
|
||||||
|
uint16_t bmElementCaps;
|
||||||
|
uint8_t iElement;
|
||||||
|
} midi_desc_element_t;
|
||||||
|
|
||||||
|
/// MIDI Element Descriptor with multiple pins
|
||||||
|
#define midi_desc_element_n_t(input_num) \
|
||||||
|
struct TU_ATTR_PACKED { \
|
||||||
|
uint8_t bLength; \
|
||||||
|
uint8_t bDescriptorType; \
|
||||||
|
uint8_t bDescriptorSubType; \
|
||||||
|
uint8_t bElementID; \
|
||||||
|
uint8_t bNrInputPins; \
|
||||||
|
struct TU_ATTR_PACKED { \
|
||||||
|
uint8_t baSourceID; \
|
||||||
|
uint8_t baSourcePin; \
|
||||||
|
} pins[input_num]; \
|
||||||
|
uint8_t bNrOutputPins; \
|
||||||
|
uint8_t bInTerminalLink; \
|
||||||
|
uint8_t bOutTerminalLink; \
|
||||||
|
uint8_t bElCapsSize; \
|
||||||
|
uint16_t bmElementCaps; \
|
||||||
|
uint8_t iElement; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
@ -0,0 +1,546 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_MIDI)
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INCLUDE
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "midi_device.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t buffer[4];
|
||||||
|
uint8_t index;
|
||||||
|
uint8_t total;
|
||||||
|
}midid_stream_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t itf_num;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
|
||||||
|
// For Stream read()/write() API
|
||||||
|
// Messages are always 4 bytes long, queue them for reading and writing so the
|
||||||
|
// callers can use the Stream interface with single-byte read/write calls.
|
||||||
|
midid_stream_t stream_write;
|
||||||
|
midid_stream_t stream_read;
|
||||||
|
|
||||||
|
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||||
|
// FIFO
|
||||||
|
tu_fifo_t rx_ff;
|
||||||
|
tu_fifo_t tx_ff;
|
||||||
|
uint8_t rx_ff_buf[CFG_TUD_MIDI_RX_BUFSIZE];
|
||||||
|
uint8_t tx_ff_buf[CFG_TUD_MIDI_TX_BUFSIZE];
|
||||||
|
|
||||||
|
#if CFG_FIFO_MUTEX
|
||||||
|
osal_mutex_def_t rx_ff_mutex;
|
||||||
|
osal_mutex_def_t tx_ff_mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Endpoint Transfer buffer
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_MIDI_EP_BUFSIZE];
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_MIDI_EP_BUFSIZE];
|
||||||
|
|
||||||
|
} midid_interface_t;
|
||||||
|
|
||||||
|
#define ITF_MEM_RESET_SIZE offsetof(midid_interface_t, rx_ff)
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
CFG_TUSB_MEM_SECTION midid_interface_t _midid_itf[CFG_TUD_MIDI];
|
||||||
|
|
||||||
|
bool tud_midi_n_mounted (uint8_t itf)
|
||||||
|
{
|
||||||
|
midid_interface_t* midi = &_midid_itf[itf];
|
||||||
|
return midi->ep_in && midi->ep_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _prep_out_transaction (midid_interface_t* p_midi)
|
||||||
|
{
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
uint16_t available = tu_fifo_remaining(&p_midi->rx_ff);
|
||||||
|
|
||||||
|
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||||
|
// TODO Actually we can still carry out the transfer, keeping count of received bytes
|
||||||
|
// and slowly move it to the FIFO when read().
|
||||||
|
// This pre-check reduces endpoint claiming
|
||||||
|
TU_VERIFY(available >= sizeof(p_midi->epout_buf), );
|
||||||
|
|
||||||
|
// claim endpoint
|
||||||
|
TU_VERIFY(usbd_edpt_claim(rhport, p_midi->ep_out), );
|
||||||
|
|
||||||
|
// fifo can be changed before endpoint is claimed
|
||||||
|
available = tu_fifo_remaining(&p_midi->rx_ff);
|
||||||
|
|
||||||
|
if ( available >= sizeof(p_midi->epout_buf) ) {
|
||||||
|
usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, sizeof(p_midi->epout_buf));
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Release endpoint since we don't make any transfer
|
||||||
|
usbd_edpt_release(rhport, p_midi->ep_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// READ API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
uint32_t tud_midi_n_available(uint8_t itf, uint8_t cable_num)
|
||||||
|
{
|
||||||
|
(void) cable_num;
|
||||||
|
|
||||||
|
midid_interface_t* midi = &_midid_itf[itf];
|
||||||
|
midid_stream_t const* stream = &midi->stream_read;
|
||||||
|
|
||||||
|
// when using with packet API stream total & index are both zero
|
||||||
|
return tu_fifo_count(&midi->rx_ff) + (uint8_t) (stream->total - stream->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tud_midi_n_stream_read(uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
(void) cable_num;
|
||||||
|
TU_VERIFY(bufsize, 0);
|
||||||
|
|
||||||
|
uint8_t* buf8 = (uint8_t*) buffer;
|
||||||
|
|
||||||
|
midid_interface_t* midi = &_midid_itf[itf];
|
||||||
|
midid_stream_t* stream = &midi->stream_read;
|
||||||
|
|
||||||
|
uint32_t total_read = 0;
|
||||||
|
while( bufsize )
|
||||||
|
{
|
||||||
|
// Get new packet from fifo, then set packet expected bytes
|
||||||
|
if ( stream->total == 0 )
|
||||||
|
{
|
||||||
|
// return if there is no more data from fifo
|
||||||
|
if ( !tud_midi_n_packet_read(itf, stream->buffer) ) return total_read;
|
||||||
|
|
||||||
|
uint8_t const code_index = stream->buffer[0] & 0x0f;
|
||||||
|
|
||||||
|
// MIDI 1.0 Table 4-1: Code Index Number Classifications
|
||||||
|
switch(code_index)
|
||||||
|
{
|
||||||
|
case MIDI_CIN_MISC:
|
||||||
|
case MIDI_CIN_CABLE_EVENT:
|
||||||
|
// These are reserved and unused, possibly issue somewhere, skip this packet
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIDI_CIN_SYSEX_END_1BYTE:
|
||||||
|
case MIDI_CIN_1BYTE_DATA:
|
||||||
|
stream->total = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MIDI_CIN_SYSCOM_2BYTE :
|
||||||
|
case MIDI_CIN_SYSEX_END_2BYTE :
|
||||||
|
case MIDI_CIN_PROGRAM_CHANGE :
|
||||||
|
case MIDI_CIN_CHANNEL_PRESSURE :
|
||||||
|
stream->total = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
stream->total = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy data up to bufsize
|
||||||
|
uint8_t const count = (uint8_t) tu_min32(stream->total - stream->index, bufsize);
|
||||||
|
|
||||||
|
// Skip the header (1st byte) in the buffer
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(buf8, bufsize, stream->buffer + 1 + stream->index, count));
|
||||||
|
|
||||||
|
total_read += count;
|
||||||
|
stream->index += count;
|
||||||
|
buf8 += count;
|
||||||
|
bufsize -= count;
|
||||||
|
|
||||||
|
// complete current event packet, reset stream
|
||||||
|
if ( stream->total == stream->index )
|
||||||
|
{
|
||||||
|
stream->index = 0;
|
||||||
|
stream->total = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4])
|
||||||
|
{
|
||||||
|
midid_interface_t* midi = &_midid_itf[itf];
|
||||||
|
TU_VERIFY(midi->ep_out);
|
||||||
|
|
||||||
|
uint32_t const num_read = tu_fifo_read_n(&midi->rx_ff, packet, 4);
|
||||||
|
_prep_out_transaction(midi);
|
||||||
|
return (num_read == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// WRITE API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
static uint32_t write_flush(midid_interface_t* midi)
|
||||||
|
{
|
||||||
|
// No data to send
|
||||||
|
if ( !tu_fifo_count(&midi->tx_ff) ) return 0;
|
||||||
|
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
|
||||||
|
// skip if previous transfer not complete
|
||||||
|
TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
|
||||||
|
|
||||||
|
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
{
|
||||||
|
TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 );
|
||||||
|
return count;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Release endpoint since we don't make any transfer
|
||||||
|
usbd_edpt_release(rhport, midi->ep_in);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tud_midi_n_stream_write(uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
midid_interface_t* midi = &_midid_itf[itf];
|
||||||
|
TU_VERIFY(midi->ep_in, 0);
|
||||||
|
|
||||||
|
midid_stream_t* stream = &midi->stream_write;
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
|
while ( (i < bufsize) && (tu_fifo_remaining(&midi->tx_ff) >= 4) )
|
||||||
|
{
|
||||||
|
uint8_t const data = buffer[i];
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if ( stream->index == 0 )
|
||||||
|
{
|
||||||
|
//------------- New event packet -------------//
|
||||||
|
|
||||||
|
uint8_t const msg = data >> 4;
|
||||||
|
|
||||||
|
stream->index = 2;
|
||||||
|
stream->buffer[1] = data;
|
||||||
|
|
||||||
|
// Check to see if we're still in a SysEx transmit.
|
||||||
|
if ( ((stream->buffer[0]) & 0xF) == MIDI_CIN_SYSEX_START )
|
||||||
|
{
|
||||||
|
if ( data == MIDI_STATUS_SYSEX_END )
|
||||||
|
{
|
||||||
|
stream->buffer[0] = (uint8_t) ((cable_num << 4) | MIDI_CIN_SYSEX_END_1BYTE);
|
||||||
|
stream->total = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream->total = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE )
|
||||||
|
{
|
||||||
|
// Channel Voice Messages
|
||||||
|
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
|
||||||
|
stream->total = 4;
|
||||||
|
}
|
||||||
|
else if ( msg == 0xC || msg == 0xD)
|
||||||
|
{
|
||||||
|
// Channel Voice Messages, two-byte variants (Program Change and Channel Pressure)
|
||||||
|
stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg);
|
||||||
|
stream->total = 3;
|
||||||
|
}
|
||||||
|
else if ( msg == 0xf )
|
||||||
|
{
|
||||||
|
// System message
|
||||||
|
if ( data == MIDI_STATUS_SYSEX_START )
|
||||||
|
{
|
||||||
|
stream->buffer[0] = MIDI_CIN_SYSEX_START;
|
||||||
|
stream->total = 4;
|
||||||
|
}
|
||||||
|
else if ( data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT )
|
||||||
|
{
|
||||||
|
stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE;
|
||||||
|
stream->total = 3;
|
||||||
|
}
|
||||||
|
else if ( data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER )
|
||||||
|
{
|
||||||
|
stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE;
|
||||||
|
stream->total = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE;
|
||||||
|
stream->total = 2;
|
||||||
|
}
|
||||||
|
stream->buffer[0] |= (uint8_t)(cable_num << 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Pack individual bytes if we don't support packing them into words.
|
||||||
|
stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf);
|
||||||
|
stream->buffer[2] = 0;
|
||||||
|
stream->buffer[3] = 0;
|
||||||
|
stream->index = 2;
|
||||||
|
stream->total = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//------------- On-going (buffering) packet -------------//
|
||||||
|
|
||||||
|
TU_ASSERT(stream->index < 4, i);
|
||||||
|
stream->buffer[stream->index] = data;
|
||||||
|
stream->index++;
|
||||||
|
|
||||||
|
// See if this byte ends a SysEx.
|
||||||
|
if ( (stream->buffer[0] & 0xF) == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END )
|
||||||
|
{
|
||||||
|
stream->buffer[0] = (uint8_t) ((cable_num << 4) | (MIDI_CIN_SYSEX_START + (stream->index - 1)));
|
||||||
|
stream->total = stream->index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send out packet
|
||||||
|
if ( stream->index == stream->total )
|
||||||
|
{
|
||||||
|
// zeroes unused bytes
|
||||||
|
for(uint8_t idx = stream->total; idx < 4; idx++) stream->buffer[idx] = 0;
|
||||||
|
|
||||||
|
uint16_t const count = tu_fifo_write_n(&midi->tx_ff, stream->buffer, 4);
|
||||||
|
|
||||||
|
// complete current event packet, reset stream
|
||||||
|
stream->index = stream->total = 0;
|
||||||
|
|
||||||
|
// FIFO overflown, since we already check fifo remaining. It is probably race condition
|
||||||
|
TU_ASSERT(count == 4, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_flush(midi);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4])
|
||||||
|
{
|
||||||
|
midid_interface_t* midi = &_midid_itf[itf];
|
||||||
|
TU_VERIFY(midi->ep_in);
|
||||||
|
|
||||||
|
if (tu_fifo_remaining(&midi->tx_ff) < 4) return false;
|
||||||
|
|
||||||
|
tu_fifo_write_n(&midi->tx_ff, packet, 4);
|
||||||
|
write_flush(midi);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void midid_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(_midid_itf, sizeof(_midid_itf));
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
|
||||||
|
{
|
||||||
|
midid_interface_t* midi = &_midid_itf[i];
|
||||||
|
|
||||||
|
// config fifo
|
||||||
|
tu_fifo_config(&midi->rx_ff, midi->rx_ff_buf, CFG_TUD_MIDI_RX_BUFSIZE, 1, false); // true, true
|
||||||
|
tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
|
||||||
|
|
||||||
|
#if CFG_FIFO_MUTEX
|
||||||
|
tu_fifo_config_mutex(&midi->rx_ff, NULL, osal_mutex_create(&midi->rx_ff_mutex));
|
||||||
|
tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex), NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void midid_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
|
||||||
|
{
|
||||||
|
midid_interface_t* midi = &_midid_itf[i];
|
||||||
|
tu_memclr(midi, ITF_MEM_RESET_SIZE);
|
||||||
|
tu_fifo_clear(&midi->rx_ff);
|
||||||
|
tu_fifo_clear(&midi->tx_ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
|
||||||
|
{
|
||||||
|
// 1st Interface is Audio Control v1
|
||||||
|
TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass &&
|
||||||
|
AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass &&
|
||||||
|
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0);
|
||||||
|
|
||||||
|
uint16_t drv_len = tu_desc_len(desc_itf);
|
||||||
|
uint8_t const * p_desc = tu_desc_next(desc_itf);
|
||||||
|
|
||||||
|
// Skip Class Specific descriptors
|
||||||
|
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
|
||||||
|
{
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd Interface is MIDI Streaming
|
||||||
|
TU_VERIFY(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
|
||||||
|
tusb_desc_interface_t const * desc_midi = (tusb_desc_interface_t const *) p_desc;
|
||||||
|
|
||||||
|
TU_VERIFY(TUSB_CLASS_AUDIO == desc_midi->bInterfaceClass &&
|
||||||
|
AUDIO_SUBCLASS_MIDI_STREAMING == desc_midi->bInterfaceSubClass &&
|
||||||
|
AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_midi->bInterfaceProtocol, 0);
|
||||||
|
|
||||||
|
// Find available interface
|
||||||
|
midid_interface_t * p_midi = NULL;
|
||||||
|
for(uint8_t i=0; i<CFG_TUD_MIDI; i++)
|
||||||
|
{
|
||||||
|
if ( _midid_itf[i].ep_in == 0 && _midid_itf[i].ep_out == 0 )
|
||||||
|
{
|
||||||
|
p_midi = &_midid_itf[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TU_ASSERT(p_midi);
|
||||||
|
|
||||||
|
p_midi->itf_num = desc_midi->bInterfaceNumber;
|
||||||
|
(void) p_midi->itf_num;
|
||||||
|
|
||||||
|
// next descriptor
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
|
||||||
|
// Find and open endpoint descriptors
|
||||||
|
uint8_t found_endpoints = 0;
|
||||||
|
while ( (found_endpoints < desc_midi->bNumEndpoints) && (drv_len <= max_len) )
|
||||||
|
{
|
||||||
|
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
|
||||||
|
{
|
||||||
|
TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
|
||||||
|
uint8_t ep_addr = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
|
||||||
|
|
||||||
|
if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
|
||||||
|
{
|
||||||
|
p_midi->ep_in = ep_addr;
|
||||||
|
} else {
|
||||||
|
p_midi->ep_out = ep_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class Specific MIDI Stream endpoint descriptor
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
|
||||||
|
found_endpoints += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare for incoming data
|
||||||
|
_prep_out_transaction(p_midi);
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool midid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
(void) stage;
|
||||||
|
(void) request;
|
||||||
|
|
||||||
|
// driver doesn't support any request yet
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void) result;
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
uint8_t itf;
|
||||||
|
midid_interface_t* p_midi;
|
||||||
|
|
||||||
|
// Identify which interface to use
|
||||||
|
for (itf = 0; itf < CFG_TUD_MIDI; itf++)
|
||||||
|
{
|
||||||
|
p_midi = &_midid_itf[itf];
|
||||||
|
if ( ( ep_addr == p_midi->ep_out ) || ( ep_addr == p_midi->ep_in ) ) break;
|
||||||
|
}
|
||||||
|
TU_ASSERT(itf < CFG_TUD_MIDI);
|
||||||
|
|
||||||
|
// receive new data
|
||||||
|
if ( ep_addr == p_midi->ep_out )
|
||||||
|
{
|
||||||
|
tu_fifo_write_n(&p_midi->rx_ff, p_midi->epout_buf, (uint16_t) xferred_bytes);
|
||||||
|
|
||||||
|
// invoke receive callback if available
|
||||||
|
if (tud_midi_rx_cb) tud_midi_rx_cb(itf);
|
||||||
|
|
||||||
|
// prepare for next
|
||||||
|
// TODO for now ep_out is not used by public API therefore there is no race condition,
|
||||||
|
// and does not need to claim like ep_in
|
||||||
|
_prep_out_transaction(p_midi);
|
||||||
|
}
|
||||||
|
else if ( ep_addr == p_midi->ep_in )
|
||||||
|
{
|
||||||
|
if (0 == write_flush(p_midi))
|
||||||
|
{
|
||||||
|
// If there is no data left, a ZLP should be sent if
|
||||||
|
// xferred_bytes is multiple of EP size and not zero
|
||||||
|
if ( !tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
|
||||||
|
{
|
||||||
|
if ( usbd_edpt_claim(rhport, p_midi->ep_in) )
|
||||||
|
{
|
||||||
|
usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_MIDI_DEVICE_H_
|
||||||
|
#define _TUSB_MIDI_DEVICE_H_
|
||||||
|
|
||||||
|
#include "class/audio/audio.h"
|
||||||
|
#include "midi.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Driver Configuration
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#if !defined(CFG_TUD_MIDI_EP_BUFSIZE) && defined(CFG_TUD_MIDI_EPSIZE)
|
||||||
|
#warning CFG_TUD_MIDI_EPSIZE is renamed to CFG_TUD_MIDI_EP_BUFSIZE, please update to use the new name
|
||||||
|
#define CFG_TUD_MIDI_EP_BUFSIZE CFG_TUD_MIDI_EPSIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_MIDI_EP_BUFSIZE
|
||||||
|
#define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** \addtogroup MIDI_Serial Serial
|
||||||
|
* @{
|
||||||
|
* \defgroup MIDI_Serial_Device Device
|
||||||
|
* @{ */
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API (Multiple Interfaces)
|
||||||
|
// CFG_TUD_MIDI > 1
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Check if midi interface is mounted
|
||||||
|
bool tud_midi_n_mounted (uint8_t itf);
|
||||||
|
|
||||||
|
// Get the number of bytes available for reading
|
||||||
|
uint32_t tud_midi_n_available (uint8_t itf, uint8_t cable_num);
|
||||||
|
|
||||||
|
// Read byte stream (legacy)
|
||||||
|
uint32_t tud_midi_n_stream_read (uint8_t itf, uint8_t cable_num, void* buffer, uint32_t bufsize);
|
||||||
|
|
||||||
|
// Write byte Stream (legacy)
|
||||||
|
uint32_t tud_midi_n_stream_write (uint8_t itf, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize);
|
||||||
|
|
||||||
|
// Read event packet (4 bytes)
|
||||||
|
bool tud_midi_n_packet_read (uint8_t itf, uint8_t packet[4]);
|
||||||
|
|
||||||
|
// Write event packet (4 bytes)
|
||||||
|
bool tud_midi_n_packet_write (uint8_t itf, uint8_t const packet[4]);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API (Single Interface)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
static inline bool tud_midi_mounted (void);
|
||||||
|
static inline uint32_t tud_midi_available (void);
|
||||||
|
|
||||||
|
static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize);
|
||||||
|
static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize);
|
||||||
|
|
||||||
|
static inline bool tud_midi_packet_read (uint8_t packet[4]);
|
||||||
|
static inline bool tud_midi_packet_write (uint8_t const packet[4]);
|
||||||
|
|
||||||
|
//------------- Deprecated API name -------------//
|
||||||
|
// TODO remove after 0.10.0 release
|
||||||
|
|
||||||
|
TU_ATTR_DEPRECATED("tud_midi_read() is renamed to tud_midi_stream_read()")
|
||||||
|
static inline uint32_t tud_midi_read (void* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
return tud_midi_stream_read(buffer, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
TU_ATTR_DEPRECATED("tud_midi_write() is renamed to tud_midi_stream_write()")
|
||||||
|
static inline uint32_t tud_midi_write(uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
return tud_midi_stream_write(cable_num, buffer, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TU_ATTR_DEPRECATED("tud_midi_send() is renamed to tud_midi_packet_write()")
|
||||||
|
static inline bool tud_midi_send(uint8_t packet[4])
|
||||||
|
{
|
||||||
|
return tud_midi_packet_write(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
TU_ATTR_DEPRECATED("tud_midi_receive() is renamed to tud_midi_packet_read()")
|
||||||
|
static inline bool tud_midi_receive(uint8_t packet[4])
|
||||||
|
{
|
||||||
|
return tud_midi_packet_read(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callback API (weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
TU_ATTR_WEAK void tud_midi_rx_cb(uint8_t itf);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Inline Functions
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
static inline bool tud_midi_mounted (void)
|
||||||
|
{
|
||||||
|
return tud_midi_n_mounted(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_midi_available (void)
|
||||||
|
{
|
||||||
|
return tud_midi_n_available(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_midi_stream_read (void* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
return tud_midi_n_stream_read(0, 0, buffer, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_midi_stream_write (uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
return tud_midi_n_stream_write(0, cable_num, buffer, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_midi_packet_read (uint8_t packet[4])
|
||||||
|
{
|
||||||
|
return tud_midi_n_packet_read(0, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_midi_packet_write (uint8_t const packet[4])
|
||||||
|
{
|
||||||
|
return tud_midi_n_packet_write(0, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void midid_init (void);
|
||||||
|
void midid_reset (uint8_t rhport);
|
||||||
|
uint16_t midid_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool midid_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
bool midid_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_MIDI_DEVICE_H_ */
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
/** @} */
|
382
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/msc/msc 2.h
Normal file
382
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/msc/msc 2.h
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_MSC_H_
|
||||||
|
#define _TUSB_MSC_H_
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Mass Storage Class Constant
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
/// MassStorage Subclass
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D
|
||||||
|
MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device
|
||||||
|
MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device
|
||||||
|
MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device
|
||||||
|
MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device
|
||||||
|
MSC_SUBCLASS_SCSI ///< SCSI transparent command set
|
||||||
|
}msc_subclass_type_t;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian)
|
||||||
|
MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief MassStorage Protocol.
|
||||||
|
/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt)
|
||||||
|
MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt)
|
||||||
|
MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport
|
||||||
|
}msc_protocol_type_t;
|
||||||
|
|
||||||
|
/// MassStorage Class-Specific Control Request
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MSC_REQ_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15
|
||||||
|
MSC_REQ_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host.
|
||||||
|
}msc_request_type_t;
|
||||||
|
|
||||||
|
/// \brief Command Block Status Values
|
||||||
|
/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed
|
||||||
|
/// successfully. A non-zero value shall indicate a failure during command execution according to the following
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED
|
||||||
|
MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED
|
||||||
|
MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR
|
||||||
|
}msc_csw_status_t;
|
||||||
|
|
||||||
|
/// Command Block Wrapper
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint32_t signature; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW.
|
||||||
|
uint32_t tag; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW.
|
||||||
|
uint32_t total_bytes; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags.
|
||||||
|
uint8_t dir; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host.
|
||||||
|
uint8_t lun; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero.
|
||||||
|
uint8_t cmd_len; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16
|
||||||
|
uint8_t command[16]; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block
|
||||||
|
}msc_cbw_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct");
|
||||||
|
|
||||||
|
/// Command Status Wrapper
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW.
|
||||||
|
uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW.
|
||||||
|
uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device
|
||||||
|
uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t
|
||||||
|
}msc_csw_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct");
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// SCSI Constant
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/// SCSI Command Operation Code
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation.
|
||||||
|
SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device.
|
||||||
|
SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters.
|
||||||
|
SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command.
|
||||||
|
SCSI_CMD_START_STOP_UNIT = 0x1B,
|
||||||
|
SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E,
|
||||||
|
SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device.
|
||||||
|
SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device.
|
||||||
|
SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed
|
||||||
|
SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer.
|
||||||
|
SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them.
|
||||||
|
}scsi_cmd_type_t;
|
||||||
|
|
||||||
|
/// SCSI Sense Key
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SCSI_SENSE_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command
|
||||||
|
SCSI_SENSE_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive.
|
||||||
|
SCSI_SENSE_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed.
|
||||||
|
SCSI_SENSE_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition.
|
||||||
|
SCSI_SENSE_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test.
|
||||||
|
SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters
|
||||||
|
SCSI_SENSE_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset.
|
||||||
|
SCSI_SENSE_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed.
|
||||||
|
SCSI_SENSE_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key.
|
||||||
|
SCSI_SENSE_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command.
|
||||||
|
SCSI_SENSE_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison.
|
||||||
|
SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium.
|
||||||
|
SCSI_SENSE_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium.
|
||||||
|
}scsi_sense_key_type_t;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// SCSI Primary Command (SPC-4)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/// SCSI Test Unit Ready Command
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY
|
||||||
|
uint8_t lun ; ///< Logical Unit
|
||||||
|
uint8_t reserved[3] ;
|
||||||
|
uint8_t control ;
|
||||||
|
} scsi_test_unit_ready_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct");
|
||||||
|
|
||||||
|
/// SCSI Inquiry Command
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY
|
||||||
|
uint8_t reserved1 ;
|
||||||
|
uint8_t page_code ;
|
||||||
|
uint8_t reserved2 ;
|
||||||
|
uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred.
|
||||||
|
uint8_t control ;
|
||||||
|
} scsi_inquiry_t, scsi_request_sense_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct");
|
||||||
|
|
||||||
|
/// SCSI Inquiry Response Data
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t peripheral_device_type : 5;
|
||||||
|
uint8_t peripheral_qualifier : 3;
|
||||||
|
|
||||||
|
uint8_t : 7;
|
||||||
|
uint8_t is_removable : 1;
|
||||||
|
|
||||||
|
uint8_t version;
|
||||||
|
|
||||||
|
uint8_t response_data_format : 4;
|
||||||
|
uint8_t hierarchical_support : 1;
|
||||||
|
uint8_t normal_aca : 1;
|
||||||
|
uint8_t : 2;
|
||||||
|
|
||||||
|
uint8_t additional_length;
|
||||||
|
|
||||||
|
uint8_t protect : 1;
|
||||||
|
uint8_t : 2;
|
||||||
|
uint8_t third_party_copy : 1;
|
||||||
|
uint8_t target_port_group_support : 2;
|
||||||
|
uint8_t access_control_coordinator : 1;
|
||||||
|
uint8_t scc_support : 1;
|
||||||
|
|
||||||
|
uint8_t addr16 : 1;
|
||||||
|
uint8_t : 3;
|
||||||
|
uint8_t multi_port : 1;
|
||||||
|
uint8_t : 1; // vendor specific
|
||||||
|
uint8_t enclosure_service : 1;
|
||||||
|
uint8_t : 1;
|
||||||
|
|
||||||
|
uint8_t : 1; // vendor specific
|
||||||
|
uint8_t cmd_que : 1;
|
||||||
|
uint8_t : 2;
|
||||||
|
uint8_t sync : 1;
|
||||||
|
uint8_t wbus16 : 1;
|
||||||
|
uint8_t : 2;
|
||||||
|
|
||||||
|
uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product.
|
||||||
|
uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor.
|
||||||
|
uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor.
|
||||||
|
} scsi_inquiry_resp_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_inquiry_resp_t) == 36, "size is not correct");
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
|
||||||
|
uint8_t valid : 1;
|
||||||
|
|
||||||
|
uint8_t reserved;
|
||||||
|
|
||||||
|
uint8_t sense_key : 4;
|
||||||
|
uint8_t : 1;
|
||||||
|
uint8_t ili : 1; ///< Incorrect length indicator
|
||||||
|
uint8_t end_of_medium : 1;
|
||||||
|
uint8_t filemark : 1;
|
||||||
|
|
||||||
|
uint32_t information;
|
||||||
|
uint8_t add_sense_len;
|
||||||
|
uint32_t command_specific_info;
|
||||||
|
uint8_t add_sense_code;
|
||||||
|
uint8_t add_sense_qualifier;
|
||||||
|
uint8_t field_replaceable_unit_code;
|
||||||
|
|
||||||
|
uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout
|
||||||
|
|
||||||
|
} scsi_sense_fixed_resp_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_sense_fixed_resp_t) == 18, "size is not correct");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6
|
||||||
|
|
||||||
|
uint8_t : 3;
|
||||||
|
uint8_t disable_block_descriptor : 1;
|
||||||
|
uint8_t : 4;
|
||||||
|
|
||||||
|
uint8_t page_code : 6;
|
||||||
|
uint8_t page_control : 2;
|
||||||
|
|
||||||
|
uint8_t subpage_code;
|
||||||
|
uint8_t alloc_length;
|
||||||
|
uint8_t control;
|
||||||
|
} scsi_mode_sense6_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_t) == 6, "size is not correct");
|
||||||
|
|
||||||
|
// This is only a Mode parameter header(6).
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t data_len;
|
||||||
|
uint8_t medium_type;
|
||||||
|
|
||||||
|
uint8_t reserved : 7;
|
||||||
|
bool write_protected : 1;
|
||||||
|
|
||||||
|
uint8_t block_descriptor_len;
|
||||||
|
} scsi_mode_sense6_resp_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(scsi_mode_sense6_resp_t) == 4, "size is not correct");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
|
||||||
|
uint8_t reserved[3];
|
||||||
|
uint8_t prohibit_removal;
|
||||||
|
uint8_t control;
|
||||||
|
} scsi_prevent_allow_medium_removal_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code;
|
||||||
|
|
||||||
|
uint8_t immded : 1;
|
||||||
|
uint8_t : 7;
|
||||||
|
|
||||||
|
uint8_t TU_RESERVED;
|
||||||
|
|
||||||
|
uint8_t power_condition_mod : 4;
|
||||||
|
uint8_t : 4;
|
||||||
|
|
||||||
|
uint8_t start : 1;
|
||||||
|
uint8_t load_eject : 1;
|
||||||
|
uint8_t no_flush : 1;
|
||||||
|
uint8_t : 1;
|
||||||
|
uint8_t power_condition : 4;
|
||||||
|
|
||||||
|
uint8_t control;
|
||||||
|
} scsi_start_stop_unit_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(scsi_start_stop_unit_t) == 6, "size is not correct");
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// SCSI MMC
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
/// SCSI Read Format Capacity: Write Capacity
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code;
|
||||||
|
uint8_t reserved[6];
|
||||||
|
uint16_t alloc_length;
|
||||||
|
uint8_t control;
|
||||||
|
} scsi_read_format_capacity_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED{
|
||||||
|
uint8_t reserved[3];
|
||||||
|
uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it.
|
||||||
|
|
||||||
|
uint32_t block_num; /// Number of Logical Blocks
|
||||||
|
uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present
|
||||||
|
|
||||||
|
uint8_t reserved2;
|
||||||
|
uint16_t block_size_u16;
|
||||||
|
|
||||||
|
} scsi_read_format_capacity_data_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct");
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// SCSI Block Command (SBC-3)
|
||||||
|
// NOTE: All data in SCSI command are in Big Endian
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/// SCSI Read Capacity 10 Command: Read Capacity
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10
|
||||||
|
uint8_t reserved1 ;
|
||||||
|
uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
|
||||||
|
uint16_t reserved2 ;
|
||||||
|
uint8_t partial_medium_indicator ;
|
||||||
|
uint8_t control ;
|
||||||
|
} scsi_read_capacity10_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct");
|
||||||
|
|
||||||
|
/// SCSI Read Capacity 10 Response Data
|
||||||
|
typedef struct {
|
||||||
|
uint32_t last_lba ; ///< The last Logical Block Address of the device
|
||||||
|
uint32_t block_size ; ///< Block size in bytes
|
||||||
|
} scsi_read_capacity10_resp_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_read_capacity10_resp_t) == 8, "size is not correct");
|
||||||
|
|
||||||
|
/// SCSI Read 10 Command
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t cmd_code ; ///< SCSI OpCode
|
||||||
|
uint8_t reserved ; // has LUN according to wiki
|
||||||
|
uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
|
||||||
|
uint8_t reserved2 ;
|
||||||
|
uint16_t block_count ; ///< Number of Blocks used by this command
|
||||||
|
uint8_t control ;
|
||||||
|
} scsi_read10_t, scsi_write10_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct");
|
||||||
|
TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_MSC_H_ */
|
@ -0,0 +1,952 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_MSC)
|
||||||
|
|
||||||
|
#include "device/dcd.h" // for faking dcd_event_xfer_complete
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "msc_device.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Can be selectively disabled to reduce logging when troubleshooting other driver
|
||||||
|
#define MSC_DEBUG 2
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MSC_STAGE_CMD = 0,
|
||||||
|
MSC_STAGE_DATA,
|
||||||
|
MSC_STAGE_STATUS,
|
||||||
|
MSC_STAGE_STATUS_SENT,
|
||||||
|
MSC_STAGE_NEED_RESET,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// TODO optimize alignment
|
||||||
|
CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
|
||||||
|
CFG_TUSB_MEM_ALIGN msc_csw_t csw;
|
||||||
|
|
||||||
|
uint8_t itf_num;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
|
||||||
|
// Bulk Only Transfer (BOT) Protocol
|
||||||
|
uint8_t stage;
|
||||||
|
uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw
|
||||||
|
uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage
|
||||||
|
|
||||||
|
// Sense Response Data
|
||||||
|
uint8_t sense_key;
|
||||||
|
uint8_t add_sense_code;
|
||||||
|
uint8_t add_sense_qualifier;
|
||||||
|
}mscd_interface_t;
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static mscd_interface_t _mscd_itf;
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
|
||||||
|
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
|
||||||
|
|
||||||
|
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
|
||||||
|
static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir)
|
||||||
|
{
|
||||||
|
return tu_bit_test(dir, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc)
|
||||||
|
{
|
||||||
|
// Data residue is always = host expect - actual transferred
|
||||||
|
p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
|
||||||
|
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS_SENT;
|
||||||
|
return usbd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc)
|
||||||
|
{
|
||||||
|
p_msc->stage = MSC_STAGE_CMD;
|
||||||
|
return usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status)
|
||||||
|
{
|
||||||
|
msc_cbw_t const * p_cbw = &p_msc->cbw;
|
||||||
|
msc_csw_t * p_csw = &p_msc->csw;
|
||||||
|
|
||||||
|
p_csw->status = status;
|
||||||
|
p_csw->data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len;
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
|
||||||
|
// failed but sense key is not set: default to Illegal Request
|
||||||
|
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||||
|
|
||||||
|
// If there is data stage and not yet complete, stall it
|
||||||
|
if ( p_cbw->total_bytes && p_csw->data_residue )
|
||||||
|
{
|
||||||
|
if ( is_data_in(p_cbw->dir) )
|
||||||
|
{
|
||||||
|
usbd_edpt_stall(rhport, p_msc->ep_in);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usbd_edpt_stall(rhport, p_msc->ep_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t rdwr10_get_lba(uint8_t const command[])
|
||||||
|
{
|
||||||
|
// use offsetof to avoid pointer to the odd/unaligned address
|
||||||
|
uint32_t const lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
|
||||||
|
|
||||||
|
// lba is in Big Endian
|
||||||
|
return tu_ntohl(lba);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t rdwr10_get_blockcount(msc_cbw_t const* cbw)
|
||||||
|
{
|
||||||
|
uint16_t const block_count = tu_unaligned_read16(cbw->command + offsetof(scsi_write10_t, block_count));
|
||||||
|
return tu_ntohs(block_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t rdwr10_get_blocksize(msc_cbw_t const* cbw)
|
||||||
|
{
|
||||||
|
// first extract block count in the command
|
||||||
|
uint16_t const block_count = rdwr10_get_blockcount(cbw);
|
||||||
|
|
||||||
|
// invalid block count
|
||||||
|
if (block_count == 0) return 0;
|
||||||
|
|
||||||
|
return (uint16_t) (cbw->total_bytes / block_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw)
|
||||||
|
{
|
||||||
|
uint8_t status = MSC_CSW_STATUS_PASSED;
|
||||||
|
uint16_t const block_count = rdwr10_get_blockcount(cbw);
|
||||||
|
|
||||||
|
if ( cbw->total_bytes == 0 )
|
||||||
|
{
|
||||||
|
if ( block_count )
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di) or case 3 (Hn < Do) \r\n");
|
||||||
|
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// no data transfer, only exist in complaint test suite
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
if ( SCSI_CMD_READ_10 == cbw->command[0] && !is_data_in(cbw->dir) )
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI case 10 (Ho <> Di)\r\n");
|
||||||
|
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||||
|
}
|
||||||
|
else if ( SCSI_CMD_WRITE_10 == cbw->command[0] && is_data_in(cbw->dir) )
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI case 8 (Hi <> Do)\r\n");
|
||||||
|
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||||
|
}
|
||||||
|
else if ( 0 == block_count )
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI case 4 Hi > Dn (READ10) or case 9 Ho > Dn (WRITE10) \r\n");
|
||||||
|
status = MSC_CSW_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
else if ( cbw->total_bytes / block_count == 0 )
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " Computed block size = 0. SCSI case 7 Hi < Di (READ10) or case 13 Ho < Do (WRIT10)\r\n");
|
||||||
|
status = MSC_CSW_STATUS_PHASE_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Debug
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
#if CFG_TUSB_DEBUG >= 2
|
||||||
|
|
||||||
|
TU_ATTR_UNUSED tu_static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
|
||||||
|
{
|
||||||
|
{ .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
|
||||||
|
{ .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
|
||||||
|
{ .key = SCSI_CMD_MODE_SELECT_6 , .data = "Mode_Select 6" },
|
||||||
|
{ .key = SCSI_CMD_MODE_SENSE_6 , .data = "Mode_Sense 6" },
|
||||||
|
{ .key = SCSI_CMD_START_STOP_UNIT , .data = "Start Stop Unit" },
|
||||||
|
{ .key = SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL , .data = "Prevent/Allow Medium Removal" },
|
||||||
|
{ .key = SCSI_CMD_READ_CAPACITY_10 , .data = "Read Capacity10" },
|
||||||
|
{ .key = SCSI_CMD_REQUEST_SENSE , .data = "Request Sense" },
|
||||||
|
{ .key = SCSI_CMD_READ_FORMAT_CAPACITY , .data = "Read Format Capacity" },
|
||||||
|
{ .key = SCSI_CMD_READ_10 , .data = "Read10" },
|
||||||
|
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
|
||||||
|
};
|
||||||
|
|
||||||
|
TU_ATTR_UNUSED tu_static tu_lookup_table_t const _msc_scsi_cmd_table =
|
||||||
|
{
|
||||||
|
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
|
||||||
|
.items = _msc_scsi_cmd_lookup
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// APPLICATION API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier)
|
||||||
|
{
|
||||||
|
(void) lun;
|
||||||
|
|
||||||
|
_mscd_itf.sense_key = sense_key;
|
||||||
|
_mscd_itf.add_sense_code = add_sense_code;
|
||||||
|
_mscd_itf.add_sense_qualifier = add_sense_qualifier;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_sense_medium_not_present(uint8_t lun)
|
||||||
|
{
|
||||||
|
// default sense is NOT READY, MEDIUM NOT PRESENT
|
||||||
|
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void mscd_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mscd_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||||
|
{
|
||||||
|
// only support SCSI's BOT protocol
|
||||||
|
TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
|
||||||
|
MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
|
||||||
|
MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
|
||||||
|
|
||||||
|
// msc driver length is fixed
|
||||||
|
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
|
||||||
|
|
||||||
|
// Max length must be at least 1 interface + 2 endpoints
|
||||||
|
TU_ASSERT(max_len >= drv_len, 0);
|
||||||
|
|
||||||
|
mscd_interface_t * p_msc = &_mscd_itf;
|
||||||
|
p_msc->itf_num = itf_desc->bInterfaceNumber;
|
||||||
|
|
||||||
|
// Open endpoint pair
|
||||||
|
TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
|
||||||
|
|
||||||
|
// Prepare for Command Block Wrapper
|
||||||
|
TU_ASSERT( prepare_cbw(rhport, p_msc), drv_len);
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void proc_bot_reset(mscd_interface_t* p_msc)
|
||||||
|
{
|
||||||
|
p_msc->stage = MSC_STAGE_CMD;
|
||||||
|
p_msc->total_len = 0;
|
||||||
|
p_msc->xferred_len = 0;
|
||||||
|
|
||||||
|
p_msc->sense_key = 0;
|
||||||
|
p_msc->add_sense_code = 0;
|
||||||
|
p_msc->add_sense_qualifier = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
// nothing to do with DATA & ACK stage
|
||||||
|
if (stage != CONTROL_STAGE_SETUP) return true;
|
||||||
|
|
||||||
|
mscd_interface_t* p_msc = &_mscd_itf;
|
||||||
|
|
||||||
|
// Clear Endpoint Feature (stall) for recovery
|
||||||
|
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
|
||||||
|
TUSB_REQ_RCPT_ENDPOINT == request->bmRequestType_bit.recipient &&
|
||||||
|
TUSB_REQ_CLEAR_FEATURE == request->bRequest &&
|
||||||
|
TUSB_REQ_FEATURE_EDPT_HALT == request->wValue )
|
||||||
|
{
|
||||||
|
uint8_t const ep_addr = tu_u16_low(request->wIndex);
|
||||||
|
|
||||||
|
if ( p_msc->stage == MSC_STAGE_NEED_RESET )
|
||||||
|
{
|
||||||
|
// reset recovery is required to recover from this stage
|
||||||
|
// Clear Stall request cannot resolve this -> continue to stall endpoint
|
||||||
|
usbd_edpt_stall(rhport, ep_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( ep_addr == p_msc->ep_in )
|
||||||
|
{
|
||||||
|
if ( p_msc->stage == MSC_STAGE_STATUS )
|
||||||
|
{
|
||||||
|
// resume sending SCSI status if we are in this stage previously before stalled
|
||||||
|
TU_ASSERT( send_csw(rhport, p_msc) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( ep_addr == p_msc->ep_out )
|
||||||
|
{
|
||||||
|
if ( p_msc->stage == MSC_STAGE_CMD )
|
||||||
|
{
|
||||||
|
// part of reset recovery (probably due to invalid CBW) -> prepare for new command
|
||||||
|
// Note: skip if already queued previously
|
||||||
|
if ( usbd_edpt_ready(rhport, p_msc->ep_out) )
|
||||||
|
{
|
||||||
|
TU_ASSERT( prepare_cbw(rhport, p_msc) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From this point only handle class request only
|
||||||
|
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||||
|
|
||||||
|
switch ( request->bRequest )
|
||||||
|
{
|
||||||
|
case MSC_REQ_RESET:
|
||||||
|
TU_LOG(MSC_DEBUG, " MSC BOT Reset\r\n");
|
||||||
|
TU_VERIFY(request->wValue == 0 && request->wLength == 0);
|
||||||
|
|
||||||
|
// driver state reset
|
||||||
|
proc_bot_reset(p_msc);
|
||||||
|
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSC_REQ_GET_MAX_LUN:
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " MSC Get Max Lun\r\n");
|
||||||
|
TU_VERIFY(request->wValue == 0 && request->wLength == 1);
|
||||||
|
|
||||||
|
uint8_t maxlun = 1;
|
||||||
|
if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
|
||||||
|
TU_VERIFY(maxlun);
|
||||||
|
|
||||||
|
// MAX LUN is minus 1 by specs
|
||||||
|
maxlun--;
|
||||||
|
|
||||||
|
tud_control_xfer(rhport, request, &maxlun, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: return false; // stall unsupported request
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void) event;
|
||||||
|
|
||||||
|
mscd_interface_t* p_msc = &_mscd_itf;
|
||||||
|
msc_cbw_t const * p_cbw = &p_msc->cbw;
|
||||||
|
msc_csw_t * p_csw = &p_msc->csw;
|
||||||
|
|
||||||
|
switch (p_msc->stage)
|
||||||
|
{
|
||||||
|
case MSC_STAGE_CMD:
|
||||||
|
//------------- new CBW received -------------//
|
||||||
|
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
|
||||||
|
if(ep_addr != p_msc->ep_out) return true;
|
||||||
|
|
||||||
|
if ( !(xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE) )
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI CBW is not valid\r\n");
|
||||||
|
|
||||||
|
// BOT 6.6.1 If CBW is not valid stall both endpoints until reset recovery
|
||||||
|
p_msc->stage = MSC_STAGE_NEED_RESET;
|
||||||
|
|
||||||
|
// invalid CBW stall both endpoints
|
||||||
|
usbd_edpt_stall(rhport, p_msc->ep_in);
|
||||||
|
usbd_edpt_stall(rhport, p_msc->ep_out);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
|
||||||
|
//TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
|
||||||
|
|
||||||
|
p_csw->signature = MSC_CSW_SIGNATURE;
|
||||||
|
p_csw->tag = p_cbw->tag;
|
||||||
|
p_csw->data_residue = 0;
|
||||||
|
p_csw->status = MSC_CSW_STATUS_PASSED;
|
||||||
|
|
||||||
|
/*------------- Parse command and prepare DATA -------------*/
|
||||||
|
p_msc->stage = MSC_STAGE_DATA;
|
||||||
|
p_msc->total_len = p_cbw->total_bytes;
|
||||||
|
p_msc->xferred_len = 0;
|
||||||
|
|
||||||
|
// Read10 or Write10
|
||||||
|
if ( (SCSI_CMD_READ_10 == p_cbw->command[0]) || (SCSI_CMD_WRITE_10 == p_cbw->command[0]) )
|
||||||
|
{
|
||||||
|
uint8_t const status = rdwr10_validate_cmd(p_cbw);
|
||||||
|
|
||||||
|
if ( status != MSC_CSW_STATUS_PASSED)
|
||||||
|
{
|
||||||
|
fail_scsi_op(rhport, p_msc, status);
|
||||||
|
}else if ( p_cbw->total_bytes )
|
||||||
|
{
|
||||||
|
if (SCSI_CMD_READ_10 == p_cbw->command[0])
|
||||||
|
{
|
||||||
|
proc_read10_cmd(rhport, p_msc);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
proc_write10_cmd(rhport, p_msc);
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// no data transfer, only exist in complaint test suite
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For other SCSI commands
|
||||||
|
// 1. OUT : queue transfer (invoke app callback after done)
|
||||||
|
// 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
|
||||||
|
if ( (p_cbw->total_bytes > 0 ) && !is_data_in(p_cbw->dir) )
|
||||||
|
{
|
||||||
|
if (p_cbw->total_bytes > sizeof(_mscd_buf))
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI reject non READ10/WRITE10 with large data\r\n");
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Didn't check for case 9 (Ho > Dn), which requires examining scsi command first
|
||||||
|
// but it is OK to just receive data then responded with failed status
|
||||||
|
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, (uint16_t) p_msc->total_len) );
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// First process if it is a built-in commands
|
||||||
|
int32_t resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
|
||||||
|
|
||||||
|
// Invoke user callback if not built-in
|
||||||
|
if ( (resplen < 0) && (p_msc->sense_key == 0) )
|
||||||
|
{
|
||||||
|
resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( resplen < 0 )
|
||||||
|
{
|
||||||
|
// unsupported command
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI unsupported or failed command\r\n");
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
}
|
||||||
|
else if (resplen == 0)
|
||||||
|
{
|
||||||
|
if (p_cbw->total_bytes)
|
||||||
|
{
|
||||||
|
// 6.7 The 13 Cases: case 4 (Hi > Dn)
|
||||||
|
// TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// case 1 Hn = Dn: all good
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( p_cbw->total_bytes == 0 )
|
||||||
|
{
|
||||||
|
// 6.7 The 13 Cases: case 2 (Hn < Di)
|
||||||
|
// TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// cannot return more than host expect
|
||||||
|
p_msc->total_len = tu_min32((uint32_t) resplen, p_cbw->total_bytes);
|
||||||
|
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) p_msc->total_len) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSC_STAGE_DATA:
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI Data [Lun%u]\r\n", p_cbw->lun);
|
||||||
|
//TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
|
||||||
|
|
||||||
|
if (SCSI_CMD_READ_10 == p_cbw->command[0])
|
||||||
|
{
|
||||||
|
p_msc->xferred_len += xferred_bytes;
|
||||||
|
|
||||||
|
if ( p_msc->xferred_len >= p_msc->total_len )
|
||||||
|
{
|
||||||
|
// Data Stage is complete
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
proc_read10_cmd(rhport, p_msc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
|
||||||
|
{
|
||||||
|
proc_write10_new_data(rhport, p_msc, xferred_bytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_msc->xferred_len += xferred_bytes;
|
||||||
|
|
||||||
|
// OUT transfer, invoke callback if needed
|
||||||
|
if ( !is_data_in(p_cbw->dir) )
|
||||||
|
{
|
||||||
|
int32_t cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, (uint16_t) p_msc->total_len);
|
||||||
|
|
||||||
|
if ( cb_result < 0 )
|
||||||
|
{
|
||||||
|
// unsupported command
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI unsupported command\r\n");
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// TODO haven't implement this scenario any further yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p_msc->xferred_len >= p_msc->total_len )
|
||||||
|
{
|
||||||
|
// Data Stage is complete
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This scenario with command that take more than one transfer is already rejected at Command stage
|
||||||
|
TU_BREAKPOINT();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSC_STAGE_STATUS:
|
||||||
|
// processed immediately after this switch, supposedly to be empty
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSC_STAGE_STATUS_SENT:
|
||||||
|
// Wait for the Status phase to complete
|
||||||
|
if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
|
||||||
|
{
|
||||||
|
TU_LOG(MSC_DEBUG, " SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status);
|
||||||
|
// TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
|
||||||
|
|
||||||
|
// Invoke complete callback if defined
|
||||||
|
// Note: There is racing issue with samd51 + qspi flash testing with arduino
|
||||||
|
// if complete_cb() is invoked after queuing the status.
|
||||||
|
switch(p_cbw->command[0])
|
||||||
|
{
|
||||||
|
case SCSI_CMD_READ_10:
|
||||||
|
if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_WRITE_10:
|
||||||
|
if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TU_ASSERT( prepare_cbw(rhport, p_msc) );
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Any xfer ended here is consider unknown error, ignore it
|
||||||
|
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default : break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p_msc->stage == MSC_STAGE_STATUS )
|
||||||
|
{
|
||||||
|
// skip status if epin is currently stalled, will do it when received Clear Stall request
|
||||||
|
if ( !usbd_edpt_stalled(rhport, p_msc->ep_in) )
|
||||||
|
{
|
||||||
|
if ( (p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir) )
|
||||||
|
{
|
||||||
|
// 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status
|
||||||
|
// TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len);
|
||||||
|
usbd_edpt_stall(rhport, p_msc->ep_in);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
TU_ASSERT( send_csw(rhport, p_msc) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TU_CHECK_MCU(OPT_MCU_CXD56)
|
||||||
|
// WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD.
|
||||||
|
// There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and
|
||||||
|
// hope everything will work
|
||||||
|
if ( usbd_edpt_stalled(rhport, p_msc->ep_in) )
|
||||||
|
{
|
||||||
|
usbd_edpt_clear_stall(rhport, p_msc->ep_in);
|
||||||
|
send_csw(rhport, p_msc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------*/
|
||||||
|
/* SCSI Command Process
|
||||||
|
*------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
|
||||||
|
// In case of a failed status, sense key must be set for reason of failure
|
||||||
|
static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
(void) bufsize; // TODO refractor later
|
||||||
|
int32_t resplen;
|
||||||
|
|
||||||
|
mscd_interface_t* p_msc = &_mscd_itf;
|
||||||
|
|
||||||
|
switch ( scsi_cmd[0] )
|
||||||
|
{
|
||||||
|
case SCSI_CMD_TEST_UNIT_READY:
|
||||||
|
resplen = 0;
|
||||||
|
if ( !tud_msc_test_unit_ready_cb(lun) )
|
||||||
|
{
|
||||||
|
// Failed status response
|
||||||
|
resplen = - 1;
|
||||||
|
|
||||||
|
// set default sense if not set by callback
|
||||||
|
if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_START_STOP_UNIT:
|
||||||
|
resplen = 0;
|
||||||
|
|
||||||
|
if (tud_msc_start_stop_cb)
|
||||||
|
{
|
||||||
|
scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
|
||||||
|
if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
|
||||||
|
{
|
||||||
|
// Failed status response
|
||||||
|
resplen = - 1;
|
||||||
|
|
||||||
|
// set default sense if not set by callback
|
||||||
|
if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_READ_CAPACITY_10:
|
||||||
|
{
|
||||||
|
uint32_t block_count;
|
||||||
|
uint32_t block_size;
|
||||||
|
uint16_t block_size_u16;
|
||||||
|
|
||||||
|
tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
|
||||||
|
block_size = (uint32_t) block_size_u16;
|
||||||
|
|
||||||
|
// Invalid block size/count from callback, possibly unit is not ready
|
||||||
|
// stall this request, set sense key to NOT READY
|
||||||
|
if (block_count == 0 || block_size == 0)
|
||||||
|
{
|
||||||
|
resplen = -1;
|
||||||
|
|
||||||
|
// set default sense if not set by callback
|
||||||
|
if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
scsi_read_capacity10_resp_t read_capa10;
|
||||||
|
|
||||||
|
read_capa10.last_lba = tu_htonl(block_count-1);
|
||||||
|
read_capa10.block_size = tu_htonl(block_size);
|
||||||
|
|
||||||
|
resplen = sizeof(read_capa10);
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_capa10, (size_t) resplen));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_READ_FORMAT_CAPACITY:
|
||||||
|
{
|
||||||
|
scsi_read_format_capacity_data_t read_fmt_capa =
|
||||||
|
{
|
||||||
|
.list_length = 8,
|
||||||
|
.block_num = 0,
|
||||||
|
.descriptor_type = 2, // formatted media
|
||||||
|
.block_size_u16 = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t block_count;
|
||||||
|
uint16_t block_size;
|
||||||
|
|
||||||
|
tud_msc_capacity_cb(lun, &block_count, &block_size);
|
||||||
|
|
||||||
|
// Invalid block size/count from callback, possibly unit is not ready
|
||||||
|
// stall this request, set sense key to NOT READY
|
||||||
|
if (block_count == 0 || block_size == 0)
|
||||||
|
{
|
||||||
|
resplen = -1;
|
||||||
|
|
||||||
|
// set default sense if not set by callback
|
||||||
|
if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
read_fmt_capa.block_num = tu_htonl(block_count);
|
||||||
|
read_fmt_capa.block_size_u16 = tu_htons(block_size);
|
||||||
|
|
||||||
|
resplen = sizeof(read_fmt_capa);
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &read_fmt_capa, (size_t) resplen));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_INQUIRY:
|
||||||
|
{
|
||||||
|
scsi_inquiry_resp_t inquiry_rsp =
|
||||||
|
{
|
||||||
|
.is_removable = 1,
|
||||||
|
.version = 2,
|
||||||
|
.response_data_format = 2,
|
||||||
|
.additional_length = sizeof(scsi_inquiry_resp_t) - 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
// vendor_id, product_id, product_rev is space padded string
|
||||||
|
memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
|
||||||
|
memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
|
||||||
|
memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
|
||||||
|
|
||||||
|
tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
|
||||||
|
|
||||||
|
resplen = sizeof(inquiry_rsp);
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &inquiry_rsp, (size_t) resplen));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_MODE_SENSE_6:
|
||||||
|
{
|
||||||
|
scsi_mode_sense6_resp_t mode_resp =
|
||||||
|
{
|
||||||
|
.data_len = 3,
|
||||||
|
.medium_type = 0,
|
||||||
|
.write_protected = false,
|
||||||
|
.reserved = 0,
|
||||||
|
.block_descriptor_len = 0 // no block descriptor are included
|
||||||
|
};
|
||||||
|
|
||||||
|
bool writable = true;
|
||||||
|
if ( tud_msc_is_writable_cb )
|
||||||
|
{
|
||||||
|
writable = tud_msc_is_writable_cb(lun);
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_resp.write_protected = !writable;
|
||||||
|
|
||||||
|
resplen = sizeof(mode_resp);
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &mode_resp, (size_t) resplen));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCSI_CMD_REQUEST_SENSE:
|
||||||
|
{
|
||||||
|
scsi_sense_fixed_resp_t sense_rsp =
|
||||||
|
{
|
||||||
|
.response_code = 0x70, // current, fixed format
|
||||||
|
.valid = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
|
||||||
|
sense_rsp.sense_key = (uint8_t) (p_msc->sense_key & 0x0F);
|
||||||
|
sense_rsp.add_sense_code = p_msc->add_sense_code;
|
||||||
|
sense_rsp.add_sense_qualifier = p_msc->add_sense_qualifier;
|
||||||
|
|
||||||
|
resplen = sizeof(sense_rsp);
|
||||||
|
TU_VERIFY(0 == tu_memcpy_s(buffer, bufsize, &sense_rsp, (size_t) resplen));
|
||||||
|
|
||||||
|
// request sense callback could overwrite the sense data
|
||||||
|
if (tud_msc_request_sense_cb)
|
||||||
|
{
|
||||||
|
resplen = tud_msc_request_sense_cb(lun, buffer, (uint16_t) bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear sense data after copy
|
||||||
|
tud_msc_set_sense(lun, 0, 0, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: resplen = -1; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resplen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
|
||||||
|
{
|
||||||
|
msc_cbw_t const * p_cbw = &p_msc->cbw;
|
||||||
|
|
||||||
|
// block size already verified not zero
|
||||||
|
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
|
||||||
|
|
||||||
|
// Adjust lba with transferred bytes
|
||||||
|
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
|
||||||
|
|
||||||
|
// remaining bytes capped at class buffer
|
||||||
|
int32_t nbytes = (int32_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
|
||||||
|
|
||||||
|
// Application can consume smaller bytes
|
||||||
|
uint32_t const offset = p_msc->xferred_len % block_sz;
|
||||||
|
nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_buf, (uint32_t) nbytes);
|
||||||
|
|
||||||
|
if ( nbytes < 0 )
|
||||||
|
{
|
||||||
|
// negative means error -> endpoint is stalled & status in CSW set to failed
|
||||||
|
TU_LOG(MSC_DEBUG, " tud_msc_read10_cb() return -1\r\n");
|
||||||
|
|
||||||
|
// set sense
|
||||||
|
set_sense_medium_not_present(p_cbw->lun);
|
||||||
|
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
}
|
||||||
|
else if ( nbytes == 0 )
|
||||||
|
{
|
||||||
|
// zero means not ready -> simulate an transfer complete so that this driver callback will fired again
|
||||||
|
dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, (uint16_t) nbytes), );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
|
||||||
|
{
|
||||||
|
msc_cbw_t const * p_cbw = &p_msc->cbw;
|
||||||
|
bool writable = true;
|
||||||
|
|
||||||
|
if ( tud_msc_is_writable_cb )
|
||||||
|
{
|
||||||
|
writable = tud_msc_is_writable_cb(p_cbw->lun);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !writable )
|
||||||
|
{
|
||||||
|
// Not writable, complete this SCSI op with error
|
||||||
|
// Sense = Write protected
|
||||||
|
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00);
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remaining bytes capped at class buffer
|
||||||
|
uint16_t nbytes = (uint16_t) tu_min32(sizeof(_mscd_buf), p_cbw->total_bytes-p_msc->xferred_len);
|
||||||
|
|
||||||
|
// Write10 callback will be called later when usb transfer complete
|
||||||
|
TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
|
||||||
|
}
|
||||||
|
|
||||||
|
// process new data arrived from WRITE10
|
||||||
|
static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
msc_cbw_t const * p_cbw = &p_msc->cbw;
|
||||||
|
|
||||||
|
// block size already verified not zero
|
||||||
|
uint16_t const block_sz = rdwr10_get_blocksize(p_cbw);
|
||||||
|
|
||||||
|
// Adjust lba with transferred bytes
|
||||||
|
uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz);
|
||||||
|
|
||||||
|
// Invoke callback to consume new data
|
||||||
|
uint32_t const offset = p_msc->xferred_len % block_sz;
|
||||||
|
int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_buf, xferred_bytes);
|
||||||
|
|
||||||
|
if ( nbytes < 0 )
|
||||||
|
{
|
||||||
|
// negative means error -> failed this scsi op
|
||||||
|
TU_LOG(MSC_DEBUG, " tud_msc_write10_cb() return -1\r\n");
|
||||||
|
|
||||||
|
// update actual byte before failed
|
||||||
|
p_msc->xferred_len += xferred_bytes;
|
||||||
|
|
||||||
|
// Set sense
|
||||||
|
set_sense_medium_not_present(p_cbw->lun);
|
||||||
|
|
||||||
|
fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Application consume less than what we got (including zero)
|
||||||
|
if ( (uint32_t) nbytes < xferred_bytes )
|
||||||
|
{
|
||||||
|
uint32_t const left_over = xferred_bytes - (uint32_t) nbytes;
|
||||||
|
if ( nbytes > 0 )
|
||||||
|
{
|
||||||
|
p_msc->xferred_len += (uint16_t) nbytes;
|
||||||
|
memmove(_mscd_buf, _mscd_buf+nbytes, left_over);
|
||||||
|
}
|
||||||
|
|
||||||
|
// simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
|
||||||
|
dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Application consume all bytes in our buffer
|
||||||
|
p_msc->xferred_len += xferred_bytes;
|
||||||
|
|
||||||
|
if ( p_msc->xferred_len >= p_msc->total_len )
|
||||||
|
{
|
||||||
|
// Data Stage is complete
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// prepare to receive more data from host
|
||||||
|
proc_write10_cmd(rhport, p_msc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_MSC_DEVICE_H_
|
||||||
|
#define _TUSB_MSC_DEVICE_H_
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
#include "msc.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Driver Configuration
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#if !defined(CFG_TUD_MSC_EP_BUFSIZE) & defined(CFG_TUD_MSC_BUFSIZE)
|
||||||
|
// TODO warn user to use new name later on
|
||||||
|
// #warning CFG_TUD_MSC_BUFSIZE is renamed to CFG_TUD_MSC_EP_BUFSIZE, please update to use the new name
|
||||||
|
#define CFG_TUD_MSC_EP_BUFSIZE CFG_TUD_MSC_BUFSIZE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_MSC_EP_BUFSIZE
|
||||||
|
#error CFG_TUD_MSC_EP_BUFSIZE must be defined, value of a block size should work well, the more the better
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct");
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Set SCSI sense response
|
||||||
|
bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callbacks (WEAK is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Invoked when received SCSI READ10 command
|
||||||
|
// - Address = lba * BLOCK_SIZE + offset
|
||||||
|
// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
|
||||||
|
//
|
||||||
|
// - Application fill the buffer (up to bufsize) with address contents and return number of read byte. If
|
||||||
|
// - read < bufsize : These bytes are transferred first and callback invoked again for remaining data.
|
||||||
|
//
|
||||||
|
// - read == 0 : Indicate application is not ready yet e.g disk I/O busy.
|
||||||
|
// Callback invoked again with the same parameters later on.
|
||||||
|
//
|
||||||
|
// - read < 0 : Indicate application error e.g invalid address. This request will be STALLed
|
||||||
|
// and return failed status in command status wrapper phase.
|
||||||
|
int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
|
||||||
|
|
||||||
|
// Invoked when received SCSI WRITE10 command
|
||||||
|
// - Address = lba * BLOCK_SIZE + offset
|
||||||
|
// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
|
||||||
|
//
|
||||||
|
// - Application write data from buffer to address contents (up to bufsize) and return number of written byte. If
|
||||||
|
// - write < bufsize : callback invoked again with remaining data later on.
|
||||||
|
//
|
||||||
|
// - write == 0 : Indicate application is not ready yet e.g disk I/O busy.
|
||||||
|
// Callback invoked again with the same parameters later on.
|
||||||
|
//
|
||||||
|
// - write < 0 : Indicate application error e.g invalid address. This request will be STALLed
|
||||||
|
// and return failed status in command status wrapper phase.
|
||||||
|
//
|
||||||
|
// TODO change buffer to const uint8_t*
|
||||||
|
int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
|
||||||
|
|
||||||
|
// Invoked when received SCSI_CMD_INQUIRY
|
||||||
|
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||||
|
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]);
|
||||||
|
|
||||||
|
// Invoked when received Test Unit Ready command.
|
||||||
|
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||||
|
bool tud_msc_test_unit_ready_cb(uint8_t lun);
|
||||||
|
|
||||||
|
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||||
|
// Application update block count and block size
|
||||||
|
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when received an SCSI command not in built-in list below.
|
||||||
|
* - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
|
||||||
|
* - READ10 and WRITE10 has their own callbacks
|
||||||
|
*
|
||||||
|
* \param[in] lun Logical unit number
|
||||||
|
* \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
|
||||||
|
* \param[out] buffer Buffer for SCSI Data Stage.
|
||||||
|
* - For INPUT: application must fill this with response.
|
||||||
|
* - For OUTPUT it holds the Data from host
|
||||||
|
* \param[in] bufsize Buffer's length.
|
||||||
|
*
|
||||||
|
* \return Actual bytes processed, can be zero for no-data command.
|
||||||
|
* \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
|
||||||
|
* endpoint and return failed status in command status wrapper phase.
|
||||||
|
*/
|
||||||
|
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize);
|
||||||
|
|
||||||
|
/*------------- Optional callbacks -------------*/
|
||||||
|
|
||||||
|
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
|
||||||
|
TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
|
||||||
|
|
||||||
|
// Invoked when received Start Stop Unit command
|
||||||
|
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||||
|
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||||
|
TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
|
||||||
|
|
||||||
|
// Invoked when received REQUEST_SENSE
|
||||||
|
TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
|
||||||
|
|
||||||
|
// Invoked when Read10 command is complete
|
||||||
|
TU_ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);
|
||||||
|
|
||||||
|
// Invoke when Write10 command is complete, can be used to flush flash caching
|
||||||
|
TU_ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t lun);
|
||||||
|
|
||||||
|
// Invoked when command in tud_msc_scsi_cb is complete
|
||||||
|
TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[16]);
|
||||||
|
|
||||||
|
// Invoked to check if device is writable as part of SCSI WRITE10
|
||||||
|
TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void mscd_init (void);
|
||||||
|
void mscd_reset (uint8_t rhport);
|
||||||
|
uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool mscd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request);
|
||||||
|
bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_MSC_DEVICE_H_ */
|
@ -0,0 +1,525 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if CFG_TUH_ENABLED && CFG_TUH_MSC
|
||||||
|
|
||||||
|
#include "host/usbh.h"
|
||||||
|
#include "host/usbh_classdriver.h"
|
||||||
|
|
||||||
|
#include "msc_host.h"
|
||||||
|
|
||||||
|
// Debug level, TUSB_CFG_DEBUG must be at least this level for debug message
|
||||||
|
#define MSCH_DEBUG 2
|
||||||
|
|
||||||
|
#define TU_LOG_MSCH(...) TU_LOG(MSCH_DEBUG, __VA_ARGS__)
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MSC_STAGE_IDLE = 0,
|
||||||
|
MSC_STAGE_CMD,
|
||||||
|
MSC_STAGE_DATA,
|
||||||
|
MSC_STAGE_STATUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t itf_num;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
|
||||||
|
uint8_t max_lun;
|
||||||
|
|
||||||
|
volatile bool configured; // Receive SET_CONFIGURE
|
||||||
|
volatile bool mounted; // Enumeration is complete
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32_t block_size;
|
||||||
|
uint32_t block_count;
|
||||||
|
} capacity[CFG_TUH_MSC_MAXLUN];
|
||||||
|
|
||||||
|
//------------- SCSI -------------//
|
||||||
|
uint8_t stage;
|
||||||
|
void* buffer;
|
||||||
|
tuh_msc_complete_cb_t complete_cb;
|
||||||
|
uintptr_t complete_arg;
|
||||||
|
|
||||||
|
CFG_TUH_MEM_ALIGN msc_cbw_t cbw;
|
||||||
|
CFG_TUH_MEM_ALIGN msc_csw_t csw;
|
||||||
|
}msch_interface_t;
|
||||||
|
|
||||||
|
CFG_TUH_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUH_DEVICE_MAX];
|
||||||
|
|
||||||
|
// buffer used to read scsi information when mounted
|
||||||
|
// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
|
||||||
|
CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
|
||||||
|
static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
|
||||||
|
|
||||||
|
TU_ATTR_ALWAYS_INLINE
|
||||||
|
static inline msch_interface_t* get_itf(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
return &_msch_itf[dev_addr-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// PUBLIC API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
return p_msc->max_lun;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
return p_msc->capacity[lun].block_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
return p_msc->capacity[lun].block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_mounted(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
return p_msc->mounted;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_ready(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// PUBLIC API: SCSI COMMAND
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
|
||||||
|
{
|
||||||
|
tu_memclr(cbw, sizeof(msc_cbw_t));
|
||||||
|
cbw->signature = MSC_CBW_SIGNATURE;
|
||||||
|
cbw->tag = 0x54555342; // TUSB
|
||||||
|
cbw->lun = lun;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
TU_VERIFY(p_msc->configured);
|
||||||
|
|
||||||
|
// TODO claim endpoint
|
||||||
|
|
||||||
|
p_msc->cbw = *cbw;
|
||||||
|
p_msc->stage = MSC_STAGE_CMD;
|
||||||
|
p_msc->buffer = data;
|
||||||
|
p_msc->complete_cb = complete_cb;
|
||||||
|
p_msc->complete_arg = arg;
|
||||||
|
|
||||||
|
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
TU_VERIFY(p_msc->configured);
|
||||||
|
|
||||||
|
msc_cbw_t cbw;
|
||||||
|
cbw_init(&cbw, lun);
|
||||||
|
|
||||||
|
cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
|
||||||
|
cbw.dir = TUSB_DIR_IN_MASK;
|
||||||
|
cbw.cmd_len = sizeof(scsi_read_capacity10_t);
|
||||||
|
cbw.command[0] = SCSI_CMD_READ_CAPACITY_10;
|
||||||
|
|
||||||
|
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
TU_VERIFY(p_msc->mounted);
|
||||||
|
|
||||||
|
msc_cbw_t cbw;
|
||||||
|
cbw_init(&cbw, lun);
|
||||||
|
|
||||||
|
cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
|
||||||
|
cbw.dir = TUSB_DIR_IN_MASK;
|
||||||
|
cbw.cmd_len = sizeof(scsi_inquiry_t);
|
||||||
|
|
||||||
|
scsi_inquiry_t const cmd_inquiry =
|
||||||
|
{
|
||||||
|
.cmd_code = SCSI_CMD_INQUIRY,
|
||||||
|
.alloc_length = sizeof(scsi_inquiry_resp_t)
|
||||||
|
};
|
||||||
|
memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
|
||||||
|
|
||||||
|
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
TU_VERIFY(p_msc->configured);
|
||||||
|
|
||||||
|
msc_cbw_t cbw;
|
||||||
|
cbw_init(&cbw, lun);
|
||||||
|
|
||||||
|
cbw.total_bytes = 0;
|
||||||
|
cbw.dir = TUSB_DIR_OUT;
|
||||||
|
cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
|
||||||
|
cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
|
||||||
|
cbw.command[1] = lun; // according to wiki TODO need verification
|
||||||
|
|
||||||
|
return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||||
|
{
|
||||||
|
msc_cbw_t cbw;
|
||||||
|
cbw_init(&cbw, lun);
|
||||||
|
|
||||||
|
cbw.total_bytes = 18; // TODO sense response
|
||||||
|
cbw.dir = TUSB_DIR_IN_MASK;
|
||||||
|
cbw.cmd_len = sizeof(scsi_request_sense_t);
|
||||||
|
|
||||||
|
scsi_request_sense_t const cmd_request_sense =
|
||||||
|
{
|
||||||
|
.cmd_code = SCSI_CMD_REQUEST_SENSE,
|
||||||
|
.alloc_length = 18
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
|
||||||
|
|
||||||
|
return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
TU_VERIFY(p_msc->mounted);
|
||||||
|
|
||||||
|
msc_cbw_t cbw;
|
||||||
|
cbw_init(&cbw, lun);
|
||||||
|
|
||||||
|
cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
|
||||||
|
cbw.dir = TUSB_DIR_IN_MASK;
|
||||||
|
cbw.cmd_len = sizeof(scsi_read10_t);
|
||||||
|
|
||||||
|
scsi_read10_t const cmd_read10 =
|
||||||
|
{
|
||||||
|
.cmd_code = SCSI_CMD_READ_10,
|
||||||
|
.lba = tu_htonl(lba),
|
||||||
|
.block_count = tu_htons(block_count)
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
|
||||||
|
|
||||||
|
return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
TU_VERIFY(p_msc->mounted);
|
||||||
|
|
||||||
|
msc_cbw_t cbw;
|
||||||
|
cbw_init(&cbw, lun);
|
||||||
|
|
||||||
|
cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
|
||||||
|
cbw.dir = TUSB_DIR_OUT;
|
||||||
|
cbw.cmd_len = sizeof(scsi_write10_t);
|
||||||
|
|
||||||
|
scsi_write10_t const cmd_write10 =
|
||||||
|
{
|
||||||
|
.cmd_code = SCSI_CMD_WRITE_10,
|
||||||
|
.lba = tu_htonl(lba),
|
||||||
|
.block_count = tu_htons(block_count)
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
|
||||||
|
|
||||||
|
return tuh_msc_scsi_command(dev_addr, &cbw, (void*)(uintptr_t) buffer, complete_cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// MSC interface Reset (not used now)
|
||||||
|
bool tuh_msc_reset(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
tusb_control_request_t const new_request =
|
||||||
|
{
|
||||||
|
.bmRequestType_bit =
|
||||||
|
{
|
||||||
|
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||||
|
.type = TUSB_REQ_TYPE_CLASS,
|
||||||
|
.direction = TUSB_DIR_OUT
|
||||||
|
},
|
||||||
|
.bRequest = MSC_REQ_RESET,
|
||||||
|
.wValue = 0,
|
||||||
|
.wIndex = p_msc->itf_num,
|
||||||
|
.wLength = 0
|
||||||
|
};
|
||||||
|
TU_ASSERT( usbh_control_xfer( dev_addr, &new_request, NULL ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// CLASS-USBH API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void msch_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(_msch_itf, sizeof(_msch_itf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void msch_close(uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
TU_VERIFY(dev_addr <= CFG_TUH_DEVICE_MAX, );
|
||||||
|
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
|
||||||
|
// invoke Application Callback
|
||||||
|
if (p_msc->mounted && tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
|
||||||
|
|
||||||
|
tu_memclr(p_msc, sizeof(msch_interface_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
msc_cbw_t const * cbw = &p_msc->cbw;
|
||||||
|
msc_csw_t * csw = &p_msc->csw;
|
||||||
|
|
||||||
|
switch (p_msc->stage)
|
||||||
|
{
|
||||||
|
case MSC_STAGE_CMD:
|
||||||
|
// Must be Command Block
|
||||||
|
TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
|
||||||
|
|
||||||
|
if ( cbw->total_bytes && p_msc->buffer )
|
||||||
|
{
|
||||||
|
// Data stage if any
|
||||||
|
p_msc->stage = MSC_STAGE_DATA;
|
||||||
|
|
||||||
|
uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
|
||||||
|
TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, (uint16_t) cbw->total_bytes));
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Status stage
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSC_STAGE_DATA:
|
||||||
|
// Status stage
|
||||||
|
p_msc->stage = MSC_STAGE_STATUS;
|
||||||
|
TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, (uint16_t) sizeof(msc_csw_t)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSC_STAGE_STATUS:
|
||||||
|
// SCSI op is complete
|
||||||
|
p_msc->stage = MSC_STAGE_IDLE;
|
||||||
|
|
||||||
|
if (p_msc->complete_cb)
|
||||||
|
{
|
||||||
|
tuh_msc_complete_data_t const cb_data =
|
||||||
|
{
|
||||||
|
.cbw = cbw,
|
||||||
|
.csw = csw,
|
||||||
|
.scsi_data = p_msc->buffer,
|
||||||
|
.user_arg = p_msc->complete_arg
|
||||||
|
};
|
||||||
|
p_msc->complete_cb(dev_addr, &cb_data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unknown state
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MSC Enumeration
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
static void config_get_maxlun_complete (tuh_xfer_t* xfer);
|
||||||
|
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data);
|
||||||
|
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||||
|
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||||
|
|
||||||
|
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
|
||||||
|
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
|
||||||
|
|
||||||
|
// msc driver length is fixed
|
||||||
|
uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
|
||||||
|
TU_ASSERT(drv_len <= max_len);
|
||||||
|
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf);
|
||||||
|
|
||||||
|
for(uint32_t i=0; i<2; i++)
|
||||||
|
{
|
||||||
|
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
|
||||||
|
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
|
||||||
|
|
||||||
|
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
|
||||||
|
{
|
||||||
|
p_msc->ep_in = ep_desc->bEndpointAddress;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
p_msc->ep_out = ep_desc->bEndpointAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
p_msc->itf_num = desc_itf->bInterfaceNumber;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)
|
||||||
|
{
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
TU_ASSERT(p_msc->itf_num == itf_num);
|
||||||
|
|
||||||
|
p_msc->configured = true;
|
||||||
|
|
||||||
|
//------------- Get Max Lun -------------//
|
||||||
|
TU_LOG_MSCH("MSC Get Max Lun\r\n");
|
||||||
|
tusb_control_request_t const request =
|
||||||
|
{
|
||||||
|
.bmRequestType_bit =
|
||||||
|
{
|
||||||
|
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||||
|
.type = TUSB_REQ_TYPE_CLASS,
|
||||||
|
.direction = TUSB_DIR_IN
|
||||||
|
},
|
||||||
|
.bRequest = MSC_REQ_GET_MAX_LUN,
|
||||||
|
.wValue = 0,
|
||||||
|
.wIndex = itf_num,
|
||||||
|
.wLength = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
tuh_xfer_t xfer =
|
||||||
|
{
|
||||||
|
.daddr = dev_addr,
|
||||||
|
.ep_addr = 0,
|
||||||
|
.setup = &request,
|
||||||
|
.buffer = &p_msc->max_lun,
|
||||||
|
.complete_cb = config_get_maxlun_complete,
|
||||||
|
.user_data = 0
|
||||||
|
};
|
||||||
|
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_get_maxlun_complete (tuh_xfer_t* xfer)
|
||||||
|
{
|
||||||
|
uint8_t const daddr = xfer->daddr;
|
||||||
|
msch_interface_t* p_msc = get_itf(daddr);
|
||||||
|
|
||||||
|
// STALL means zero
|
||||||
|
p_msc->max_lun = (XFER_RESULT_SUCCESS == xfer->result) ? _msch_buffer[0] : 0;
|
||||||
|
p_msc->max_lun++; // MAX LUN is minus 1 by specs
|
||||||
|
|
||||||
|
// TODO multiple LUN support
|
||||||
|
TU_LOG_MSCH("SCSI Test Unit Ready\r\n");
|
||||||
|
uint8_t const lun = 0;
|
||||||
|
tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool config_test_unit_ready_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
|
||||||
|
{
|
||||||
|
msc_cbw_t const* cbw = cb_data->cbw;
|
||||||
|
msc_csw_t const* csw = cb_data->csw;
|
||||||
|
|
||||||
|
if (csw->status == 0)
|
||||||
|
{
|
||||||
|
// Unit is ready, read its capacity
|
||||||
|
TU_LOG_MSCH("SCSI Read Capacity\r\n");
|
||||||
|
tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete, 0);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Note: During enumeration, some device fails Test Unit Ready and require a few retries
|
||||||
|
// with Request Sense to start working !!
|
||||||
|
// TODO limit number of retries
|
||||||
|
TU_LOG_MSCH("SCSI Request Sense\r\n");
|
||||||
|
TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool config_request_sense_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
|
||||||
|
{
|
||||||
|
msc_cbw_t const* cbw = cb_data->cbw;
|
||||||
|
msc_csw_t const* csw = cb_data->csw;
|
||||||
|
|
||||||
|
TU_ASSERT(csw->status == 0);
|
||||||
|
TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete, 0));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool config_read_capacity_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
|
||||||
|
{
|
||||||
|
msc_cbw_t const* cbw = cb_data->cbw;
|
||||||
|
msc_csw_t const* csw = cb_data->csw;
|
||||||
|
|
||||||
|
TU_ASSERT(csw->status == 0);
|
||||||
|
|
||||||
|
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||||
|
|
||||||
|
// Capacity response field: Block size and Last LBA are both Big-Endian
|
||||||
|
scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer);
|
||||||
|
p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
|
||||||
|
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
|
||||||
|
|
||||||
|
// Mark enumeration is complete
|
||||||
|
p_msc->mounted = true;
|
||||||
|
if (tuh_msc_mount_cb) tuh_msc_mount_cb(dev_addr);
|
||||||
|
|
||||||
|
// notify usbh that driver enumeration is complete
|
||||||
|
usbh_driver_set_config_complete(dev_addr, p_msc->itf_num);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_MSC_HOST_H_
|
||||||
|
#define _TUSB_MSC_HOST_H_
|
||||||
|
|
||||||
|
#include "msc.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Class Driver Configuration
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#ifndef CFG_TUH_MSC_MAXLUN
|
||||||
|
#define CFG_TUH_MSC_MAXLUN 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
msc_cbw_t const* cbw; // SCSI command
|
||||||
|
msc_csw_t const* csw; // SCSI status
|
||||||
|
void* scsi_data; // SCSI Data
|
||||||
|
uintptr_t user_arg; // user argument
|
||||||
|
}tuh_msc_complete_data_t;
|
||||||
|
|
||||||
|
typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, tuh_msc_complete_data_t const* cb_data);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Check if device supports MassStorage interface.
|
||||||
|
// This function true after tuh_msc_mounted_cb() and false after tuh_msc_unmounted_cb()
|
||||||
|
bool tuh_msc_mounted(uint8_t dev_addr);
|
||||||
|
|
||||||
|
// Check if the interface is currently ready or busy transferring data
|
||||||
|
bool tuh_msc_ready(uint8_t dev_addr);
|
||||||
|
|
||||||
|
// Get Max Lun
|
||||||
|
uint8_t tuh_msc_get_maxlun(uint8_t dev_addr);
|
||||||
|
|
||||||
|
// Get number of block
|
||||||
|
uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun);
|
||||||
|
|
||||||
|
// Get block size in bytes
|
||||||
|
uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
|
||||||
|
|
||||||
|
// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
|
||||||
|
// Complete callback is invoked when SCSI op is complete.
|
||||||
|
// return true if success, false if there is already pending operation.
|
||||||
|
bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||||
|
|
||||||
|
// Perform SCSI Inquiry command
|
||||||
|
// Complete callback is invoked when SCSI op is complete.
|
||||||
|
bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||||
|
|
||||||
|
// Perform SCSI Test Unit Ready command
|
||||||
|
// Complete callback is invoked when SCSI op is complete.
|
||||||
|
bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||||
|
|
||||||
|
// Perform SCSI Request Sense 10 command
|
||||||
|
// Complete callback is invoked when SCSI op is complete.
|
||||||
|
bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||||
|
|
||||||
|
// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
|
||||||
|
// Complete callback is invoked when SCSI op is complete.
|
||||||
|
bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||||
|
|
||||||
|
// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
|
||||||
|
// Complete callback is invoked when SCSI op is complete.
|
||||||
|
bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||||
|
|
||||||
|
// Perform SCSI Read Capacity 10 command
|
||||||
|
// Complete callback is invoked when SCSI op is complete.
|
||||||
|
// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
|
||||||
|
// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
|
||||||
|
bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb, uintptr_t arg);
|
||||||
|
|
||||||
|
//------------- Application Callback -------------//
|
||||||
|
|
||||||
|
// Invoked when a device with MassStorage interface is mounted
|
||||||
|
TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr);
|
||||||
|
|
||||||
|
// Invoked when a device with MassStorage interface is unmounted
|
||||||
|
TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
void msch_init (void);
|
||||||
|
bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||||
|
bool msch_set_config (uint8_t dev_addr, uint8_t itf_num);
|
||||||
|
void msch_close (uint8_t dev_addr);
|
||||||
|
bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_MSC_HOST_H_ */
|
@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Peter Lawrence
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if ( CFG_TUD_ENABLED && CFG_TUD_ECM_RNDIS )
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "net_device.h"
|
||||||
|
#include "rndis_protocol.h"
|
||||||
|
|
||||||
|
void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
|
||||||
|
uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
|
||||||
|
|
||||||
|
uint8_t ep_notif;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
|
||||||
|
bool ecm_mode;
|
||||||
|
|
||||||
|
// Endpoint descriptor use to open/close when receiving SetInterface
|
||||||
|
// TODO since configuration descriptor may not be long-lived memory, we should
|
||||||
|
// keep a copy of endpoint attribute instead
|
||||||
|
uint8_t const * ecm_desc_epdata;
|
||||||
|
|
||||||
|
} netd_interface_t;
|
||||||
|
|
||||||
|
#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
|
||||||
|
#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static
|
||||||
|
uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static
|
||||||
|
uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
|
||||||
|
|
||||||
|
struct ecm_notify_struct
|
||||||
|
{
|
||||||
|
tusb_control_request_t header;
|
||||||
|
uint32_t downlink, uplink;
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static const struct ecm_notify_struct ecm_notify_nc =
|
||||||
|
{
|
||||||
|
.header = {
|
||||||
|
.bmRequestType = 0xA1,
|
||||||
|
.bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
|
||||||
|
.wValue = 1 /* Connected */,
|
||||||
|
.wLength = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static const struct ecm_notify_struct ecm_notify_csc =
|
||||||
|
{
|
||||||
|
.header = {
|
||||||
|
.bmRequestType = 0xA1,
|
||||||
|
.bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
|
||||||
|
.wLength = 8,
|
||||||
|
},
|
||||||
|
.downlink = 9728000,
|
||||||
|
.uplink = 9728000,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO remove CFG_TUSB_MEM_SECTION, control internal buffer is already in this special section
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static union
|
||||||
|
{
|
||||||
|
uint8_t rndis_buf[120];
|
||||||
|
struct ecm_notify_struct ecm_buf;
|
||||||
|
} notify;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// TODO remove CFG_TUSB_MEM_SECTION
|
||||||
|
CFG_TUSB_MEM_SECTION tu_static netd_interface_t _netd_itf;
|
||||||
|
|
||||||
|
tu_static bool can_xmit;
|
||||||
|
|
||||||
|
void tud_network_recv_renew(void)
|
||||||
|
{
|
||||||
|
usbd_edpt_xfer(0, _netd_itf.ep_out, received, sizeof(received));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_in_xfer(uint8_t *buf, uint16_t len)
|
||||||
|
{
|
||||||
|
can_xmit = false;
|
||||||
|
usbd_edpt_xfer(0, _netd_itf.ep_in, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void netd_report(uint8_t *buf, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
|
||||||
|
// skip if previous report not yet acknowledged by host
|
||||||
|
if ( usbd_edpt_busy(rhport, _netd_itf.ep_notif) ) return;
|
||||||
|
usbd_edpt_xfer(rhport, _netd_itf.ep_notif, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void netd_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(&_netd_itf, sizeof(_netd_itf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void netd_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
netd_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||||
|
{
|
||||||
|
bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass &&
|
||||||
|
TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
|
||||||
|
TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol);
|
||||||
|
|
||||||
|
bool const is_ecm = (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
|
||||||
|
CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
|
||||||
|
0x00 == itf_desc->bInterfaceProtocol);
|
||||||
|
|
||||||
|
TU_VERIFY(is_rndis || is_ecm, 0);
|
||||||
|
|
||||||
|
// confirm interface hasn't already been allocated
|
||||||
|
TU_ASSERT(0 == _netd_itf.ep_notif, 0);
|
||||||
|
|
||||||
|
// sanity check the descriptor
|
||||||
|
_netd_itf.ecm_mode = is_ecm;
|
||||||
|
|
||||||
|
//------------- Management Interface -------------//
|
||||||
|
_netd_itf.itf_num = itf_desc->bInterfaceNumber;
|
||||||
|
|
||||||
|
uint16_t drv_len = sizeof(tusb_desc_interface_t);
|
||||||
|
uint8_t const * p_desc = tu_desc_next( itf_desc );
|
||||||
|
|
||||||
|
// Communication Functional Descriptors
|
||||||
|
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
|
||||||
|
{
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification endpoint (if any)
|
||||||
|
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
|
||||||
|
{
|
||||||
|
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
|
||||||
|
|
||||||
|
_netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
|
||||||
|
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------- Data Interface -------------//
|
||||||
|
// - RNDIS Data followed immediately by a pair of endpoints
|
||||||
|
// - CDC-ECM data interface has 2 alternate settings
|
||||||
|
// - 0 : zero endpoints for inactive (default)
|
||||||
|
// - 1 : IN & OUT endpoints for active networking
|
||||||
|
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
|
||||||
|
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
|
||||||
|
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
|
||||||
|
|
||||||
|
// Pair of endpoints
|
||||||
|
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
|
||||||
|
|
||||||
|
if ( _netd_itf.ecm_mode )
|
||||||
|
{
|
||||||
|
// ECM by default is in-active, save the endpoint attribute
|
||||||
|
// to open later when received setInterface
|
||||||
|
_netd_itf.ecm_desc_epdata = p_desc;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Open endpoint pair for RNDIS
|
||||||
|
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
|
||||||
|
|
||||||
|
tud_network_init_cb();
|
||||||
|
|
||||||
|
// we are ready to transmit a packet
|
||||||
|
can_xmit = true;
|
||||||
|
|
||||||
|
// prepare for incoming packets
|
||||||
|
tud_network_recv_renew();
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_len += 2*sizeof(tusb_desc_endpoint_t);
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ecm_report(bool nc)
|
||||||
|
{
|
||||||
|
notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc;
|
||||||
|
notify.ecm_buf.header.wIndex = _netd_itf.itf_num;
|
||||||
|
netd_report((uint8_t *)¬ify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
if ( stage == CONTROL_STAGE_SETUP )
|
||||||
|
{
|
||||||
|
switch ( request->bmRequestType_bit.type )
|
||||||
|
{
|
||||||
|
case TUSB_REQ_TYPE_STANDARD:
|
||||||
|
switch ( request->bRequest )
|
||||||
|
{
|
||||||
|
case TUSB_REQ_GET_INTERFACE:
|
||||||
|
{
|
||||||
|
uint8_t const req_itfnum = (uint8_t) request->wIndex;
|
||||||
|
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
|
||||||
|
|
||||||
|
tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TUSB_REQ_SET_INTERFACE:
|
||||||
|
{
|
||||||
|
uint8_t const req_itfnum = (uint8_t) request->wIndex;
|
||||||
|
uint8_t const req_alt = (uint8_t) request->wValue;
|
||||||
|
|
||||||
|
// Only valid for Data Interface with Alternate is either 0 or 1
|
||||||
|
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
|
||||||
|
|
||||||
|
// ACM-ECM only: qequest to enable/disable network activities
|
||||||
|
TU_VERIFY(_netd_itf.ecm_mode);
|
||||||
|
|
||||||
|
_netd_itf.itf_data_alt = req_alt;
|
||||||
|
|
||||||
|
if ( _netd_itf.itf_data_alt )
|
||||||
|
{
|
||||||
|
// TODO since we don't actually close endpoint
|
||||||
|
// hack here to not re-open it
|
||||||
|
if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
|
||||||
|
{
|
||||||
|
TU_ASSERT(_netd_itf.ecm_desc_epdata);
|
||||||
|
TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
|
||||||
|
|
||||||
|
// TODO should be merge with RNDIS's after endpoint opened
|
||||||
|
// Also should have opposite callback for application to disable network !!
|
||||||
|
tud_network_init_cb();
|
||||||
|
can_xmit = true; // we are ready to transmit a packet
|
||||||
|
tud_network_recv_renew(); // prepare for incoming packets
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// TODO close the endpoint pair
|
||||||
|
// For now pretend that we did, this should have no harm since host won't try to
|
||||||
|
// communicate with the endpoints again
|
||||||
|
// _netd_itf.ep_in = _netd_itf.ep_out = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unsupported request
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TUSB_REQ_TYPE_CLASS:
|
||||||
|
TU_VERIFY (_netd_itf.itf_num == request->wIndex);
|
||||||
|
|
||||||
|
if (_netd_itf.ecm_mode)
|
||||||
|
{
|
||||||
|
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
|
||||||
|
if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
|
||||||
|
{
|
||||||
|
tud_control_xfer(rhport, request, NULL, 0);
|
||||||
|
ecm_report(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
|
||||||
|
{
|
||||||
|
rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
|
||||||
|
uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
|
||||||
|
TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
|
||||||
|
tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) msglen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tud_control_xfer(rhport, request, notify.rndis_buf, (uint16_t) sizeof(notify.rndis_buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unsupported request
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( stage == CONTROL_STAGE_DATA )
|
||||||
|
{
|
||||||
|
// Handle RNDIS class control OUT only
|
||||||
|
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
|
||||||
|
request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
|
||||||
|
_netd_itf.itf_num == request->wIndex)
|
||||||
|
{
|
||||||
|
if ( !_netd_itf.ecm_mode )
|
||||||
|
{
|
||||||
|
rndis_class_set_handler(notify.rndis_buf, request->wLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_incoming_packet(uint32_t len)
|
||||||
|
{
|
||||||
|
uint8_t *pnt = received;
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
if (_netd_itf.ecm_mode)
|
||||||
|
{
|
||||||
|
size = len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
|
||||||
|
if (len >= sizeof(rndis_data_packet_t))
|
||||||
|
if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
|
||||||
|
if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
|
||||||
|
{
|
||||||
|
pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
|
||||||
|
size = r->DataLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tud_network_recv_cb(pnt, (uint16_t) size))
|
||||||
|
{
|
||||||
|
/* if a buffer was never handled by user code, we must renew on the user's behalf */
|
||||||
|
tud_network_recv_renew();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
(void) result;
|
||||||
|
|
||||||
|
/* new packet received */
|
||||||
|
if ( ep_addr == _netd_itf.ep_out )
|
||||||
|
{
|
||||||
|
handle_incoming_packet(xferred_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* data transmission finished */
|
||||||
|
if ( ep_addr == _netd_itf.ep_in )
|
||||||
|
{
|
||||||
|
/* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */
|
||||||
|
|
||||||
|
if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) )
|
||||||
|
{
|
||||||
|
do_in_xfer(NULL, 0); /* a ZLP is needed */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* we're finally finished */
|
||||||
|
can_xmit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) )
|
||||||
|
{
|
||||||
|
if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_network_can_xmit(uint16_t size)
|
||||||
|
{
|
||||||
|
(void)size;
|
||||||
|
|
||||||
|
return can_xmit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tud_network_xmit(void *ref, uint16_t arg)
|
||||||
|
{
|
||||||
|
uint8_t *data;
|
||||||
|
uint16_t len;
|
||||||
|
|
||||||
|
if (!can_xmit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
|
||||||
|
data = transmitted + len;
|
||||||
|
|
||||||
|
len += tud_network_xmit_cb(data, ref, arg);
|
||||||
|
|
||||||
|
if (!_netd_itf.ecm_mode)
|
||||||
|
{
|
||||||
|
rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
|
||||||
|
memset(hdr, 0, sizeof(rndis_data_packet_t));
|
||||||
|
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
|
||||||
|
hdr->MessageLength = len;
|
||||||
|
hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset);
|
||||||
|
hdr->DataLength = len - sizeof(rndis_data_packet_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_in_xfer(transmitted, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021, Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _TUSB_NCM_H_
|
||||||
|
#define _TUSB_NCM_H_
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Table 4.3 Data Class Interface Protocol Codes
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
|
||||||
|
} ncm_data_interface_protocol_code_t;
|
||||||
|
|
||||||
|
|
||||||
|
// Table 6.2 Class-Specific Request Codes for Network Control Model subclass
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NCM_SET_ETHERNET_MULTICAST_FILTERS = 0x40,
|
||||||
|
NCM_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41,
|
||||||
|
NCM_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42,
|
||||||
|
NCM_SET_ETHERNET_PACKET_FILTER = 0x43,
|
||||||
|
NCM_GET_ETHERNET_STATISTIC = 0x44,
|
||||||
|
NCM_GET_NTB_PARAMETERS = 0x80,
|
||||||
|
NCM_GET_NET_ADDRESS = 0x81,
|
||||||
|
NCM_SET_NET_ADDRESS = 0x82,
|
||||||
|
NCM_GET_NTB_FORMAT = 0x83,
|
||||||
|
NCM_SET_NTB_FORMAT = 0x84,
|
||||||
|
NCM_GET_NTB_INPUT_SIZE = 0x85,
|
||||||
|
NCM_SET_NTB_INPUT_SIZE = 0x86,
|
||||||
|
NCM_GET_MAX_DATAGRAM_SIZE = 0x87,
|
||||||
|
NCM_SET_MAX_DATAGRAM_SIZE = 0x88,
|
||||||
|
NCM_GET_CRC_MODE = 0x89,
|
||||||
|
NCM_SET_CRC_MODE = 0x8A,
|
||||||
|
} ncm_request_code_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,511 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Jacob Berg Potter
|
||||||
|
* Copyright (c) 2020 Peter Lawrence
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if ( CFG_TUD_ENABLED && CFG_TUD_NCM )
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
#include "net_device.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#define NTH16_SIGNATURE 0x484D434E
|
||||||
|
#define NDP16_SIGNATURE_NCM0 0x304D434E
|
||||||
|
#define NDP16_SIGNATURE_NCM1 0x314D434E
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint16_t wLength;
|
||||||
|
uint16_t bmNtbFormatsSupported;
|
||||||
|
uint32_t dwNtbInMaxSize;
|
||||||
|
uint16_t wNdbInDivisor;
|
||||||
|
uint16_t wNdbInPayloadRemainder;
|
||||||
|
uint16_t wNdbInAlignment;
|
||||||
|
uint16_t wReserved;
|
||||||
|
uint32_t dwNtbOutMaxSize;
|
||||||
|
uint16_t wNdbOutDivisor;
|
||||||
|
uint16_t wNdbOutPayloadRemainder;
|
||||||
|
uint16_t wNdbOutAlignment;
|
||||||
|
uint16_t wNtbOutMaxDatagrams;
|
||||||
|
} ntb_parameters_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint32_t dwSignature;
|
||||||
|
uint16_t wHeaderLength;
|
||||||
|
uint16_t wSequence;
|
||||||
|
uint16_t wBlockLength;
|
||||||
|
uint16_t wNdpIndex;
|
||||||
|
} nth16_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint16_t wDatagramIndex;
|
||||||
|
uint16_t wDatagramLength;
|
||||||
|
} ndp16_datagram_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint32_t dwSignature;
|
||||||
|
uint16_t wLength;
|
||||||
|
uint16_t wNextNdpIndex;
|
||||||
|
ndp16_datagram_t datagram[];
|
||||||
|
} ndp16_t;
|
||||||
|
|
||||||
|
typedef union TU_ATTR_PACKED {
|
||||||
|
struct {
|
||||||
|
nth16_t nth;
|
||||||
|
ndp16_t ndp;
|
||||||
|
};
|
||||||
|
uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
|
||||||
|
} transmit_ntb_t;
|
||||||
|
|
||||||
|
struct ecm_notify_struct
|
||||||
|
{
|
||||||
|
tusb_control_request_t header;
|
||||||
|
uint32_t downlink, uplink;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
|
||||||
|
uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
|
||||||
|
|
||||||
|
uint8_t ep_notif;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
|
||||||
|
const ndp16_t *ndp;
|
||||||
|
uint8_t num_datagrams, current_datagram_index;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
REPORT_SPEED,
|
||||||
|
REPORT_CONNECTED,
|
||||||
|
REPORT_DONE
|
||||||
|
} report_state;
|
||||||
|
bool report_pending;
|
||||||
|
|
||||||
|
uint8_t current_ntb; // Index in transmit_ntb[] that is currently being filled with datagrams
|
||||||
|
uint8_t datagram_count; // Number of datagrams in transmit_ntb[current_ntb]
|
||||||
|
uint16_t next_datagram_offset; // Offset in transmit_ntb[current_ntb].data to place the next datagram
|
||||||
|
uint16_t ntb_in_size; // Maximum size of transmitted (IN to host) NTBs; initially CFG_TUD_NCM_IN_NTB_MAX_SIZE
|
||||||
|
uint8_t max_datagrams_per_ntb; // Maximum number of datagrams per NTB; initially CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
|
||||||
|
|
||||||
|
uint16_t nth_sequence; // Sequence number counter for transmitted NTBs
|
||||||
|
|
||||||
|
bool transferring;
|
||||||
|
|
||||||
|
} ncm_interface_t;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL OBJECT & FUNCTION DECLARATION
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = {
|
||||||
|
.wLength = sizeof(ntb_parameters_t),
|
||||||
|
.bmNtbFormatsSupported = 0x01,
|
||||||
|
.dwNtbInMaxSize = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
|
||||||
|
.wNdbInDivisor = 4,
|
||||||
|
.wNdbInPayloadRemainder = 0,
|
||||||
|
.wNdbInAlignment = CFG_TUD_NCM_ALIGNMENT,
|
||||||
|
.wReserved = 0,
|
||||||
|
.dwNtbOutMaxSize = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
|
||||||
|
.wNdbOutDivisor = 4,
|
||||||
|
.wNdbOutPayloadRemainder = 0,
|
||||||
|
.wNdbOutAlignment = CFG_TUD_NCM_ALIGNMENT,
|
||||||
|
.wNtbOutMaxDatagrams = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static transmit_ntb_t transmit_ntb[2];
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t receive_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
|
||||||
|
|
||||||
|
tu_static ncm_interface_t ncm_interface;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the NTB state in ncm_interface to be ready to add datagrams.
|
||||||
|
*/
|
||||||
|
static void ncm_prepare_for_tx(void) {
|
||||||
|
ncm_interface.datagram_count = 0;
|
||||||
|
// datagrams start after all the headers
|
||||||
|
ncm_interface.next_datagram_offset = sizeof(nth16_t) + sizeof(ndp16_t)
|
||||||
|
+ ((CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB + 1) * sizeof(ndp16_datagram_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If not already transmitting, start sending the current NTB to the host and swap buffers
|
||||||
|
* to start filling the other one with datagrams.
|
||||||
|
*/
|
||||||
|
static void ncm_start_tx(void) {
|
||||||
|
if (ncm_interface.transferring) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
|
||||||
|
size_t ntb_length = ncm_interface.next_datagram_offset;
|
||||||
|
|
||||||
|
// Fill in NTB header
|
||||||
|
ntb->nth.dwSignature = NTH16_SIGNATURE;
|
||||||
|
ntb->nth.wHeaderLength = sizeof(nth16_t);
|
||||||
|
ntb->nth.wSequence = ncm_interface.nth_sequence++;
|
||||||
|
ntb->nth.wBlockLength = ntb_length;
|
||||||
|
ntb->nth.wNdpIndex = sizeof(nth16_t);
|
||||||
|
|
||||||
|
// Fill in NDP16 header and terminator
|
||||||
|
ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
|
||||||
|
ntb->ndp.wLength = sizeof(ndp16_t) + (ncm_interface.datagram_count + 1) * sizeof(ndp16_datagram_t);
|
||||||
|
ntb->ndp.wNextNdpIndex = 0;
|
||||||
|
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = 0;
|
||||||
|
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = 0;
|
||||||
|
|
||||||
|
// Kick off an endpoint transfer
|
||||||
|
usbd_edpt_xfer(0, ncm_interface.ep_in, ntb->data, ntb_length);
|
||||||
|
ncm_interface.transferring = true;
|
||||||
|
|
||||||
|
// Swap to the other NTB and clear it out
|
||||||
|
ncm_interface.current_ntb = 1 - ncm_interface.current_ntb;
|
||||||
|
ncm_prepare_for_tx();
|
||||||
|
}
|
||||||
|
|
||||||
|
tu_static struct ecm_notify_struct ncm_notify_connected =
|
||||||
|
{
|
||||||
|
.header = {
|
||||||
|
.bmRequestType_bit = {
|
||||||
|
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||||
|
.type = TUSB_REQ_TYPE_CLASS,
|
||||||
|
.direction = TUSB_DIR_IN
|
||||||
|
},
|
||||||
|
.bRequest = CDC_NOTIF_NETWORK_CONNECTION,
|
||||||
|
.wValue = 1 /* Connected */,
|
||||||
|
.wLength = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
tu_static struct ecm_notify_struct ncm_notify_speed_change =
|
||||||
|
{
|
||||||
|
.header = {
|
||||||
|
.bmRequestType_bit = {
|
||||||
|
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||||
|
.type = TUSB_REQ_TYPE_CLASS,
|
||||||
|
.direction = TUSB_DIR_IN
|
||||||
|
},
|
||||||
|
.bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
|
||||||
|
.wLength = 8,
|
||||||
|
},
|
||||||
|
.downlink = 10000000,
|
||||||
|
.uplink = 10000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
void tud_network_recv_renew(void)
|
||||||
|
{
|
||||||
|
if (!ncm_interface.num_datagrams)
|
||||||
|
{
|
||||||
|
usbd_edpt_xfer(0, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ndp16_t *ndp = ncm_interface.ndp;
|
||||||
|
const int i = ncm_interface.current_datagram_index;
|
||||||
|
ncm_interface.current_datagram_index++;
|
||||||
|
ncm_interface.num_datagrams--;
|
||||||
|
|
||||||
|
tud_network_recv_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
void netd_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(&ncm_interface, sizeof(ncm_interface));
|
||||||
|
ncm_interface.ntb_in_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE;
|
||||||
|
ncm_interface.max_datagrams_per_ntb = CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB;
|
||||||
|
ncm_prepare_for_tx();
|
||||||
|
}
|
||||||
|
|
||||||
|
void netd_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
netd_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||||
|
{
|
||||||
|
// confirm interface hasn't already been allocated
|
||||||
|
TU_ASSERT(0 == ncm_interface.ep_notif, 0);
|
||||||
|
|
||||||
|
//------------- Management Interface -------------//
|
||||||
|
ncm_interface.itf_num = itf_desc->bInterfaceNumber;
|
||||||
|
|
||||||
|
uint16_t drv_len = sizeof(tusb_desc_interface_t);
|
||||||
|
uint8_t const * p_desc = tu_desc_next( itf_desc );
|
||||||
|
|
||||||
|
// Communication Functional Descriptors
|
||||||
|
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
|
||||||
|
{
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification endpoint (if any)
|
||||||
|
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
|
||||||
|
{
|
||||||
|
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
|
||||||
|
|
||||||
|
ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
|
||||||
|
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------- Data Interface -------------//
|
||||||
|
// - CDC-NCM data interface has 2 alternate settings
|
||||||
|
// - 0 : zero endpoints for inactive (default)
|
||||||
|
// - 1 : IN & OUT endpoints for transfer of NTBs
|
||||||
|
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
|
||||||
|
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
|
||||||
|
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
} while((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
|
||||||
|
|
||||||
|
// Pair of endpoints
|
||||||
|
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
|
||||||
|
|
||||||
|
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
|
||||||
|
|
||||||
|
drv_len += 2*sizeof(tusb_desc_endpoint_t);
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ncm_report(void)
|
||||||
|
{
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
if (ncm_interface.report_state == REPORT_SPEED) {
|
||||||
|
ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
|
||||||
|
usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
|
||||||
|
ncm_interface.report_state = REPORT_CONNECTED;
|
||||||
|
ncm_interface.report_pending = true;
|
||||||
|
} else if (ncm_interface.report_state == REPORT_CONNECTED) {
|
||||||
|
ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
|
||||||
|
usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected));
|
||||||
|
ncm_interface.report_state = REPORT_DONE;
|
||||||
|
ncm_interface.report_pending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TU_ATTR_WEAK void tud_network_link_state_cb(bool state)
|
||||||
|
{
|
||||||
|
(void)state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle class control request
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
if ( stage != CONTROL_STAGE_SETUP ) return true;
|
||||||
|
|
||||||
|
switch ( request->bmRequestType_bit.type )
|
||||||
|
{
|
||||||
|
case TUSB_REQ_TYPE_STANDARD:
|
||||||
|
switch ( request->bRequest )
|
||||||
|
{
|
||||||
|
case TUSB_REQ_GET_INTERFACE:
|
||||||
|
{
|
||||||
|
uint8_t const req_itfnum = (uint8_t) request->wIndex;
|
||||||
|
TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum);
|
||||||
|
|
||||||
|
tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TUSB_REQ_SET_INTERFACE:
|
||||||
|
{
|
||||||
|
uint8_t const req_itfnum = (uint8_t) request->wIndex;
|
||||||
|
uint8_t const req_alt = (uint8_t) request->wValue;
|
||||||
|
|
||||||
|
// Only valid for Data Interface with Alternate is either 0 or 1
|
||||||
|
TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2);
|
||||||
|
|
||||||
|
if (req_alt != ncm_interface.itf_data_alt) {
|
||||||
|
ncm_interface.itf_data_alt = req_alt;
|
||||||
|
|
||||||
|
if (ncm_interface.itf_data_alt) {
|
||||||
|
if (!usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
|
||||||
|
tud_network_recv_renew(); // prepare for incoming datagrams
|
||||||
|
}
|
||||||
|
if (!ncm_interface.report_pending) {
|
||||||
|
ncm_report();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tud_network_link_state_cb(ncm_interface.itf_data_alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
tud_control_status(rhport, request);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unsupported request
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TUSB_REQ_TYPE_CLASS:
|
||||||
|
TU_VERIFY (ncm_interface.itf_num == request->wIndex);
|
||||||
|
|
||||||
|
if (NCM_GET_NTB_PARAMETERS == request->bRequest)
|
||||||
|
{
|
||||||
|
tud_control_xfer(rhport, request, (void*)(uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unsupported request
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_incoming_datagram(uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t size = len;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TU_ASSERT(size >= sizeof(nth16_t), );
|
||||||
|
|
||||||
|
const nth16_t *hdr = (const nth16_t *)receive_ntb;
|
||||||
|
TU_ASSERT(hdr->dwSignature == NTH16_SIGNATURE, );
|
||||||
|
TU_ASSERT(hdr->wNdpIndex >= sizeof(nth16_t) && (hdr->wNdpIndex + sizeof(ndp16_t)) <= len, );
|
||||||
|
|
||||||
|
const ndp16_t *ndp = (const ndp16_t *)(receive_ntb + hdr->wNdpIndex);
|
||||||
|
TU_ASSERT(ndp->dwSignature == NDP16_SIGNATURE_NCM0 || ndp->dwSignature == NDP16_SIGNATURE_NCM1, );
|
||||||
|
TU_ASSERT(hdr->wNdpIndex + ndp->wLength <= len, );
|
||||||
|
|
||||||
|
int num_datagrams = (ndp->wLength - 12) / 4;
|
||||||
|
ncm_interface.current_datagram_index = 0;
|
||||||
|
ncm_interface.num_datagrams = 0;
|
||||||
|
ncm_interface.ndp = ndp;
|
||||||
|
for (int i = 0; i < num_datagrams && ndp->datagram[i].wDatagramIndex && ndp->datagram[i].wDatagramLength; i++)
|
||||||
|
{
|
||||||
|
ncm_interface.num_datagrams++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tud_network_recv_renew();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
(void) result;
|
||||||
|
|
||||||
|
/* new datagram receive_ntb */
|
||||||
|
if (ep_addr == ncm_interface.ep_out )
|
||||||
|
{
|
||||||
|
handle_incoming_datagram(xferred_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* data transmission finished */
|
||||||
|
if (ep_addr == ncm_interface.ep_in )
|
||||||
|
{
|
||||||
|
if (ncm_interface.transferring) {
|
||||||
|
ncm_interface.transferring = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are datagrams queued up that we tried to send while this NTB was being emitted, send them now
|
||||||
|
if (ncm_interface.datagram_count && ncm_interface.itf_data_alt == 1) {
|
||||||
|
ncm_start_tx();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ep_addr == ncm_interface.ep_notif )
|
||||||
|
{
|
||||||
|
ncm_interface.report_pending = false;
|
||||||
|
ncm_report();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// poll network driver for its ability to accept another packet to transmit
|
||||||
|
bool tud_network_can_xmit(uint16_t size)
|
||||||
|
{
|
||||||
|
TU_VERIFY(ncm_interface.itf_data_alt == 1);
|
||||||
|
|
||||||
|
if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
|
||||||
|
TU_LOG2("NTB full [by count]\r\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t next_datagram_offset = ncm_interface.next_datagram_offset;
|
||||||
|
if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
|
||||||
|
TU_LOG2("ntb full [by size]\r\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tud_network_xmit(void *ref, uint16_t arg)
|
||||||
|
{
|
||||||
|
transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
|
||||||
|
size_t next_datagram_offset = ncm_interface.next_datagram_offset;
|
||||||
|
|
||||||
|
uint16_t size = tud_network_xmit_cb(ntb->data + next_datagram_offset, ref, arg);
|
||||||
|
|
||||||
|
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset;
|
||||||
|
ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size;
|
||||||
|
|
||||||
|
ncm_interface.datagram_count++;
|
||||||
|
next_datagram_offset += size;
|
||||||
|
|
||||||
|
// round up so the next datagram is aligned correctly
|
||||||
|
next_datagram_offset += (CFG_TUD_NCM_ALIGNMENT - 1);
|
||||||
|
next_datagram_offset -= (next_datagram_offset % CFG_TUD_NCM_ALIGNMENT);
|
||||||
|
|
||||||
|
ncm_interface.next_datagram_offset = next_datagram_offset;
|
||||||
|
|
||||||
|
ncm_start_tx();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Peter Lawrence
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_NET_DEVICE_H_
|
||||||
|
#define _TUSB_NET_DEVICE_H_
|
||||||
|
|
||||||
|
#include "class/cdc/cdc.h"
|
||||||
|
|
||||||
|
#if CFG_TUD_ECM_RNDIS && CFG_TUD_NCM
|
||||||
|
#error "Cannot enable both ECM_RNDIS and NCM network drivers"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ncm.h"
|
||||||
|
|
||||||
|
/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
|
||||||
|
#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||||
|
|
||||||
|
/* Maximum Transmission Unit (in bytes) of the network, including Ethernet header */
|
||||||
|
#ifndef CFG_TUD_NET_MTU
|
||||||
|
#define CFG_TUD_NET_MTU 1514
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
|
||||||
|
#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
|
||||||
|
#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
|
||||||
|
#define CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_NCM_ALIGNMENT
|
||||||
|
#define CFG_TUD_NCM_ALIGNMENT 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// indicate to network driver that client has finished with the packet provided to network_recv_cb()
|
||||||
|
void tud_network_recv_renew(void);
|
||||||
|
|
||||||
|
// poll network driver for its ability to accept another packet to transmit
|
||||||
|
bool tud_network_can_xmit(uint16_t size);
|
||||||
|
|
||||||
|
// if network_can_xmit() returns true, network_xmit() can be called once
|
||||||
|
void tud_network_xmit(void *ref, uint16_t arg);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callbacks (WEAK is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// client must provide this: return false if the packet buffer was not accepted
|
||||||
|
bool tud_network_recv_cb(const uint8_t *src, uint16_t size);
|
||||||
|
|
||||||
|
// client must provide this: copy from network stack packet pointer to dst
|
||||||
|
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg);
|
||||||
|
|
||||||
|
//------------- ECM/RNDIS -------------//
|
||||||
|
|
||||||
|
// client must provide this: initialize any network state back to the beginning
|
||||||
|
void tud_network_init_cb(void);
|
||||||
|
|
||||||
|
// client must provide this: 48-bit MAC address
|
||||||
|
// TODO removed later since it is not part of tinyusb stack
|
||||||
|
extern uint8_t tud_network_mac_address[6];
|
||||||
|
|
||||||
|
//------------- NCM -------------//
|
||||||
|
|
||||||
|
// callback to client providing optional indication of internal state of network driver
|
||||||
|
void tud_network_link_state_cb(bool state);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL USBD-CLASS DRIVER API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void netd_init (void);
|
||||||
|
void netd_reset (uint8_t rhport);
|
||||||
|
uint16_t netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||||
|
void netd_report (uint8_t *buf, uint16_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_NET_DEVICE_H_ */
|
@ -0,0 +1,318 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 N Conrad
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_USBTMC_H__
|
||||||
|
#define _TUSB_USBTMC_H__
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Implements USBTMC Revision 1.0, April 14, 2003
|
||||||
|
|
||||||
|
String descriptors must have a "LANGID=0x409"/US English string.
|
||||||
|
Characters must be 0x20 (' ') to 0x7E ('~') ASCII,
|
||||||
|
But MUST not contain: "/:?\*
|
||||||
|
Also must not have leading or trailing space (' ')
|
||||||
|
Device descriptor must state USB version 0x0200 or greater
|
||||||
|
|
||||||
|
If USB488DeviceCapabilites.D2 = 1 (SR1), then there must be a INT endpoint.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define USBTMC_VERSION 0x0100
|
||||||
|
#define USBTMC_488_VERSION 0x0100
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
USBTMC_MSGID_DEV_DEP_MSG_OUT = 1u,
|
||||||
|
USBTMC_MSGID_DEV_DEP_MSG_IN = 2u,
|
||||||
|
USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT = 126u,
|
||||||
|
USBTMC_MSGID_VENDOR_SPECIFIC_IN = 127u,
|
||||||
|
USBTMC_MSGID_USB488_TRIGGER = 128u,
|
||||||
|
} usbtmc_msgid_enum;
|
||||||
|
|
||||||
|
/// \brief Message header (For BULK OUT and BULK IN); 4 bytes
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t MsgID ; ///< Message type ID (usbtmc_msgid_enum)
|
||||||
|
uint8_t bTag ; ///< Transfer ID 1<=bTag<=255
|
||||||
|
uint8_t bTagInverse ; ///< Complement of the tag
|
||||||
|
uint8_t _reserved ; ///< Must be 0x00
|
||||||
|
} usbtmc_msg_header_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
usbtmc_msg_header_t header;
|
||||||
|
uint8_t data[8];
|
||||||
|
} usbtmc_msg_generic_t;
|
||||||
|
|
||||||
|
/* Uses on the bulk-out endpoint: */
|
||||||
|
// Next 8 bytes are message-specific
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
usbtmc_msg_header_t header ; ///< Header
|
||||||
|
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
unsigned int EOM : 1 ; ///< EOM set on last byte
|
||||||
|
} bmTransferAttributes;
|
||||||
|
uint8_t _reserved[3];
|
||||||
|
} usbtmc_msg_request_dev_dep_out;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_out) == 12u, "struct wrong length");
|
||||||
|
|
||||||
|
// Next 8 bytes are message-specific
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
usbtmc_msg_header_t header ; ///< Header
|
||||||
|
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
unsigned int TermCharEnabled : 1 ; ///< "The Bulk-IN transfer must terminate on the specified TermChar."; CAPABILITIES must list TermChar
|
||||||
|
} bmTransferAttributes;
|
||||||
|
uint8_t TermChar;
|
||||||
|
uint8_t _reserved[2];
|
||||||
|
} usbtmc_msg_request_dev_dep_in;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_dev_dep_in) == 12u, "struct wrong length");
|
||||||
|
|
||||||
|
/* Bulk-in headers */
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
usbtmc_msg_header_t header;
|
||||||
|
uint32_t TransferSize;
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t EOM: 1; ///< Last byte of transfer is the end of the message
|
||||||
|
uint8_t UsingTermChar: 1; ///< Support TermChar && Request.TermCharEnabled && last char in transfer is TermChar
|
||||||
|
} bmTransferAttributes;
|
||||||
|
uint8_t _reserved[3];
|
||||||
|
} usbtmc_msg_dev_dep_msg_in_header_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_msg_dev_dep_msg_in_header_t) == 12u, "struct wrong length");
|
||||||
|
|
||||||
|
/* Unsupported vendor things.... Are these ever used?*/
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
usbtmc_msg_header_t header ; ///< Header
|
||||||
|
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||||
|
uint8_t _reserved[4];
|
||||||
|
} usbtmc_msg_request_vendor_specific_out;
|
||||||
|
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_out) == 12u, "struct wrong length");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
usbtmc_msg_header_t header ; ///< Header
|
||||||
|
uint32_t TransferSize ; ///< Transfer size; LSB first
|
||||||
|
uint8_t _reserved[4];
|
||||||
|
} usbtmc_msg_request_vendor_specific_in;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_msg_request_vendor_specific_in) == 12u, "struct wrong length");
|
||||||
|
|
||||||
|
// Control request type should use tusb_control_request_t
|
||||||
|
|
||||||
|
/*
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
struct {
|
||||||
|
unsigned int Recipient : 5 ; ///< EOM set on last byte
|
||||||
|
unsigned int Type : 2 ; ///< EOM set on last byte
|
||||||
|
unsigned int DirectionToHost : 1 ; ///< 0 is OUT, 1 is IN
|
||||||
|
} bmRequestType;
|
||||||
|
uint8_t bRequest ; ///< If bmRequestType.Type = Class, see usmtmc_request_type_enum
|
||||||
|
uint16_t wValue ;
|
||||||
|
uint16_t wIndex ;
|
||||||
|
uint16_t wLength ; // Number of bytes in data stage
|
||||||
|
} usbtmc_class_specific_control_req;
|
||||||
|
|
||||||
|
*/
|
||||||
|
// bulk-in protocol errors
|
||||||
|
enum {
|
||||||
|
USBTMC_BULK_IN_ERR_INCOMPLETE_HEADER = 1u,
|
||||||
|
USBTMC_BULK_IN_ERR_UNSUPPORTED = 2u,
|
||||||
|
USBTMC_BULK_IN_ERR_BAD_PARAMETER = 3u,
|
||||||
|
USBTMC_BULK_IN_ERR_DATA_TOO_SHORT = 4u,
|
||||||
|
USBTMC_BULK_IN_ERR_DATA_TOO_LONG = 5u,
|
||||||
|
};
|
||||||
|
// built-in halt errors
|
||||||
|
enum {
|
||||||
|
USBTMC_BULK_IN_ERR = 1u, ///< receives a USBTMC command message that expects a response while a
|
||||||
|
/// Bulk-IN transfer is in progress
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT = 1u,
|
||||||
|
USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS = 2u,
|
||||||
|
USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN = 3u,
|
||||||
|
USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS = 4u,
|
||||||
|
USBTMC_bREQUEST_INITIATE_CLEAR = 5u,
|
||||||
|
USBTMC_bREQUEST_CHECK_CLEAR_STATUS = 6u,
|
||||||
|
USBTMC_bREQUEST_GET_CAPABILITIES = 7u,
|
||||||
|
|
||||||
|
USBTMC_bREQUEST_INDICATOR_PULSE = 64u, // Optional
|
||||||
|
|
||||||
|
/****** USBTMC 488 *************/
|
||||||
|
USB488_bREQUEST_READ_STATUS_BYTE = 128u,
|
||||||
|
USB488_bREQUEST_REN_CONTROL = 160u,
|
||||||
|
USB488_bREQUEST_GO_TO_LOCAL = 161u,
|
||||||
|
USB488_bREQUEST_LOCAL_LOCKOUT = 162u,
|
||||||
|
|
||||||
|
} usmtmc_request_type_enum;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
USBTMC_STATUS_SUCCESS = 0x01,
|
||||||
|
USBTMC_STATUS_PENDING = 0x02,
|
||||||
|
USBTMC_STATUS_FAILED = 0x80,
|
||||||
|
USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS = 0x81,
|
||||||
|
USBTMC_STATUS_SPLIT_NOT_IN_PROGRESS = 0x82,
|
||||||
|
USBTMC_STATUS_SPLIT_IN_PROGRESS = 0x83,
|
||||||
|
|
||||||
|
/****** USBTMC 488 *************/
|
||||||
|
USB488_STATUS_INTERRUPT_IN_BUSY = 0x20
|
||||||
|
} usbtmc_status_enum;
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* Control Responses
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t USBTMC_status; ///< usbtmc_status_enum
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint16_t bcdUSBTMC; ///< USBTMC_VERSION
|
||||||
|
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
unsigned int listenOnly :1;
|
||||||
|
unsigned int talkOnly :1;
|
||||||
|
unsigned int supportsIndicatorPulse :1;
|
||||||
|
} bmIntfcCapabilities;
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
unsigned int canEndBulkInOnTermChar :1;
|
||||||
|
} bmDevCapabilities;
|
||||||
|
uint8_t _reserved2[6];
|
||||||
|
uint8_t _reserved3[12];
|
||||||
|
} usbtmc_response_capabilities_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_t) == 0x18, "struct wrong length");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t USBTMC_status;
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
unsigned int BulkInFifoBytes :1;
|
||||||
|
} bmClear;
|
||||||
|
} usbtmc_get_clear_status_rsp_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_get_clear_status_rsp_t) == 2u, "struct wrong length");
|
||||||
|
|
||||||
|
// Used for both abort bulk IN and bulk OUT
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t USBTMC_status;
|
||||||
|
uint8_t bTag;
|
||||||
|
} usbtmc_initiate_abort_rsp_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_get_clear_status_rsp_t) == 2u, "struct wrong length");
|
||||||
|
|
||||||
|
// Used for both check_abort_bulk_in_status and check_abort_bulk_out_status
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t USBTMC_status;
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
unsigned int BulkInFifoBytes : 1; ///< Has queued data or a short packet that is queued
|
||||||
|
} bmAbortBulkIn;
|
||||||
|
uint8_t _reserved[2]; ///< Must be zero
|
||||||
|
uint32_t NBYTES_RXD_TXD;
|
||||||
|
} usbtmc_check_abort_bulk_rsp_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_check_abort_bulk_rsp_t) == 8u, "struct wrong length");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t USBTMC_status; ///< usbtmc_status_enum
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint16_t bcdUSBTMC; ///< USBTMC_VERSION
|
||||||
|
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t listenOnly :1;
|
||||||
|
uint8_t talkOnly :1;
|
||||||
|
uint8_t supportsIndicatorPulse :1;
|
||||||
|
} bmIntfcCapabilities;
|
||||||
|
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t canEndBulkInOnTermChar :1;
|
||||||
|
} bmDevCapabilities;
|
||||||
|
|
||||||
|
uint8_t _reserved2[6];
|
||||||
|
uint16_t bcdUSB488;
|
||||||
|
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t supportsTrigger :1;
|
||||||
|
uint8_t supportsREN_GTL_LLO :1;
|
||||||
|
uint8_t is488_2 :1;
|
||||||
|
} bmIntfcCapabilities488;
|
||||||
|
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t DT1 :1;
|
||||||
|
uint8_t RL1 :1;
|
||||||
|
uint8_t SR1 :1;
|
||||||
|
uint8_t SCPI :1;
|
||||||
|
} bmDevCapabilities488;
|
||||||
|
uint8_t _reserved3[8];
|
||||||
|
} usbtmc_response_capabilities_488_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_response_capabilities_488_t) == 0x18, "struct wrong length");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
uint8_t USBTMC_status;
|
||||||
|
uint8_t bTag;
|
||||||
|
uint8_t statusByte;
|
||||||
|
} usbtmc_read_stb_rsp_488_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length");
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
struct TU_ATTR_PACKED
|
||||||
|
{
|
||||||
|
unsigned int bTag : 7;
|
||||||
|
unsigned int one : 1;
|
||||||
|
} bNotify1;
|
||||||
|
uint8_t StatusByte;
|
||||||
|
} usbtmc_read_stb_interrupt_488_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_interrupt_488_t) == 2u, "struct wrong length");
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,890 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Nathan Conrad
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This library is not fully reentrant, though it is reentrant from the view
|
||||||
|
* of either the application layer or the USB stack. Due to its locking,
|
||||||
|
* it is not safe to call its functions from interrupts.
|
||||||
|
*
|
||||||
|
* The one exception is that its functions may not be called from the application
|
||||||
|
* until the USB stack is initialized. This should not be a problem since the
|
||||||
|
* device shouldn't be sending messages until it receives a request from the
|
||||||
|
* host.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In the case of single-CPU "no OS", this task is never preempted other than by
|
||||||
|
* interrupts, and the USBTMC code isn't called by interrupts, so all is OK. For "no OS",
|
||||||
|
* the mutex structure's main effect is to disable the USB interrupts.
|
||||||
|
* With an OS, this class driver uses the OSAL to perform locking. The code uses a single lock
|
||||||
|
* and does not call outside of this class with a lock held, so deadlocks won't happen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Limitations:
|
||||||
|
// "vendor-specific" commands are not handled.
|
||||||
|
// Dealing with "termchar" must be handled by the application layer,
|
||||||
|
// though additional error checking is does in this module.
|
||||||
|
// talkOnly and listenOnly are NOT supported. They're not permitted
|
||||||
|
// in USB488, anyway.
|
||||||
|
|
||||||
|
/* Supported:
|
||||||
|
*
|
||||||
|
* Notification pulse
|
||||||
|
* Trigger
|
||||||
|
* Read status byte (both by interrupt endpoint and control message)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// USBTMC 3.2.2 error conditions not strictly followed
|
||||||
|
// No local lock-out, REN, or GTL.
|
||||||
|
// Clear message available status byte at the correct time? (488 4.3.1.3)
|
||||||
|
// Ability to defer status byte transmission
|
||||||
|
// Transmission of status byte in response to USB488 SRQ condition
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_USBTMC)
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "usbtmc_device.h"
|
||||||
|
|
||||||
|
#ifdef xDEBUG
|
||||||
|
#include "uart_util.h"
|
||||||
|
tu_static char logMsg[150];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Buffer size must be an exact multiple of the max packet size for both
|
||||||
|
// bulk (up to 64 bytes for FS, 512 bytes for HS). In addation, this driver
|
||||||
|
// imposes a minimum buffer size of 32 bytes.
|
||||||
|
#define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The state machine does not allow simultaneous reading and writing. This is
|
||||||
|
* consistent with USBTMC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STATE_CLOSED, // Endpoints have not yet been opened since USB reset
|
||||||
|
STATE_NAK, // Bulk-out endpoint is in NAK state.
|
||||||
|
STATE_IDLE, // Bulk-out endpoint is waiting for CMD.
|
||||||
|
STATE_RCV, // Bulk-out is receiving DEV_DEP message
|
||||||
|
STATE_TX_REQUESTED,
|
||||||
|
STATE_TX_INITIATED,
|
||||||
|
STATE_TX_SHORTED,
|
||||||
|
STATE_CLEARING,
|
||||||
|
STATE_ABORTING_BULK_IN,
|
||||||
|
STATE_ABORTING_BULK_IN_SHORTED, // aborting, and short packet has been queued for transmission
|
||||||
|
STATE_ABORTING_BULK_IN_ABORTED, // aborting, and short packet has been transmitted
|
||||||
|
STATE_ABORTING_BULK_OUT,
|
||||||
|
STATE_NUM_STATES
|
||||||
|
} usbtmcd_state_enum;
|
||||||
|
|
||||||
|
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
typedef usbtmc_response_capabilities_488_t usbtmc_capabilities_specific_t;
|
||||||
|
#else
|
||||||
|
typedef usbtmc_response_capabilities_t usbtmc_capabilities_specific_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
volatile usbtmcd_state_enum state;
|
||||||
|
|
||||||
|
uint8_t itf_id;
|
||||||
|
uint8_t rhport;
|
||||||
|
uint8_t ep_bulk_in;
|
||||||
|
uint8_t ep_bulk_out;
|
||||||
|
uint8_t ep_int_in;
|
||||||
|
// IN buffer is only used for first packet, not the remainder
|
||||||
|
// in order to deal with prepending header
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE];
|
||||||
|
uint32_t ep_bulk_in_wMaxPacketSize;
|
||||||
|
// OUT buffer receives one packet at a time
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE];
|
||||||
|
uint32_t ep_bulk_out_wMaxPacketSize;
|
||||||
|
|
||||||
|
uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
|
||||||
|
uint32_t transfer_size_sent; // To keep track of data bytes that have been queued in FIFO (not header bytes)
|
||||||
|
|
||||||
|
uint8_t lastBulkOutTag; // used for aborts (mostly)
|
||||||
|
uint8_t lastBulkInTag; // used for aborts (mostly)
|
||||||
|
|
||||||
|
uint8_t const * devInBuffer; // pointer to application-layer used for transmissions
|
||||||
|
|
||||||
|
usbtmc_capabilities_specific_t const * capabilities;
|
||||||
|
} usbtmc_interface_state_t;
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION tu_static usbtmc_interface_state_t usbtmc_state =
|
||||||
|
{
|
||||||
|
.itf_id = 0xFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We need all headers to fit in a single packet in this implementation, 32 bytes will fit all standard USBTMC headers
|
||||||
|
TU_VERIFY_STATIC(USBTMCD_BUFFER_SIZE >= 32u,"USBTMC dev buffer size too small");
|
||||||
|
|
||||||
|
static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len);
|
||||||
|
static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
tu_static uint8_t termChar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tu_static uint8_t termCharRequested = false;
|
||||||
|
|
||||||
|
#if OSAL_MUTEX_REQUIRED
|
||||||
|
static OSAL_MUTEX_DEF(usbtmcLockBuffer);
|
||||||
|
#endif
|
||||||
|
osal_mutex_t usbtmcLock;
|
||||||
|
|
||||||
|
// Our own private lock, mostly for the state variable.
|
||||||
|
#define criticalEnter() do { (void) osal_mutex_lock(usbtmcLock,OSAL_TIMEOUT_WAIT_FOREVER); } while (0)
|
||||||
|
#define criticalLeave() do { (void) osal_mutex_unlock(usbtmcLock); } while (0)
|
||||||
|
|
||||||
|
bool atomicChangeState(usbtmcd_state_enum expectedState, usbtmcd_state_enum newState)
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
criticalEnter();
|
||||||
|
usbtmcd_state_enum oldState = usbtmc_state.state;
|
||||||
|
if (oldState == expectedState)
|
||||||
|
{
|
||||||
|
usbtmc_state.state = newState;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
criticalLeave();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// called from app
|
||||||
|
// We keep a reference to the buffer, so it MUST not change until the app is
|
||||||
|
// notified that the transfer is complete.
|
||||||
|
// length of data is specified in the hdr.
|
||||||
|
|
||||||
|
// We can't just send the whole thing at once because we need to concatanate the
|
||||||
|
// header with the data.
|
||||||
|
bool tud_usbtmc_transmit_dev_msg_data(
|
||||||
|
const void * data, size_t len,
|
||||||
|
bool endOfMessage,
|
||||||
|
bool usingTermChar)
|
||||||
|
{
|
||||||
|
const unsigned int txBufLen = sizeof(usbtmc_state.ep_bulk_in_buf);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
TU_ASSERT(len > 0u);
|
||||||
|
TU_ASSERT(len <= usbtmc_state.transfer_size_remaining);
|
||||||
|
TU_ASSERT(usbtmc_state.transfer_size_sent == 0u);
|
||||||
|
if(usingTermChar)
|
||||||
|
{
|
||||||
|
TU_ASSERT(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
|
||||||
|
TU_ASSERT(termCharRequested);
|
||||||
|
TU_ASSERT(((uint8_t const*)data)[len-1u] == termChar);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TU_VERIFY(usbtmc_state.state == STATE_TX_REQUESTED);
|
||||||
|
usbtmc_msg_dev_dep_msg_in_header_t *hdr = (usbtmc_msg_dev_dep_msg_in_header_t*)usbtmc_state.ep_bulk_in_buf;
|
||||||
|
tu_varclr(hdr);
|
||||||
|
hdr->header.MsgID = USBTMC_MSGID_DEV_DEP_MSG_IN;
|
||||||
|
hdr->header.bTag = usbtmc_state.lastBulkInTag;
|
||||||
|
hdr->header.bTagInverse = (uint8_t)~(usbtmc_state.lastBulkInTag);
|
||||||
|
hdr->TransferSize = len;
|
||||||
|
hdr->bmTransferAttributes.EOM = endOfMessage;
|
||||||
|
hdr->bmTransferAttributes.UsingTermChar = usingTermChar;
|
||||||
|
|
||||||
|
// Copy in the header
|
||||||
|
const size_t headerLen = sizeof(*hdr);
|
||||||
|
const size_t dataLen = ((headerLen + hdr->TransferSize) <= txBufLen) ?
|
||||||
|
len : (txBufLen - headerLen);
|
||||||
|
const size_t packetLen = headerLen + dataLen;
|
||||||
|
|
||||||
|
memcpy((uint8_t*)(usbtmc_state.ep_bulk_in_buf) + headerLen, data, dataLen);
|
||||||
|
usbtmc_state.transfer_size_remaining = len - dataLen;
|
||||||
|
usbtmc_state.transfer_size_sent = dataLen;
|
||||||
|
usbtmc_state.devInBuffer = (uint8_t const*) data + (dataLen);
|
||||||
|
|
||||||
|
bool stateChanged =
|
||||||
|
atomicChangeState(STATE_TX_REQUESTED, (packetLen >= txBufLen) ? STATE_TX_INITIATED : STATE_TX_SHORTED);
|
||||||
|
TU_VERIFY(stateChanged);
|
||||||
|
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usbtmcd_init_cb(void)
|
||||||
|
{
|
||||||
|
usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
|
||||||
|
#ifndef NDEBUG
|
||||||
|
# if CFG_TUD_USBTMC_ENABLE_488
|
||||||
|
if (usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger) {
|
||||||
|
TU_ASSERT(&tud_usbtmc_msg_trigger_cb != NULL,);
|
||||||
|
}
|
||||||
|
// Per USB488 spec: table 8
|
||||||
|
TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.listenOnly,);
|
||||||
|
TU_ASSERT(!usbtmc_state.capabilities->bmIntfcCapabilities.talkOnly,);
|
||||||
|
# endif
|
||||||
|
if (usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse) {
|
||||||
|
TU_ASSERT(&tud_usbtmc_indicator_pulse_cb != NULL,);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
usbtmcLock = osal_mutex_create(&usbtmcLockBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
|
||||||
|
{
|
||||||
|
(void)rhport;
|
||||||
|
|
||||||
|
uint16_t drv_len;
|
||||||
|
uint8_t const * p_desc;
|
||||||
|
uint8_t found_endpoints = 0;
|
||||||
|
|
||||||
|
TU_VERIFY(itf_desc->bInterfaceClass == TUD_USBTMC_APP_CLASS , 0);
|
||||||
|
TU_VERIFY(itf_desc->bInterfaceSubClass == TUD_USBTMC_APP_SUBCLASS, 0);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Only 2 or 3 endpoints are allowed for USBTMC.
|
||||||
|
TU_ASSERT((itf_desc->bNumEndpoints == 2) || (itf_desc->bNumEndpoints ==3), 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TU_ASSERT(usbtmc_state.state == STATE_CLOSED, 0);
|
||||||
|
|
||||||
|
// Interface
|
||||||
|
drv_len = 0u;
|
||||||
|
p_desc = (uint8_t const *) itf_desc;
|
||||||
|
|
||||||
|
usbtmc_state.itf_id = itf_desc->bInterfaceNumber;
|
||||||
|
usbtmc_state.rhport = rhport;
|
||||||
|
|
||||||
|
while (found_endpoints < itf_desc->bNumEndpoints && drv_len <= max_len)
|
||||||
|
{
|
||||||
|
if ( TUSB_DESC_ENDPOINT == p_desc[DESC_OFFSET_TYPE])
|
||||||
|
{
|
||||||
|
tusb_desc_endpoint_t const *ep_desc = (tusb_desc_endpoint_t const *)p_desc;
|
||||||
|
switch(ep_desc->bmAttributes.xfer) {
|
||||||
|
case TUSB_XFER_BULK:
|
||||||
|
// Ensure buffer is an exact multiple of the maxPacketSize
|
||||||
|
TU_ASSERT((USBTMCD_BUFFER_SIZE % tu_edpt_packet_size(ep_desc)) == 0, 0);
|
||||||
|
if (tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN)
|
||||||
|
{
|
||||||
|
usbtmc_state.ep_bulk_in = ep_desc->bEndpointAddress;
|
||||||
|
usbtmc_state.ep_bulk_in_wMaxPacketSize = tu_edpt_packet_size(ep_desc);
|
||||||
|
} else {
|
||||||
|
usbtmc_state.ep_bulk_out = ep_desc->bEndpointAddress;
|
||||||
|
usbtmc_state.ep_bulk_out_wMaxPacketSize = tu_edpt_packet_size(ep_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case TUSB_XFER_INTERRUPT:
|
||||||
|
#ifndef NDEBUG
|
||||||
|
TU_ASSERT(tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN, 0);
|
||||||
|
TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
|
||||||
|
#endif
|
||||||
|
usbtmc_state.ep_int_in = ep_desc->bEndpointAddress;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
TU_ASSERT(false, 0);
|
||||||
|
}
|
||||||
|
TU_ASSERT( usbd_edpt_open(rhport, ep_desc), 0);
|
||||||
|
found_endpoints++;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv_len += tu_desc_len(p_desc);
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bulk endpoints are required, but interrupt IN is optional
|
||||||
|
#ifndef NDEBUG
|
||||||
|
TU_ASSERT(usbtmc_state.ep_bulk_in != 0, 0);
|
||||||
|
TU_ASSERT(usbtmc_state.ep_bulk_out != 0, 0);
|
||||||
|
if (itf_desc->bNumEndpoints == 2)
|
||||||
|
{
|
||||||
|
TU_ASSERT(usbtmc_state.ep_int_in == 0, 0);
|
||||||
|
}
|
||||||
|
else if (itf_desc->bNumEndpoints == 3)
|
||||||
|
{
|
||||||
|
TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
|
||||||
|
}
|
||||||
|
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
if(usbtmc_state.capabilities->bmIntfcCapabilities488.is488_2 ||
|
||||||
|
usbtmc_state.capabilities->bmDevCapabilities488.SR1)
|
||||||
|
{
|
||||||
|
TU_ASSERT(usbtmc_state.ep_int_in != 0, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
atomicChangeState(STATE_CLOSED, STATE_NAK);
|
||||||
|
tud_usbtmc_open_cb(itf_desc->iInterface);
|
||||||
|
|
||||||
|
return drv_len;
|
||||||
|
}
|
||||||
|
// Tell USBTMC class to set its bulk-in EP to ACK so that it can
|
||||||
|
// receive USBTMC commands.
|
||||||
|
// Returns false if it was already in an ACK state or is busy
|
||||||
|
// processing a command (such as a clear). Returns true if it was
|
||||||
|
// in the NAK state and successfully transitioned to the ACK wait
|
||||||
|
// state.
|
||||||
|
bool tud_usbtmc_start_bus_read()
|
||||||
|
{
|
||||||
|
usbtmcd_state_enum oldState = usbtmc_state.state;
|
||||||
|
switch(oldState)
|
||||||
|
{
|
||||||
|
// These may transition to IDLE
|
||||||
|
case STATE_NAK:
|
||||||
|
case STATE_ABORTING_BULK_IN_ABORTED:
|
||||||
|
TU_VERIFY(atomicChangeState(oldState, STATE_IDLE));
|
||||||
|
break;
|
||||||
|
// When receiving, let it remain receiving
|
||||||
|
case STATE_RCV:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_bulk_out, usbtmc_state.ep_bulk_out_buf, (uint16_t)usbtmc_state.ep_bulk_out_wMaxPacketSize));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usbtmcd_reset_cb(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void)rhport;
|
||||||
|
usbtmc_capabilities_specific_t const * capabilities = tud_usbtmc_get_capabilities_cb();
|
||||||
|
|
||||||
|
criticalEnter();
|
||||||
|
tu_varclr(&usbtmc_state);
|
||||||
|
usbtmc_state.capabilities = capabilities;
|
||||||
|
usbtmc_state.itf_id = 0xFFu;
|
||||||
|
criticalLeave();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_devMsgOutStart(uint8_t rhport, void *data, size_t len)
|
||||||
|
{
|
||||||
|
(void)rhport;
|
||||||
|
// return true upon failure, as we can assume error is being handled elsewhere.
|
||||||
|
TU_VERIFY(atomicChangeState(STATE_IDLE, STATE_RCV), true);
|
||||||
|
usbtmc_state.transfer_size_sent = 0u;
|
||||||
|
|
||||||
|
// must be a header, should have been confirmed before calling here.
|
||||||
|
usbtmc_msg_request_dev_dep_out *msg = (usbtmc_msg_request_dev_dep_out*)data;
|
||||||
|
usbtmc_state.transfer_size_remaining = msg->TransferSize;
|
||||||
|
TU_VERIFY(tud_usbtmc_msgBulkOut_start_cb(msg));
|
||||||
|
|
||||||
|
TU_VERIFY(handle_devMsgOut(rhport, (uint8_t*)data + sizeof(*msg), len - sizeof(*msg), len));
|
||||||
|
usbtmc_state.lastBulkOutTag = msg->header.bTag;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_devMsgOut(uint8_t rhport, void *data, size_t len, size_t packetLen)
|
||||||
|
{
|
||||||
|
(void)rhport;
|
||||||
|
// return true upon failure, as we can assume error is being handled elsewhere.
|
||||||
|
TU_VERIFY(usbtmc_state.state == STATE_RCV,true);
|
||||||
|
|
||||||
|
bool shortPacket = (packetLen < usbtmc_state.ep_bulk_out_wMaxPacketSize);
|
||||||
|
|
||||||
|
// Packet is to be considered complete when we get enough data or at a short packet.
|
||||||
|
bool atEnd = false;
|
||||||
|
if(len >= usbtmc_state.transfer_size_remaining || shortPacket)
|
||||||
|
{
|
||||||
|
atEnd = true;
|
||||||
|
TU_VERIFY(atomicChangeState(STATE_RCV, STATE_NAK));
|
||||||
|
}
|
||||||
|
|
||||||
|
len = tu_min32(len, usbtmc_state.transfer_size_remaining);
|
||||||
|
|
||||||
|
usbtmc_state.transfer_size_remaining -= len;
|
||||||
|
usbtmc_state.transfer_size_sent += len;
|
||||||
|
|
||||||
|
// App may (should?) call the wait_for_bus() command at this point
|
||||||
|
if(!tud_usbtmc_msg_data_cb(data, len, atEnd))
|
||||||
|
{
|
||||||
|
// TODO: Go to an error state upon failure other than just stalling the EP?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_devMsgIn(void *data, size_t len)
|
||||||
|
{
|
||||||
|
TU_VERIFY(len == sizeof(usbtmc_msg_request_dev_dep_in));
|
||||||
|
usbtmc_msg_request_dev_dep_in *msg = (usbtmc_msg_request_dev_dep_in*)data;
|
||||||
|
bool stateChanged = atomicChangeState(STATE_IDLE, STATE_TX_REQUESTED);
|
||||||
|
TU_VERIFY(stateChanged);
|
||||||
|
usbtmc_state.lastBulkInTag = msg->header.bTag;
|
||||||
|
usbtmc_state.transfer_size_remaining = msg->TransferSize;
|
||||||
|
usbtmc_state.transfer_size_sent = 0u;
|
||||||
|
|
||||||
|
termCharRequested = msg->bmTransferAttributes.TermCharEnabled;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
termChar = msg->TermChar;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(termCharRequested)
|
||||||
|
TU_VERIFY(usbtmc_state.capabilities->bmDevCapabilities.canEndBulkInOnTermChar);
|
||||||
|
|
||||||
|
TU_VERIFY(tud_usbtmc_msgBulkIn_request_cb(msg));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
TU_VERIFY(result == XFER_RESULT_SUCCESS);
|
||||||
|
//uart_tx_str_sync("TMC XFER CB\r\n");
|
||||||
|
if(usbtmc_state.state == STATE_CLEARING) {
|
||||||
|
return true; /* I think we can ignore everything here */
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ep_addr == usbtmc_state.ep_bulk_out)
|
||||||
|
{
|
||||||
|
usbtmc_msg_generic_t *msg = NULL;
|
||||||
|
|
||||||
|
switch(usbtmc_state.state)
|
||||||
|
{
|
||||||
|
case STATE_IDLE:
|
||||||
|
{
|
||||||
|
TU_VERIFY(xferred_bytes >= sizeof(usbtmc_msg_generic_t));
|
||||||
|
msg = (usbtmc_msg_generic_t*)(usbtmc_state.ep_bulk_out_buf);
|
||||||
|
uint8_t invInvTag = (uint8_t)~(msg->header.bTagInverse);
|
||||||
|
TU_VERIFY(msg->header.bTag == invInvTag);
|
||||||
|
TU_VERIFY(msg->header.bTag != 0x00);
|
||||||
|
|
||||||
|
switch(msg->header.MsgID) {
|
||||||
|
case USBTMC_MSGID_DEV_DEP_MSG_OUT:
|
||||||
|
if(!handle_devMsgOutStart(rhport, msg, xferred_bytes))
|
||||||
|
{
|
||||||
|
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USBTMC_MSGID_DEV_DEP_MSG_IN:
|
||||||
|
TU_VERIFY(handle_devMsgIn(msg, xferred_bytes));
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
case USBTMC_MSGID_USB488_TRIGGER:
|
||||||
|
// Spec says we halt the EP if we didn't declare we support it.
|
||||||
|
TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities488.supportsTrigger);
|
||||||
|
TU_VERIFY(tud_usbtmc_msg_trigger_cb(msg));
|
||||||
|
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case USBTMC_MSGID_VENDOR_SPECIFIC_MSG_OUT:
|
||||||
|
case USBTMC_MSGID_VENDOR_SPECIFIC_IN:
|
||||||
|
default:
|
||||||
|
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case STATE_RCV:
|
||||||
|
if(!handle_devMsgOut(rhport, usbtmc_state.ep_bulk_out_buf, xferred_bytes, xferred_bytes))
|
||||||
|
{
|
||||||
|
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case STATE_ABORTING_BULK_OUT:
|
||||||
|
// Should be stalled by now, shouldn't have received a packet.
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case STATE_TX_REQUESTED:
|
||||||
|
case STATE_TX_INITIATED:
|
||||||
|
case STATE_ABORTING_BULK_IN:
|
||||||
|
case STATE_ABORTING_BULK_IN_SHORTED:
|
||||||
|
case STATE_ABORTING_BULK_IN_ABORTED:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ep_addr == usbtmc_state.ep_bulk_in)
|
||||||
|
{
|
||||||
|
switch(usbtmc_state.state) {
|
||||||
|
case STATE_TX_SHORTED:
|
||||||
|
TU_VERIFY(atomicChangeState(STATE_TX_SHORTED, STATE_NAK));
|
||||||
|
TU_VERIFY(tud_usbtmc_msgBulkIn_complete_cb());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_TX_INITIATED:
|
||||||
|
if(usbtmc_state.transfer_size_remaining >= sizeof(usbtmc_state.ep_bulk_in_buf))
|
||||||
|
{
|
||||||
|
// FIXME! This removes const below!
|
||||||
|
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
|
||||||
|
(void*)(uintptr_t) usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf)));
|
||||||
|
usbtmc_state.devInBuffer += sizeof(usbtmc_state.ep_bulk_in_buf);
|
||||||
|
usbtmc_state.transfer_size_remaining -= sizeof(usbtmc_state.ep_bulk_in_buf);
|
||||||
|
usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.ep_bulk_in_buf);
|
||||||
|
}
|
||||||
|
else // last packet
|
||||||
|
{
|
||||||
|
size_t packetLen = usbtmc_state.transfer_size_remaining;
|
||||||
|
memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, usbtmc_state.transfer_size_remaining);
|
||||||
|
usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.transfer_size_remaining);
|
||||||
|
usbtmc_state.transfer_size_remaining = 0;
|
||||||
|
usbtmc_state.devInBuffer = NULL;
|
||||||
|
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf, (uint16_t)packetLen) );
|
||||||
|
if(((packetLen % usbtmc_state.ep_bulk_in_wMaxPacketSize) != 0) || (packetLen == 0 ))
|
||||||
|
{
|
||||||
|
usbtmc_state.state = STATE_TX_SHORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case STATE_ABORTING_BULK_IN:
|
||||||
|
// need to send short packet (ZLP?)
|
||||||
|
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
|
||||||
|
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case STATE_ABORTING_BULK_IN_SHORTED:
|
||||||
|
/* Done. :)*/
|
||||||
|
usbtmc_state.state = STATE_ABORTING_BULK_IN_ABORTED;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TU_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ep_addr == usbtmc_state.ep_int_in) {
|
||||||
|
// Good?
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoked when a control transfer occurred on an interface of this class
|
||||||
|
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||||
|
// return false to stall control endpoint (e.g unsupported request)
|
||||||
|
bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||||
|
{
|
||||||
|
// nothing to do with DATA and ACK stage
|
||||||
|
if ( stage != CONTROL_STAGE_SETUP ) return true;
|
||||||
|
|
||||||
|
uint8_t tmcStatusCode = USBTMC_STATUS_FAILED;
|
||||||
|
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
uint8_t bTag;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if((request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) &&
|
||||||
|
(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT) &&
|
||||||
|
(request->bRequest == TUSB_REQ_CLEAR_FEATURE) &&
|
||||||
|
(request->wValue == TUSB_REQ_FEATURE_EDPT_HALT))
|
||||||
|
{
|
||||||
|
uint32_t ep_addr = (request->wIndex);
|
||||||
|
|
||||||
|
// At this point, a transfer MAY be in progress. Based on USB spec, when clearing bulk EP HALT,
|
||||||
|
// the EP transfer buffer needs to be cleared and DTOG needs to be reset, even if
|
||||||
|
// the EP is not halted. The only USBD API interface to do this is to stall and then un-stall the EP.
|
||||||
|
if(ep_addr == usbtmc_state.ep_bulk_out)
|
||||||
|
{
|
||||||
|
criticalEnter();
|
||||||
|
usbd_edpt_stall(rhport, (uint8_t)ep_addr);
|
||||||
|
usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr);
|
||||||
|
usbtmc_state.state = STATE_NAK; // USBD core has placed EP in NAK state for us
|
||||||
|
criticalLeave();
|
||||||
|
tud_usbtmc_bulkOut_clearFeature_cb();
|
||||||
|
}
|
||||||
|
else if (ep_addr == usbtmc_state.ep_bulk_in)
|
||||||
|
{
|
||||||
|
usbd_edpt_stall(rhport, (uint8_t)ep_addr);
|
||||||
|
usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr);
|
||||||
|
tud_usbtmc_bulkIn_clearFeature_cb();
|
||||||
|
}
|
||||||
|
else if ((usbtmc_state.ep_int_in != 0) && (ep_addr == usbtmc_state.ep_int_in))
|
||||||
|
{
|
||||||
|
// Clearing interrupt in EP
|
||||||
|
usbd_edpt_stall(rhport, (uint8_t)ep_addr);
|
||||||
|
usbd_edpt_clear_stall(rhport, (uint8_t)ep_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we only handle class requests.
|
||||||
|
if(request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verification that we own the interface is unneeded since it's been routed to us specifically.
|
||||||
|
|
||||||
|
switch(request->bRequest)
|
||||||
|
{
|
||||||
|
// USBTMC required requests
|
||||||
|
case USBTMC_bREQUEST_INITIATE_ABORT_BULK_OUT:
|
||||||
|
{
|
||||||
|
usbtmc_initiate_abort_rsp_t rsp = {
|
||||||
|
.bTag = usbtmc_state.lastBulkOutTag,
|
||||||
|
};
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
|
||||||
|
TU_VERIFY(request->wLength == sizeof(rsp));
|
||||||
|
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
|
||||||
|
|
||||||
|
// wValue is the requested bTag to abort
|
||||||
|
if(usbtmc_state.state != STATE_RCV)
|
||||||
|
{
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
else if(usbtmc_state.lastBulkOutTag == (request->wValue & 0x7Fu))
|
||||||
|
{
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||||
|
// Check if we've queued a short packet
|
||||||
|
criticalEnter();
|
||||||
|
usbtmc_state.state = STATE_ABORTING_BULK_OUT;
|
||||||
|
criticalLeave();
|
||||||
|
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_out_cb(&(rsp.USBTMC_status)));
|
||||||
|
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||||
|
}
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case USBTMC_bREQUEST_CHECK_ABORT_BULK_OUT_STATUS:
|
||||||
|
{
|
||||||
|
usbtmc_check_abort_bulk_rsp_t rsp = {
|
||||||
|
.USBTMC_status = USBTMC_STATUS_SUCCESS,
|
||||||
|
.NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent
|
||||||
|
};
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
|
||||||
|
TU_VERIFY(request->wLength == sizeof(rsp));
|
||||||
|
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
|
||||||
|
TU_VERIFY(tud_usbtmc_check_abort_bulk_out_cb(&rsp));
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case USBTMC_bREQUEST_INITIATE_ABORT_BULK_IN:
|
||||||
|
{
|
||||||
|
usbtmc_initiate_abort_rsp_t rsp = {
|
||||||
|
.bTag = usbtmc_state.lastBulkInTag,
|
||||||
|
};
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,interface
|
||||||
|
TU_VERIFY(request->wLength == sizeof(rsp));
|
||||||
|
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_in);
|
||||||
|
// wValue is the requested bTag to abort
|
||||||
|
if((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED) &&
|
||||||
|
usbtmc_state.lastBulkInTag == (request->wValue & 0x7Fu))
|
||||||
|
{
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||||
|
usbtmc_state.transfer_size_remaining = 0u;
|
||||||
|
// Check if we've queued a short packet
|
||||||
|
criticalEnter();
|
||||||
|
usbtmc_state.state = ((usbtmc_state.transfer_size_sent % usbtmc_state.ep_bulk_in_wMaxPacketSize) == 0) ?
|
||||||
|
STATE_ABORTING_BULK_IN : STATE_ABORTING_BULK_IN_SHORTED;
|
||||||
|
criticalLeave();
|
||||||
|
if(usbtmc_state.transfer_size_sent == 0)
|
||||||
|
{
|
||||||
|
// Send short packet, nothing is in the buffer yet
|
||||||
|
TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in, usbtmc_state.ep_bulk_in_buf,(uint16_t)0u));
|
||||||
|
usbtmc_state.state = STATE_ABORTING_BULK_IN_SHORTED;
|
||||||
|
}
|
||||||
|
TU_VERIFY(tud_usbtmc_initiate_abort_bulk_in_cb(&(rsp.USBTMC_status)));
|
||||||
|
}
|
||||||
|
else if((usbtmc_state.state == STATE_TX_REQUESTED || usbtmc_state.state == STATE_TX_INITIATED))
|
||||||
|
{ // FIXME: Unsure how to check if the OUT endpoint fifo is non-empty....
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_TRANSFER_NOT_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case USBTMC_bREQUEST_CHECK_ABORT_BULK_IN_STATUS:
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA2); // in,class,EP
|
||||||
|
TU_VERIFY(request->wLength == 8u);
|
||||||
|
|
||||||
|
usbtmc_check_abort_bulk_rsp_t rsp =
|
||||||
|
{
|
||||||
|
.USBTMC_status = USBTMC_STATUS_FAILED,
|
||||||
|
.bmAbortBulkIn =
|
||||||
|
{
|
||||||
|
.BulkInFifoBytes = (usbtmc_state.state != STATE_ABORTING_BULK_IN_ABORTED)
|
||||||
|
},
|
||||||
|
.NBYTES_RXD_TXD = usbtmc_state.transfer_size_sent,
|
||||||
|
};
|
||||||
|
TU_VERIFY(tud_usbtmc_check_abort_bulk_in_cb(&rsp));
|
||||||
|
criticalEnter();
|
||||||
|
switch(usbtmc_state.state)
|
||||||
|
{
|
||||||
|
case STATE_ABORTING_BULK_IN_ABORTED:
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||||
|
usbtmc_state.state = STATE_IDLE;
|
||||||
|
break;
|
||||||
|
case STATE_ABORTING_BULK_IN:
|
||||||
|
case STATE_ABORTING_BULK_OUT:
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_PENDING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
criticalLeave();
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case USBTMC_bREQUEST_INITIATE_CLEAR:
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
|
||||||
|
TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
|
||||||
|
// After receiving an INITIATE_CLEAR request, the device must Halt the Bulk-OUT endpoint, queue the
|
||||||
|
// control endpoint response shown in Table 31, and clear all input buffers and output buffers.
|
||||||
|
usbd_edpt_stall(rhport, usbtmc_state.ep_bulk_out);
|
||||||
|
usbtmc_state.transfer_size_remaining = 0;
|
||||||
|
criticalEnter();
|
||||||
|
usbtmc_state.state = STATE_CLEARING;
|
||||||
|
criticalLeave();
|
||||||
|
TU_VERIFY(tud_usbtmc_initiate_clear_cb(&tmcStatusCode));
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode,sizeof(tmcStatusCode)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case USBTMC_bREQUEST_CHECK_CLEAR_STATUS:
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
|
||||||
|
usbtmc_get_clear_status_rsp_t clearStatusRsp = {0};
|
||||||
|
TU_VERIFY(request->wLength == sizeof(clearStatusRsp));
|
||||||
|
|
||||||
|
if(usbd_edpt_busy(rhport, usbtmc_state.ep_bulk_in))
|
||||||
|
{
|
||||||
|
// Stuff stuck in TX buffer?
|
||||||
|
clearStatusRsp.bmClear.BulkInFifoBytes = 1;
|
||||||
|
clearStatusRsp.USBTMC_status = USBTMC_STATUS_PENDING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Let app check if it's clear
|
||||||
|
TU_VERIFY(tud_usbtmc_check_clear_cb(&clearStatusRsp));
|
||||||
|
}
|
||||||
|
if(clearStatusRsp.USBTMC_status == USBTMC_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
criticalEnter();
|
||||||
|
usbtmc_state.state = STATE_IDLE;
|
||||||
|
criticalLeave();
|
||||||
|
}
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&clearStatusRsp,sizeof(clearStatusRsp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case USBTMC_bREQUEST_GET_CAPABILITIES:
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
|
||||||
|
TU_VERIFY(request->wLength == sizeof(*(usbtmc_state.capabilities)));
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)(uintptr_t) usbtmc_state.capabilities, sizeof(*usbtmc_state.capabilities)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// USBTMC Optional Requests
|
||||||
|
|
||||||
|
case USBTMC_bREQUEST_INDICATOR_PULSE: // Optional
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
|
||||||
|
TU_VERIFY(request->wLength == sizeof(tmcStatusCode));
|
||||||
|
TU_VERIFY(usbtmc_state.capabilities->bmIntfcCapabilities.supportsIndicatorPulse);
|
||||||
|
TU_VERIFY(tud_usbtmc_indicator_pulse_cb(request, &tmcStatusCode));
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&tmcStatusCode, sizeof(tmcStatusCode)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
|
||||||
|
// USB488 required requests
|
||||||
|
case USB488_bREQUEST_READ_STATUS_BYTE:
|
||||||
|
{
|
||||||
|
usbtmc_read_stb_rsp_488_t rsp;
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
|
||||||
|
TU_VERIFY(request->wLength == sizeof(rsp)); // in,class,interface
|
||||||
|
|
||||||
|
bTag = request->wValue & 0x7F;
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA1);
|
||||||
|
TU_VERIFY((request->wValue & (~0x7F)) == 0u); // Other bits are required to be zero (USB488v1.0 Table 11)
|
||||||
|
TU_VERIFY(bTag >= 0x02 && bTag <= 127);
|
||||||
|
TU_VERIFY(request->wIndex == usbtmc_state.itf_id);
|
||||||
|
TU_VERIFY(request->wLength == 0x0003);
|
||||||
|
rsp.bTag = (uint8_t)bTag;
|
||||||
|
if(usbtmc_state.ep_int_in != 0)
|
||||||
|
{
|
||||||
|
rsp.statusByte = 0x00; // Use interrupt endpoint, instead. Must be 0x00 (USB488v1.0 4.3.1.2)
|
||||||
|
if(usbd_edpt_busy(rhport, usbtmc_state.ep_int_in))
|
||||||
|
{
|
||||||
|
rsp.USBTMC_status = USB488_STATUS_INTERRUPT_IN_BUSY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
|
||||||
|
usbtmc_read_stb_interrupt_488_t intMsg =
|
||||||
|
{
|
||||||
|
.bNotify1 = {
|
||||||
|
.one = 1,
|
||||||
|
.bTag = bTag & 0x7Fu,
|
||||||
|
},
|
||||||
|
.StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status))
|
||||||
|
};
|
||||||
|
// Must be queued before control request response sent (USB488v1.0 4.3.1.2)
|
||||||
|
usbd_edpt_xfer(rhport, usbtmc_state.ep_int_in, (void*)&intMsg, sizeof(intMsg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rsp.statusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status));
|
||||||
|
}
|
||||||
|
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp, sizeof(rsp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// USB488 optional requests
|
||||||
|
case USB488_bREQUEST_REN_CONTROL:
|
||||||
|
case USB488_bREQUEST_GO_TO_LOCAL:
|
||||||
|
case USB488_bREQUEST_LOCAL_LOCKOUT:
|
||||||
|
{
|
||||||
|
TU_VERIFY(request->bmRequestType == 0xA1); // in,class,interface
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CFG_TUD_TSMC */
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 N Conrad
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CLASS_USBTMC_USBTMC_DEVICE_H_
|
||||||
|
#define CLASS_USBTMC_USBTMC_DEVICE_H_
|
||||||
|
|
||||||
|
#include "usbtmc.h"
|
||||||
|
|
||||||
|
// Enable 488 mode by default
|
||||||
|
#if !defined(CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
#define CFG_TUD_USBTMC_ENABLE_488 (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/***********************************************
|
||||||
|
* Functions to be implemented by the class implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
// In order to proceed, app must call call tud_usbtmc_start_bus_read(rhport) during or soon after:
|
||||||
|
// * tud_usbtmc_open_cb
|
||||||
|
// * tud_usbtmc_msg_data_cb
|
||||||
|
// * tud_usbtmc_msgBulkIn_complete_cb
|
||||||
|
// * tud_usbtmc_msg_trigger_cb
|
||||||
|
// * (successful) tud_usbtmc_check_abort_bulk_out_cb
|
||||||
|
// * (successful) tud_usbtmc_check_abort_bulk_in_cb
|
||||||
|
// * (successful) tud_usmtmc_bulkOut_clearFeature_cb
|
||||||
|
|
||||||
|
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
usbtmc_response_capabilities_488_t const * tud_usbtmc_get_capabilities_cb(void);
|
||||||
|
#else
|
||||||
|
usbtmc_response_capabilities_t const * tud_usbtmc_get_capabilities_cb(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void tud_usbtmc_open_cb(uint8_t interface_id);
|
||||||
|
|
||||||
|
bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const * msgHeader);
|
||||||
|
// transfer_complete does not imply that a message is complete.
|
||||||
|
bool tud_usbtmc_msg_data_cb( void *data, size_t len, bool transfer_complete);
|
||||||
|
void tud_usbtmc_bulkOut_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
|
||||||
|
|
||||||
|
bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const * request);
|
||||||
|
bool tud_usbtmc_msgBulkIn_complete_cb(void);
|
||||||
|
void tud_usbtmc_bulkIn_clearFeature_cb(void); // Notice to clear and abort the pending BULK out transfer
|
||||||
|
|
||||||
|
bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult);
|
||||||
|
bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult);
|
||||||
|
bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult);
|
||||||
|
|
||||||
|
bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
|
||||||
|
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
|
||||||
|
bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp);
|
||||||
|
|
||||||
|
// Indicator pulse should be 0.5 to 1.0 seconds long
|
||||||
|
TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult);
|
||||||
|
|
||||||
|
#if (CFG_TUD_USBTMC_ENABLE_488)
|
||||||
|
uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult);
|
||||||
|
TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
|
||||||
|
//TU_ATTR_WEAK bool tud_usbtmc_app_go_to_local_cb();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*******************************************
|
||||||
|
* Called from app
|
||||||
|
*
|
||||||
|
* We keep a reference to the buffer, so it MUST not change until the app is
|
||||||
|
* notified that the transfer is complete.
|
||||||
|
******************************************/
|
||||||
|
|
||||||
|
bool tud_usbtmc_transmit_dev_msg_data(
|
||||||
|
const void * data, size_t len,
|
||||||
|
bool endOfMessage, bool usingTermChar);
|
||||||
|
|
||||||
|
bool tud_usbtmc_start_bus_read(void);
|
||||||
|
|
||||||
|
|
||||||
|
/* "callbacks" from USB device core */
|
||||||
|
|
||||||
|
uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
void usbtmcd_reset_cb(uint8_t rhport);
|
||||||
|
bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||||
|
bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
void usbtmcd_init_cb(void);
|
||||||
|
|
||||||
|
/************************************************************
|
||||||
|
* USBTMC Descriptor Templates
|
||||||
|
*************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */
|
287
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/vendor/vendor_device 2.c
vendored
Normal file
287
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/vendor/vendor_device 2.c
vendored
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "tusb_option.h"
|
||||||
|
|
||||||
|
#if (CFG_TUD_ENABLED && CFG_TUD_VENDOR)
|
||||||
|
|
||||||
|
#include "device/usbd.h"
|
||||||
|
#include "device/usbd_pvt.h"
|
||||||
|
|
||||||
|
#include "vendor_device.h"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// MACRO CONSTANT TYPEDEF
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t itf_num;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
|
||||||
|
/*------------- From this point, data is not cleared by bus reset -------------*/
|
||||||
|
tu_fifo_t rx_ff;
|
||||||
|
tu_fifo_t tx_ff;
|
||||||
|
|
||||||
|
uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
|
||||||
|
uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
|
||||||
|
|
||||||
|
#if CFG_FIFO_MUTEX
|
||||||
|
osal_mutex_def_t rx_ff_mutex;
|
||||||
|
osal_mutex_def_t tx_ff_mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Endpoint Transfer buffer
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
|
||||||
|
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_VENDOR_EPSIZE];
|
||||||
|
} vendord_interface_t;
|
||||||
|
|
||||||
|
CFG_TUSB_MEM_SECTION tu_static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
|
||||||
|
|
||||||
|
#define ITF_MEM_RESET_SIZE offsetof(vendord_interface_t, rx_ff)
|
||||||
|
|
||||||
|
|
||||||
|
bool tud_vendor_n_mounted (uint8_t itf)
|
||||||
|
{
|
||||||
|
return _vendord_itf[itf].ep_in && _vendord_itf[itf].ep_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tud_vendor_n_available (uint8_t itf)
|
||||||
|
{
|
||||||
|
return tu_fifo_count(&_vendord_itf[itf].rx_ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8)
|
||||||
|
{
|
||||||
|
return tu_fifo_peek(&_vendord_itf[itf].rx_ff, u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Read API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
static void _prep_out_transaction (vendord_interface_t* p_itf)
|
||||||
|
{
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
|
||||||
|
// claim endpoint
|
||||||
|
TU_VERIFY(usbd_edpt_claim(rhport, p_itf->ep_out), );
|
||||||
|
|
||||||
|
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||||
|
uint16_t max_read = tu_fifo_remaining(&p_itf->rx_ff);
|
||||||
|
if ( max_read >= CFG_TUD_VENDOR_EPSIZE )
|
||||||
|
{
|
||||||
|
usbd_edpt_xfer(rhport, p_itf->ep_out, p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Release endpoint since we don't make any transfer
|
||||||
|
usbd_edpt_release(rhport, p_itf->ep_out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||||
|
uint32_t num_read = tu_fifo_read_n(&p_itf->rx_ff, buffer, (uint16_t) bufsize);
|
||||||
|
_prep_out_transaction(p_itf);
|
||||||
|
return num_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tud_vendor_n_read_flush (uint8_t itf)
|
||||||
|
{
|
||||||
|
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||||
|
tu_fifo_clear(&p_itf->rx_ff);
|
||||||
|
_prep_out_transaction(p_itf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Write API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||||
|
uint16_t ret = tu_fifo_write_n(&p_itf->tx_ff, buffer, (uint16_t) bufsize);
|
||||||
|
|
||||||
|
// flush if queue more than packet size
|
||||||
|
if (tu_fifo_count(&p_itf->tx_ff) >= CFG_TUD_VENDOR_EPSIZE) {
|
||||||
|
tud_vendor_n_write_flush(itf);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tud_vendor_n_write_flush (uint8_t itf)
|
||||||
|
{
|
||||||
|
vendord_interface_t* p_itf = &_vendord_itf[itf];
|
||||||
|
|
||||||
|
// Skip if usb is not ready yet
|
||||||
|
TU_VERIFY( tud_ready(), 0 );
|
||||||
|
|
||||||
|
// No data to send
|
||||||
|
if ( !tu_fifo_count(&p_itf->tx_ff) ) return 0;
|
||||||
|
|
||||||
|
uint8_t const rhport = 0;
|
||||||
|
|
||||||
|
// Claim the endpoint
|
||||||
|
TU_VERIFY( usbd_edpt_claim(rhport, p_itf->ep_in), 0 );
|
||||||
|
|
||||||
|
// Pull data from FIFO
|
||||||
|
uint16_t const count = tu_fifo_read_n(&p_itf->tx_ff, p_itf->epin_buf, sizeof(p_itf->epin_buf));
|
||||||
|
|
||||||
|
if ( count )
|
||||||
|
{
|
||||||
|
TU_ASSERT( usbd_edpt_xfer(rhport, p_itf->ep_in, p_itf->epin_buf, count), 0 );
|
||||||
|
return count;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Release endpoint since we don't make any transfer
|
||||||
|
// Note: data is dropped if terminal is not connected
|
||||||
|
usbd_edpt_release(rhport, p_itf->ep_in);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tud_vendor_n_write_available (uint8_t itf)
|
||||||
|
{
|
||||||
|
return tu_fifo_remaining(&_vendord_itf[itf].tx_ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// USBD Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void vendord_init(void)
|
||||||
|
{
|
||||||
|
tu_memclr(_vendord_itf, sizeof(_vendord_itf));
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
|
||||||
|
{
|
||||||
|
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||||
|
|
||||||
|
// config fifo
|
||||||
|
tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
|
||||||
|
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
|
||||||
|
|
||||||
|
#if CFG_FIFO_MUTEX
|
||||||
|
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
|
||||||
|
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vendord_reset(uint8_t rhport)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
|
||||||
|
{
|
||||||
|
vendord_interface_t* p_itf = &_vendord_itf[i];
|
||||||
|
|
||||||
|
tu_memclr(p_itf, ITF_MEM_RESET_SIZE);
|
||||||
|
tu_fifo_clear(&p_itf->rx_ff);
|
||||||
|
tu_fifo_clear(&p_itf->tx_ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
|
||||||
|
{
|
||||||
|
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
|
||||||
|
|
||||||
|
uint8_t const * p_desc = tu_desc_next(desc_itf);
|
||||||
|
uint8_t const * desc_end = p_desc + max_len;
|
||||||
|
|
||||||
|
// Find available interface
|
||||||
|
vendord_interface_t* p_vendor = NULL;
|
||||||
|
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
|
||||||
|
{
|
||||||
|
if ( _vendord_itf[i].ep_in == 0 && _vendord_itf[i].ep_out == 0 )
|
||||||
|
{
|
||||||
|
p_vendor = &_vendord_itf[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TU_VERIFY(p_vendor, 0);
|
||||||
|
|
||||||
|
p_vendor->itf_num = desc_itf->bInterfaceNumber;
|
||||||
|
if (desc_itf->bNumEndpoints)
|
||||||
|
{
|
||||||
|
// skip non-endpoint descriptors
|
||||||
|
while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) )
|
||||||
|
{
|
||||||
|
p_desc = tu_desc_next(p_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open endpoint pair with usbd helper
|
||||||
|
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in), 0);
|
||||||
|
|
||||||
|
p_desc += desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
|
||||||
|
|
||||||
|
// Prepare for incoming data
|
||||||
|
if ( p_vendor->ep_out )
|
||||||
|
{
|
||||||
|
_prep_out_transaction(p_vendor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( p_vendor->ep_in ) tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint16_t) ((uintptr_t) p_desc - (uintptr_t) desc_itf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
(void) result;
|
||||||
|
|
||||||
|
uint8_t itf = 0;
|
||||||
|
vendord_interface_t* p_itf = _vendord_itf;
|
||||||
|
|
||||||
|
for ( ; ; itf++, p_itf++)
|
||||||
|
{
|
||||||
|
if (itf >= TU_ARRAY_SIZE(_vendord_itf)) return false;
|
||||||
|
|
||||||
|
if ( ( ep_addr == p_itf->ep_out ) || ( ep_addr == p_itf->ep_in ) ) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ep_addr == p_itf->ep_out )
|
||||||
|
{
|
||||||
|
// Receive new data
|
||||||
|
tu_fifo_write_n(&p_itf->rx_ff, p_itf->epout_buf, (uint16_t) xferred_bytes);
|
||||||
|
|
||||||
|
// Invoked callback if any
|
||||||
|
if (tud_vendor_rx_cb) tud_vendor_rx_cb(itf);
|
||||||
|
|
||||||
|
_prep_out_transaction(p_itf);
|
||||||
|
}
|
||||||
|
else if ( ep_addr == p_itf->ep_in )
|
||||||
|
{
|
||||||
|
if (tud_vendor_tx_cb) tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
|
||||||
|
// Send complete, try to send more if possible
|
||||||
|
tud_vendor_n_write_flush(itf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
150
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/vendor/vendor_device 2.h
vendored
Normal file
150
JumperlessNano/Adafruit_TinyUSB_Arduino/src/class/vendor/vendor_device 2.h
vendored
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TUSB_VENDOR_DEVICE_H_
|
||||||
|
#define _TUSB_VENDOR_DEVICE_H_
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_VENDOR_EPSIZE
|
||||||
|
#define CFG_TUD_VENDOR_EPSIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API (Multiple Interfaces)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
bool tud_vendor_n_mounted (uint8_t itf);
|
||||||
|
|
||||||
|
uint32_t tud_vendor_n_available (uint8_t itf);
|
||||||
|
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
|
||||||
|
bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8);
|
||||||
|
void tud_vendor_n_read_flush (uint8_t itf);
|
||||||
|
|
||||||
|
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
|
||||||
|
uint32_t tud_vendor_n_write_flush (uint8_t itf);
|
||||||
|
uint32_t tud_vendor_n_write_available (uint8_t itf);
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
|
||||||
|
|
||||||
|
// backward compatible
|
||||||
|
#define tud_vendor_n_flush(itf) tud_vendor_n_write_flush(itf)
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API (Single Port)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
static inline bool tud_vendor_mounted (void);
|
||||||
|
static inline uint32_t tud_vendor_available (void);
|
||||||
|
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize);
|
||||||
|
static inline bool tud_vendor_peek (uint8_t* ui8);
|
||||||
|
static inline void tud_vendor_read_flush (void);
|
||||||
|
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize);
|
||||||
|
static inline uint32_t tud_vendor_write_str (char const* str);
|
||||||
|
static inline uint32_t tud_vendor_write_available (void);
|
||||||
|
static inline uint32_t tud_vendor_write_flush (void);
|
||||||
|
|
||||||
|
// backward compatible
|
||||||
|
#define tud_vendor_flush() tud_vendor_write_flush()
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callback API (weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// Invoked when received new data
|
||||||
|
TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf);
|
||||||
|
// Invoked when last rx transfer finished
|
||||||
|
TU_ATTR_WEAK void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Inline Functions
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_write(itf, str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_vendor_mounted (void)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_mounted(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_available (void)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_available(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_read(0, buffer, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tud_vendor_peek (uint8_t* ui8)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_peek(0, ui8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tud_vendor_read_flush(void)
|
||||||
|
{
|
||||||
|
tud_vendor_n_read_flush(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_write(0, buffer, bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_write_flush (void)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_write_flush(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_write_str (char const* str)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_write_str(0, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tud_vendor_write_available (void)
|
||||||
|
{
|
||||||
|
return tud_vendor_n_write_available(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Internal Class Driver API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void vendord_init(void);
|
||||||
|
void vendord_reset(uint8_t rhport);
|
||||||
|
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TUSB_VENDOR_DEVICE_H_ */
|
@ -0,0 +1,559 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Koji KITAYAMA
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TUSB_VIDEO_H_
|
||||||
|
#define TUSB_VIDEO_H_
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
|
||||||
|
// Table 3-19 Color Matching Descriptor
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_COLOR_PRIMARIES_UNDEFINED = 0x00,
|
||||||
|
VIDEO_COLOR_PRIMARIES_BT709, // sRGB (default)
|
||||||
|
VIDEO_COLOR_PRIMARIES_BT470_2M,
|
||||||
|
VIDEO_COLOR_PRIMARIES_BT470_2BG,
|
||||||
|
VIDEO_COLOR_PRIMARIES_SMPTE170M,
|
||||||
|
VIDEO_COLOR_PRIMARIES_SMPTE240M,
|
||||||
|
} video_color_primaries_t;
|
||||||
|
|
||||||
|
// Table 3-19 Color Matching Descriptor
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_COLOR_XFER_CH_UNDEFINED = 0x00,
|
||||||
|
VIDEO_COLOR_XFER_CH_BT709, // default
|
||||||
|
VIDEO_COLOR_XFER_CH_BT470_2M,
|
||||||
|
VIDEO_COLOR_XFER_CH_BT470_2BG,
|
||||||
|
VIDEO_COLOR_XFER_CH_SMPTE170M,
|
||||||
|
VIDEO_COLOR_XFER_CH_SMPTE240M,
|
||||||
|
VIDEO_COLOR_XFER_CH_LINEAR,
|
||||||
|
VIDEO_COLOR_XFER_CH_SRGB,
|
||||||
|
} video_color_transfer_characteristics_t;
|
||||||
|
|
||||||
|
// Table 3-19 Color Matching Descriptor
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_COLOR_COEF_UNDEFINED = 0x00,
|
||||||
|
VIDEO_COLOR_COEF_BT709,
|
||||||
|
VIDEO_COLOR_COEF_FCC,
|
||||||
|
VIDEO_COLOR_COEF_BT470_2BG,
|
||||||
|
VIDEO_COLOR_COEF_SMPTE170M, // BT.601 default
|
||||||
|
VIDEO_COLOR_COEF_SMPTE240M,
|
||||||
|
} video_color_matrix_coefficients_t;
|
||||||
|
|
||||||
|
/* 4.2.1.2 Request Error Code Control */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_ERROR_NONE = 0, /* The request succeeded. */
|
||||||
|
VIDEO_ERROR_NOT_READY,
|
||||||
|
VIDEO_ERROR_WRONG_STATE,
|
||||||
|
VIDEO_ERROR_POWER,
|
||||||
|
VIDEO_ERROR_OUT_OF_RANGE,
|
||||||
|
VIDEO_ERROR_INVALID_UNIT,
|
||||||
|
VIDEO_ERROR_INVALID_CONTROL,
|
||||||
|
VIDEO_ERROR_INVALID_REQUEST,
|
||||||
|
VIDEO_ERROR_INVALID_VALUE_WITHIN_RANGE,
|
||||||
|
VIDEO_ERROR_UNKNOWN = 0xFF,
|
||||||
|
} video_error_code_t;
|
||||||
|
|
||||||
|
/* A.2 Interface Subclass */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_SUBCLASS_UNDEFINED = 0x00,
|
||||||
|
VIDEO_SUBCLASS_CONTROL,
|
||||||
|
VIDEO_SUBCLASS_STREAMING,
|
||||||
|
VIDEO_SUBCLASS_INTERFACE_COLLECTION,
|
||||||
|
} video_subclass_type_t;
|
||||||
|
|
||||||
|
/* A.3 Interface Protocol */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_ITF_PROTOCOL_UNDEFINED = 0x00,
|
||||||
|
VIDEO_ITF_PROTOCOL_15,
|
||||||
|
} video_interface_protocol_code_t;
|
||||||
|
|
||||||
|
/* A.5 Class-Specific VideoControl Interface Descriptor Subtypes */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_CS_ITF_VC_UNDEFINED = 0x00,
|
||||||
|
VIDEO_CS_ITF_VC_HEADER,
|
||||||
|
VIDEO_CS_ITF_VC_INPUT_TERMINAL,
|
||||||
|
VIDEO_CS_ITF_VC_OUTPUT_TERMINAL,
|
||||||
|
VIDEO_CS_ITF_VC_SELECTOR_UNIT,
|
||||||
|
VIDEO_CS_ITF_VC_PROCESSING_UNIT,
|
||||||
|
VIDEO_CS_ITF_VC_EXTENSION_UNIT,
|
||||||
|
VIDEO_CS_ITF_VC_ENCODING_UNIT,
|
||||||
|
VIDEO_CS_ITF_VC_MAX,
|
||||||
|
} video_cs_vc_interface_subtype_t;
|
||||||
|
|
||||||
|
/* A.6 Class-Specific VideoStreaming Interface Descriptor Subtypes */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_CS_ITF_VS_UNDEFINED = 0x00,
|
||||||
|
VIDEO_CS_ITF_VS_INPUT_HEADER = 0x01,
|
||||||
|
VIDEO_CS_ITF_VS_OUTPUT_HEADER = 0x02,
|
||||||
|
VIDEO_CS_ITF_VS_STILL_IMAGE_FRAME = 0x03,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED = 0x04,
|
||||||
|
VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED = 0x05,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_MJPEG = 0x06,
|
||||||
|
VIDEO_CS_ITF_VS_FRAME_MJPEG = 0x07,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_MPEG2TS = 0x0A,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_DV = 0x0C,
|
||||||
|
VIDEO_CS_ITF_VS_COLORFORMAT = 0x0D,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED = 0x10,
|
||||||
|
VIDEO_CS_ITF_VS_FRAME_FRAME_BASED = 0x11,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_STREAM_BASED = 0x12,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_H264 = 0x13,
|
||||||
|
VIDEO_CS_ITF_VS_FRAME_H264 = 0x14,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_H264_SIMULCAST = 0x15,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_VP8 = 0x16,
|
||||||
|
VIDEO_CS_ITF_VS_FRAME_VP8 = 0x17,
|
||||||
|
VIDEO_CS_ITF_VS_FORMAT_VP8_SIMULCAST = 0x18,
|
||||||
|
} video_cs_vs_interface_subtype_t;
|
||||||
|
|
||||||
|
/* A.7. Class-Specific Endpoint Descriptor Subtypes */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_CS_EP_UNDEFINED = 0x00,
|
||||||
|
VIDEO_CS_EP_GENERAL,
|
||||||
|
VIDEO_CS_EP_ENDPOINT,
|
||||||
|
VIDEO_CS_EP_INTERRUPT
|
||||||
|
} video_cs_ep_subtype_t;
|
||||||
|
|
||||||
|
/* A.8 Class-Specific Request Codes */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_REQUEST_UNDEFINED = 0x00,
|
||||||
|
VIDEO_REQUEST_SET_CUR = 0x01,
|
||||||
|
VIDEO_REQUEST_SET_CUR_ALL = 0x11,
|
||||||
|
VIDEO_REQUEST_GET_CUR = 0x81,
|
||||||
|
VIDEO_REQUEST_GET_MIN = 0x82,
|
||||||
|
VIDEO_REQUEST_GET_MAX = 0x83,
|
||||||
|
VIDEO_REQUEST_GET_RES = 0x84,
|
||||||
|
VIDEO_REQUEST_GET_LEN = 0x85,
|
||||||
|
VIDEO_REQUEST_GET_INFO = 0x86,
|
||||||
|
VIDEO_REQUEST_GET_DEF = 0x87,
|
||||||
|
VIDEO_REQUEST_GET_CUR_ALL = 0x91,
|
||||||
|
VIDEO_REQUEST_GET_MIN_ALL = 0x92,
|
||||||
|
VIDEO_REQUEST_GET_MAX_ALL = 0x93,
|
||||||
|
VIDEO_REQUEST_GET_RES_ALL = 0x94,
|
||||||
|
VIDEO_REQUEST_GET_DEF_ALL = 0x97
|
||||||
|
} video_control_request_t;
|
||||||
|
|
||||||
|
/* A.9.1 VideoControl Interface Control Selectors */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_VC_CTL_UNDEFINED = 0x00,
|
||||||
|
VIDEO_VC_CTL_VIDEO_POWER_MODE,
|
||||||
|
VIDEO_VC_CTL_REQUEST_ERROR_CODE,
|
||||||
|
} video_interface_control_selector_t;
|
||||||
|
|
||||||
|
/* A.9.8 VideoStreaming Interface Control Selectors */
|
||||||
|
typedef enum {
|
||||||
|
VIDEO_VS_CTL_UNDEFINED = 0x00,
|
||||||
|
VIDEO_VS_CTL_PROBE,
|
||||||
|
VIDEO_VS_CTL_COMMIT,
|
||||||
|
VIDEO_VS_CTL_STILL_PROBE,
|
||||||
|
VIDEO_VS_CTL_STILL_COMMIT,
|
||||||
|
VIDEO_VS_CTL_STILL_IMAGE_TRIGGER,
|
||||||
|
VIDEO_VS_CTL_STREAM_ERROR_CODE,
|
||||||
|
VIDEO_VS_CTL_GENERATE_KEY_FRAME,
|
||||||
|
VIDEO_VS_CTL_UPDATE_FRAME_SEGMENT,
|
||||||
|
VIDEO_VS_CTL_SYNCH_DELAY_CONTROL,
|
||||||
|
} video_interface_streaming_selector_t;
|
||||||
|
|
||||||
|
/* B. Terminal Types */
|
||||||
|
typedef enum {
|
||||||
|
// Terminal
|
||||||
|
VIDEO_TT_VENDOR_SPECIFIC = 0x0100,
|
||||||
|
VIDEO_TT_STREAMING = 0x0101,
|
||||||
|
|
||||||
|
// Input
|
||||||
|
VIDEO_ITT_VENDOR_SPECIFIC = 0x0200,
|
||||||
|
VIDEO_ITT_CAMERA = 0x0201,
|
||||||
|
VIDEO_ITT_MEDIA_TRANSPORT_INPUT = 0x0202,
|
||||||
|
|
||||||
|
// Output
|
||||||
|
VIDEO_OTT_VENDOR_SPECIFIC = 0x0300,
|
||||||
|
VIDEO_OTT_DISPLAY = 0x0301,
|
||||||
|
VIDEO_OTT_MEDIA_TRANSPORT_OUTPUT = 0x0302,
|
||||||
|
|
||||||
|
// External
|
||||||
|
VIDEO_ETT_VENDOR_SPEIFIC = 0x0400,
|
||||||
|
VIDEO_ETT_COMPOSITE_CONNECTOR = 0x0401,
|
||||||
|
VIDEO_ETT_SVIDEO_CONNECTOR = 0x0402,
|
||||||
|
VIDEO_ETT_COMPONENT_CONNECTOR = 0x0403,
|
||||||
|
} video_terminal_type_t;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Descriptors
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/* 2.3.4.2 */
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint16_t bcdUVC;
|
||||||
|
uint16_t wTotalLength;
|
||||||
|
uint32_t dwClockFrequency;
|
||||||
|
uint8_t bInCollection;
|
||||||
|
uint8_t baInterfaceNr[];
|
||||||
|
} tusb_desc_cs_video_ctl_itf_hdr_t;
|
||||||
|
|
||||||
|
/* 2.4.3.3 */
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bHeaderLength;
|
||||||
|
union {
|
||||||
|
uint8_t bmHeaderInfo;
|
||||||
|
struct {
|
||||||
|
uint8_t FrameID: 1;
|
||||||
|
uint8_t EndOfFrame: 1;
|
||||||
|
uint8_t PresentationTime: 1;
|
||||||
|
uint8_t SourceClockReference: 1;
|
||||||
|
uint8_t PayloadSpecific: 1;
|
||||||
|
uint8_t StillImage: 1;
|
||||||
|
uint8_t Error: 1;
|
||||||
|
uint8_t EndOfHeader: 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} tusb_video_payload_header_t;
|
||||||
|
|
||||||
|
/* 3.9.2.1 */
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bNumFormats;
|
||||||
|
uint16_t wTotalLength;
|
||||||
|
uint8_t bEndpointAddress;
|
||||||
|
uint8_t bmInfo;
|
||||||
|
uint8_t bTerminalLink;
|
||||||
|
uint8_t bStillCaptureMethod;
|
||||||
|
uint8_t bTriggerSupport;
|
||||||
|
uint8_t bTriggerUsage;
|
||||||
|
uint8_t bControlSize;
|
||||||
|
uint8_t bmaControls[];
|
||||||
|
} tusb_desc_cs_video_stm_itf_in_hdr_t;
|
||||||
|
|
||||||
|
/* 3.9.2.2 */
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bNumFormats;
|
||||||
|
uint16_t wTotalLength;
|
||||||
|
uint8_t bEndpointAddress;
|
||||||
|
uint8_t bTerminalLink;
|
||||||
|
uint8_t bControlSize;
|
||||||
|
uint8_t bmaControls[];
|
||||||
|
} tusb_desc_cs_video_stm_itf_out_hdr_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bNumFormats;
|
||||||
|
uint16_t wTotalLength;
|
||||||
|
uint8_t bEndpointAddress;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t bmInfo;
|
||||||
|
uint8_t bTerminalLink;
|
||||||
|
uint8_t bStillCaptureMethod;
|
||||||
|
uint8_t bTriggerSupport;
|
||||||
|
uint8_t bTriggerUsage;
|
||||||
|
uint8_t bControlSize;
|
||||||
|
uint8_t bmaControls[];
|
||||||
|
} input;
|
||||||
|
struct {
|
||||||
|
uint8_t bEndpointAddress;
|
||||||
|
uint8_t bTerminalLink;
|
||||||
|
uint8_t bControlSize;
|
||||||
|
uint8_t bmaControls[];
|
||||||
|
} output;
|
||||||
|
};
|
||||||
|
} tusb_desc_cs_video_stm_itf_hdr_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bFormatIndex;
|
||||||
|
uint8_t bNumFrameDescriptors;
|
||||||
|
uint8_t guidFormat[16];
|
||||||
|
uint8_t bBitsPerPixel;
|
||||||
|
uint8_t bDefaultFrameIndex;
|
||||||
|
uint8_t bAspectRatioX;
|
||||||
|
uint8_t bAspectRatioY;
|
||||||
|
uint8_t bmInterlaceFlags;
|
||||||
|
uint8_t bCopyProtect;
|
||||||
|
} tusb_desc_cs_video_fmt_uncompressed_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bFormatIndex;
|
||||||
|
uint8_t bNumFrameDescriptors;
|
||||||
|
uint8_t bmFlags;
|
||||||
|
uint8_t bDefaultFrameIndex;
|
||||||
|
uint8_t bAspectRatioX;
|
||||||
|
uint8_t bAspectRatioY;
|
||||||
|
uint8_t bmInterlaceFlags;
|
||||||
|
uint8_t bCopyProtect;
|
||||||
|
} tusb_desc_cs_video_fmt_mjpeg_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bFormatIndex;
|
||||||
|
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
|
||||||
|
uint8_t bFormatType;
|
||||||
|
} tusb_desc_cs_video_fmt_dv_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bFormatIndex;
|
||||||
|
uint8_t bNumFrameDescriptors;
|
||||||
|
uint8_t guidFormat[16];
|
||||||
|
uint8_t bBitsPerPixel;
|
||||||
|
uint8_t bDefaultFrameIndex;
|
||||||
|
uint8_t bAspectRatioX;
|
||||||
|
uint8_t bAspectRatioY;
|
||||||
|
uint8_t bmInterlaceFlags;
|
||||||
|
uint8_t bCopyProtect;
|
||||||
|
uint8_t bVaribaleSize;
|
||||||
|
} tusb_desc_cs_video_fmt_frame_based_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bFrameIndex;
|
||||||
|
uint8_t bmCapabilities;
|
||||||
|
uint16_t wWidth;
|
||||||
|
uint16_t wHeight;
|
||||||
|
uint32_t dwMinBitRate;
|
||||||
|
uint32_t dwMaxBitRate;
|
||||||
|
uint32_t dwMaxVideoFrameBufferSize; /* deprecated */
|
||||||
|
uint32_t dwDefaultFrameInterval;
|
||||||
|
uint8_t bFrameIntervalType;
|
||||||
|
uint32_t dwFrameInterval[];
|
||||||
|
} tusb_desc_cs_video_frm_uncompressed_t;
|
||||||
|
|
||||||
|
typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t;
|
||||||
|
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bFrameIndex;
|
||||||
|
uint8_t bmCapabilities;
|
||||||
|
uint16_t wWidth;
|
||||||
|
uint16_t wHeight;
|
||||||
|
uint32_t dwMinBitRate;
|
||||||
|
uint32_t dwMaxBitRate;
|
||||||
|
uint32_t dwDefaultFrameInterval;
|
||||||
|
uint8_t bFrameIntervalType;
|
||||||
|
uint32_t dwBytesPerLine;
|
||||||
|
uint32_t dwFrameInterval[];
|
||||||
|
} tusb_desc_cs_video_frm_frame_based_t;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Requests
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/* 4.3.1.1 */
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
union {
|
||||||
|
uint8_t bmHint;
|
||||||
|
struct TU_ATTR_PACKED {
|
||||||
|
uint16_t dwFrameInterval: 1;
|
||||||
|
uint16_t wKeyFrameRatel : 1;
|
||||||
|
uint16_t wPFrameRate : 1;
|
||||||
|
uint16_t wCompQuality : 1;
|
||||||
|
uint16_t wCompWindowSize: 1;
|
||||||
|
uint16_t : 0;
|
||||||
|
} Hint;
|
||||||
|
};
|
||||||
|
uint8_t bFormatIndex;
|
||||||
|
uint8_t bFrameIndex;
|
||||||
|
uint32_t dwFrameInterval;
|
||||||
|
uint16_t wKeyFrameRate;
|
||||||
|
uint16_t wPFrameRate;
|
||||||
|
uint16_t wCompQuality;
|
||||||
|
uint16_t wCompWindowSize;
|
||||||
|
uint16_t wDelay;
|
||||||
|
uint32_t dwMaxVideoFrameSize;
|
||||||
|
uint32_t dwMaxPayloadTransferSize;
|
||||||
|
uint32_t dwClockFrequency;
|
||||||
|
union {
|
||||||
|
uint8_t bmFramingInfo;
|
||||||
|
struct TU_ATTR_PACKED {
|
||||||
|
uint8_t FrameID : 1;
|
||||||
|
uint8_t EndOfFrame: 1;
|
||||||
|
uint8_t EndOfSlice: 1;
|
||||||
|
uint8_t : 0;
|
||||||
|
} FramingInfo;
|
||||||
|
};
|
||||||
|
uint8_t bPreferedVersion;
|
||||||
|
uint8_t bMinVersion;
|
||||||
|
uint8_t bMaxVersion;
|
||||||
|
uint8_t bUsage;
|
||||||
|
uint8_t bBitDepthLuma;
|
||||||
|
uint8_t bmSettings;
|
||||||
|
uint8_t bMaxNumberOfRefFramesPlus1;
|
||||||
|
uint16_t bmRateControlModes;
|
||||||
|
uint64_t bmLayoutPerStream;
|
||||||
|
} video_probe_and_commit_control_t;
|
||||||
|
|
||||||
|
TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not correct");
|
||||||
|
|
||||||
|
#define TUD_VIDEO_DESC_IAD_LEN 8
|
||||||
|
#define TUD_VIDEO_DESC_STD_VC_LEN 9
|
||||||
|
#define TUD_VIDEO_DESC_CS_VC_LEN 12
|
||||||
|
#define TUD_VIDEO_DESC_INPUT_TERM_LEN 8
|
||||||
|
#define TUD_VIDEO_DESC_OUTPUT_TERM_LEN 9
|
||||||
|
#define TUD_VIDEO_DESC_CAMERA_TERM_LEN 18
|
||||||
|
#define TUD_VIDEO_DESC_STD_VS_LEN 9
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_IN_LEN 13
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6
|
||||||
|
|
||||||
|
/* 2.2 compression formats */
|
||||||
|
#define TUD_VIDEO_GUID_YUY2 0x59,0x55,0x59,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||||
|
#define TUD_VIDEO_GUID_NV12 0x4E,0x56,0x31,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||||
|
#define TUD_VIDEO_GUID_M420 0x4D,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||||
|
#define TUD_VIDEO_GUID_I420 0x49,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71
|
||||||
|
|
||||||
|
#define TUD_VIDEO_DESC_IAD(_firstitfs, _nitfs, _stridx) \
|
||||||
|
TUD_VIDEO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, \
|
||||||
|
_firstitfs, _nitfs, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_INTERFACE_COLLECTION, \
|
||||||
|
VIDEO_ITF_PROTOCOL_UNDEFINED, _stridx
|
||||||
|
|
||||||
|
#define TUD_VIDEO_DESC_STD_VC(_itfnum, _nEPs, _stridx) \
|
||||||
|
TUD_VIDEO_DESC_STD_VC_LEN, TUSB_DESC_INTERFACE, _itfnum, /* fixed to zero */ 0x00, \
|
||||||
|
_nEPs, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_CONTROL, VIDEO_ITF_PROTOCOL_15, _stridx
|
||||||
|
|
||||||
|
/* 3.7.2 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VC(_bcdUVC, _totallen, _clkfreq, ...) \
|
||||||
|
TUD_VIDEO_DESC_CS_VC_LEN + (TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_HEADER, \
|
||||||
|
U16_TO_U8S_LE(_bcdUVC), U16_TO_U8S_LE((_totallen) + TUD_VIDEO_DESC_CS_VC_LEN + (TU_ARGS_NUM(__VA_ARGS__))), \
|
||||||
|
U32_TO_U8S_LE(_clkfreq), TU_ARGS_NUM(__VA_ARGS__), __VA_ARGS__
|
||||||
|
|
||||||
|
/* 3.7.2.1 */
|
||||||
|
#define TUD_VIDEO_DESC_INPUT_TERM(_tid, _tt, _at, _stridx) \
|
||||||
|
TUD_VIDEO_DESC_INPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_INPUT_TERMINAL, \
|
||||||
|
_tid, U16_TO_U8S_LE(_tt), _at, _stridx
|
||||||
|
|
||||||
|
/* 3.7.2.2 */
|
||||||
|
#define TUD_VIDEO_DESC_OUTPUT_TERM(_tid, _tt, _at, _srcid, _stridx) \
|
||||||
|
TUD_VIDEO_DESC_OUTPUT_TERM_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_OUTPUT_TERMINAL, \
|
||||||
|
_tid, U16_TO_U8S_LE(_tt), _at, _srcid, _stridx
|
||||||
|
|
||||||
|
/* 3.7.2.3 */
|
||||||
|
#define TUD_VIDEO_DESC_CAMERA_TERM(_tid, _at, _stridx, _focal_min, _focal_max, _focal, _ctls) \
|
||||||
|
TUD_VIDEO_DESC_CAMERA_TERM_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VC_INPUT_TERMINAL, \
|
||||||
|
_tid, U16_TO_U8S_LE(VIDEO_ITT_CAMERA), _at, _stridx, \
|
||||||
|
U16_TO_U8S_LE(_focal_min), U16_TO_U8S_LE(_focal_max), U16_TO_U8S_LE(_focal), 3, \
|
||||||
|
TU_U32_BYTE0(_ctls), TU_U32_BYTE1(_ctls), TU_U32_BYTE2(_ctls)
|
||||||
|
|
||||||
|
/* 3.9.1 */
|
||||||
|
#define TUD_VIDEO_DESC_STD_VS(_itfnum, _alt, _epn, _stridx) \
|
||||||
|
TUD_VIDEO_DESC_STD_VS_LEN, TUSB_DESC_INTERFACE, _itfnum, _alt, \
|
||||||
|
_epn, TUSB_CLASS_VIDEO, VIDEO_SUBCLASS_STREAMING, VIDEO_ITF_PROTOCOL_15, _stridx
|
||||||
|
|
||||||
|
/* 3.9.2.1 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_INPUT(_numfmt, _totallen, _ep, _inf, _termlnk, _sticaptmeth, _trgspt, _trgusg, ...) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_IN_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, \
|
||||||
|
VIDEO_CS_ITF_VS_INPUT_HEADER, _numfmt, \
|
||||||
|
U16_TO_U8S_LE((_totallen) + TUD_VIDEO_DESC_CS_VS_IN_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__))), \
|
||||||
|
_ep, _inf, _termlnk, _sticaptmeth, _trgspt, _trgusg, (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||||
|
|
||||||
|
/* 3.9.2.2 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_OUTPUT(_numfmt, _totallen, _ep, _inf, _termlnk, ...) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_OUT_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__)), TUSB_DESC_CS_INTERFACE, \
|
||||||
|
VIDEO_CS_ITF_VS_OUTPUT_HEADER, _numfmt, \
|
||||||
|
U16_TO_U8S_LE((_totallen) + TUD_VIDEO_DESC_CS_VS_OUT_LEN + (_numfmt) * (TU_ARGS_NUM(__VA_ARGS__))), \
|
||||||
|
_ep, _inf, _termlnk, (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||||
|
|
||||||
|
/* Uncompressed 3.1.1 */
|
||||||
|
#define TUD_VIDEO_GUID(_g0,_g1,_g2,_g3,_g4,_g5,_g6,_g7,_g8,_g9,_g10,_g11,_g12,_g13,_g14,_g15) _g0,_g1,_g2,_g3,_g4,_g5,_g6,_g7,_g8,_g9,_g10,_g11,_g12,_g13,_g14,_g15
|
||||||
|
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfrmdesc, \
|
||||||
|
_guid, _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED, \
|
||||||
|
_fmtidx, _numfrmdesc, TUD_VIDEO_GUID(_guid), \
|
||||||
|
_bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp
|
||||||
|
|
||||||
|
/* Uncompressed 3.1.2 Table 3-3 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, _minfrminterval, _maxfrminterval, _frmintervalstep) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, \
|
||||||
|
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||||
|
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), 0, \
|
||||||
|
U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep)
|
||||||
|
|
||||||
|
/* Uncompressed 3.1.2 Table 3-4 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
|
||||||
|
TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, \
|
||||||
|
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||||
|
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||||
|
|
||||||
|
/* Motion-JPEG 3.1.1 Table 3-1 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG(_fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_MJPEG, \
|
||||||
|
_fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp
|
||||||
|
|
||||||
|
/* Motion-JPEG 3.1.1 Table 3-2 and 3-3 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, _minfrminterval, _maxfrminterval, _frmintervalstep) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \
|
||||||
|
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||||
|
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), 0, \
|
||||||
|
U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep)
|
||||||
|
|
||||||
|
/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \
|
||||||
|
TUSB_DESC_CS_INTERFACE, VIDEO_CS_VS_INTERFACE_FRAME_MJPEG, \
|
||||||
|
_frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \
|
||||||
|
U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__
|
||||||
|
|
||||||
|
/* 3.9.2.6 */
|
||||||
|
#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(_color, _trns, _mat) \
|
||||||
|
TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN, \
|
||||||
|
TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_COLORFORMAT, \
|
||||||
|
_color, _trns, _mat
|
||||||
|
|
||||||
|
/* 3.10.1.1 */
|
||||||
|
#define TUD_VIDEO_DESC_EP_ISO(_ep, _epsize, _ep_interval) \
|
||||||
|
7, TUSB_DESC_ENDPOINT, _ep, (uint8_t) (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS),\
|
||||||
|
U16_TO_U8S_LE(_epsize), _ep_interval
|
||||||
|
|
||||||
|
/* 3.10.1.2 */
|
||||||
|
#define TUD_VIDEO_DESC_EP_BULK(_ep, _epsize, _ep_interval) \
|
||||||
|
7, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), _ep_interval
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||||
|
* Copyright (c) 2021 Koji KITAYAMA
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of the TinyUSB stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TUSB_VIDEO_DEVICE_H_
|
||||||
|
#define TUSB_VIDEO_DEVICE_H_
|
||||||
|
|
||||||
|
#include "common/tusb_common.h"
|
||||||
|
#include "video.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application API (Multiple Ports)
|
||||||
|
// CFG_TUD_VIDEO > 1
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/** Return true if streaming
|
||||||
|
*
|
||||||
|
* @param[in] ctl_idx Destination control interface index
|
||||||
|
* @param[in] stm_idx Destination streaming interface index */
|
||||||
|
bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx);
|
||||||
|
|
||||||
|
/** Transfer a frame
|
||||||
|
*
|
||||||
|
* @param[in] ctl_idx Destination control interface index
|
||||||
|
* @param[in] stm_idx Destination streaming interface index
|
||||||
|
* @param[in] buffer Frame buffer. The caller must not use this buffer until the operation is completed.
|
||||||
|
* @param[in] bufsize Byte size of the frame buffer */
|
||||||
|
bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize);
|
||||||
|
|
||||||
|
/*------------- Optional callbacks -------------*/
|
||||||
|
/** Invoked when compeletion of a frame transfer
|
||||||
|
*
|
||||||
|
* @param[in] ctl_idx Destination control interface index
|
||||||
|
* @param[in] stm_idx Destination streaming interface index */
|
||||||
|
TU_ATTR_WEAK void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// Application Callback API (weak is optional)
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
/** Invoked when SET_POWER_MODE request received
|
||||||
|
*
|
||||||
|
* @param[in] ctl_idx Destination control interface index
|
||||||
|
* @param[in] stm_idx Destination streaming interface index
|
||||||
|
* @return video_error_code_t */
|
||||||
|
TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod);
|
||||||
|
|
||||||
|
/** Invoked when VS_COMMIT_CONTROL(SET_CUR) request received
|
||||||
|
*
|
||||||
|
* @param[in] ctl_idx Destination control interface index
|
||||||
|
* @param[in] stm_idx Destination streaming interface index
|
||||||
|
* @param[in] parameters Video streaming parameters
|
||||||
|
* @return video_error_code_t */
|
||||||
|
TU_ATTR_WEAK int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx,
|
||||||
|
video_probe_and_commit_control_t const *parameters);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
// INTERNAL USBD-CLASS DRIVER API
|
||||||
|
//--------------------------------------------------------------------+
|
||||||
|
void videod_init (void);
|
||||||
|
void videod_reset (uint8_t rhport);
|
||||||
|
uint16_t videod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
|
||||||
|
bool videod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
|
||||||
|
bool videod_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -1,523 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* This file is part of the TinyUSB stack.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "tusb_option.h"
|
|
||||||
|
|
||||||
#if CFG_TUH_ENABLED || CFG_TUD_ENABLED
|
|
||||||
|
|
||||||
#include "tusb.h"
|
|
||||||
#include "common/tusb_private.h"
|
|
||||||
|
|
||||||
#if CFG_TUD_ENABLED
|
|
||||||
#include "device/usbd_pvt.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CFG_TUH_ENABLED
|
|
||||||
#include "host/usbh_classdriver.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
// Public API
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
|
|
||||||
bool tusb_init(void)
|
|
||||||
{
|
|
||||||
#if CFG_TUD_ENABLED && defined(TUD_OPT_RHPORT)
|
|
||||||
// init device stack CFG_TUSB_RHPORTx_MODE must be defined
|
|
||||||
TU_ASSERT ( tud_init(TUD_OPT_RHPORT) );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT)
|
|
||||||
// init host stack CFG_TUSB_RHPORTx_MODE must be defined
|
|
||||||
TU_ASSERT( tuh_init(TUH_OPT_RHPORT) );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool tusb_inited(void)
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
#if CFG_TUD_ENABLED
|
|
||||||
ret = ret || tud_inited();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CFG_TUH_ENABLED
|
|
||||||
ret = ret || tuh_inited();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
// Descriptor helper
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
|
|
||||||
uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1)
|
|
||||||
{
|
|
||||||
while(desc+1 < end)
|
|
||||||
{
|
|
||||||
if ( desc[1] == byte1 ) return desc;
|
|
||||||
desc += desc[DESC_OFFSET_LEN];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t const * tu_desc_find2(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2)
|
|
||||||
{
|
|
||||||
while(desc+2 < end)
|
|
||||||
{
|
|
||||||
if ( desc[1] == byte1 && desc[2] == byte2) return desc;
|
|
||||||
desc += desc[DESC_OFFSET_LEN];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t byte1, uint8_t byte2, uint8_t byte3)
|
|
||||||
{
|
|
||||||
while(desc+3 < end)
|
|
||||||
{
|
|
||||||
if (desc[1] == byte1 && desc[2] == byte2 && desc[3] == byte3) return desc;
|
|
||||||
desc += desc[DESC_OFFSET_LEN];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
// Endpoint Helper for both Host and Device stack
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
|
|
||||||
bool tu_edpt_claim(tu_edpt_state_t* ep_state, osal_mutex_t mutex)
|
|
||||||
{
|
|
||||||
(void) mutex;
|
|
||||||
|
|
||||||
// pre-check to help reducing mutex lock
|
|
||||||
TU_VERIFY((ep_state->busy == 0) && (ep_state->claimed == 0));
|
|
||||||
(void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
|
||||||
|
|
||||||
// can only claim the endpoint if it is not busy and not claimed yet.
|
|
||||||
bool const available = (ep_state->busy == 0) && (ep_state->claimed == 0);
|
|
||||||
if (available)
|
|
||||||
{
|
|
||||||
ep_state->claimed = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
(void) osal_mutex_unlock(mutex);
|
|
||||||
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool tu_edpt_release(tu_edpt_state_t* ep_state, osal_mutex_t mutex)
|
|
||||||
{
|
|
||||||
(void) mutex;
|
|
||||||
|
|
||||||
(void) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
|
||||||
|
|
||||||
// can only release the endpoint if it is claimed and not busy
|
|
||||||
bool const ret = (ep_state->claimed == 1) && (ep_state->busy == 0);
|
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
ep_state->claimed = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
(void) osal_mutex_unlock(mutex);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool tu_edpt_validate(tusb_desc_endpoint_t const * desc_ep, tusb_speed_t speed)
|
|
||||||
{
|
|
||||||
uint16_t const max_packet_size = tu_edpt_packet_size(desc_ep);
|
|
||||||
TU_LOG2(" Open EP %02X with Size = %u\r\n", desc_ep->bEndpointAddress, max_packet_size);
|
|
||||||
|
|
||||||
switch (desc_ep->bmAttributes.xfer)
|
|
||||||
{
|
|
||||||
case TUSB_XFER_ISOCHRONOUS:
|
|
||||||
{
|
|
||||||
uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 1023);
|
|
||||||
TU_ASSERT(max_packet_size <= spec_size);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TUSB_XFER_BULK:
|
|
||||||
if (speed == TUSB_SPEED_HIGH)
|
|
||||||
{
|
|
||||||
// Bulk highspeed must be EXACTLY 512
|
|
||||||
TU_ASSERT(max_packet_size == 512);
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
// TODO Bulk fullspeed can only be 8, 16, 32, 64
|
|
||||||
TU_ASSERT(max_packet_size <= 64);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TUSB_XFER_INTERRUPT:
|
|
||||||
{
|
|
||||||
uint16_t const spec_size = (speed == TUSB_SPEED_HIGH ? 1024 : 64);
|
|
||||||
TU_ASSERT(max_packet_size <= spec_size);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tu_edpt_bind_driver(uint8_t ep2drv[][2], tusb_desc_interface_t const* desc_itf, uint16_t desc_len, uint8_t driver_id)
|
|
||||||
{
|
|
||||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
|
||||||
uint8_t const* desc_end = p_desc + desc_len;
|
|
||||||
|
|
||||||
while( p_desc < desc_end )
|
|
||||||
{
|
|
||||||
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
|
|
||||||
{
|
|
||||||
uint8_t const ep_addr = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress;
|
|
||||||
|
|
||||||
TU_LOG(2, " Bind EP %02x to driver id %u\r\n", ep_addr, driver_id);
|
|
||||||
ep2drv[tu_edpt_number(ep_addr)][tu_edpt_dir(ep_addr)] = driver_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
p_desc = tu_desc_next(p_desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len)
|
|
||||||
{
|
|
||||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
|
||||||
uint16_t len = 0;
|
|
||||||
|
|
||||||
while (itf_count--)
|
|
||||||
{
|
|
||||||
// Next on interface desc
|
|
||||||
len += tu_desc_len(desc_itf);
|
|
||||||
p_desc = tu_desc_next(p_desc);
|
|
||||||
|
|
||||||
while (len < max_len)
|
|
||||||
{
|
|
||||||
// return on IAD regardless of itf count
|
|
||||||
if ( tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION ) return len;
|
|
||||||
|
|
||||||
if ( (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) &&
|
|
||||||
((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0 )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
len += tu_desc_len(p_desc);
|
|
||||||
p_desc = tu_desc_next(p_desc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
// Endpoint Stream Helper for both Host and Device stack
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
|
|
||||||
bool tu_edpt_stream_init(tu_edpt_stream_t* s, bool is_host, bool is_tx, bool overwritable,
|
|
||||||
void* ff_buf, uint16_t ff_bufsize, uint8_t* ep_buf, uint16_t ep_bufsize)
|
|
||||||
{
|
|
||||||
osal_mutex_t new_mutex = osal_mutex_create(&s->ff_mutex);
|
|
||||||
(void) new_mutex;
|
|
||||||
(void) is_tx;
|
|
||||||
|
|
||||||
s->is_host = is_host;
|
|
||||||
tu_fifo_config(&s->ff, ff_buf, ff_bufsize, 1, overwritable);
|
|
||||||
tu_fifo_config_mutex(&s->ff, is_tx ? new_mutex : NULL, is_tx ? NULL : new_mutex);
|
|
||||||
|
|
||||||
s->ep_buf = ep_buf;
|
|
||||||
s->ep_bufsize = ep_bufsize;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TU_ATTR_ALWAYS_INLINE static inline
|
|
||||||
bool stream_claim(tu_edpt_stream_t* s)
|
|
||||||
{
|
|
||||||
if (s->is_host)
|
|
||||||
{
|
|
||||||
#if CFG_TUH_ENABLED
|
|
||||||
return usbh_edpt_claim(s->daddr, s->ep_addr);
|
|
||||||
#endif
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
#if CFG_TUD_ENABLED
|
|
||||||
return usbd_edpt_claim(s->rhport, s->ep_addr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TU_ATTR_ALWAYS_INLINE static inline
|
|
||||||
bool stream_xfer(tu_edpt_stream_t* s, uint16_t count)
|
|
||||||
{
|
|
||||||
if (s->is_host)
|
|
||||||
{
|
|
||||||
#if CFG_TUH_ENABLED
|
|
||||||
return usbh_edpt_xfer(s->daddr, s->ep_addr, count ? s->ep_buf : NULL, count);
|
|
||||||
#endif
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
#if CFG_TUD_ENABLED
|
|
||||||
return usbd_edpt_xfer(s->rhport, s->ep_addr, count ? s->ep_buf : NULL, count);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TU_ATTR_ALWAYS_INLINE static inline
|
|
||||||
bool stream_release(tu_edpt_stream_t* s)
|
|
||||||
{
|
|
||||||
if (s->is_host)
|
|
||||||
{
|
|
||||||
#if CFG_TUH_ENABLED
|
|
||||||
return usbh_edpt_release(s->daddr, s->ep_addr);
|
|
||||||
#endif
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
#if CFG_TUD_ENABLED
|
|
||||||
return usbd_edpt_release(s->rhport, s->ep_addr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
// Stream Write
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
|
|
||||||
bool tu_edpt_stream_write_zlp_if_needed(tu_edpt_stream_t* s, uint32_t last_xferred_bytes)
|
|
||||||
{
|
|
||||||
// ZLP condition: no pending data, last transferred bytes is multiple of packet size
|
|
||||||
TU_VERIFY( !tu_fifo_count(&s->ff) && last_xferred_bytes && (0 == (last_xferred_bytes & (s->ep_packetsize-1))) );
|
|
||||||
|
|
||||||
TU_VERIFY( stream_claim(s) );
|
|
||||||
TU_ASSERT( stream_xfer(s, 0) );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t tu_edpt_stream_write_xfer(tu_edpt_stream_t* s)
|
|
||||||
{
|
|
||||||
// skip if no data
|
|
||||||
TU_VERIFY( tu_fifo_count(&s->ff), 0 );
|
|
||||||
|
|
||||||
// Claim the endpoint
|
|
||||||
TU_VERIFY( stream_claim(s), 0 );
|
|
||||||
|
|
||||||
// Pull data from FIFO -> EP buf
|
|
||||||
uint16_t const count = tu_fifo_read_n(&s->ff, s->ep_buf, s->ep_bufsize);
|
|
||||||
|
|
||||||
if ( count )
|
|
||||||
{
|
|
||||||
TU_ASSERT( stream_xfer(s, count), 0 );
|
|
||||||
return count;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
// Release endpoint since we don't make any transfer
|
|
||||||
// Note: data is dropped if terminal is not connected
|
|
||||||
stream_release(s);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t tu_edpt_stream_write(tu_edpt_stream_t* s, void const *buffer, uint32_t bufsize)
|
|
||||||
{
|
|
||||||
TU_VERIFY(bufsize); // TODO support ZLP
|
|
||||||
|
|
||||||
uint16_t ret = tu_fifo_write_n(&s->ff, buffer, (uint16_t) bufsize);
|
|
||||||
|
|
||||||
// flush if fifo has more than packet size or
|
|
||||||
// in rare case: fifo depth is configured too small (which never reach packet size)
|
|
||||||
if ( (tu_fifo_count(&s->ff) >= s->ep_packetsize) || (tu_fifo_depth(&s->ff) < s->ep_packetsize) )
|
|
||||||
{
|
|
||||||
tu_edpt_stream_write_xfer(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
// Stream Read
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
|
|
||||||
uint32_t tu_edpt_stream_read_xfer(tu_edpt_stream_t* s)
|
|
||||||
{
|
|
||||||
uint16_t available = tu_fifo_remaining(&s->ff);
|
|
||||||
|
|
||||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
|
||||||
// TODO Actually we can still carry out the transfer, keeping count of received bytes
|
|
||||||
// and slowly move it to the FIFO when read().
|
|
||||||
// This pre-check reduces endpoint claiming
|
|
||||||
TU_VERIFY(available >= s->ep_packetsize);
|
|
||||||
|
|
||||||
// claim endpoint
|
|
||||||
TU_VERIFY(stream_claim(s), 0);
|
|
||||||
|
|
||||||
// get available again since fifo can be changed before endpoint is claimed
|
|
||||||
available = tu_fifo_remaining(&s->ff);
|
|
||||||
|
|
||||||
if ( available >= s->ep_packetsize )
|
|
||||||
{
|
|
||||||
// multiple of packet size limit by ep bufsize
|
|
||||||
uint16_t count = (uint16_t) (available & ~(s->ep_packetsize -1));
|
|
||||||
count = tu_min16(count, s->ep_bufsize);
|
|
||||||
|
|
||||||
TU_ASSERT( stream_xfer(s, count), 0 );
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
// Release endpoint since we don't make any transfer
|
|
||||||
stream_release(s);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t tu_edpt_stream_read(tu_edpt_stream_t* s, void* buffer, uint32_t bufsize)
|
|
||||||
{
|
|
||||||
uint32_t num_read = tu_fifo_read_n(&s->ff, buffer, (uint16_t) bufsize);
|
|
||||||
tu_edpt_stream_read_xfer(s);
|
|
||||||
return num_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
// Debug
|
|
||||||
//--------------------------------------------------------------------+
|
|
||||||
|
|
||||||
#if CFG_TUSB_DEBUG
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#if CFG_TUSB_DEBUG >= 2
|
|
||||||
|
|
||||||
char const* const tu_str_speed[] = { "Full", "Low", "High" };
|
|
||||||
char const* const tu_str_std_request[] =
|
|
||||||
{
|
|
||||||
"Get Status" ,
|
|
||||||
"Clear Feature" ,
|
|
||||||
"Reserved" ,
|
|
||||||
"Set Feature" ,
|
|
||||||
"Reserved" ,
|
|
||||||
"Set Address" ,
|
|
||||||
"Get Descriptor" ,
|
|
||||||
"Set Descriptor" ,
|
|
||||||
"Get Configuration" ,
|
|
||||||
"Set Configuration" ,
|
|
||||||
"Get Interface" ,
|
|
||||||
"Set Interface" ,
|
|
||||||
"Synch Frame"
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void dump_str_line(uint8_t const* buf, uint16_t count)
|
|
||||||
{
|
|
||||||
tu_printf(" |");
|
|
||||||
|
|
||||||
// each line is 16 bytes
|
|
||||||
for(uint16_t i=0; i<count; i++)
|
|
||||||
{
|
|
||||||
const char ch = buf[i];
|
|
||||||
tu_printf("%c", isprint(ch) ? ch : '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
tu_printf("|\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print out memory contents
|
|
||||||
* - buf : buffer
|
|
||||||
* - count : number of item
|
|
||||||
* - indent: prefix spaces on every line
|
|
||||||
*/
|
|
||||||
void tu_print_mem(void const *buf, uint32_t count, uint8_t indent)
|
|
||||||
{
|
|
||||||
uint8_t const size = 1; // fixed 1 byte for now
|
|
||||||
|
|
||||||
if ( !buf || !count )
|
|
||||||
{
|
|
||||||
tu_printf("NULL\r\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t const *buf8 = (uint8_t const *) buf;
|
|
||||||
|
|
||||||
char format[] = "%00X";
|
|
||||||
format[2] += 2*size;
|
|
||||||
|
|
||||||
const uint8_t item_per_line = 16 / size;
|
|
||||||
|
|
||||||
for(unsigned int i=0; i<count; i++)
|
|
||||||
{
|
|
||||||
unsigned int value=0;
|
|
||||||
|
|
||||||
if ( i%item_per_line == 0 )
|
|
||||||
{
|
|
||||||
// Print Ascii
|
|
||||||
if ( i != 0 )
|
|
||||||
{
|
|
||||||
dump_str_line(buf8-16, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(uint8_t s=0; s < indent; s++) tu_printf(" ");
|
|
||||||
|
|
||||||
// print offset or absolute address
|
|
||||||
tu_printf("%04X: ", 16*i/item_per_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
tu_memcpy_s(&value, sizeof(value), buf8, size);
|
|
||||||
buf8 += size;
|
|
||||||
|
|
||||||
tu_printf(" ");
|
|
||||||
tu_printf(format, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill up last row to 16 for printing ascii
|
|
||||||
const uint32_t remain = count%16;
|
|
||||||
uint8_t nback = (uint8_t)(remain ? remain : 16);
|
|
||||||
|
|
||||||
if ( remain )
|
|
||||||
{
|
|
||||||
for(uint32_t i=0; i< 16-remain; i++)
|
|
||||||
{
|
|
||||||
tu_printf(" ");
|
|
||||||
for(int j=0; j<2*size; j++) tu_printf(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dump_str_line(buf8-nback, nback);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // host or device enabled
|
|
@ -33,3 +33,5 @@ lib_deps =
|
|||||||
adafruit/Adafruit NeoPixel@^1.11.0
|
adafruit/Adafruit NeoPixel@^1.11.0
|
||||||
robtillaart/MCP_DAC@^0.2.0
|
robtillaart/MCP_DAC@^0.2.0
|
||||||
adafruit/Adafruit SSD1306@^2.5.9
|
adafruit/Adafruit SSD1306@^2.5.9
|
||||||
|
https://github.com/pschatzmann/logic-analyzer.git
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
#include "FileParsing.h"
|
#include "FileParsing.h"
|
||||||
|
#include "RotaryEncoder.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "LittleFS.h"
|
#include "LittleFS.h"
|
||||||
#include "MatrixStateRP2040.h"
|
#include "MatrixStateRP2040.h"
|
||||||
@ -30,32 +31,301 @@ File nodeFile;
|
|||||||
|
|
||||||
File wokwiFile;
|
File wokwiFile;
|
||||||
|
|
||||||
|
File nodeFileBuffer;
|
||||||
|
|
||||||
unsigned long timeToFP = 0;
|
unsigned long timeToFP = 0;
|
||||||
|
|
||||||
void savePreformattedNodeFile(int source)
|
const char rotaryConnectionString[] = "{ AREF-GND, D11-GND, D10-UART_TX, D12-UART_RX, D13-GPIO_0, ";
|
||||||
{
|
|
||||||
LittleFS.remove("nodeFile.txt");
|
void createSlots(int slot, int addRotaryConnections)
|
||||||
|
{
|
||||||
|
if (slot == -1)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_SLOTS; i++)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
// int length = 0;
|
||||||
|
// nodeFile = LittleFS.open("nodeFileSlot" + String(i) + ".txt", "r+");
|
||||||
|
// //nodeFile.seek(0);
|
||||||
|
// if (addRotaryConnections == 0)
|
||||||
|
// {
|
||||||
|
// while(nodeFile.available())
|
||||||
|
// {
|
||||||
|
// if (rotaryConnectionString[index] == nodeFile.read())
|
||||||
|
// {
|
||||||
|
// //nodeFile.write(' ');
|
||||||
|
// index++;
|
||||||
|
|
||||||
|
// } else
|
||||||
|
// length++;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// nodeFile.seek(0);
|
||||||
|
// nodeFile.write("f { ");
|
||||||
|
// for (int i = 0; i < (index-length); i++)
|
||||||
|
// {
|
||||||
|
// nodeFile.seek(index+i);
|
||||||
|
// uint8_t c = nodeFile.read();
|
||||||
|
// nodeFile.seek(i);
|
||||||
|
// nodeFile.print('t');
|
||||||
|
// }
|
||||||
|
// Serial.println(index);
|
||||||
|
|
||||||
|
// Serial.println(length);
|
||||||
|
// nodeFile.seek(0);
|
||||||
|
// while(nodeFile.available())
|
||||||
|
// {
|
||||||
|
// Serial.write(nodeFile.read());
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// nodeFile.close();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
nodeFile = LittleFS.open("nodeFileSlot" + String(i) + ".txt", "w");
|
||||||
|
if (i >= 0)
|
||||||
|
{
|
||||||
|
// nodeFile.print("{ 83-103, 81-100, 82-110, 117-110, 85-111, 114-111, 80-112, 116-112, ");
|
||||||
|
// nodeFile.print("{ D5-GND, A3-GND, A5-GPIO_0, D4-UART_TX, D6-UART_RX, ");
|
||||||
|
// nodeFile.print("{ AREF-D8, D8-ADC0, ADC0-GPIO_0, D11-GND, D10-ADC2, ADC2-UART_TX, D12-ADC1, ADC1-UART_RX, D13-GND, ");
|
||||||
|
if (addRotaryConnections > 0)
|
||||||
|
{
|
||||||
|
nodeFile.print("{ AREF-GND, D11-GND, D10-UART_TX, D12-UART_RX, D13-GPIO_0, ");
|
||||||
|
nodeFile.print(i * 4 + 1);
|
||||||
|
nodeFile.print("-");
|
||||||
|
nodeFile.print(i * 4 + 2);
|
||||||
|
nodeFile.print(", }");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeFile.print("{ \n\r } \n\r");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodeFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "w");
|
||||||
|
|
||||||
|
// nodeFile.print("{ D12-D7, D7-ADC0, ADC0-UART_RX, D11-GND, D10-ADC2, ADC2-UART_TX, AREF-ADC1, ADC1-GPIO_0, D13-GND, ");
|
||||||
|
if (addRotaryConnections > 0)
|
||||||
|
{
|
||||||
|
nodeFile.print("{ AREF-GND, D11-GND, D10-UART_TX, D12-UART_RX, D13-GPIO_0, } ");
|
||||||
|
// nodeFile.print(slot * 4 + 1);
|
||||||
|
// nodeFile.print("-");
|
||||||
|
// nodeFile.print(slot * 4 + 2);
|
||||||
|
// nodeFile.print(", } ");
|
||||||
|
}
|
||||||
|
nodeFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void inputNodeFileList(void)
|
||||||
|
{
|
||||||
|
Serial.println("Paste the nodeFile list here");
|
||||||
|
while (Serial.available() == 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
nodeFileString.clear();
|
||||||
|
|
||||||
|
while (Serial.available() > 0)
|
||||||
|
{
|
||||||
|
nodeFileString.write(Serial.read());
|
||||||
|
delayMicroseconds(100);
|
||||||
|
}
|
||||||
|
// nodeFileString.read(Serial);
|
||||||
|
// Serial.println("nodeFileString");
|
||||||
|
// Serial.println(nodeFileString);
|
||||||
|
int lastTokenIndex = 0;
|
||||||
|
int tokenFound = 0;
|
||||||
|
uint8_t lastReads[8] = {' ', ' ', ' ', ' '};
|
||||||
|
uint8_t slotText[8] = {'S', 'l', 'o', 't', ' '};
|
||||||
|
uint8_t searchFor[3] = {'f', ' ', '{'};
|
||||||
|
|
||||||
|
createSafeString(nodeFileStringSlot, 1200);
|
||||||
|
nodeFileBuffer = LittleFS.open("nodeFileBuffer.txt", "w+");
|
||||||
|
nodeFileString.trim();
|
||||||
|
if (nodeFileString.endsWith("}") == 0)
|
||||||
|
{
|
||||||
|
nodeFileString.concat(" } \n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeFileString.printTo(nodeFileBuffer);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
int inFileMeat = 0;
|
||||||
|
|
||||||
|
int numberOfSlotsFound = 0;
|
||||||
|
int firstSlotNumber = 0;
|
||||||
|
int firstSlotFound = 0;
|
||||||
|
|
||||||
|
// nodeFileBuffer.seek(nodeFileBuffer.size());
|
||||||
|
// nodeFileBuffer.print("fuck \n\r");
|
||||||
|
nodeFileBuffer.seek(0);
|
||||||
|
// while (nodeFileBuffer.available())
|
||||||
|
// {
|
||||||
|
// Serial.write(nodeFileBuffer.read());
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SLOTS; i++) // this just searches for how many slots are in the pasted list
|
||||||
|
{
|
||||||
|
tokenFound = 0;
|
||||||
|
nodeFileBuffer.seek(index);
|
||||||
|
inFileMeat = 0;
|
||||||
|
|
||||||
|
while (nodeFileBuffer.available())
|
||||||
|
{
|
||||||
|
uint8_t c = nodeFileBuffer.read();
|
||||||
|
lastReads[0] = lastReads[1];
|
||||||
|
lastReads[1] = lastReads[2];
|
||||||
|
lastReads[2] = lastReads[3];
|
||||||
|
lastReads[3] = lastReads[4];
|
||||||
|
lastReads[4] = lastReads[5];
|
||||||
|
lastReads[5] = c;
|
||||||
|
|
||||||
|
if (lastReads[0] == slotText[0] && lastReads[1] == slotText[1] && lastReads[2] == slotText[2] && lastReads[3] == slotText[3] && lastReads[4] == slotText[4] && firstSlotFound == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
firstSlotFound = 1;
|
||||||
|
firstSlotNumber = lastReads[5] - '0';
|
||||||
|
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastReads[3] == searchFor[0] && lastReads[4] == searchFor[1] && lastReads[5] == searchFor[2])
|
||||||
|
{
|
||||||
|
inFileMeat = 1;
|
||||||
|
numberOfSlotsFound++;
|
||||||
|
}
|
||||||
|
if (lastReads[2] == '}')
|
||||||
|
{
|
||||||
|
inFileMeat = 0;
|
||||||
|
|
||||||
|
index++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inFileMeat == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Serial.println(numberOfSlotsFound);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||||
|
// nodeFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("\n\rFirst slot found: ");
|
||||||
|
Serial.println(firstSlotNumber, HEX);
|
||||||
|
|
||||||
|
Serial.println("Number of slots found: " + String(numberOfSlotsFound));
|
||||||
|
index = 0;
|
||||||
|
int lastSlotNumber = firstSlotNumber + numberOfSlotsFound - 1;
|
||||||
|
|
||||||
|
for (int i = firstSlotNumber; i <= lastSlotNumber; i++) // this takes the pasted list fron the serial monitor and saves it to the nodeFileSlot files
|
||||||
|
{
|
||||||
|
|
||||||
|
if (i >= firstSlotNumber && i <= lastSlotNumber)
|
||||||
|
{
|
||||||
|
Serial.println(i);
|
||||||
|
nodeFile = LittleFS.open("nodeFileSlot" + String(i) + ".txt", "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
// nodeFileStringSlot.clear();
|
||||||
|
nodeFileBuffer.seek(index);
|
||||||
|
|
||||||
|
inFileMeat = 0;
|
||||||
|
|
||||||
|
while (nodeFileBuffer.available())
|
||||||
|
{
|
||||||
|
uint8_t c = nodeFileBuffer.read();
|
||||||
|
lastReads[0] = lastReads[1];
|
||||||
|
lastReads[1] = lastReads[2];
|
||||||
|
lastReads[2] = c;
|
||||||
|
|
||||||
|
// nodeFile.write(c);
|
||||||
|
|
||||||
|
if (lastReads[0] == searchFor[0] && lastReads[1] == searchFor[1] && lastReads[2] == searchFor[2])
|
||||||
|
{
|
||||||
|
inFileMeat = 1;
|
||||||
|
}
|
||||||
|
if (lastReads[1] == '}')
|
||||||
|
{
|
||||||
|
inFileMeat = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inFileMeat == 1 && i >= firstSlotNumber && i <= lastSlotNumber)
|
||||||
|
{
|
||||||
|
nodeFile.write(c);
|
||||||
|
// Serial.print(index);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (i >= firstSlotNumber && i <= lastSlotNumber)
|
||||||
|
{
|
||||||
|
nodeFile.seek(0);
|
||||||
|
printNodeFile(i);
|
||||||
|
|
||||||
|
// Serial.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||||
|
nodeFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_SLOTS; i++)
|
||||||
|
{
|
||||||
|
// printNodeFile(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void savePreformattedNodeFile(int source, int slot, int keepEncoder)
|
||||||
|
{
|
||||||
|
|
||||||
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "w+");
|
||||||
|
Serial.println("Slot " + String(slot));
|
||||||
|
|
||||||
|
// Serial.println(nodeFile);
|
||||||
|
|
||||||
|
if (keepEncoder == 1)
|
||||||
|
{
|
||||||
|
// nodeFile.print("{ D12-D7, D7-ADC0, ADC0-UART_RX, D11-GND, D10-ADC2, ADC2-UART_TX, AREF-ADC1, ADC1-GPIO_0, D13-GND, ");
|
||||||
|
nodeFile.print("{ AREF-GND, D11-GND, D10-UART_TX, D12-UART_RX, D13-GPIO_0, ");
|
||||||
|
// Serial.println(" keeping encoder");
|
||||||
|
}
|
||||||
|
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "w+");
|
|
||||||
if (debugFP)
|
if (debugFP)
|
||||||
{
|
{
|
||||||
Serial.print("source = ");
|
Serial.print("source = ");
|
||||||
Serial.println(source);
|
Serial.println(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source == 0)
|
if (source == 0)
|
||||||
{
|
{
|
||||||
while (Serial.available() == 0)
|
while (Serial.available() == 0 || Serial.read() == 'f')
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
while (Serial.available() > 0)
|
while (Serial.available() > 0)
|
||||||
{
|
{
|
||||||
nodeFile.write(Serial.read());
|
// nodeFile.write(Serial.read());
|
||||||
|
uint8_t c = Serial.read();
|
||||||
|
// Serial.print(c);
|
||||||
|
if (c != 'f' && c != '}' && c != '{' && c != ' ' && c != '\n' && c != '\r' && c != '\t')
|
||||||
|
{
|
||||||
|
nodeFile.write(c);
|
||||||
|
}
|
||||||
|
|
||||||
delayMicroseconds(800);
|
delayMicroseconds(800);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (source == 1)
|
if (source == 1)
|
||||||
{
|
{
|
||||||
|
nodeFile.print("f 117-D1, 116-D0,");
|
||||||
while (Serial1.available() == 0)
|
while (Serial1.available() == 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -74,18 +344,27 @@ void savePreformattedNodeFile(int source)
|
|||||||
delay(1);
|
delay(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeFile.write("\n\r");
|
// nodeFile.write("\n\r");
|
||||||
|
|
||||||
// nodeFile.seek(0);
|
// nodeFile.seek(0);
|
||||||
// nodeFileString.read(nodeFile);
|
// nodeFileString.read(nodeFile);
|
||||||
// Serial.println(nodeFileString);
|
// Serial.println(nodeFileString);
|
||||||
|
if (keepEncoder == 1)
|
||||||
|
{
|
||||||
|
nodeFile.print(" } ");
|
||||||
|
// Serial.println(" keeping encoder");
|
||||||
|
}
|
||||||
|
|
||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
|
// Serial.println("Slot " + String(slot) + " saved");
|
||||||
|
// printNodeFile(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printNodeFile(void)
|
void printNodeFile(int slot)
|
||||||
{
|
{
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "r");
|
|
||||||
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "r");
|
||||||
if (!nodeFile)
|
if (!nodeFile)
|
||||||
{
|
{
|
||||||
// if (debugFP)
|
// if (debugFP)
|
||||||
@ -107,6 +386,53 @@ void printNodeFile(void)
|
|||||||
// Serial.println(nodeFileString.indexOf(","));
|
// Serial.println(nodeFileString.indexOf(","));
|
||||||
// Serial.println(nodeFileString.charAt(nodeFileString.indexOf(",")+1));
|
// Serial.println(nodeFileString.charAt(nodeFileString.indexOf(",")+1));
|
||||||
// Serial.println(nodeFileString.indexOf(","));
|
// Serial.println(nodeFileString.indexOf(","));
|
||||||
|
if (debugFP == 0)
|
||||||
|
{
|
||||||
|
// nodeFileString.replace("116-80, 117-82, 114-83, 85-100, 81-100,", "rotEnc_0,");
|
||||||
|
|
||||||
|
nodeFileString.replace("100", "GND");
|
||||||
|
nodeFileString.replace("105", "5V");
|
||||||
|
nodeFileString.replace("103", "3V3");
|
||||||
|
nodeFileString.replace("106", "DAC0");
|
||||||
|
nodeFileString.replace("107", "DAC1");
|
||||||
|
nodeFileString.replace("108", "I_P");
|
||||||
|
nodeFileString.replace("109", "I_N");
|
||||||
|
nodeFileString.replace("110", "ADC0");
|
||||||
|
nodeFileString.replace("111", "ADC1");
|
||||||
|
nodeFileString.replace("112", "ADC2");
|
||||||
|
nodeFileString.replace("113", "ADC3");
|
||||||
|
nodeFileString.replace("114", "GPIO_0");
|
||||||
|
nodeFileString.replace("116", "UART_TX");
|
||||||
|
nodeFileString.replace("117", "UART_RX");
|
||||||
|
nodeFileString.replace("118", "GPIO_18");
|
||||||
|
nodeFileString.replace("119", "GPIO_19");
|
||||||
|
nodeFileString.replace("120", "8V_P");
|
||||||
|
nodeFileString.replace("121", "8V_N");
|
||||||
|
nodeFileString.replace("70", "D0");
|
||||||
|
nodeFileString.replace("71", "D1");
|
||||||
|
nodeFileString.replace("72", "D2");
|
||||||
|
nodeFileString.replace("73", "D3");
|
||||||
|
nodeFileString.replace("74", "D4");
|
||||||
|
nodeFileString.replace("75", "D5");
|
||||||
|
nodeFileString.replace("76", "D6");
|
||||||
|
nodeFileString.replace("77", "D7");
|
||||||
|
nodeFileString.replace("78", "D8");
|
||||||
|
nodeFileString.replace("79", "D9");
|
||||||
|
nodeFileString.replace("80", "D10");
|
||||||
|
nodeFileString.replace("81", "D11");
|
||||||
|
nodeFileString.replace("82", "D12");
|
||||||
|
nodeFileString.replace("83", "D13");
|
||||||
|
nodeFileString.replace("84", "RESET");
|
||||||
|
nodeFileString.replace("85", "AREF");
|
||||||
|
nodeFileString.replace("86", "A0");
|
||||||
|
nodeFileString.replace("87", "A1");
|
||||||
|
nodeFileString.replace("88", "A2");
|
||||||
|
nodeFileString.replace("89", "A3");
|
||||||
|
nodeFileString.replace("90", "A4");
|
||||||
|
nodeFileString.replace("91", "A5");
|
||||||
|
nodeFileString.replace("92", "A6");
|
||||||
|
nodeFileString.replace("93", "A7");
|
||||||
|
}
|
||||||
|
|
||||||
if (nodeFileString.charAt(nodeFileString.indexOf(",") + 1) != '\n')
|
if (nodeFileString.charAt(nodeFileString.indexOf(",") + 1) != '\n')
|
||||||
{
|
{
|
||||||
@ -433,18 +759,21 @@ void changeWokwiDefinesToJumperless(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void clearNodeFile(void)
|
void clearNodeFile(int slot)
|
||||||
{
|
{
|
||||||
LittleFS.remove("nodeFile.txt");
|
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "w+");
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "w+");
|
||||||
|
|
||||||
nodeFile.print("!");
|
nodeFile.print("!");
|
||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeBridgeFromNodeFile(int node1, int node2)
|
void removeBridgeFromNodeFile(int node1, int node2, int slot)
|
||||||
{
|
{
|
||||||
|
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "r+");
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "r+");
|
||||||
|
/// Serial.println(nodeFile);
|
||||||
|
|
||||||
if (!nodeFile)
|
if (!nodeFile)
|
||||||
{
|
{
|
||||||
if (debugFP)
|
if (debugFP)
|
||||||
@ -461,7 +790,7 @@ void removeBridgeFromNodeFile(int node1, int node2)
|
|||||||
|
|
||||||
nodeFileString.read(nodeFile);
|
nodeFileString.read(nodeFile);
|
||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
|
// Serial.println(nodeFileString);
|
||||||
char nodeAsChar[20];
|
char nodeAsChar[20];
|
||||||
itoa(node1, nodeAsChar, 10);
|
itoa(node1, nodeAsChar, 10);
|
||||||
char paddedChar[21];
|
char paddedChar[21];
|
||||||
@ -541,7 +870,7 @@ void removeBridgeFromNodeFile(int node1, int node2)
|
|||||||
}
|
}
|
||||||
if (strstr(lines[i], paddedChar) != NULL)
|
if (strstr(lines[i], paddedChar) != NULL)
|
||||||
{
|
{
|
||||||
// Serial.println(lines[i]);
|
// Serial.println(lines[i]);
|
||||||
// delay(1);
|
// delay(1);
|
||||||
|
|
||||||
for (int j = 0; j < 18; j++)
|
for (int j = 0; j < 18; j++)
|
||||||
@ -582,15 +911,18 @@ void removeBridgeFromNodeFile(int node1, int node2)
|
|||||||
nodeFileString.concat("}\n\r");
|
nodeFileString.concat("}\n\r");
|
||||||
|
|
||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "w+");
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "w+");
|
||||||
|
|
||||||
nodeFile.write(nodeFileString.c_str());
|
nodeFile.write(nodeFileString.c_str());
|
||||||
|
|
||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addBridgeToNodeFile(int node1, int node2)
|
void addBridgeToNodeFile(int node1, int node2, int slot)
|
||||||
{
|
{
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "r+");
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "r+");
|
||||||
|
// Serial.println(nodeFile);
|
||||||
|
|
||||||
if (!nodeFile)
|
if (!nodeFile)
|
||||||
{
|
{
|
||||||
if (debugFP)
|
if (debugFP)
|
||||||
@ -670,12 +1002,11 @@ void addBridgeToNodeFile(int node1, int node2)
|
|||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeToNodeFile(void)
|
void writeToNodeFile(int slot)
|
||||||
{
|
{
|
||||||
|
|
||||||
LittleFS.remove("nodeFile.txt");
|
nodeFile = LittleFS.open("nodeFile" + String(slot) + ".txt", "w");
|
||||||
delayMicroseconds(100);
|
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "w+");
|
|
||||||
if (!nodeFile)
|
if (!nodeFile)
|
||||||
{
|
{
|
||||||
if (debugFP)
|
if (debugFP)
|
||||||
@ -729,11 +1060,12 @@ void writeToNodeFile(void)
|
|||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void openNodeFile()
|
void openNodeFile(int slot)
|
||||||
{
|
{
|
||||||
timeToFP = millis();
|
timeToFP = millis();
|
||||||
|
|
||||||
nodeFile = LittleFS.open("nodeFile.txt", "r");
|
nodeFile = LittleFS.open("nodeFileSlot" + String(slot) + ".txt", "r");
|
||||||
|
|
||||||
if (!nodeFile)
|
if (!nodeFile)
|
||||||
{
|
{
|
||||||
if (debugFP)
|
if (debugFP)
|
||||||
@ -743,11 +1075,12 @@ void openNodeFile()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (debugFP)
|
if (debugFP)
|
||||||
Serial.println("\n\ropened nodeFile.txt\n\n\rloading bridges from file\n\r");
|
Serial.println("\n\ropened nodeFileSlot" + String(slot) + +".txt\n\n\rloading bridges from file\n\r");
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeFileString.clear();
|
nodeFileString.clear();
|
||||||
nodeFileString.read(nodeFile);
|
nodeFileString.read(nodeFile);
|
||||||
|
// Serial.println(nodeFileString);
|
||||||
|
|
||||||
nodeFile.close();
|
nodeFile.close();
|
||||||
splitStringToFields();
|
splitStringToFields();
|
||||||
@ -1124,7 +1457,7 @@ void debugFlagInit(void)
|
|||||||
EEPROM.write(RAILBRIGHTNESSADDRESS, DEFAULTRAILBRIGHTNESS);
|
EEPROM.write(RAILBRIGHTNESSADDRESS, DEFAULTRAILBRIGHTNESS);
|
||||||
EEPROM.write(SPECIALBRIGHTNESSADDRESS, DEFAULTSPECIALNETBRIGHTNESS);
|
EEPROM.write(SPECIALBRIGHTNESSADDRESS, DEFAULTSPECIALNETBRIGHTNESS);
|
||||||
EEPROM.write(PROBESWAPADDRESS, 0);
|
EEPROM.write(PROBESWAPADDRESS, 0);
|
||||||
|
EEPROM.write(ROTARYENCODER_MODE_ADDRESS, 0);
|
||||||
|
|
||||||
EEPROM.commit();
|
EEPROM.commit();
|
||||||
delay(5);
|
delay(5);
|
||||||
@ -1146,6 +1479,8 @@ void debugFlagInit(void)
|
|||||||
|
|
||||||
debugLEDs = EEPROM.read(DEBUG_LEDSADDRESS);
|
debugLEDs = EEPROM.read(DEBUG_LEDSADDRESS);
|
||||||
|
|
||||||
|
rotaryEncoderMode = EEPROM.read(ROTARYENCODER_MODE_ADDRESS);
|
||||||
|
|
||||||
probeSwap = EEPROM.read(PROBESWAPADDRESS);
|
probeSwap = EEPROM.read(PROBESWAPADDRESS);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@ -1200,6 +1535,11 @@ void debugFlagInit(void)
|
|||||||
EEPROM.write(SPECIALBRIGHTNESSADDRESS, DEFAULTSPECIALNETBRIGHTNESS);
|
EEPROM.write(SPECIALBRIGHTNESSADDRESS, DEFAULTSPECIALNETBRIGHTNESS);
|
||||||
LEDbrightnessSpecial = DEFAULTSPECIALNETBRIGHTNESS;
|
LEDbrightnessSpecial = DEFAULTSPECIALNETBRIGHTNESS;
|
||||||
}
|
}
|
||||||
|
if (rotaryEncoderMode != 0 && rotaryEncoderMode != 1)
|
||||||
|
{
|
||||||
|
EEPROM.write(ROTARYENCODER_MODE_ADDRESS, 0);
|
||||||
|
rotaryEncoderMode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
EEPROM.commit();
|
EEPROM.commit();
|
||||||
delay(5);
|
delay(5);
|
||||||
@ -1363,6 +1703,24 @@ void debugFlagSet(int flag)
|
|||||||
debugLEDs = true;
|
debugLEDs = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 10:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
EEPROM.write(ROTARYENCODER_MODE_ADDRESS, 0);
|
||||||
|
|
||||||
|
rotaryEncoderMode = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 11:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
EEPROM.write(ROTARYENCODER_MODE_ADDRESS, 1);
|
||||||
|
|
||||||
|
rotaryEncoderMode = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delay(4);
|
delay(4);
|
||||||
EEPROM.commit();
|
EEPROM.commit();
|
||||||
|
@ -14,24 +14,26 @@ extern bool debugNTCC2;
|
|||||||
extern bool debugLEDs;
|
extern bool debugLEDs;
|
||||||
extern bool debugMM;
|
extern bool debugMM;
|
||||||
|
|
||||||
|
// #include "RotaryEncoder.h"
|
||||||
|
|
||||||
|
|
||||||
//extern File nodeFile;
|
//extern File nodeFile;
|
||||||
|
void createSlots(int slot = -1, int addRotaryConnections = 0);
|
||||||
|
void inputNodeFileList(void);
|
||||||
//this just opens the file, takes out all the bullshit, and then populates the newBridge array
|
//this just opens the file, takes out all the bullshit, and then populates the newBridge array
|
||||||
void parseWokwiFileToNodeFile();
|
void parseWokwiFileToNodeFile();
|
||||||
void changeWokwiDefinesToJumperless ();
|
void changeWokwiDefinesToJumperless ();
|
||||||
void writeToNodeFile(void);
|
void writeToNodeFile(int slot = 0);
|
||||||
void removeBridgeFromNodeFile(int node1, int node2 = -1);
|
void removeBridgeFromNodeFile(int node1, int node2 = -1, int slot = 0);
|
||||||
void addBridgeToNodeFile(int node1, int node2);
|
void addBridgeToNodeFile(int node1, int node2, int slot = 0);
|
||||||
void savePreformattedNodeFile (int source = 0);
|
void savePreformattedNodeFile (int source = 0, int slot = 0, int keepEncoder = 1);
|
||||||
|
|
||||||
void openNodeFile();
|
void openNodeFile(int slot = 0);
|
||||||
|
|
||||||
void splitStringToFields();
|
void splitStringToFields();
|
||||||
|
|
||||||
void replaceSFNamesWithDefinedInts();
|
void replaceSFNamesWithDefinedInts();
|
||||||
void printNodeFile(void);
|
void printNodeFile(int slot = 0);
|
||||||
void replaceNanoNamesWithDefinedInts();
|
void replaceNanoNamesWithDefinedInts();
|
||||||
|
|
||||||
void parseStringToBridges();
|
void parseStringToBridges();
|
||||||
@ -43,7 +45,7 @@ void runCommandAfterReset(char);
|
|||||||
|
|
||||||
void debugFlagSet(int flag);
|
void debugFlagSet(int flag);
|
||||||
void debugFlagInit(void);
|
void debugFlagInit(void);
|
||||||
void clearNodeFile(void);
|
void clearNodeFile(int slot = 0);
|
||||||
int lenHelper(int);
|
int lenHelper(int);
|
||||||
int printLen(int);
|
int printLen(int);
|
||||||
|
|
||||||
|
@ -16,7 +16,13 @@ extern volatile int sendAllPathsCore2;
|
|||||||
#define FSSTUFF 1
|
#define FSSTUFF 1
|
||||||
|
|
||||||
|
|
||||||
|
#define QUADRATURE_A_PIN 16
|
||||||
|
#define QUADRATURE_B_PIN 17
|
||||||
|
#define BUTTON_ENC 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define ROTARYENCODER_MODE_ADDRESS 31
|
||||||
#define DEBUG_FILEPARSINGADDRESS 32
|
#define DEBUG_FILEPARSINGADDRESS 32
|
||||||
#define TIME_FILEPARSINGADDRESS 33
|
#define TIME_FILEPARSINGADDRESS 33
|
||||||
#define DEBUG_NETMANAGERADDRESS 34
|
#define DEBUG_NETMANAGERADDRESS 34
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include "NetsToChipConnections.h"
|
#include "NetsToChipConnections.h"
|
||||||
#include "MatrixStateRP2040.h"
|
#include "MatrixStateRP2040.h"
|
||||||
#include "FileParsing.h"
|
#include "FileParsing.h"
|
||||||
|
#include "NetManager.h"
|
||||||
|
#include "NetsToChipConnections.h"
|
||||||
|
|
||||||
Adafruit_NeoPixel leds(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
|
Adafruit_NeoPixel leds(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
|
||||||
|
|
||||||
@ -463,6 +465,142 @@ char LEDbrightnessMenu(void)
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t savedLEDcolors[NUM_SLOTS][LED_COUNT];
|
||||||
|
int slotLEDpositions[20] = {
|
||||||
|
98,
|
||||||
|
99,
|
||||||
|
100,
|
||||||
|
101,
|
||||||
|
102,
|
||||||
|
103,
|
||||||
|
104,
|
||||||
|
105,
|
||||||
|
};
|
||||||
|
int rotaryEncoderPositions[6] = {
|
||||||
|
97, // AREF
|
||||||
|
95, // D13 (button)
|
||||||
|
94, // D12 (encoder A)
|
||||||
|
93, // D11 (encoder GND)
|
||||||
|
92, // D10 (encoder B)
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t slotSelectionColors[12] = {
|
||||||
|
0x084080, // preview
|
||||||
|
0x005555, // active
|
||||||
|
0x102000, // inactive
|
||||||
|
0x000000, // off
|
||||||
|
0x881261, // preview+active
|
||||||
|
|
||||||
|
0x253500, // rotary encoder High
|
||||||
|
0x000060, // rotary encoder Low
|
||||||
|
|
||||||
|
0x550011, // button High
|
||||||
|
0x001C05, // button Low
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void saveRawColors(int slot)
|
||||||
|
{
|
||||||
|
if (slot == -1)
|
||||||
|
{
|
||||||
|
slot = netSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < LED_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (i >= slotLEDpositions[0] && i <= slotLEDpositions[NUM_SLOTS - 1])
|
||||||
|
{
|
||||||
|
// savedLEDcolors[slot][i] = slotSelectionColors[1];
|
||||||
|
// Serial.print(i);
|
||||||
|
// Serial.print("\t");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
savedLEDcolors[slot][i] = leds.getPixelColor(i);
|
||||||
|
// Serial.print(savedLEDcolors[slot][i], HEX);
|
||||||
|
// Serial.print("\t");
|
||||||
|
// if (i % 30 == 0)
|
||||||
|
// {
|
||||||
|
// Serial.println("\n\r");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
// Serial.println(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshSavedColors(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_SLOTS; i++)
|
||||||
|
{
|
||||||
|
clearAllNTCC();
|
||||||
|
openNodeFile(i);
|
||||||
|
getNodesToConnect();
|
||||||
|
bridgesToPaths();
|
||||||
|
clearLEDs();
|
||||||
|
assignNetColors();
|
||||||
|
showNets();
|
||||||
|
lightUpRail();
|
||||||
|
saveRawColors(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showSavedColors(int slot)
|
||||||
|
{
|
||||||
|
if (slot == -1)
|
||||||
|
{
|
||||||
|
slot = netSlot;
|
||||||
|
}
|
||||||
|
// Serial.println(savedLEDcolors[slot][0], HEX);
|
||||||
|
|
||||||
|
// if (savedLEDcolors[slot][110] == 0) // checking a nano header LED because it should always dimly lit
|
||||||
|
if (true)
|
||||||
|
{
|
||||||
|
// for (int i = 0; i < LED_COUNT; i++)
|
||||||
|
// {
|
||||||
|
// Serial.print(i);
|
||||||
|
|
||||||
|
// Serial.print(" ");
|
||||||
|
// Serial.println(savedLEDcolors[slot][i], HEX);
|
||||||
|
|
||||||
|
// }
|
||||||
|
// Serial.print("\rNo saved colors for slot ");
|
||||||
|
// Serial.print(slot);
|
||||||
|
clearAllNTCC();
|
||||||
|
openNodeFile(slot);
|
||||||
|
getNodesToConnect();
|
||||||
|
bridgesToPaths();
|
||||||
|
// clearLEDsExceptRails();
|
||||||
|
// leds.clear();
|
||||||
|
|
||||||
|
assignNetColors();
|
||||||
|
showNets();
|
||||||
|
lightUpRail();
|
||||||
|
// delayMicroseconds(100);
|
||||||
|
// saveRawColors(slot);
|
||||||
|
}
|
||||||
|
if (rotaryEncoderMode == 1)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < LED_COUNT; i++)
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, savedLEDcolors[slot][i]);
|
||||||
|
|
||||||
|
if (i == slotLEDpositions[netSlot])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[1]);
|
||||||
|
}
|
||||||
|
if (i == slotLEDpositions[slot])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[0]);
|
||||||
|
}
|
||||||
|
if (slot == netSlot && i == slotLEDpositions[netSlot])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showLEDsCore2 = 1;
|
||||||
|
// leds.show();
|
||||||
|
}
|
||||||
|
|
||||||
void assignNetColors(void)
|
void assignNetColors(void)
|
||||||
{
|
{
|
||||||
// numberOfNets = 60;
|
// numberOfNets = 60;
|
||||||
@ -852,7 +990,7 @@ void lightUpNet(int netNumber, int node, int onOff, int brightness2, int hueShif
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
turnOffSkippedNodes();
|
// turnOffSkippedNodes();
|
||||||
/* Serial.print("color: ");
|
/* Serial.print("color: ");
|
||||||
Serial.print(color,HEX);
|
Serial.print(color,HEX);
|
||||||
Serial.print(" r: ");
|
Serial.print(" r: ");
|
||||||
@ -873,7 +1011,7 @@ void lightUpNet(int netNumber, int node, int onOff, int brightness2, int hueShif
|
|||||||
|
|
||||||
void turnOffSkippedNodes(void)
|
void turnOffSkippedNodes(void)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
for (int i = 0; i < numberOfPaths; i++)
|
for (int i = 0; i < numberOfPaths; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -1200,22 +1338,100 @@ void lightUpRail(int logo, int rail, int onOff, int brightness2, int switchPosit
|
|||||||
Serial.print("\n\rled brightness: ");
|
Serial.print("\n\rled brightness: ");
|
||||||
Serial.print(LEDbrightness);
|
Serial.print(LEDbrightness);
|
||||||
*/
|
*/
|
||||||
|
// uint32_t rawOtherColors[8] =
|
||||||
|
// {0x020008, // headerglow
|
||||||
|
// 0x550008, // logo / status
|
||||||
|
// 0x0055AA, // logoflash / statusflash
|
||||||
|
// 0x301A02, // +8V
|
||||||
|
// 0x120932, // -8V
|
||||||
|
// 0x443434, // UART TX
|
||||||
|
// 0x324244, // UART RX
|
||||||
|
// 0x232323}; // unassigned
|
||||||
|
|
||||||
brightness2 = LEDbrightnessRail;
|
brightness2 = LEDbrightnessRail;
|
||||||
|
|
||||||
if (logo == -1 && logoFlash == 0)
|
// if (rotaryEncoderMode == 1 )
|
||||||
|
// {
|
||||||
|
|
||||||
|
// rawOtherColors[1] = 0x550018;
|
||||||
|
// rawOtherColors[2] = 0x0045BA;
|
||||||
|
// } else
|
||||||
|
// {
|
||||||
|
// rawOtherColors[1] = 0x550008;
|
||||||
|
// rawOtherColors[2] = 0x0055AA;
|
||||||
|
// }
|
||||||
|
if (logo == -1 && logoFlash == 0 || rotaryEncoderMode == 1)
|
||||||
{
|
{
|
||||||
leds.setPixelColor(110, rawOtherColors[1]);
|
leds.setPixelColor(110, rawOtherColors[1]);
|
||||||
// Serial.println(RgbToHsv(unpackRgb(0x550008)).v);
|
// Serial.println(RgbToHsv(unpackRgb(0x550008)).v);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
leds.setPixelColor(110, rawOtherColors[2]);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 80; i <= 109; i++)
|
for (int i = 80; i <= 109; i++)
|
||||||
{
|
{
|
||||||
if (leds.getPixelColor(i) == 0 && leds.getPixelColor(i) != rawOtherColors[0])
|
|
||||||
|
if (leds.getPixelColor(i) == 0)// || (rotaryEncoderMode == 0 && leds.getPixelColor(i) == slotSelectionColors[8]) )
|
||||||
{
|
{
|
||||||
leds.setPixelColor(i, rawOtherColors[0]);
|
leds.setPixelColor(i, rawOtherColors[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (rotaryEncoderMode == 1)
|
||||||
|
{
|
||||||
|
if (i == slotLEDpositions[netSlot])
|
||||||
|
{
|
||||||
|
// continue;
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[4]);
|
||||||
|
}
|
||||||
|
if (i == rotaryEncoderPositions[0])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[8]);
|
||||||
|
}
|
||||||
|
if (i == rotaryEncoderPositions[1])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[7 + encoderIsPressed]);
|
||||||
|
}
|
||||||
|
if (i == rotaryEncoderPositions[2])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[6 - rotState]);
|
||||||
|
}
|
||||||
|
if (i == rotaryEncoderPositions[3])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[8]);
|
||||||
|
}
|
||||||
|
if (i == rotaryEncoderPositions[4])
|
||||||
|
{
|
||||||
|
leds.setPixelColor(i, slotSelectionColors[5 + rotState]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// int rotaryEncoderPositions[6]= {
|
||||||
|
// 97, //AREF
|
||||||
|
// 95, //D13 (button)
|
||||||
|
// 94, //D12 (encoder A)
|
||||||
|
// 93, //D11 (encoder GND)
|
||||||
|
// 92, //D10 (encoder B)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// uint32_t slotSelectionColors[10] = {
|
||||||
|
// 0x084080, //preview
|
||||||
|
// 0x005555, //active
|
||||||
|
// 0x102000, //inactive
|
||||||
|
// 0x000000, //off
|
||||||
|
// 0x881261, //preview+active
|
||||||
|
|
||||||
|
// 0x334400, //rotary encoder High
|
||||||
|
// 0x000066, //rotary encoder Low
|
||||||
|
|
||||||
|
// 0x770011, //button High
|
||||||
|
// 0x004423, //button Low
|
||||||
|
|
||||||
|
// };
|
||||||
|
|
||||||
leds.setPixelColor(83, scaleDownBrightness(rawSpecialNetColors[1]));
|
leds.setPixelColor(83, scaleDownBrightness(rawSpecialNetColors[1]));
|
||||||
leds.setPixelColor(108, scaleDownBrightness(rawSpecialNetColors[1]));
|
leds.setPixelColor(108, scaleDownBrightness(rawSpecialNetColors[1]));
|
||||||
@ -1274,7 +1490,8 @@ void showNets(void)
|
|||||||
|
|
||||||
lightUpNet(i);
|
lightUpNet(i);
|
||||||
}
|
}
|
||||||
showLEDsCore2 = 1;
|
// saveRawColors();
|
||||||
|
// showLEDsCore2 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rgbColor HsvToRgb(hsvColor hsv)
|
rgbColor HsvToRgb(hsvColor hsv)
|
||||||
@ -1382,10 +1599,10 @@ void randomColors(uint32_t color, int wait)
|
|||||||
{
|
{
|
||||||
|
|
||||||
count = random(0, 8);
|
count = random(0, 8);
|
||||||
if (i > 80)
|
// if (i > 80)
|
||||||
{
|
// {
|
||||||
count = random(0, 22);
|
// count = random(0, 22);
|
||||||
}
|
// }
|
||||||
|
|
||||||
byte colorValR = random(60, 0x3f);
|
byte colorValR = random(60, 0x3f);
|
||||||
byte colorValG = random(60, 0x30);
|
byte colorValG = random(60, 0x30);
|
||||||
@ -1419,7 +1636,7 @@ void randomColors(uint32_t color, int wait)
|
|||||||
// color = color | (color >> 1);
|
// color = color | (color >> 1);
|
||||||
|
|
||||||
leds.setPixelColor(i, color); // Set pixel's color (in RAM)
|
leds.setPixelColor(i, color); // Set pixel's color (in RAM)
|
||||||
lightUpRail(-1, -1, 1, LEDbrightnessRail);
|
// lightUpRail(-1, -1, 1, LEDbrightnessRail);
|
||||||
showLEDsCore2 = 2; // Update strip to match
|
showLEDsCore2 = 2; // Update strip to match
|
||||||
// Pause for a moment
|
// Pause for a moment
|
||||||
}
|
}
|
||||||
@ -1570,7 +1787,7 @@ void rainbowBounce(int wait)
|
|||||||
leds.setPixelColor(i, rgbPacked);
|
leds.setPixelColor(i, rgbPacked);
|
||||||
}
|
}
|
||||||
|
|
||||||
showLEDsCore2 = 2;
|
showLEDsCore2 = 3;
|
||||||
delayMicroseconds((wait * 1000) * ((j / 20.0)));
|
delayMicroseconds((wait * 1000) * ((j / 20.0)));
|
||||||
}
|
}
|
||||||
for (long j = 40; j >= 0; j -= 1)
|
for (long j = 40; j >= 0; j -= 1)
|
||||||
@ -1594,7 +1811,7 @@ void rainbowBounce(int wait)
|
|||||||
leds.setPixelColor(i, rgbPacked);
|
leds.setPixelColor(i, rgbPacked);
|
||||||
}
|
}
|
||||||
|
|
||||||
showLEDsCore2 = 2;
|
showLEDsCore2 = 3;
|
||||||
delayMicroseconds((wait * 1000) * ((j / 20.0)));
|
delayMicroseconds((wait * 1000) * ((j / 20.0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "JumperlessDefinesRP2040.h"
|
#include "JumperlessDefinesRP2040.h"
|
||||||
#include "Adafruit_NeoPixel.h"
|
#include "Adafruit_NeoPixel.h"
|
||||||
#include "NetsToChipConnections.h"
|
#include "NetsToChipConnections.h"
|
||||||
|
#include "RotaryEncoder.h"
|
||||||
|
|
||||||
#define LED_PIN 25 //change this to 0 if you want to run this on a Pico to control the lights on a Jumperlux
|
#define LED_PIN 25 //change this to 0 if you want to run this on a Pico to control the lights on a Jumperlux
|
||||||
#define LED_COUNT 111
|
#define LED_COUNT 111
|
||||||
@ -110,17 +111,24 @@ const int railsToPixelMap[4][5] = {{70,73,74,77,78},//top positive rail
|
|||||||
const int pixelsToRails[20] = {B_RAIL_NEG, B_RAIL_POS, B_RAIL_POS, B_RAIL_NEG, B_RAIL_NEG, B_RAIL_POS, B_RAIL_POS, B_RAIL_NEG, B_RAIL_NEG, B_RAIL_POS,
|
const int pixelsToRails[20] = {B_RAIL_NEG, B_RAIL_POS, B_RAIL_POS, B_RAIL_NEG, B_RAIL_NEG, B_RAIL_POS, B_RAIL_POS, B_RAIL_NEG, B_RAIL_NEG, B_RAIL_POS,
|
||||||
T_RAIL_POS, T_RAIL_NEG, T_RAIL_NEG, T_RAIL_POS, T_RAIL_POS, T_RAIL_NEG, T_RAIL_NEG, T_RAIL_POS, T_RAIL_POS, T_RAIL_NEG};
|
T_RAIL_POS, T_RAIL_NEG, T_RAIL_NEG, T_RAIL_POS, T_RAIL_POS, T_RAIL_NEG, T_RAIL_NEG, T_RAIL_POS, T_RAIL_POS, T_RAIL_NEG};
|
||||||
|
|
||||||
|
extern uint32_t savedLEDcolors[NUM_SLOTS][LED_COUNT];
|
||||||
|
|
||||||
|
|
||||||
extern rgbColor netColors[MAX_NETS];
|
extern rgbColor netColors[MAX_NETS];
|
||||||
struct rgbColor shiftHue (struct rgbColor colorToShift, int hueShift = 0, int brightnessShift = 0, int saturationShift = 0,int specialFunction = -1);
|
struct rgbColor shiftHue (struct rgbColor colorToShift, int hueShift = 0, int brightnessShift = 0, int saturationShift = 0,int specialFunction = -1);
|
||||||
void initLEDs(void);
|
void initLEDs(void);
|
||||||
char LEDbrightnessMenu(void);
|
char LEDbrightnessMenu(void);
|
||||||
|
|
||||||
|
void refreshSavedColors(void);
|
||||||
|
void saveRawColors(int slot = -1);
|
||||||
|
void showSavedColors(int slot = -1);
|
||||||
|
|
||||||
void clearLEDs(void);
|
void clearLEDs(void);
|
||||||
void randomColors(uint32_t color, int wait);
|
void randomColors(uint32_t color, int wait);
|
||||||
void rainbowy(int ,int, int wait);
|
void rainbowy(int ,int, int wait);
|
||||||
void showNets(void);
|
void showNets(void);
|
||||||
void assignNetColors ();
|
void assignNetColors ();
|
||||||
void lightUpRail (int logo = -1, int railNumber = -1, int onOff = 1, int brightness = DEFAULTRAILBRIGHTNESS, int supplySwitchPosition= 1);
|
void lightUpRail (int logo = -1, int railNumber = -1, int onOff = 1, int brightness = DEFAULTRAILBRIGHTNESS, int supplySwitchPosition= 0);
|
||||||
|
|
||||||
void lightUpNet (int netNumber = 0 , int node = -1, int onOff = 1, int brightness = DEFAULTBRIGHTNESS, int hueShift = 0);//-1 means all nodes (default)
|
void lightUpNet (int netNumber = 0 , int node = -1, int onOff = 1, int brightness = DEFAULTBRIGHTNESS, int hueShift = 0);//-1 means all nodes (default)
|
||||||
void lightUpNode (int node, uint32_t color);
|
void lightUpNode (int node, uint32_t color);
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
#include "MachineCommands.h"
|
#include "MachineCommands.h"
|
||||||
|
|
||||||
bool debugMM = false;
|
bool debugMM = true;
|
||||||
// char inputBuffer[INPUTBUFFERLENGTH] = {0};
|
// char inputBuffer[INPUTBUFFERLENGTH] = {0};
|
||||||
|
|
||||||
File nodeFileMachineMode;
|
File nodeFileMachineMode;
|
||||||
|
@ -174,6 +174,10 @@ void clearAllNTCC(void)
|
|||||||
|
|
||||||
void sortPathsByNet(void) // not actually sorting, just copying the bridges and nets back from netStruct so they're both in the same order
|
void sortPathsByNet(void) // not actually sorting, just copying the bridges and nets back from netStruct so they're both in the same order
|
||||||
{
|
{
|
||||||
|
if (debugNTCC)
|
||||||
|
{
|
||||||
|
Serial.println("sortPathsByNet()");
|
||||||
|
}
|
||||||
timeToSort = micros();
|
timeToSort = micros();
|
||||||
numberOfPaths = 0;
|
numberOfPaths = 0;
|
||||||
pathIndex = 0;
|
pathIndex = 0;
|
||||||
@ -283,6 +287,10 @@ void sortPathsByNet(void) // not actually sorting, just copying the bridges and
|
|||||||
|
|
||||||
void bridgesToPaths(void)
|
void bridgesToPaths(void)
|
||||||
{
|
{
|
||||||
|
if (debugNTCC)
|
||||||
|
{
|
||||||
|
Serial.println("bridgesToPaths()");
|
||||||
|
}
|
||||||
sortPathsByNet();
|
sortPathsByNet();
|
||||||
for (int i = 0; i < numberOfPaths; i++)
|
for (int i = 0; i < numberOfPaths; i++)
|
||||||
{
|
{
|
||||||
@ -339,11 +347,15 @@ void bridgesToPaths(void)
|
|||||||
|
|
||||||
void commitPaths(void)
|
void commitPaths(void)
|
||||||
{
|
{
|
||||||
|
if (debugNTCC2)
|
||||||
|
{
|
||||||
|
Serial.println("commitPaths()\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < numberOfPaths; i++)
|
for (int i = 0; i < numberOfPaths; i++)
|
||||||
{
|
{
|
||||||
duplicateSFnets();
|
duplicateSFnets();
|
||||||
|
|
||||||
// Serial.print(i);
|
// Serial.print(i);
|
||||||
// Serial.print(" \t");
|
// Serial.print(" \t");
|
||||||
|
|
||||||
@ -687,10 +699,11 @@ void commitPaths(void)
|
|||||||
path[i].y[1] = -2;
|
path[i].y[1] = -2;
|
||||||
|
|
||||||
path[i].sameChip = true;
|
path[i].sameChip = true;
|
||||||
if (false)
|
//Serial.print(" ?????????");
|
||||||
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
|
|
||||||
Serial.print(" \t\n\rchip[0]: ");
|
Serial.print(" \t\t\tchip[0]: ");
|
||||||
Serial.print(chipNumToChar(path[i].chip[0]));
|
Serial.print(chipNumToChar(path[i].chip[0]));
|
||||||
|
|
||||||
Serial.print(" x[0]: ");
|
Serial.print(" x[0]: ");
|
||||||
@ -727,6 +740,8 @@ void commitPaths(void)
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
duplicateSFnets();
|
duplicateSFnets();
|
||||||
|
// printPathsCompact();
|
||||||
|
// printChipStatus();
|
||||||
resolveAltPaths();
|
resolveAltPaths();
|
||||||
|
|
||||||
// duplicateSFnets();
|
// duplicateSFnets();
|
||||||
@ -734,6 +749,10 @@ void commitPaths(void)
|
|||||||
|
|
||||||
void duplicateSFnets(void)
|
void duplicateSFnets(void)
|
||||||
{
|
{
|
||||||
|
// if (debugNTCC2)
|
||||||
|
// {
|
||||||
|
// Serial.println("duplicateSFnets()");
|
||||||
|
// }
|
||||||
for (int i = 0; i < 26; i++)
|
for (int i = 0; i < 26; i++)
|
||||||
{
|
{
|
||||||
if (ch[duplucateSFnodes[i][0]].xStatus[duplucateSFnodes[i][1]] > 0)
|
if (ch[duplucateSFnodes[i][0]].xStatus[duplucateSFnodes[i][1]] > 0)
|
||||||
@ -756,6 +775,10 @@ void duplicateSFnets(void)
|
|||||||
|
|
||||||
void resolveAltPaths(void)
|
void resolveAltPaths(void)
|
||||||
{
|
{
|
||||||
|
if (debugNTCC2)
|
||||||
|
{
|
||||||
|
Serial.println("resolveAltPaths()");
|
||||||
|
}
|
||||||
int couldFindPath = -1;
|
int couldFindPath = -1;
|
||||||
|
|
||||||
for (int i = 0; i <= numberOfPaths; i++)
|
for (int i = 0; i <= numberOfPaths; i++)
|
||||||
@ -1122,6 +1145,8 @@ void resolveAltPaths(void)
|
|||||||
|
|
||||||
if (path[i].altPathNeeded == true)
|
if (path[i].altPathNeeded == true)
|
||||||
{
|
{
|
||||||
|
// Serial.print("PATH: ");
|
||||||
|
// Serial.print(i);
|
||||||
|
|
||||||
switch (path[i].pathType)
|
switch (path[i].pathType)
|
||||||
{
|
{
|
||||||
@ -1253,7 +1278,7 @@ void resolveAltPaths(void)
|
|||||||
for (int chipFreeY = 0; chipFreeY < 8; chipFreeY++)
|
for (int chipFreeY = 0; chipFreeY < 8; chipFreeY++)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (debugNTCC3)
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
Serial.print("\n\r");
|
Serial.print("\n\r");
|
||||||
Serial.print("path: ");
|
Serial.print("path: ");
|
||||||
@ -1593,6 +1618,7 @@ void resolveAltPaths(void)
|
|||||||
case NANOtoSF:
|
case NANOtoSF:
|
||||||
case NANOtoNANO:
|
case NANOtoNANO:
|
||||||
{
|
{
|
||||||
|
//debugNTCC2 = true;
|
||||||
if (debugNTCC2)
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
Serial.println(" NANOtoNANO");
|
Serial.println(" NANOtoNANO");
|
||||||
@ -1613,7 +1639,7 @@ void resolveAltPaths(void)
|
|||||||
|
|
||||||
if (debugNTCC2)
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
Serial.println("\n\n\rL to Special Function via ADCs\n\r");
|
// Serial.println("\n\n\rL to Special Function via ADCs\n\r");
|
||||||
}
|
}
|
||||||
int whichIsL = 0;
|
int whichIsL = 0;
|
||||||
int whichIsSF = 1;
|
int whichIsSF = 1;
|
||||||
@ -1641,8 +1667,8 @@ void resolveAltPaths(void)
|
|||||||
Serial.println(" ");
|
Serial.println(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch[CHIP_L].xStatus[whichADC + 2] != -1 && ch[CHIP_L].xStatus[whichADC + 2] != path[i].net)
|
//if (ch[CHIP_L].xStatus[whichADC + 2] != -1 && ch[CHIP_L].xStatus[whichADC + 2] != path[i].net)
|
||||||
//if (true)
|
if (true)
|
||||||
{
|
{
|
||||||
if (debugNTCC2)
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
@ -1661,11 +1687,16 @@ void resolveAltPaths(void)
|
|||||||
// break;
|
// break;
|
||||||
// continue;
|
// continue;
|
||||||
// int foundHop = 0;
|
// int foundHop = 0;
|
||||||
|
|
||||||
|
// Serial.println ("\t\t\tt\t\t\t\t\t\t\tfsglksjggskdlf;gjs");
|
||||||
for (int hopBB = 0; hopBB < 8; hopBB++)
|
for (int hopBB = 0; hopBB < 8; hopBB++)
|
||||||
{
|
{
|
||||||
if (debugNTCC2)
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
Serial.print("\n\r");
|
Serial.print("\n\r");
|
||||||
|
Serial.print("Path: ");
|
||||||
|
Serial.print(i);
|
||||||
|
Serial.println(" \n\r ");
|
||||||
Serial.print("hopBB: ");
|
Serial.print("hopBB: ");
|
||||||
Serial.println(hopBB);
|
Serial.println(hopBB);
|
||||||
Serial.print("chip[0]: ");
|
Serial.print("chip[0]: ");
|
||||||
@ -1749,6 +1780,8 @@ void resolveAltPaths(void)
|
|||||||
Serial.println(xMapForNode(path[i].node1, sfChip1));
|
Serial.println(xMapForNode(path[i].node1, sfChip1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// printChipStatus();
|
||||||
|
// printPathsCompact();
|
||||||
|
|
||||||
// if (whichIsL == 1)
|
// if (whichIsL == 1)
|
||||||
// {
|
// {
|
||||||
@ -1770,7 +1803,7 @@ void resolveAltPaths(void)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Serial.print("\n\r\t\t\t\t\tfuck ");
|
// Serial.print("\n\r\t\t\t\t\tfuck ");
|
||||||
// Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
// Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||||
path[i].x[whichIsL] = whichADC + 2;
|
path[i].x[whichIsL] = whichADC + 2;
|
||||||
ch[CHIP_L].xStatus[whichADC + 2] = path[i].net;
|
ch[CHIP_L].xStatus[whichADC + 2] = path[i].net;
|
||||||
path[i].y[whichIsL] = -2;
|
path[i].y[whichIsL] = -2;
|
||||||
@ -1939,6 +1972,7 @@ void resolveAltPaths(void)
|
|||||||
}
|
}
|
||||||
// printChipStatus();
|
// printChipStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2006,10 +2040,11 @@ void resolveAltPaths(void)
|
|||||||
|
|
||||||
for (int bb = 0; bb < 8; bb++) // this is a long winded way to do this but it's at least slightly readable
|
for (int bb = 0; bb < 8; bb++) // this is a long winded way to do this but it's at least slightly readable
|
||||||
{
|
{
|
||||||
// Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
|
||||||
int sfChip1 = path[i].chip[0];
|
int sfChip1 = path[i].chip[0];
|
||||||
int sfChip2 = path[i].chip[1];
|
int sfChip2 = path[i].chip[1];
|
||||||
// Serial.print("path: ");
|
//Serial.print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||||
|
// Serial.print("\tpath: ");
|
||||||
// Serial.println(i);
|
// Serial.println(i);
|
||||||
|
|
||||||
int chip1Lane = xMapForNode(sfChip1, bb);
|
int chip1Lane = xMapForNode(sfChip1, bb);
|
||||||
@ -2036,13 +2071,21 @@ void resolveAltPaths(void)
|
|||||||
|
|
||||||
if (ch[bb].xStatus[chip1Lane] == path[i].net || ch[bb].xStatus[chip1Lane] == -1)
|
if (ch[bb].xStatus[chip1Lane] == path[i].net || ch[bb].xStatus[chip1Lane] == -1)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (ch[bb].xStatus[chip2Lane] == path[i].net || ch[bb].xStatus[chip2Lane] == -1)
|
if (ch[bb].xStatus[chip2Lane] == path[i].net || ch[bb].xStatus[chip2Lane] == -1)
|
||||||
{
|
{
|
||||||
|
// Serial.println("VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV");
|
||||||
|
// Serial.print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||||
|
// Serial.print("\tpath: ");
|
||||||
|
// Serial.println(i);
|
||||||
// Serial.print("bb:\t");
|
// Serial.print("bb:\t");
|
||||||
// Serial.println(bb);
|
// Serial.print(bb);
|
||||||
|
|
||||||
// printPathsCompact();
|
// printPathsCompact();
|
||||||
// printChipStatus();
|
// printChipStatus();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (giveUpOnL == 1)
|
if (giveUpOnL == 1)
|
||||||
{
|
{
|
||||||
if (debugNTCC2)
|
if (debugNTCC2)
|
||||||
@ -2073,10 +2116,17 @@ void resolveAltPaths(void)
|
|||||||
|
|
||||||
path[i].x[0] = xMapForNode(path[i].node1, path[i].chip[0]);
|
path[i].x[0] = xMapForNode(path[i].node1, path[i].chip[0]);
|
||||||
path[i].x[1] = xMapForNode(path[i].node2, path[i].chip[1]);
|
path[i].x[1] = xMapForNode(path[i].node2, path[i].chip[1]);
|
||||||
|
ch[path[i].chip[0]].xStatus[xMapForNode(path[i].node1, path[i].chip[0])] = path[i].net;
|
||||||
|
ch[path[i].chip[1]].xStatus[xMapForNode(path[i].node2, path[i].chip[1])] = path[i].net;
|
||||||
|
|
||||||
path[i].y[0] = bb;
|
path[i].y[0] = bb;
|
||||||
path[i].y[1] = bb;
|
path[i].y[1] = bb;
|
||||||
|
|
||||||
|
// Serial.print(">>>> path ");
|
||||||
|
// Serial.println(i);
|
||||||
|
ch[path[i].chip[0]].yStatus[bb] = path[i].net;
|
||||||
|
ch[path[i].chip[1]].yStatus[bb] = path[i].net;
|
||||||
|
|
||||||
if (debugNTCC2)
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
Serial.print("\n\r");
|
Serial.print("\n\r");
|
||||||
@ -2094,8 +2144,13 @@ void resolveAltPaths(void)
|
|||||||
}
|
}
|
||||||
foundHop = 1;
|
foundHop = 1;
|
||||||
couldFindPath = i;
|
couldFindPath = i;
|
||||||
// printPathsCompact();
|
|
||||||
// printChipStatus();
|
|
||||||
|
// Serial.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||||
|
// printPathsCompact();
|
||||||
|
// printChipStatus();
|
||||||
|
|
||||||
|
// Serial.print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2144,9 +2199,15 @@ void resolveAltPaths(void)
|
|||||||
|
|
||||||
path[i].x[0] = xMapForNode(path[i].node1, path[i].chip[0]);
|
path[i].x[0] = xMapForNode(path[i].node1, path[i].chip[0]);
|
||||||
path[i].x[1] = xMapForNode(path[i].node2, path[i].chip[1]);
|
path[i].x[1] = xMapForNode(path[i].node2, path[i].chip[1]);
|
||||||
|
ch[path[i].chip[0]].xStatus[xMapForNode(path[i].node1, path[i].chip[0])] = path[i].net;
|
||||||
|
ch[path[i].chip[1]].xStatus[xMapForNode(path[i].node2, path[i].chip[1])] = path[i].net;
|
||||||
|
// Serial.print(">>>> path ");
|
||||||
|
// Serial.println(i);
|
||||||
|
|
||||||
path[i].y[0] = bb;
|
path[i].y[0] = bb;
|
||||||
path[i].y[1] = bb;
|
path[i].y[1] = bb;
|
||||||
|
ch[path[i].chip[0]].yStatus[bb] = path[i].net;
|
||||||
|
ch[path[i].chip[1]].yStatus[bb] = path[i].net;
|
||||||
|
|
||||||
if (debugNTCC2)
|
if (debugNTCC2)
|
||||||
{
|
{
|
||||||
@ -2241,6 +2302,12 @@ void resolveUncommittedHops2(void)
|
|||||||
|
|
||||||
void resolveUncommittedHops(void)
|
void resolveUncommittedHops(void)
|
||||||
{
|
{
|
||||||
|
if (debugNTCC2)
|
||||||
|
{
|
||||||
|
Serial.print("\n\r");
|
||||||
|
Serial.print("resolveUncommittedHops()");
|
||||||
|
Serial.print("\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
/// return;
|
/// return;
|
||||||
|
|
||||||
@ -2672,27 +2739,29 @@ void resolveUncommittedHops(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial.print("\n\rpath: ");
|
|
||||||
// Serial.print(i);
|
|
||||||
// Serial.print("\n\rchip: ");
|
|
||||||
// Serial.print(chip);
|
|
||||||
// Serial.print("\n\rfreeYsearch: ");
|
|
||||||
// Serial.print(freeYsearch);
|
|
||||||
// Serial.print("\n\rstatus: ");
|
|
||||||
// Serial.print(ch[sameChips[1][chip]].yStatus[freeYsearch]);
|
|
||||||
// Serial.println(" \n\n\r");
|
|
||||||
|
|
||||||
if (ch[sameChips[1][chip]].yStatus[freeYsearch] == -1 || ch[sameChips[1][chip]].yStatus[freeYsearch] == path[i].net)
|
if (ch[sameChips[1][chip]].yStatus[freeYsearch] == -1 || ch[sameChips[1][chip]].yStatus[freeYsearch] == path[i].net)
|
||||||
{
|
{
|
||||||
|
// Serial.print("path: ");
|
||||||
|
// Serial.print(i);
|
||||||
|
// Serial.print("\n\rchip: ");
|
||||||
|
// printChipNumToChar(chip);
|
||||||
|
// Serial.print("\n\rfreeYsearch: ");
|
||||||
|
// Serial.print(freeYsearch);
|
||||||
|
// Serial.print("\n\rstatus: ");
|
||||||
|
// Serial.print(ch[sameChips[1][chip]].yStatus[freeYsearch]);
|
||||||
|
// Serial.println(" \n\n\r");
|
||||||
|
|
||||||
|
|
||||||
freeY = freeYsearch;
|
freeY = freeYsearch;
|
||||||
// Serial.print("freeY: ");
|
// Serial.print("freeY: ");
|
||||||
// Serial.println(freeY);
|
// Serial.println(freeY);
|
||||||
|
|
||||||
if (path[i].y[chip] == -2)
|
if (path[i].y[chip] == -2)
|
||||||
{
|
{
|
||||||
// Serial.print("\n\n\rfreeY After search : ");
|
// Serial.print("\n\n\rfreeY After search : ");
|
||||||
// Serial.print(freeY);
|
// Serial.print(freeY);
|
||||||
|
|
||||||
if (chip == 2)
|
if (chip == 2)
|
||||||
{
|
{
|
||||||
@ -2712,6 +2781,9 @@ void resolveUncommittedHops(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Serial.print("path[i].y[chip]: ");
|
||||||
|
// Serial.print(path[i].y[chip]);
|
||||||
|
// Serial.println("!!!!!!!!!!!!!!!!!!");
|
||||||
path[i].y[chip] = freeY;
|
path[i].y[chip] = freeY;
|
||||||
ch[sameChips[1][chip]].yStatus[freeY] = path[i].net;
|
ch[sameChips[1][chip]].yStatus[freeY] = path[i].net;
|
||||||
}
|
}
|
||||||
@ -2731,6 +2803,22 @@ void resolveUncommittedHops(void)
|
|||||||
/// Serial.println(" \r\n");
|
/// Serial.println(" \r\n");
|
||||||
// break;
|
// break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Serial.print("\n\rpath: ");
|
||||||
|
// Serial.print(i);
|
||||||
|
// Serial.print("\n\rchip: ");
|
||||||
|
// Serial.print(chip);
|
||||||
|
// Serial.print("\n\rfreeYsearch: ");
|
||||||
|
// Serial.print(freeYsearch);
|
||||||
|
// Serial.print("\n\rstatus: ");
|
||||||
|
// Serial.print(ch[sameChips[1][chip]].yStatus[freeYsearch]);
|
||||||
|
// Serial.println(" \n\n\r");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2903,6 +2991,12 @@ void printChipStatus(void)
|
|||||||
|
|
||||||
void findStartAndEndChips(int node1, int node2, int pathIdx)
|
void findStartAndEndChips(int node1, int node2, int pathIdx)
|
||||||
{
|
{
|
||||||
|
if (debugNTCC2)
|
||||||
|
{
|
||||||
|
Serial.print("findStartAndEndChips()\t ");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
bothNodes[0] = node1;
|
bothNodes[0] = node1;
|
||||||
bothNodes[1] = node2;
|
bothNodes[1] = node2;
|
||||||
startEndChip[0] = -1;
|
startEndChip[0] = -1;
|
||||||
|
@ -14,19 +14,20 @@
|
|||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "hardware/pwm.h"
|
#include "hardware/pwm.h"
|
||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
|
#include "RotaryEncoder.h"
|
||||||
|
|
||||||
#include <Adafruit_GFX.h>
|
#include <Adafruit_GFX.h>
|
||||||
#include <Adafruit_SSD1306.h>
|
#include <Adafruit_SSD1306.h>
|
||||||
|
|
||||||
|
#define OLED_CONNECTED 0
|
||||||
|
|
||||||
#define SCREEN_WIDTH 128 // OLED display width, in pixels
|
#define SCREEN_WIDTH 128 // OLED display width, in pixels
|
||||||
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
|
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
|
||||||
|
|
||||||
|
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
|
|
||||||
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
|
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
|
||||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
|
||||||
|
|
||||||
|
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
|
||||||
int probeSwap = 0;
|
int probeSwap = 0;
|
||||||
int probeHalfPeriodus = 20;
|
int probeHalfPeriodus = 20;
|
||||||
@ -60,6 +61,9 @@ int justSelectedConnectedNodes = 0;
|
|||||||
int voltageSelection = SUPPLY_3V3;
|
int voltageSelection = SUPPLY_3V3;
|
||||||
int voltageChosen = 0;
|
int voltageChosen = 0;
|
||||||
|
|
||||||
|
int wasRotaryMode = 0;
|
||||||
|
|
||||||
|
|
||||||
int rainbowList[13][3] = {
|
int rainbowList[13][3] = {
|
||||||
{40, 50, 80},
|
{40, 50, 80},
|
||||||
{88, 33, 70},
|
{88, 33, 70},
|
||||||
@ -82,65 +86,80 @@ int justCleared = 1;
|
|||||||
|
|
||||||
char oledBuffer[32] = " ";
|
char oledBuffer[32] = " ";
|
||||||
|
|
||||||
void drawchar(void) {
|
void drawchar(void)
|
||||||
display.clearDisplay();
|
|
||||||
if (isSpace(oledBuffer[7]) == true )
|
|
||||||
{
|
{
|
||||||
display.setTextSize(3); // Normal 1:1 pixel scale
|
|
||||||
display.setCursor(0, 5); // Start at top-left corner
|
if (OLED_CONNECTED == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
display.clearDisplay();
|
||||||
|
if (isSpace(oledBuffer[7]) == true)
|
||||||
|
{
|
||||||
|
display.setTextSize(3); // Normal 1:1 pixel scale
|
||||||
|
display.setCursor(0, 5); // Start at top-left corner
|
||||||
|
}
|
||||||
|
else if (isSpace(oledBuffer[10]) == true && isSpace(oledBuffer[11]) == true)
|
||||||
|
{
|
||||||
|
display.setTextSize(2); // Normal 1:1 pixel scale
|
||||||
|
display.setCursor(0, 9); // Start at top-left corner
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
display.setTextSize(1); // Normal 1:1 pixel scale
|
||||||
|
display.setCursor(0, 0); // Start at top-left corner
|
||||||
|
}
|
||||||
|
|
||||||
|
display.setTextColor(SSD1306_WHITE); // Draw white text
|
||||||
|
|
||||||
|
display.cp437(true); // Use full 256 char 'Code Page 437' font
|
||||||
|
|
||||||
|
// Not all the characters will fit on the display. This is normal.
|
||||||
|
// Library will draw what it can and the rest will be clipped.
|
||||||
|
display.write(oledBuffer);
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
oledBuffer[i] = ' ';
|
||||||
|
}
|
||||||
|
/// delay(2000);
|
||||||
}
|
}
|
||||||
else if (isSpace(oledBuffer[10]) == true && isSpace(oledBuffer[11]) == true)
|
|
||||||
{
|
|
||||||
display.setTextSize(2); // Normal 1:1 pixel scale
|
|
||||||
display.setCursor(0, 9); // Start at top-left corner
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
display.setTextSize(1); // Normal 1:1 pixel scale
|
|
||||||
display.setCursor(0, 0); // Start at top-left corner
|
|
||||||
}
|
|
||||||
|
|
||||||
display.setTextColor(SSD1306_WHITE); // Draw white text
|
|
||||||
|
|
||||||
display.cp437(true); // Use full 256 char 'Code Page 437' font
|
|
||||||
|
|
||||||
// Not all the characters will fit on the display. This is normal.
|
|
||||||
// Library will draw what it can and the rest will be clipped.
|
|
||||||
display.write(oledBuffer);
|
|
||||||
|
|
||||||
display.display();
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
oledBuffer[i] = ' ';
|
|
||||||
}
|
|
||||||
///delay(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int probeMode(int pin, int setOrClear)
|
int probeMode(int pin, int setOrClear)
|
||||||
{
|
{
|
||||||
|
// wasRotaryMode = rotaryEncoderMode;
|
||||||
|
// rotaryEncoderMode = 0;
|
||||||
|
if (OLED_CONNECTED == 1)
|
||||||
|
{
|
||||||
|
|
||||||
display.display();
|
Serial.print("\n\r\t Probing mode\n\n\r");
|
||||||
display.clearDisplay();
|
|
||||||
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
|
||||||
Serial.println(F("SSD1306 allocation failed"));
|
|
||||||
for(;;); // Don't proceed, loop forever
|
|
||||||
}
|
|
||||||
if (setOrClear == 1)
|
|
||||||
{
|
|
||||||
sprintf(oledBuffer, "connect ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sprintf(oledBuffer, "clear");
|
|
||||||
}
|
|
||||||
|
|
||||||
drawchar();
|
display.display();
|
||||||
|
display.clearDisplay();
|
||||||
|
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
|
||||||
|
{
|
||||||
|
// Serial.println("SSD1306 allocation failed");
|
||||||
|
// for(;;); // Don't proceed, loop forever
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Serial.println("SSD1306 allocation success");
|
||||||
|
}
|
||||||
|
if (setOrClear == 1)
|
||||||
|
{
|
||||||
|
sprintf(oledBuffer, "connect ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf(oledBuffer, "clear");
|
||||||
|
}
|
||||||
|
|
||||||
|
drawchar();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("\n\r\t Probing mode\n\n\r");
|
||||||
probeSwap = EEPROM.read(PROBESWAPADDRESS);
|
probeSwap = EEPROM.read(PROBESWAPADDRESS);
|
||||||
|
|
||||||
if (probeSwap == 0)
|
if (probeSwap == 0)
|
||||||
@ -154,6 +173,7 @@ int probeMode(int pin, int setOrClear)
|
|||||||
buttonPin = 19;
|
buttonPin = 19;
|
||||||
}
|
}
|
||||||
restartProbing:
|
restartProbing:
|
||||||
|
probeActive = 1;
|
||||||
int lastRow[10];
|
int lastRow[10];
|
||||||
|
|
||||||
int pokedNumber = 0;
|
int pokedNumber = 0;
|
||||||
@ -162,18 +182,19 @@ restartProbing:
|
|||||||
|
|
||||||
if (numberOfNets == 0)
|
if (numberOfNets == 0)
|
||||||
{
|
{
|
||||||
clearNodeFile();
|
// clearNodeFile(netSlot);
|
||||||
}
|
}
|
||||||
clearAllNTCC();
|
clearAllNTCC();
|
||||||
openNodeFile();
|
openNodeFile(netSlot);
|
||||||
getNodesToConnect();
|
getNodesToConnect();
|
||||||
|
|
||||||
bridgesToPaths();
|
bridgesToPaths();
|
||||||
// clearLEDs();
|
clearLEDs();
|
||||||
assignNetColors();
|
assignNetColors();
|
||||||
delay(18);
|
showNets();
|
||||||
|
//delay(18);
|
||||||
showLEDsCore2 = 1;
|
showLEDsCore2 = 1;
|
||||||
delay(28);
|
//delay(28);
|
||||||
int probedNodes[40][2];
|
int probedNodes[40][2];
|
||||||
int probedNodesIndex = 0;
|
int probedNodesIndex = 0;
|
||||||
|
|
||||||
@ -327,8 +348,8 @@ restartProbing:
|
|||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
//itoa(nodesToConnect[0], oledBuffer, 10);
|
// itoa(nodesToConnect[0], oledBuffer, 10);
|
||||||
//drawchar();
|
// drawchar();
|
||||||
Serial.print("\r\t");
|
Serial.print("\r\t");
|
||||||
|
|
||||||
if (nodesToConnect[node1or2] == SUPPLY_3V3 || nodesToConnect[node1or2] == SUPPLY_5V && voltageChosen == 0)
|
if (nodesToConnect[node1or2] == SUPPLY_3V3 || nodesToConnect[node1or2] == SUPPLY_5V && voltageChosen == 0)
|
||||||
@ -375,9 +396,6 @@ restartProbing:
|
|||||||
{
|
{
|
||||||
sprintf(oledBuffer, "%s", definesToChar(nodesToConnect[0]));
|
sprintf(oledBuffer, "%s", definesToChar(nodesToConnect[0]));
|
||||||
drawchar();
|
drawchar();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node1or2 >= 2 || (setOrClear == 0 && node1or2 >= 1))
|
if (node1or2 >= 2 || (setOrClear == 0 && node1or2 >= 1))
|
||||||
@ -390,27 +408,26 @@ restartProbing:
|
|||||||
|
|
||||||
if (setOrClear == 1 && (nodesToConnect[0] != nodesToConnect[1]) && nodesToConnect[0] != -1 && nodesToConnect[1] != -1)
|
if (setOrClear == 1 && (nodesToConnect[0] != nodesToConnect[1]) && nodesToConnect[0] != -1 && nodesToConnect[1] != -1)
|
||||||
{
|
{
|
||||||
// char oledBuffer2[32];
|
// char oledBuffer2[32];
|
||||||
//int charsPrinted = 0;
|
// int charsPrinted = 0;
|
||||||
Serial.print("\r \r");
|
Serial.print("\r \r");
|
||||||
// itoa(nodesToConnect[0], oledBuffer, 10);
|
// itoa(nodesToConnect[0], oledBuffer, 10);
|
||||||
printNodeOrName(nodesToConnect[0]);
|
printNodeOrName(nodesToConnect[0]);
|
||||||
Serial.print(" - ");
|
Serial.print(" - ");
|
||||||
printNodeOrName(nodesToConnect[1]);
|
printNodeOrName(nodesToConnect[1]);
|
||||||
printNodeOrName(nodesToConnect[1]);
|
|
||||||
printNodeOrName(nodesToConnect[1]);
|
Serial.print(" \t ");
|
||||||
Serial.print(" \t ");
|
|
||||||
Serial.print("connected\n\r");
|
Serial.print("connected\n\r");
|
||||||
|
|
||||||
char node1Name[12];
|
char node1Name[12];
|
||||||
|
|
||||||
//node1Name = (char)definesToChar(nodesToConnect[0]);
|
// node1Name = (char)definesToChar(nodesToConnect[0]);
|
||||||
|
|
||||||
strcpy(node1Name, definesToChar(nodesToConnect[0]));
|
strcpy(node1Name, definesToChar(nodesToConnect[0]));
|
||||||
|
|
||||||
char node2Name[12];
|
char node2Name[12];
|
||||||
|
|
||||||
//node2Name = (char)definesToChar(nodesToConnect[1]);
|
// node2Name = (char)definesToChar(nodesToConnect[1]);
|
||||||
|
|
||||||
strcpy(node2Name, definesToChar(nodesToConnect[1]));
|
strcpy(node2Name, definesToChar(nodesToConnect[1]));
|
||||||
|
|
||||||
@ -418,21 +435,22 @@ restartProbing:
|
|||||||
|
|
||||||
drawchar();
|
drawchar();
|
||||||
|
|
||||||
addBridgeToNodeFile(nodesToConnect[0], nodesToConnect[1]);
|
addBridgeToNodeFile(nodesToConnect[0], nodesToConnect[1], netSlot);
|
||||||
// rainbowIndex++;
|
// rainbowIndex++;
|
||||||
if (rainbowIndex > 1)
|
if (rainbowIndex > 1)
|
||||||
{
|
{
|
||||||
rainbowIndex = 0;
|
rainbowIndex = 0;
|
||||||
}
|
}
|
||||||
|
showSavedColors(netSlot);
|
||||||
|
|
||||||
clearAllNTCC();
|
// clearAllNTCC();
|
||||||
openNodeFile();
|
// openNodeFile(netSlot);
|
||||||
getNodesToConnect();
|
// getNodesToConnect();
|
||||||
|
|
||||||
bridgesToPaths();
|
// bridgesToPaths();
|
||||||
// clearLEDs();
|
// // clearLEDs();
|
||||||
leds.clear();
|
// leds.clear();
|
||||||
assignNetColors();
|
// assignNetColors();
|
||||||
// Serial.print("bridgesToPaths\n\r");
|
// Serial.print("bridgesToPaths\n\r");
|
||||||
// delay(18);
|
// delay(18);
|
||||||
// showNets();
|
// showNets();
|
||||||
@ -457,24 +475,25 @@ restartProbing:
|
|||||||
Serial.print("\r \r");
|
Serial.print("\r \r");
|
||||||
printNodeOrName(nodesToConnect[0]);
|
printNodeOrName(nodesToConnect[0]);
|
||||||
Serial.print("\t cleared\n\r");
|
Serial.print("\t cleared\n\r");
|
||||||
removeBridgeFromNodeFile(nodesToConnect[0]);
|
removeBridgeFromNodeFile(nodesToConnect[0], -1, netSlot);
|
||||||
leds.setPixelColor(nodesToPixelMap[nodesToConnect[0]], 0, 0, 0);
|
leds.setPixelColor(nodesToPixelMap[nodesToConnect[0]], 0, 0, 0);
|
||||||
|
|
||||||
// leds.setPixelColor(nodesToPixelMap[nodesToConnect[1]], 0, 0, 0);
|
// leds.setPixelColor(nodesToPixelMap[nodesToConnect[1]], 0, 0, 0);
|
||||||
rainbowIndex = 12;
|
rainbowIndex = 12;
|
||||||
// printNodeFile();
|
// printNodeFile();
|
||||||
clearAllNTCC();
|
showSavedColors(netSlot);
|
||||||
openNodeFile();
|
// clearAllNTCC();
|
||||||
getNodesToConnect();
|
// openNodeFile(netSlot);
|
||||||
|
// getNodesToConnect();
|
||||||
|
|
||||||
bridgesToPaths();
|
// bridgesToPaths();
|
||||||
// clearLEDs();
|
// // clearLEDs();
|
||||||
leds.clear();
|
// leds.clear();
|
||||||
assignNetColors();
|
// assignNetColors();
|
||||||
// Serial.print("bridgesToPaths\n\r");
|
// // Serial.print("bridgesToPaths\n\r");
|
||||||
// delay(18);
|
// // delay(18);
|
||||||
// showNets();
|
// // showNets();
|
||||||
showLEDsCore2 = 1;
|
// showLEDsCore2 = 1;
|
||||||
// sendAllPathsCore2 = 1;
|
// sendAllPathsCore2 = 1;
|
||||||
// logoFlash = 1;
|
// logoFlash = 1;
|
||||||
delay(25);
|
delay(25);
|
||||||
@ -561,7 +580,7 @@ restartProbing:
|
|||||||
}
|
}
|
||||||
digitalWrite(RESETPIN, LOW);
|
digitalWrite(RESETPIN, LOW);
|
||||||
clearAllNTCC();
|
clearAllNTCC();
|
||||||
openNodeFile();
|
openNodeFile(netSlot);
|
||||||
getNodesToConnect();
|
getNodesToConnect();
|
||||||
|
|
||||||
bridgesToPaths();
|
bridgesToPaths();
|
||||||
@ -570,8 +589,8 @@ restartProbing:
|
|||||||
assignNetColors();
|
assignNetColors();
|
||||||
// // Serial.print("bridgesToPaths\n\r");
|
// // Serial.print("bridgesToPaths\n\r");
|
||||||
// delay(18);
|
// delay(18);
|
||||||
// // showNets();
|
showNets();
|
||||||
// showLEDsCore2 = 1;
|
showLEDsCore2 = 1;
|
||||||
rawOtherColors[1] = 0x550004;
|
rawOtherColors[1] = 0x550004;
|
||||||
showLEDsCore2 = 1;
|
showLEDsCore2 = 1;
|
||||||
|
|
||||||
@ -581,9 +600,10 @@ restartProbing:
|
|||||||
delay(300);
|
delay(300);
|
||||||
row[1] = -2;
|
row[1] = -2;
|
||||||
|
|
||||||
//sprintf(oledBuffer, " ");
|
// sprintf(oledBuffer, " ");
|
||||||
drawchar();
|
drawchar();
|
||||||
|
|
||||||
|
// rotaryEncoderMode = wasRotaryMode;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,7 +880,7 @@ int checkProbeButton(void)
|
|||||||
{
|
{
|
||||||
int buttonState = 0;
|
int buttonState = 0;
|
||||||
|
|
||||||
pinMode(buttonPin, INPUT);
|
pinMode(buttonPin, INPUT);
|
||||||
startProbe(10000);
|
startProbe(10000);
|
||||||
|
|
||||||
if (readFloatingOrState(buttonPin, 0) == probe)
|
if (readFloatingOrState(buttonPin, 0) == probe)
|
||||||
@ -872,7 +892,7 @@ pinMode(buttonPin, INPUT);
|
|||||||
buttonState = 0;
|
buttonState = 0;
|
||||||
}
|
}
|
||||||
stopProbe();
|
stopProbe();
|
||||||
pinMode(buttonPin, OUTPUT );
|
pinMode(buttonPin, OUTPUT);
|
||||||
digitalWrite(buttonPin, LOW);
|
digitalWrite(buttonPin, LOW);
|
||||||
|
|
||||||
return buttonState;
|
return buttonState;
|
||||||
|
@ -10,6 +10,7 @@ extern int probePin;
|
|||||||
extern int buttonPin;
|
extern int buttonPin;
|
||||||
|
|
||||||
extern int probeSwap;
|
extern int probeSwap;
|
||||||
|
extern volatile int probeActive;
|
||||||
|
|
||||||
enum measuredState
|
enum measuredState
|
||||||
{
|
{
|
||||||
|
330
JumperlessNano/src/RotaryEncoder.cpp
Normal file
330
JumperlessNano/src/RotaryEncoder.cpp
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
#include "CH446Q.h"
|
||||||
|
#include "MatrixStateRP2040.h"
|
||||||
|
#include "NetsToChipConnections.h"
|
||||||
|
#include "LEDs.h"
|
||||||
|
#include "Peripherals.h"
|
||||||
|
#include "JumperlessDefinesRP2040.h"
|
||||||
|
#include "FileParsing.h"
|
||||||
|
#include "NetManager.h"
|
||||||
|
#include "Probing.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "hardware/pwm.h"
|
||||||
|
#include "RotaryEncoder.h"
|
||||||
|
#include "quadrature.pio.h"
|
||||||
|
|
||||||
|
volatile int slotChanged = 0;
|
||||||
|
PIO pioEnc = pio1;
|
||||||
|
uint offsetEnc, smEnc;
|
||||||
|
int netSlot = 0;
|
||||||
|
|
||||||
|
int newPositionEncoder = 0;
|
||||||
|
int lastPositionEncoder = 0;
|
||||||
|
int encoderRaw = 0;
|
||||||
|
int lastPosition = 0;
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
int lastButtonState = 0;
|
||||||
|
|
||||||
|
volatile int rotaryEncoderMode = 0;
|
||||||
|
|
||||||
|
int slotPreview = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void initRotaryEncoder(void)
|
||||||
|
{
|
||||||
|
pinMode(0, INPUT_PULLUP);
|
||||||
|
pinMode(QUADRATURE_A_PIN, INPUT_PULLUP);
|
||||||
|
pinMode(QUADRATURE_B_PIN, INPUT_PULLUP);
|
||||||
|
|
||||||
|
offsetEnc = pio_add_program(pioEnc, &quadrature_program);
|
||||||
|
smEnc = pio_claim_unused_sm(pioEnc, true);
|
||||||
|
quadrature_program_init(pioEnc, smEnc, offsetEnc, QUADRATURE_A_PIN, QUADRATURE_B_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unInitRotaryEncoder(void)
|
||||||
|
{
|
||||||
|
//pio_sm_unclaim(pioEnc, smEnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char rotaryEncoderHelp[]= "\n\r" //Github copilot, please don't help me with this
|
||||||
|
"\t\t Rotary Encoder Help\n\r"
|
||||||
|
"\t\t -------------------\n\r"
|
||||||
|
"\n\r"
|
||||||
|
" A COM B \n\r"
|
||||||
|
" _________________________________________________ \n\r"
|
||||||
|
" / D12 D11 D10 \\ \n\r"
|
||||||
|
" / . [,][,][,][][][][][][][][][][][][][] \\ \n\r"
|
||||||
|
"| ' `0 | /```\\ | | \n\r"
|
||||||
|
"| ' ` | ( O ) | | \n\r"
|
||||||
|
"| ' ` | \\___/ | | \n\r"
|
||||||
|
"| ' ' ['][ ][']{}{}{}{}{}{}{}{}{}[][][][] | \n\r"
|
||||||
|
"| ' ' D13 AREF | \n\r"
|
||||||
|
"| button | \n\r"
|
||||||
|
"\n\r"
|
||||||
|
"Stick a 5 pin rotary encoder into the board as shown above. \n\n\r"
|
||||||
|
"When rotary encoder mode is on, these pins will be connected\n\r"
|
||||||
|
"to the Jumperless [A-UART_Rx(16), B-UART_Tx(17), SW-GPIO_0] \n\n\r"
|
||||||
|
|
||||||
|
"The LEDs under A0-A7 {shown in curly braces} show which of the \n\r"
|
||||||
|
"8 slots are active/connected(pink) and previewing(blue/green)\n\n\r"
|
||||||
|
"Press the encoder button to made the previewed slot active\n\r"
|
||||||
|
"(going into probing mode will make the previewed slot active)\n\n\r"
|
||||||
|
|
||||||
|
"You can cycle through slots by entering z(next) or x(previous)\n\r"
|
||||||
|
"or by turning the rotary encoder and then pressing (obviously)\n\n\r"
|
||||||
|
|
||||||
|
"You can show the contents of all the slot files by entering s\n\r"
|
||||||
|
"(copy/paste the output into a text file on your computer) \n\n\r"
|
||||||
|
"Load files by entering o and paste the text into this terminal \n\n\r"
|
||||||
|
|
||||||
|
"Wokwi sketches will be loaded into whichever slot is active\n\n\r"
|
||||||
|
|
||||||
|
"This is a WIP, so let me know if something's broken or you want\n\r"
|
||||||
|
"something added. \n\n\r"
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
void printRotaryEncoderHelp(void)
|
||||||
|
{
|
||||||
|
Serial.print(rotaryEncoderHelp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned long previewLength = 2500;
|
||||||
|
unsigned long previewTimer = 0;
|
||||||
|
|
||||||
|
unsigned long buttonHoldTimer = 0;
|
||||||
|
unsigned long buttonHoldLength = 1000;
|
||||||
|
|
||||||
|
unsigned long buttonDebounceTimer = 0;
|
||||||
|
unsigned long buttonDebounceTimer2 = 0;
|
||||||
|
unsigned long debounceTime = 200000;
|
||||||
|
int showingPreview = 0;
|
||||||
|
int rotState = 0;
|
||||||
|
int encoderWasPressed = 1;
|
||||||
|
int encoderIsPressed = 0;
|
||||||
|
int buttonState = digitalRead(0);
|
||||||
|
|
||||||
|
int encoderAstate = 0;
|
||||||
|
int encoderBstate = 0;
|
||||||
|
int justPressed = 1;
|
||||||
|
|
||||||
|
int probeWasActive = 0;
|
||||||
|
|
||||||
|
void rotaryEncoderStuff(void)
|
||||||
|
{
|
||||||
|
probeWasActive = probeActive;
|
||||||
|
|
||||||
|
if (probeActive == 1)
|
||||||
|
{
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (probeWasActive == 1 && probeActive == 0)
|
||||||
|
// {
|
||||||
|
//Serial.print("not probing\n\r");
|
||||||
|
netSlot = slotPreview;
|
||||||
|
|
||||||
|
pio_sm_exec_wait_blocking(pioEnc, smEnc, pio_encode_in(pio_x, 32)); // PIO rotary encoder handler
|
||||||
|
|
||||||
|
encoderRaw = (pio_sm_get_blocking(pioEnc, smEnc));
|
||||||
|
lastPositionEncoder = encoderRaw;
|
||||||
|
lastPosition = position;
|
||||||
|
// if (millis() % 1000 == 0)
|
||||||
|
// {
|
||||||
|
// Serial.print("\r probing");
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (buttonState == 0)
|
||||||
|
{
|
||||||
|
encoderIsPressed = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encoderIsPressed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoderIsPressed == 1 && encoderWasPressed == 0&& (micros() - buttonDebounceTimer2 > debounceTime))
|
||||||
|
{
|
||||||
|
buttonDebounceTimer2 = micros();
|
||||||
|
encoderWasPressed = encoderIsPressed;
|
||||||
|
//showSavedColors(slotPreview);
|
||||||
|
lightUpRail();
|
||||||
|
leds.show();
|
||||||
|
}
|
||||||
|
if (encoderIsPressed == 0 && encoderWasPressed == 1 && (micros() - buttonDebounceTimer2 > debounceTime))
|
||||||
|
{
|
||||||
|
buttonDebounceTimer2 = micros();
|
||||||
|
encoderWasPressed = encoderIsPressed;
|
||||||
|
//showSavedColors(slotPreview);
|
||||||
|
lightUpRail();
|
||||||
|
leds.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((buttonState == 0 && lastButtonState == 1) && (micros() - buttonDebounceTimer > debounceTime))
|
||||||
|
{
|
||||||
|
// Serial.println("pressed\n\r");
|
||||||
|
|
||||||
|
lastPositionEncoder = encoderRaw;
|
||||||
|
// Serial.print("encoderRaw: ");
|
||||||
|
// Serial.print(encoderRaw);
|
||||||
|
// Serial.print("\t");
|
||||||
|
// Serial.print("lastPositionEncoder: ");
|
||||||
|
// Serial.print(lastPositionEncoder);
|
||||||
|
// Serial.println("\n\r");
|
||||||
|
buttonDebounceTimer = micros();
|
||||||
|
showingPreview = 0;
|
||||||
|
rawOtherColors[1] = 0x550008,
|
||||||
|
justPressed = 2;
|
||||||
|
showSavedColors(slotPreview);
|
||||||
|
lightUpRail();
|
||||||
|
leds.show();
|
||||||
|
|
||||||
|
slotChanged = 1;
|
||||||
|
netSlot = slotPreview;
|
||||||
|
buttonHoldTimer = millis();
|
||||||
|
|
||||||
|
Serial.print("\r \r");
|
||||||
|
Serial.print("\rCurrent Slot: ");
|
||||||
|
Serial.print(netSlot);
|
||||||
|
Serial.print("\t\t");
|
||||||
|
Serial.print("Selected Slot: ");
|
||||||
|
Serial.print(slotPreview);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
// lastButtonState = buttonState;
|
||||||
|
slotChanged = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastButtonState = buttonState;
|
||||||
|
|
||||||
|
if (millis() - previewTimer > previewLength && showingPreview == 1 && netSlot != slotPreview)
|
||||||
|
{
|
||||||
|
showSavedColors(netSlot);
|
||||||
|
//showSavedColors(slotPreview);
|
||||||
|
lightUpRail();
|
||||||
|
leds.show();
|
||||||
|
showingPreview = 0;
|
||||||
|
rawOtherColors[1] = 0x550008,
|
||||||
|
|
||||||
|
previewTimer = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (slotChanged == 1)
|
||||||
|
;
|
||||||
|
//delay(1);
|
||||||
|
|
||||||
|
|
||||||
|
pio_sm_exec_wait_blocking(pioEnc, smEnc, pio_encode_in(pio_x, 32)); // PIO rotary encoder handler
|
||||||
|
|
||||||
|
encoderRaw = (pio_sm_get_blocking(pioEnc, smEnc));
|
||||||
|
|
||||||
|
|
||||||
|
if (abs(lastPositionEncoder - encoderRaw) >= justPressed)
|
||||||
|
{
|
||||||
|
justPressed = 1;
|
||||||
|
// Serial.print("encoderRaw: ");
|
||||||
|
// Serial.print(encoderRaw);
|
||||||
|
// Serial.print("\t");
|
||||||
|
// Serial.print("lastPositionEncoder: ");
|
||||||
|
// Serial.print(lastPositionEncoder);
|
||||||
|
// Serial.println("\n\r");
|
||||||
|
|
||||||
|
// Serial.print ("\n\rencoderRaw: ");
|
||||||
|
// Serial.print(encoderRaw);
|
||||||
|
// Serial.print("\n\r");
|
||||||
|
|
||||||
|
// newPositionEncoder = encoderRaw;
|
||||||
|
|
||||||
|
if (lastPositionEncoder > encoderRaw)
|
||||||
|
{
|
||||||
|
slotPreview--;
|
||||||
|
if (slotPreview < 0)
|
||||||
|
{
|
||||||
|
slotPreview = NUM_SLOTS - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
position -= 1;
|
||||||
|
|
||||||
|
lastPositionEncoder = encoderRaw;
|
||||||
|
}
|
||||||
|
else if (lastPositionEncoder < encoderRaw)
|
||||||
|
{
|
||||||
|
// Serial.print("oldSlot: ");
|
||||||
|
// Serial.print(netSlot);
|
||||||
|
// Serial.print("\t");
|
||||||
|
// Serial.print("newSlot: ");
|
||||||
|
// Serial.print(netSlot);
|
||||||
|
// Serial.print("\n\r");
|
||||||
|
|
||||||
|
slotPreview++;
|
||||||
|
if (slotPreview >= NUM_SLOTS)
|
||||||
|
{
|
||||||
|
slotPreview = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
position += 1;
|
||||||
|
|
||||||
|
lastPositionEncoder = encoderRaw;
|
||||||
|
}
|
||||||
|
// Serial.print(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastPosition != position)
|
||||||
|
{
|
||||||
|
if (rotState == 0)
|
||||||
|
{
|
||||||
|
rotState = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rotState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPositionEncoder = encoderRaw;
|
||||||
|
showingPreview = 1;
|
||||||
|
// rawOtherColors[1] = 0x960095;
|
||||||
|
rawOtherColors[1] = 0x1800FF;
|
||||||
|
showSavedColors(slotPreview);
|
||||||
|
lightUpRail();
|
||||||
|
leds.show();
|
||||||
|
|
||||||
|
previewTimer = millis();
|
||||||
|
|
||||||
|
Serial.print("\r \r");
|
||||||
|
Serial.print("\rCurrent Slot: ");
|
||||||
|
Serial.print(netSlot);
|
||||||
|
Serial.print("\t\t");
|
||||||
|
Serial.print("Selected Slot: ");
|
||||||
|
Serial.print(slotPreview);
|
||||||
|
|
||||||
|
// leds.setPixelColor((lastPosition)+98, 0);
|
||||||
|
// leds.setPixelColor((position)+98, 0x460035);
|
||||||
|
|
||||||
|
// Serial.print("position+98: ");
|
||||||
|
// Serial.print(position+98);
|
||||||
|
// leds.show();
|
||||||
|
lastPosition = position;
|
||||||
|
|
||||||
|
// leds.setPixelColor(110, rawOtherColors[1]);
|
||||||
|
// showLEDsCore2 = 1;
|
||||||
|
|
||||||
|
// showLEDsCore2 = 1;
|
||||||
|
// logoFlash = 2;
|
||||||
|
}
|
||||||
|
buttonState = digitalRead(0);
|
||||||
|
// if (millis() - buttonHoldTimer > buttonHoldLength && buttonState == 0 )
|
||||||
|
// {
|
||||||
|
// //Serial.println("held\n\r");
|
||||||
|
// //refreshSavedColors();
|
||||||
|
|
||||||
|
// slotChanged = 1;
|
||||||
|
// netSlot = slotPreview;
|
||||||
|
// }
|
||||||
|
}
|
29
JumperlessNano/src/RotaryEncoder.h
Normal file
29
JumperlessNano/src/RotaryEncoder.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef ROTARYENCODER_H
|
||||||
|
#define ROTARYENCODER_H
|
||||||
|
|
||||||
|
#define NUM_SLOTS 8
|
||||||
|
|
||||||
|
extern volatile int rotaryEncoderMode;
|
||||||
|
extern int netSlot;
|
||||||
|
extern volatile int slotChanged;
|
||||||
|
|
||||||
|
extern int rotState;
|
||||||
|
extern int encoderIsPressed;
|
||||||
|
extern int showingPreview;
|
||||||
|
|
||||||
|
void initRotaryEncoder(void);
|
||||||
|
void unInitRotaryEncoder(void);
|
||||||
|
void printRotaryEncoderHelp(void);
|
||||||
|
void rotaryEncoderStuff(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -33,14 +33,25 @@
|
|||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
|
|
||||||
#include "CH446Q.h"
|
#include "CH446Q.h"
|
||||||
|
#include "RotaryEncoder.h"
|
||||||
#include "FileParsing.h"
|
#include "FileParsing.h"
|
||||||
|
|
||||||
#include "LittleFS.h"
|
#include "LittleFS.h"
|
||||||
|
|
||||||
#include "Probing.h"
|
#include "Probing.h"
|
||||||
|
|
||||||
#include "AdcUsb.h"
|
// #include "AdcUsb.h"
|
||||||
|
|
||||||
|
// #include "logic_analyzer.h"
|
||||||
|
// #include "pico/multicore.h"
|
||||||
|
// #include "hardware/watchdog.h"
|
||||||
|
|
||||||
|
// using namespace logic_analyzer;
|
||||||
|
|
||||||
|
// int pinStart=26;
|
||||||
|
// int numberOfPins=2;
|
||||||
|
// LogicAnalyzer logicAnalyzer;
|
||||||
|
// Capture capture(MAX_FREQ, MAX_FREQ_THRESHOLD);
|
||||||
|
|
||||||
Adafruit_USBD_CDC USBSer1;
|
Adafruit_USBD_CDC USBSer1;
|
||||||
|
|
||||||
@ -54,6 +65,7 @@ unsigned long lastNetConfirmTimer = 0;
|
|||||||
|
|
||||||
volatile int sendAllPathsCore2 = 0; // this signals the core 2 to send all the paths to the CH446Q
|
volatile int sendAllPathsCore2 = 0; // this signals the core 2 to send all the paths to the CH446Q
|
||||||
|
|
||||||
|
int rotEncInit = 0;
|
||||||
// https://wokwi.com/projects/367384677537829889
|
// https://wokwi.com/projects/367384677537829889
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
@ -74,7 +86,6 @@ void setup()
|
|||||||
|
|
||||||
USBSer1.begin(115200);
|
USBSer1.begin(115200);
|
||||||
|
|
||||||
|
|
||||||
#ifdef EEPROMSTUFF
|
#ifdef EEPROMSTUFF
|
||||||
EEPROM.begin(256);
|
EEPROM.begin(256);
|
||||||
debugFlagInit();
|
debugFlagInit();
|
||||||
@ -91,29 +102,32 @@ void setup()
|
|||||||
|
|
||||||
initArduino();
|
initArduino();
|
||||||
delay(4);
|
delay(4);
|
||||||
#ifdef FSSTUFF
|
|
||||||
LittleFS.begin();
|
LittleFS.begin();
|
||||||
#endif
|
|
||||||
setDac0_5Vvoltage(0.0);
|
setDac0_5Vvoltage(0.0);
|
||||||
setDac1_8Vvoltage(1.9);
|
setDac1_8Vvoltage(1.9);
|
||||||
|
createSlots(-1, rotaryEncoderMode);
|
||||||
clearAllNTCC();
|
clearAllNTCC();
|
||||||
|
|
||||||
//delay(20);
|
// if (rotaryEncoderMode == 1)
|
||||||
//setupAdcUsbStuff(); // I took this out because it was causing a crash on
|
// {
|
||||||
delay(10);
|
//rotEncInit = 1;
|
||||||
|
initRotaryEncoder();
|
||||||
|
//}
|
||||||
|
|
||||||
// lastNetConfirm(0);
|
// delay(20);
|
||||||
|
// setupAdcUsbStuff(); // I took this out because it was causing a crash on
|
||||||
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup1()
|
void setup1()
|
||||||
{
|
{
|
||||||
delay(4);
|
delay(4);
|
||||||
|
|
||||||
initCH446Q();
|
initCH446Q();
|
||||||
|
|
||||||
|
delay(4);
|
||||||
delay (4);
|
|
||||||
initLEDs();
|
initLEDs();
|
||||||
delay(4);
|
delay(4);
|
||||||
startupColors();
|
startupColors();
|
||||||
@ -121,11 +135,10 @@ delay(4);
|
|||||||
lightUpRail();
|
lightUpRail();
|
||||||
|
|
||||||
delay(4);
|
delay(4);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
showLEDsCore2 = 1;
|
showLEDsCore2 = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char connectFromArduino = '\0';
|
char connectFromArduino = '\0';
|
||||||
@ -138,7 +151,10 @@ int baudRate = 115200;
|
|||||||
|
|
||||||
int restoredNodeFile = 0;
|
int restoredNodeFile = 0;
|
||||||
|
|
||||||
const char firmwareVersion[] = "1.3.9"; //// remember to update this
|
const char firmwareVersion[] = "1.3.13"; //// remember to update this
|
||||||
|
|
||||||
|
int firstLoop = 1;
|
||||||
|
volatile int probeActive = 0;
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
@ -146,6 +162,7 @@ void loop()
|
|||||||
unsigned long timer = 0;
|
unsigned long timer = 0;
|
||||||
|
|
||||||
menu:
|
menu:
|
||||||
|
|
||||||
// showMeasurements();
|
// showMeasurements();
|
||||||
// unsigned long connecttimer = 0;
|
// unsigned long connecttimer = 0;
|
||||||
// // while (tud_connected() == 0)
|
// // while (tud_connected() == 0)
|
||||||
@ -165,16 +182,32 @@ menu:
|
|||||||
Serial.print("\tu = set baud rate for USB-Serial\n\r");
|
Serial.print("\tu = set baud rate for USB-Serial\n\r");
|
||||||
Serial.print("\tl = LED brightness / test\n\r");
|
Serial.print("\tl = LED brightness / test\n\r");
|
||||||
Serial.print("\td = toggle debug flags\n\r");
|
Serial.print("\td = toggle debug flags\n\r");
|
||||||
Serial.print("\tr = reset Arduino\n\r");
|
Serial.print("\tr = rotary encoder mode -");
|
||||||
|
rotaryEncoderMode == 1 ? Serial.print(" ON (z/x to cycle)\n\r") : Serial.print(" off\n\r");
|
||||||
Serial.print("\tp = probe connections\n\r");
|
Serial.print("\tp = probe connections\n\r");
|
||||||
Serial.print("\tc = clear nodes with probe\n\r");
|
Serial.print("\tc = clear nodes with probe\n\r");
|
||||||
Serial.print("\n\n\r");
|
Serial.print("\n\n\r");
|
||||||
|
|
||||||
|
if (firstLoop == 1 && rotaryEncoderMode == 1)
|
||||||
|
{
|
||||||
|
Serial.print("Use the rotary encoder to change slots\n\r");
|
||||||
|
Serial.print("Press the button to select\n\r");
|
||||||
|
Serial.print("\n\n\r");
|
||||||
|
firstLoop = 0;
|
||||||
|
|
||||||
|
goto loadfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial.print("Slot: ");
|
||||||
|
// Serial.println(netSlot);
|
||||||
|
|
||||||
dontshowmenu:
|
dontshowmenu:
|
||||||
|
|
||||||
connectFromArduino = '\0';
|
connectFromArduino = '\0';
|
||||||
|
|
||||||
while (Serial.available() == 0 && connectFromArduino == '\0')
|
while (Serial.available() == 0 && connectFromArduino == '\0' && slotChanged == 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (showReadings >= 1)
|
if (showReadings >= 1)
|
||||||
{
|
{
|
||||||
showMeasurements();
|
showMeasurements();
|
||||||
@ -209,10 +242,16 @@ dontshowmenu:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (slotChanged == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
// showLEDsCore2 = 1;
|
||||||
|
|
||||||
|
goto loadfile;
|
||||||
|
}
|
||||||
|
|
||||||
if (connectFromArduino != '\0')
|
if (connectFromArduino != '\0')
|
||||||
{
|
{
|
||||||
input = 'f';
|
|
||||||
// connectFromArduino = '\0';
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -232,15 +271,56 @@ skipinput:
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
|
{
|
||||||
|
int fileNo = -1;
|
||||||
|
|
||||||
|
if (Serial.available() > 0)
|
||||||
|
{
|
||||||
|
fileNo = Serial.read();
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
Serial.print("\n\n\r");
|
||||||
|
Serial.print("\tSlot File ");
|
||||||
|
Serial.print(fileNo - '0');
|
||||||
Serial.print("\n\n\r");
|
Serial.print("\n\n\r");
|
||||||
Serial.print("\tNode File\n\r");
|
Serial.print("\tNode File\n\r");
|
||||||
Serial.print("\n\ryou can paste this into the menu to reload this circuit");
|
Serial.print("\n\ryou can paste this into the menu to reload this circuit");
|
||||||
Serial.print("\n\r(make sure you grab an extra blank line at the end)\n\r");
|
Serial.print("\n\r(make sure you grab an extra blank line at the end)\n\r");
|
||||||
Serial.print("\n\n\rf ");
|
if (fileNo == -1)
|
||||||
printNodeFile();
|
{
|
||||||
Serial.print("\n\n\r");
|
for (int i = 0; i < NUM_SLOTS; i++)
|
||||||
break;
|
{
|
||||||
|
Serial.print("\n\rSlot ");
|
||||||
|
Serial.print(i);
|
||||||
|
if (i == netSlot)
|
||||||
|
{
|
||||||
|
Serial.print(" <--- current slot");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.print("\n\rnodeFileSlot");
|
||||||
|
Serial.print(i);
|
||||||
|
Serial.print(".txt\n\r");
|
||||||
|
|
||||||
|
Serial.print("\n\rf ");
|
||||||
|
printNodeFile(i);
|
||||||
|
Serial.print("\n\r");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
Serial.print("\n\rnodeFileSlot");
|
||||||
|
Serial.print(fileNo - '0');
|
||||||
|
Serial.print(".txt\n\r");
|
||||||
|
|
||||||
|
Serial.print("\n\rf ");
|
||||||
|
|
||||||
|
printNodeFile(fileNo - '0');
|
||||||
|
Serial.print("\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'v':
|
case 'v':
|
||||||
|
|
||||||
if (showReadings >= 3 || (inaConnected == 0 && showReadings >= 1))
|
if (showReadings >= 3 || (inaConnected == 0 && showReadings >= 1))
|
||||||
@ -273,13 +353,22 @@ skipinput:
|
|||||||
}
|
}
|
||||||
case 'p':
|
case 'p':
|
||||||
{
|
{
|
||||||
|
probeActive = 1;
|
||||||
|
|
||||||
|
delayMicroseconds(1500);
|
||||||
probeMode(19, 1);
|
probeMode(19, 1);
|
||||||
|
delayMicroseconds(1500);
|
||||||
|
probeActive = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'c':
|
case 'c':
|
||||||
{
|
{
|
||||||
// removeBridgeFromNodeFile(19, 1);
|
// removeBridgeFromNodeFile(19, 1);
|
||||||
|
probeActive = 1;
|
||||||
|
delayMicroseconds(1500);
|
||||||
probeMode(19, 0);
|
probeMode(19, 0);
|
||||||
|
delayMicroseconds(1500);
|
||||||
|
probeActive = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'n':
|
case 'n':
|
||||||
@ -317,21 +406,67 @@ skipinput:
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'o':
|
||||||
|
{
|
||||||
|
inputNodeFileList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// case 'a':
|
case 'x':
|
||||||
// {
|
//{
|
||||||
// resetArduino(); // reset works
|
netSlot++;
|
||||||
// // uploadArduino(); //this is unwritten
|
if (netSlot >= NUM_SLOTS)
|
||||||
// }
|
{
|
||||||
|
netSlot = 0;
|
||||||
|
}
|
||||||
|
Serial.print("\r \r");
|
||||||
|
Serial.print("Slot: ");
|
||||||
|
Serial.print(netSlot);
|
||||||
|
goto loadfile;
|
||||||
|
case 'z':
|
||||||
|
//{
|
||||||
|
netSlot--;
|
||||||
|
if (netSlot < 0)
|
||||||
|
{
|
||||||
|
netSlot = NUM_SLOTS - 1;
|
||||||
|
}
|
||||||
|
Serial.print("\r \r");
|
||||||
|
Serial.print("Slot: ");
|
||||||
|
Serial.print(netSlot);
|
||||||
|
goto loadfile;
|
||||||
|
|
||||||
|
case 'y':
|
||||||
|
{
|
||||||
|
loadfile:
|
||||||
|
clearAllNTCC();
|
||||||
|
openNodeFile(netSlot);
|
||||||
|
getNodesToConnect();
|
||||||
|
bridgesToPaths();
|
||||||
|
clearLEDs();
|
||||||
|
assignNetColors();
|
||||||
|
digitalWrite(RESETPIN, HIGH);
|
||||||
|
delayMicroseconds(100);
|
||||||
|
// Serial.print("bridgesToPaths\n\r");
|
||||||
|
digitalWrite(RESETPIN, LOW);
|
||||||
|
// showNets();
|
||||||
|
//saveRawColors(netSlot);
|
||||||
|
showSavedColors(netSlot);
|
||||||
|
sendAllPathsCore2 = 1;
|
||||||
|
slotChanged = 0;
|
||||||
|
input = ' ';
|
||||||
|
// break;
|
||||||
|
|
||||||
|
goto dontshowmenu;
|
||||||
|
}
|
||||||
case 'f':
|
case 'f':
|
||||||
|
|
||||||
readInNodesArduino = 1;
|
readInNodesArduino = 1;
|
||||||
clearAllNTCC();
|
clearAllNTCC();
|
||||||
|
|
||||||
// sendAllPathsCore2 = 1;
|
// sendAllPathsCore2 = 1;
|
||||||
timer = millis();
|
timer = millis();
|
||||||
|
|
||||||
clearNodeFile();
|
// clearNodeFile(netSlot);
|
||||||
|
|
||||||
if (connectFromArduino != '\0')
|
if (connectFromArduino != '\0')
|
||||||
{
|
{
|
||||||
@ -341,12 +476,16 @@ skipinput:
|
|||||||
{
|
{
|
||||||
serSource = 0;
|
serSource = 0;
|
||||||
}
|
}
|
||||||
|
// if (rotaryEncoderMode == 1)
|
||||||
|
// {
|
||||||
|
// createSlots(netSlot);
|
||||||
|
// }
|
||||||
|
|
||||||
savePreformattedNodeFile(serSource);
|
savePreformattedNodeFile(serSource, netSlot, rotaryEncoderMode);
|
||||||
|
|
||||||
// Serial.print("savePFNF\n\r");
|
// Serial.print("savePFNF\n\r");
|
||||||
// debugFP = 1;
|
// debugFP = 1;
|
||||||
openNodeFile();
|
openNodeFile(netSlot);
|
||||||
getNodesToConnect();
|
getNodesToConnect();
|
||||||
// Serial.print("openNF\n\r");
|
// Serial.print("openNF\n\r");
|
||||||
digitalWrite(RESETPIN, HIGH);
|
digitalWrite(RESETPIN, HIGH);
|
||||||
@ -357,7 +496,7 @@ skipinput:
|
|||||||
// Serial.print("bridgesToPaths\n\r");
|
// Serial.print("bridgesToPaths\n\r");
|
||||||
digitalWrite(RESETPIN, LOW);
|
digitalWrite(RESETPIN, LOW);
|
||||||
// showNets();
|
// showNets();
|
||||||
|
saveRawColors(netSlot);
|
||||||
sendAllPathsCore2 = 1;
|
sendAllPathsCore2 = 1;
|
||||||
|
|
||||||
if (debugNMtime)
|
if (debugNMtime)
|
||||||
@ -367,7 +506,7 @@ skipinput:
|
|||||||
Serial.print(millis() - timer);
|
Serial.print(millis() - timer);
|
||||||
Serial.print("ms");
|
Serial.print("ms");
|
||||||
}
|
}
|
||||||
|
input = ' ';
|
||||||
if (connectFromArduino != '\0')
|
if (connectFromArduino != '\0')
|
||||||
{
|
{
|
||||||
connectFromArduino = '\0';
|
connectFromArduino = '\0';
|
||||||
@ -377,7 +516,7 @@ skipinput:
|
|||||||
readInNodesArduino = 0;
|
readInNodesArduino = 0;
|
||||||
goto dontshowmenu;
|
goto dontshowmenu;
|
||||||
}
|
}
|
||||||
|
connectFromArduino = '\0';
|
||||||
readInNodesArduino = 0;
|
readInNodesArduino = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -431,7 +570,45 @@ skipinput:
|
|||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
|
|
||||||
resetArduino();
|
if (rotaryEncoderMode == 1)
|
||||||
|
{
|
||||||
|
// unInitRotaryEncoder();
|
||||||
|
|
||||||
|
rotaryEncoderMode = 0;
|
||||||
|
//createSlots(-1, rotaryEncoderMode);
|
||||||
|
// showSavedColors(netSlot);
|
||||||
|
showLEDsCore2 = 1;
|
||||||
|
debugFlagSet(10); // encoderModeOff
|
||||||
|
goto menu;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rotaryEncoderMode = 1;
|
||||||
|
if (rotEncInit == 0) // only do this once
|
||||||
|
{
|
||||||
|
createSlots(-1, rotaryEncoderMode);
|
||||||
|
// initRotaryEncoder();
|
||||||
|
rotEncInit = 1;
|
||||||
|
// Serial.print("\n\n\r (you should unplug an)");
|
||||||
|
}
|
||||||
|
printRotaryEncoderHelp();
|
||||||
|
delay(100);
|
||||||
|
// initRotaryEncoder();
|
||||||
|
// refreshSavedColors();
|
||||||
|
showSavedColors(netSlot);
|
||||||
|
showLEDsCore2 = 1;
|
||||||
|
debugFlagSet(11); // encoderModeOn
|
||||||
|
|
||||||
|
// delay(700);
|
||||||
|
// Serial.flush();
|
||||||
|
// Serial.end();
|
||||||
|
|
||||||
|
// delay(700);
|
||||||
|
// watchdog_enable(1, 1);
|
||||||
|
// while(1);
|
||||||
|
//*((volatile uint32_t*)(PPB_BASE + 0x0ED0C)) = 0x5FA0004;
|
||||||
|
}
|
||||||
|
goto dontshowmenu;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -769,6 +946,24 @@ void machineMode(void) // read in commands in machine readable format
|
|||||||
machineModeRespond(sequenceNumber, true);
|
machineModeRespond(sequenceNumber, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadFile(int slot)
|
||||||
|
{
|
||||||
|
clearAllNTCC();
|
||||||
|
openNodeFile(netSlot);
|
||||||
|
getNodesToConnect();
|
||||||
|
bridgesToPaths();
|
||||||
|
clearLEDs();
|
||||||
|
assignNetColors();
|
||||||
|
digitalWrite(RESETPIN, HIGH);
|
||||||
|
delayMicroseconds(100);
|
||||||
|
// Serial.print("bridgesToPaths\n\r");
|
||||||
|
digitalWrite(RESETPIN, LOW);
|
||||||
|
// showNets();
|
||||||
|
saveRawColors(netSlot);
|
||||||
|
sendAllPathsCore2 = 1;
|
||||||
|
/// input = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long logoFlashTimer = 0;
|
unsigned long logoFlashTimer = 0;
|
||||||
|
|
||||||
int arduinoReset = 0;
|
int arduinoReset = 0;
|
||||||
@ -813,6 +1008,10 @@ void loop1() // core 2 handles the LEDs and the CH446Q8
|
|||||||
delayMicroseconds(7200);
|
delayMicroseconds(7200);
|
||||||
sendAllPathsCore2 = 0;
|
sendAllPathsCore2 = 0;
|
||||||
}
|
}
|
||||||
|
// } else if (USBSer1.available() > 0)
|
||||||
|
// {
|
||||||
|
// logicAnalyzer.processCommand();
|
||||||
|
// }
|
||||||
|
|
||||||
if (arduinoReset == 0 && USBSer1.peek() == 0x30) // 0x30 is the first thing AVRDUDE sends
|
if (arduinoReset == 0 && USBSer1.peek() == 0x30) // 0x30 is the first thing AVRDUDE sends
|
||||||
{
|
{
|
||||||
@ -873,8 +1072,8 @@ void loop1() // core 2 handles the LEDs and the CH446Q8
|
|||||||
{
|
{
|
||||||
input = 'f';
|
input = 'f';
|
||||||
|
|
||||||
// connectFromArduino = 'f';
|
connectFromArduino = 'f';
|
||||||
// Serial.print("!!!!");
|
// Serial.print("!!!!");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -883,18 +1082,34 @@ void loop1() // core 2 handles the LEDs and the CH446Q8
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logoFlash == 2)
|
if (rotaryEncoderMode == 1)
|
||||||
{
|
{
|
||||||
logoFlashTimer = millis();
|
rotaryEncoderStuff();
|
||||||
logoFlash = 1;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showingPreview = 0;
|
||||||
|
|
||||||
|
if (logoFlash == 2)
|
||||||
|
{
|
||||||
|
logoFlashTimer = millis();
|
||||||
|
logoFlash = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logoFlash == 1 && logoFlashTimer != 0 && millis() - logoFlashTimer > (20))
|
||||||
|
{
|
||||||
|
logoFlash = 0;
|
||||||
|
logoFlashTimer = 0;
|
||||||
|
lightUpRail();
|
||||||
|
// if (rotaryEncoderMode == 1)
|
||||||
|
// {
|
||||||
|
// showSavedColors(netSlot);
|
||||||
|
// }
|
||||||
|
leds.setPixelColor(110, rawOtherColors[1]);
|
||||||
|
// showLEDsCore2 = 1;
|
||||||
|
leds.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logoFlash == 1 && logoFlashTimer != 0 && millis() - logoFlashTimer > 150)
|
// logicAnalyzer.processCommand();
|
||||||
{
|
|
||||||
logoFlash = 0;
|
|
||||||
logoFlashTimer = 0;
|
|
||||||
// lightUpRail();
|
|
||||||
leds.setPixelColor(110, rawOtherColors[1]);
|
|
||||||
leds.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
43
JumperlessNano/src/quadrature.pio
Normal file
43
JumperlessNano/src/quadrature.pio
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
; PIO Quadrature Encoder
|
||||||
|
;
|
||||||
|
; by Jamon Terrell <github@jamonterrell.com>
|
||||||
|
;
|
||||||
|
;
|
||||||
|
; SPDX-FileCopyrightText: 2022 Jamon Terrell <github@jamonterrell.com>
|
||||||
|
; SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
.program quadrature
|
||||||
|
start:
|
||||||
|
wait 0 pin 0 ; wait for B == 0
|
||||||
|
jmp PIN, wait_high ; if A == 0
|
||||||
|
mov x, !x ; x++ {
|
||||||
|
jmp x--, nop1 ;
|
||||||
|
nop1: ;
|
||||||
|
mov x, !x ; }
|
||||||
|
; BUG?!?! NO JMP OVER ELSE HERE?! somehow it works though!?
|
||||||
|
wait_high:
|
||||||
|
jmp x--, nop2 ; x-- {
|
||||||
|
nop2: ; }
|
||||||
|
|
||||||
|
wait 1 PIN 0 ; wait for B == 1
|
||||||
|
jmp PIN, wait_low ; if A == 0
|
||||||
|
jmp x--, nop3 ; x-- {
|
||||||
|
nop3: ; }
|
||||||
|
wait_low: ; else
|
||||||
|
mov x, !x ; x++ {
|
||||||
|
jmp x--, nop4 ;
|
||||||
|
nop4: ;
|
||||||
|
mov x, !x ;
|
||||||
|
jmp start ; }
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
static inline void quadrature_program_init(PIO pio, uint sm, uint offset, uint a_pin, uint b_pin) {
|
||||||
|
pio_sm_config c = quadrature_program_get_default_config(offset);
|
||||||
|
|
||||||
|
sm_config_set_in_pins(&c, b_pin);
|
||||||
|
sm_config_set_jmp_pin(&c, a_pin);
|
||||||
|
sm_config_set_in_shift(&c, false, true, 32);
|
||||||
|
pio_sm_init(pio, sm, offset, &c);
|
||||||
|
pio_sm_set_enabled(pio, sm, true);
|
||||||
|
}
|
||||||
|
%}
|
59
JumperlessNano/src/quadrature.pio.h
Normal file
59
JumperlessNano/src/quadrature.pio.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Jamon Terrell <github@jamonterrell.com>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// -------------------------------------------------- //
|
||||||
|
// This file is autogenerated by pioasm; do not edit! //
|
||||||
|
// -------------------------------------------------- //
|
||||||
|
|
||||||
|
#if !PICO_NO_HARDWARE
|
||||||
|
#include "hardware/pio.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ---------- //
|
||||||
|
// quadrature //
|
||||||
|
// ---------- //
|
||||||
|
|
||||||
|
#define quadrature_wrap_target 0
|
||||||
|
#define quadrature_wrap 12
|
||||||
|
|
||||||
|
static const uint16_t quadrature_program_instructions[] = {
|
||||||
|
// .wrap_target
|
||||||
|
0x2020, // 0: wait 0 pin, 0
|
||||||
|
0x00c5, // 1: jmp pin, 5
|
||||||
|
0xa029, // 2: mov x, !x
|
||||||
|
0x0044, // 3: jmp x--, 4
|
||||||
|
0xa029, // 4: mov x, !x
|
||||||
|
0x0046, // 5: jmp x--, 6
|
||||||
|
0x20a0, // 6: wait 1 pin, 0
|
||||||
|
0x00c9, // 7: jmp pin, 9
|
||||||
|
0x0049, // 8: jmp x--, 9
|
||||||
|
0xa029, // 9: mov x, !x
|
||||||
|
0x004b, // 10: jmp x--, 11
|
||||||
|
0xa029, // 11: mov x, !x
|
||||||
|
0x0000, // 12: jmp 0
|
||||||
|
// .wrap
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !PICO_NO_HARDWARE
|
||||||
|
static const struct pio_program quadrature_program = {
|
||||||
|
.instructions = quadrature_program_instructions,
|
||||||
|
.length = 13,
|
||||||
|
.origin = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline pio_sm_config quadrature_program_get_default_config(uint offset) {
|
||||||
|
pio_sm_config c = pio_get_default_sm_config();
|
||||||
|
sm_config_set_wrap(&c, offset + quadrature_wrap_target, offset + quadrature_wrap);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void quadrature_program_init(PIO pio, uint sm, uint offset, uint a_pin, uint b_pin) {
|
||||||
|
pio_sm_config c = quadrature_program_get_default_config(offset);
|
||||||
|
sm_config_set_in_pins(&c, b_pin);
|
||||||
|
sm_config_set_jmp_pin(&c, a_pin);
|
||||||
|
sm_config_set_in_shift(&c, false, true, 32);
|
||||||
|
pio_sm_init(pio, sm, offset, &c);
|
||||||
|
pio_sm_set_enabled(pio, sm, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -49,6 +49,9 @@ portName = ' '
|
|||||||
|
|
||||||
arduinoPort = 0
|
arduinoPort = 0
|
||||||
|
|
||||||
|
noArduinocli = True
|
||||||
|
|
||||||
|
|
||||||
if (noArduinocli == True):
|
if (noArduinocli == True):
|
||||||
|
|
||||||
disableArduinoFlashing = 1
|
disableArduinoFlashing = 1
|
||||||
@ -1326,11 +1329,11 @@ while True:
|
|||||||
lastDiagram = diagram
|
lastDiagram = diagram
|
||||||
|
|
||||||
try:
|
try:
|
||||||
#ser.write('f'.encode())
|
ser.write('f'.encode())
|
||||||
print(f)
|
#print(f)
|
||||||
time.sleep(0.4)
|
time.sleep(0.4)
|
||||||
print(p)
|
#print(p)
|
||||||
#ser.write(p.encode())
|
ser.write(p.encode())
|
||||||
|
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
Loading…
Reference in New Issue
Block a user