Added Rotary Encoder Mode and multiple saved slots

This commit is contained in:
Kevin Santo Cappuccio 2024-05-27 16:37:51 -07:00
parent 54f73c173a
commit bbd5d2bc1f
77 changed files with 19697 additions and 770 deletions

View File

@ -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,

View File

@ -1 +0,0 @@
{"hostname":"Kevins-MBP-2","username":"kevinsanto"}

View File

@ -0,0 +1 @@
{"hostname":"Kevins-MBP","username":"kevinsanto"}

View File

@ -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

View File

@ -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": ""

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

View File

@ -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
1 Ref Val Package PosX PosY Rot Side
2 A2 CH446Q LQFP44_Tight 41.390000 -150.718800 180.000000 bottom
3 B1 CH446Q LQFP44_Tight 55.067200 -150.690800 180.000000 bottom
4 C1 100uF C_1206_3216Metric_Pad1.33x1.80mm_HandSolder 88.062000 -118.579500 -90.000000 bottom
5 C4 100uF C_1206_3216Metric_Pad1.33x1.80mm_HandSolder 85.522000 -118.579500 90.000000 bottom
6 C6 100uF C_1206_3216Metric_Pad1.33x1.80mm_HandSolder 82.982000 -118.579500 90.000000 bottom
7 C7 10u C_0603_1608Metric 70.917000 -180.454000 -90.000000 bottom
8 C8 10u C_0603_1608Metric 70.917000 -184.330000 -90.000000 bottom
9 C9 1u C_0402_1005Metric 100.762000 -117.475000 -167.500000 bottom
10 C10 .1uF C_0402_1005Metric 52.959200 -123.190000 90.000000 bottom
11 C11 1u C_0402_1005Metric 104.699000 -132.588000 -60.000000 bottom
12 C12 1u C_0402_1005Metric 98.984000 -121.158000 55.000000 bottom
13 C13 27p C_0402_1005Metric 107.620000 -122.174000 -110.000000 bottom
14 C14 27p C_0402_1005Metric 111.049000 -120.777000 80.000000 bottom
15 C15 .1uF C_0402_1005Metric 52.959200 -120.904000 90.000000 bottom
16 C16 10uF C_0603_1608Metric 115.367000 -160.033000 -90.000000 bottom
17 C17 27p C_0402_1005Metric 41.834000 -136.398000 90.000000 bottom
18 C18 1u C_0402_1005Metric 98.730000 -130.302000 -122.500000 bottom
19 C19 1u C_0402_1005Metric 103.429000 -121.031000 110.000000 bottom
20 C20 1u C_0402_1005Metric 104.953000 -124.079000 -147.500000 bottom
21 C21 1u C_0402_1005Metric 111.544807 -129.434317 35.000000 bottom
22 C22 27p C_0402_1005Metric 43.358000 -136.370000 90.000000 bottom
23 C23 27p C_0402_1005Metric 44.882000 -136.398000 90.000000 bottom
24 C24 27p C_0402_1005Metric 48.438000 -134.112000 90.000000 bottom
25 C25 10uF C_0603_1608Metric 36.500000 -160.020000 90.000000 bottom
26 C26 CH446Q LQFP44_Tight 96.022600 -150.688800 180.000000 bottom
27 D61 1N5819 D_SOD-323 72.060000 -115.824000 -90.000000 bottom
28 D62 1N5819 D_SOD-323 69.520000 -115.790000 90.000000 bottom
29 D63 1N4148 D_SOD-323 63.424000 -115.790000 -90.000000 bottom
30 D65 1N5819 D_SOD-323 66.980000 -120.650000 -90.000000 bottom
31 D66 1N4148 D_SOD-323 63.551000 -120.650000 90.000000 bottom
32 D67 1N5819 D_SOD-323 69.520000 -120.650000 90.000000 bottom
33 D68 1N5819 D_SOD-323 72.060000 -120.650000 -90.000000 bottom
34 D70 CH446Q LQFP44_Tight 109.689600 -150.688800 180.000000 bottom
35 D91 LED LED_PLCC2_reversemount 49.403200 -115.138200 0.000000 bottom
36 E1 CH446Q LQFP44_Tight 41.465600 -168.494200 180.000000 bottom
37 F5 CH446Q LQFP44_Tight 55.079600 -168.494200 180.000000 bottom
38 FID2 Fiducial Fiducial_1mm_Mask2mm 47.930000 -179.070000 180.000000 bottom
39 FID3 Fiducial Fiducial_1mm_Mask2mm 103.810000 -179.070000 180.000000 bottom
40 FID4 Fiducial Fiducial_1mm_Mask2mm 42.850000 -111.760000 180.000000 bottom
41 G1 CH446Q LQFP44_Tight 95.999600 -168.494200 180.000000 bottom
42 H1 CH446Q LQFP44_Tight 109.713200 -168.494200 180.000000 bottom
43 I1 CH446Q LQFP44_Tight 68.717600 -150.690800 180.000000 bottom
44 J6 CH446Q LQFP44_Tight 68.669600 -168.494200 180.000000 bottom
45 JP1 NANO 3V3 SolderJumper_2_Bridged_Small 67.386400 -132.791200 90.000000 bottom
46 JP2 NANO 5V SolderJumper_2_Bridged_Small 68.885000 -132.791200 90.000000 bottom
47 JP13 Jumper_2_Bridged SolderJumper_2_Bridged_Small 84.353600 -132.969000 90.000000 bottom
48 JP14 Jumper_2_Bridged SolderJumper_2_Bridged_Small 82.855000 -132.969000 90.000000 bottom
49 K1 CH446Q LQFP44_Tight 82.359600 -168.494200 180.000000 bottom
50 L1 CH446Q LQFP44_Tight 82.321600 -150.690800 180.000000 bottom
51 R1 R_0603_1608Metric 60.757000 -179.070000 0.000000 bottom
52 R2 22R R_0402_1005Metric 105.588000 -118.745000 -12.500000 bottom
53 R3 22R R_0402_1005Metric 108.763000 -117.602000 -80.000000 bottom
54 R4 R_0603_1608Metric 91.110000 -179.070000 0.000000 bottom
55 R5 1k R_0402_1005Metric 104.826000 -114.808000 115.000000 bottom
56 R6 1K R_0402_1005Metric 41.834000 -134.110000 -90.000000 bottom
57 R7 1K R_0402_1005Metric 101.143000 -129.159000 -80.000000 bottom
58 R8 1K R_0402_1005Metric 43.358000 -134.110000 -90.000000 bottom
59 R9 1K R_0402_1005Metric 108.255000 -132.969000 -120.000000 bottom
60 R10 1K R_0402_1005Metric 44.882000 -134.108000 -90.000000 bottom
61 R11 1K R_0402_1005Metric 111.557000 -133.223000 -104.000000 bottom
62 R12 1k R_0402_1005Metric 110.795000 -124.968000 -7.500000 bottom
63 R13 47K R_0402_1005Metric 49.962000 -134.110000 90.000000 bottom
64 R14 47K R_0402_1005Metric 46.914000 -134.110000 -90.000000 bottom
65 R15 10K R_0402_1005Metric 49.962000 -136.396000 90.000000 bottom
66 R16 47K R_0402_1005Metric 48.438000 -136.396000 90.000000 bottom
67 R17 68K R_0402_1005Metric 46.914000 -138.682000 90.000000 bottom
68 R18 56.2K R_0402_1005Metric 49.962000 -138.682000 90.000000 bottom
69 R19 47K R_0402_1005Metric 46.914000 -136.396000 90.000000 bottom
70 R20 56.2K R_0402_1005Metric 48.438000 -138.682000 90.000000 bottom
71 R21 2K R_0402_1005Metric 41.834000 -138.682000 -90.000000 bottom
72 R22 2K R_0402_1005Metric 43.358000 -138.682000 -90.000000 bottom
73 R23 2K R_0402_1005Metric 44.882000 -138.682000 -90.000000 bottom
74 R24 1K R_0402_1005Metric 40.564000 -122.174000 90.000000 bottom
75 R26 2K R_0402_1005Metric 97.079000 -118.745000 106.000000 bottom
76 R27 2K R_0402_1005Metric 95.628822 -122.818683 130.000000 bottom
77 R29 68K R_0402_1005Metric 46.152000 -134.110000 -90.000000 bottom
78 SW1 USB BOOT SW_SPST_TL3342 110.007600 -111.937800 135.000000 bottom
79 TP5 FB/SHDN TestPoint_D0.75mm 80.442000 -119.634000 180.000000 bottom
80 TP6 VREF TestPoint_D0.75mm 80.442000 -117.602000 180.000000 bottom
81 TP7 0-5V DAC TestPoint_D0.75mm 43.866000 -105.664000 180.000000 bottom
82 TP8 +-8V DAC TestPoint_D0.75mm 45.644000 -105.664000 180.000000 bottom
83 TP9 ADC 0 IN TestPoint_D0.75mm 52.883000 -105.664000 180.000000 bottom
84 TP10 ADC 1 IN TestPoint_D0.75mm 51.359000 -105.664000 180.000000 bottom
85 TP11 ADC 2 IN TestPoint_D0.75mm 49.835000 -105.664000 180.000000 bottom
86 TP12 ADC 3 IN TestPoint_D0.75mm 48.311000 -105.664000 180.000000 bottom
87 TP13 -8V TestPoint_D0.75mm 35.738000 -118.872000 -90.000000 bottom
88 TP14 +8V TestPoint_D0.75mm 35.738000 -117.094000 0.000000 bottom
89 TP15 LED OUT TestPoint_Pad_D1.0mm 116.510000 -144.780000 90.000000 bottom
90 TP16 GPIO 0 TestPoint_Pad_D1.0mm 35.738000 -114.554000 0.000000 bottom
91 TP17 LED IN TestPoint_Pad_D1.0mm 35.230000 -144.780000 0.000000 bottom
92 TP19 GND TestPoint_Pad_D1.0mm 116.510000 -142.240000 0.000000 bottom
93 TP22 TestPoint BackMaskWindows 58.090000 -128.270000 0.000000 bottom
94 TP23 TestPoint BackMaskWindows 58.090000 -106.542937 0.000000 bottom
95 TP25 GND TestPoint_Pad_D1.0mm 35.230000 -142.240000 0.000000 bottom
96 U2 NCP1117-3.3_SOT223 SOT-223-3_TabPin2 76.784000 -182.372000 0.000000 bottom
97 U4 INA219 SOIC-8_3.9x4.9mm_P1.27mm 60.630000 -182.626000 180.000000 bottom
98 U6 INA219 SOIC-8_3.9x4.9mm_P1.27mm 91.110000 -182.753000 180.000000 bottom
99 U7 LM324 SOIC-14_3.9x8.7mm_P1.27mm 60.376000 -134.620000 -90.000000 bottom
100 U8 W25Q128JVS SOIC-8_5.23x5.23mm_P1.27mm 100.635000 -110.490000 -90.000000 bottom
101 U10 L272D SOIC-16_3.9x9.9mm_P1.27mm 46.660000 -122.377200 -90.000000 bottom
102 U11 LM324 SOIC-14_3.9x8.7mm_P1.27mm 75.870000 -134.685000 -90.000000 bottom
103 U12 MCP4822 SOIC-8_3.9x4.9mm_P1.27mm 91.364000 -134.682000 -90.000000 bottom
104 Y1 ABLS-12.000MHZ-B4-T Crystal_SMD_3225-4Pin_3.2x2.5mm 114.947000 -120.439000 90.000000 bottom

View File

@ -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
1 Ref Val Package PosX PosY Rot Side
2 A1 Arduino_Nano_v3.x Arduino_Nano 93.396000 -110.911000 -90.000000 top
3 C2 10uF CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder 72.314000 -118.110000 -90.000000 top
4 C3 10uF CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder 67.996000 -118.110000 -90.000000 top
5 C5 10uF CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder 76.759000 -118.110000 -90.000000 top
6 D1 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 39.031200 -151.130000 90.000000 top
7 D2 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 41.571200 -151.130000 90.000000 top
8 D3 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 44.111200 -151.130000 90.000000 top
9 D4 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 46.651200 -151.130000 90.000000 top
10 D5 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 49.191200 -151.130000 90.000000 top
11 D6 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 51.731200 -151.130000 90.000000 top
12 D7 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 54.271200 -151.130000 90.000000 top
13 D8 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 56.811200 -151.130000 90.000000 top
14 D9 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 59.351200 -151.130000 90.000000 top
15 D10 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 61.891200 -151.130000 90.000000 top
16 D11 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 64.440000 -151.130600 90.000000 top
17 D12 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 66.987800 -151.130600 90.000000 top
18 D13 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 69.527800 -151.130600 90.000000 top
19 D14 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 72.067800 -151.130600 90.000000 top
20 D15 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 74.607800 -151.130600 90.000000 top
21 D16 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 77.147800 -151.130600 90.000000 top
22 D17 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 79.687800 -151.130600 90.000000 top
23 D18 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 82.227800 -151.130600 90.000000 top
24 D19 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 84.767800 -151.130600 90.000000 top
25 D20 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 87.307800 -151.130600 90.000000 top
26 D21 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 89.847800 -151.129400 90.000000 top
27 D22 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 92.395600 -151.129400 90.000000 top
28 D23 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 94.935600 -151.129400 90.000000 top
29 D24 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 97.475600 -151.129400 90.000000 top
30 D25 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 100.015600 -151.129400 90.000000 top
31 D26 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 102.555600 -151.129400 90.000000 top
32 D27 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 105.095600 -151.129400 90.000000 top
33 D28 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 107.635600 -151.129400 90.000000 top
34 D29 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 110.175600 -151.129400 90.000000 top
35 D30 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 112.715600 -151.129400 90.000000 top
36 D31 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 38.973600 -168.908800 90.000000 top
37 D32 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 41.521400 -168.908800 90.000000 top
38 D33 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 44.061400 -168.908800 90.000000 top
39 D34 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 46.601400 -168.908800 90.000000 top
40 D35 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 49.141400 -168.908800 90.000000 top
41 D36 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 51.681400 -168.908800 90.000000 top
42 D37 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 54.221400 -168.908800 90.000000 top
43 D38 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 56.761400 -168.908800 90.000000 top
44 D39 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 59.301400 -168.908800 90.000000 top
45 D40 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 61.841400 -168.908800 90.000000 top
46 D41 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 64.389200 -168.909400 90.000000 top
47 D42 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 66.938000 -168.910600 90.000000 top
48 D43 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 69.478000 -168.910600 90.000000 top
49 D44 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 72.018000 -168.910600 90.000000 top
50 D45 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 74.558000 -168.910600 90.000000 top
51 D46 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 77.098000 -168.910600 90.000000 top
52 D47 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 79.638000 -168.910600 90.000000 top
53 D48 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 82.178000 -168.910600 90.000000 top
54 D49 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 84.718000 -168.910600 90.000000 top
55 D50 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 87.265800 -168.910600 90.000000 top
56 D51 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 89.814600 -168.909400 90.000000 top
57 D52 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 92.362400 -168.909400 90.000000 top
58 D53 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 94.902400 -168.909400 90.000000 top
59 D54 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 97.442400 -168.909400 90.000000 top
60 D55 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 99.982400 -168.909400 90.000000 top
61 D56 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 102.522400 -168.909400 90.000000 top
62 D57 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 105.062400 -168.909400 90.000000 top
63 D58 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 107.602400 -168.909400 90.000000 top
64 D59 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 110.142400 -168.909400 90.000000 top
65 D60 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 112.682400 -168.909400 90.000000 top
66 D71 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 106.350000 -184.150000 180.000000 top
67 D72 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 106.350000 -181.610000 180.000000 top
68 D73 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 91.110000 -181.610000 180.000000 top
69 D74 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 91.110000 -184.150000 180.000000 top
70 D75 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 75.870000 -184.150000 180.000000 top
71 D76 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 75.870000 -181.610000 180.000000 top
72 D77 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 60.630000 -181.610000 180.000000 top
73 D78 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 60.630000 -184.150000 180.000000 top
74 D79 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 45.390000 -184.150000 180.000000 top
75 D80 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 45.389400 -181.610000 180.000000 top
76 D81 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 45.390000 -135.890000 180.000000 top
77 D82 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 45.390000 -138.430000 180.000000 top
78 D83 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 60.630000 -138.430000 180.000000 top
79 D84 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 60.630000 -135.890000 180.000000 top
80 D85 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 75.870000 -135.890000 180.000000 top
81 D86 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 75.870000 -138.430000 180.000000 top
82 D87 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 91.110000 -138.430000 180.000000 top
83 D88 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 91.110000 -135.890000 180.000000 top
84 D89 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 106.350000 -135.890000 180.000000 top
85 D90 WS2812B LED_WS2812B-2020_PLCC4_2.0x2.0mm 106.350000 -138.430000 180.000000 top
86 FID1 Fiducial Fiducial_1mm_Mask2mm 47.930000 -179.070000 0.000000 top
87 FID5 Fiducial Fiducial_1mm_Mask2mm 103.810000 -179.070000 0.000000 top
88 FID6 Fiducial Fiducial_1mm_Mask2mm 104.572000 -107.696000 0.000000 top
89 J1 Debug PinHeader_1x03_P2.54mm_Vertical 92.380000 -121.285000 180.000000 top
90 J4 Breadboard Breadboard_CustomClips_Fallback 75.870000 -160.020000 0.000000 top
91 J5 USB_B_Mini USB_Mini-B_Wuerth_65100516121_Horizontal 110.001500 -111.943500 135.000000 top
92 J7 Conn_01x04_Pin GPIO_header 38.903974 -109.169200 -135.000000 top
93 J14 5V PINHEADER-1x1_vertical 55.296000 -106.578400 0.000000 top
94 SW2 SW_DP3T Slide_Switch_SSSS223600 37.388000 -120.936000 0.000000 top
95 TP18 GPIO 0 PINHEADER-1x1_vertical 115.113000 -116.840000 0.000000 top
96 TP20 LED OUT PINHEADER-1x1_vertical 95.936000 -106.426000 0.000000 top
97 TP21 LED IN PINHEADER-1x1_vertical 55.296000 -110.998000 0.000000 top
98 U1 LT1054 DIP-8_W7.62mm 80.442000 -122.428000 90.000000 top
99 U9 RP2040 RP2040-QFN-56 105.837000 -126.492000 0.000000 top

View 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",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,
1 Designator Footprint Quantity Value LCSC Part #
2 A1 Arduino_Nano 1 Arduino_Nano_v3.x
3 A2, B1, C26, D70, E1, F5, G1, H1, I1, J6, K1, L1 LQFP44_Tight 12 CH446Q
4 C1, C4, C6 1206 3 100uF
5 C10, C15 402 2 .1uF
6 C11, C12, C18, C19, C20, C21, C9 402 7 1u
7 C13, C14, C17, C22, C23, C24 402 6 27p
8 C16, C25 603 2 10uF
9 C2, C3, C5 CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder 3 10uF
10 C7, C8 603 2 10u
11 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
12 D61, D62, D65, D67, D68 D_SOD-323 5 1N5819
13 D63, D66 D_SOD-323 2 1N4148
14 D91 LED_PLCC2_reversemount 1 LED
15 FID1, FID2, FID3, FID4, FID5, FID6 Fiducial_1mm_Mask2mm 6 Fiducial
16 J1 PinHeader_1x03_P2.54mm_Vertical 1 Debug
17 J14 PINHEADER-1x1_vertical 1 5V
18 J4 Breadboard_CustomClips_Fallback 1 Breadboard
19 J5 USB_Mini-B_Wuerth_65100516121_Horizontal 1 USB_B_Mini
20 J7 GPIO_header 1 Conn_01x04_Pin
21 JP1 SolderJumper_2_Bridged_Small 1 NANO 3V3
22 JP13, JP14 SolderJumper_2_Bridged_Small 2 Jumper_2_Bridged
23 JP2 SolderJumper_2_Bridged_Small 1 NANO 5V
24 R1, R4 603 2
25 R10, R11, R12, R24, R5, R6, R7, R8, R9 402 9 1K
26 R13, R14, R16, R19 402 4 47K
27 R15 402 1 10K
28 R17, R29 402 2 68K
29 R18, R20 402 2 56.2K
30 R2, R3 402 2 22R
31 R21, R22, R23, R26, R27 402 5 2K
32 SW1 SW_SPST_TL3342 1 USB BOOT
33 SW2 Slide_Switch_SSSS223600 1 SW_DP3T
34 TP1 TestPoint_Pad_D1.0mm 1 GPIO 16
35 TP10 TestPoint_D0.75mm 1 ADC 1 IN
36 TP11 TestPoint_D0.75mm 1 ADC 2 IN
37 TP12 TestPoint_D0.75mm 1 ADC 3 IN
38 TP13 TestPoint_D0.75mm 1 -8V
39 TP14 TestPoint_D0.75mm 1 +8V
40 TP15 TestPoint_Pad_D1.0mm 1 LED OUT
41 TP16 TestPoint_Pad_D1.0mm 1 GPIO 0
42 TP17 TestPoint_Pad_D1.0mm 1 LED IN
43 TP18 PINHEADER-1x1_vertical 1 GPIO 0
44 TP19, TP25 TestPoint_Pad_D1.0mm 2 GND
45 TP2 TestPoint_Pad_D1.0mm 1 GPIO 17
46 TP20 PINHEADER-1x1_vertical 1 LED OUT
47 TP21 PINHEADER-1x1_vertical 1 LED IN
48 TP22, TP23 BackMaskWindows 2 TestPoint
49 TP3 TestPoint_Pad_D1.0mm 1 GPIO 18
50 TP4 TestPoint_Pad_D1.0mm 1 GPIO 19
51 TP5 TestPoint_D0.75mm 1 FB/SHDN
52 TP6 TestPoint_D0.75mm 1 VREF
53 TP7 TestPoint_D0.75mm 1 0-5V DAC
54 TP8 TestPoint_D0.75mm 1 +-8V DAC
55 TP9 TestPoint_D0.75mm 1 ADC 0 IN
56 U1 DIP-8_W7.62mm 1 LT1054
57 U10 SOIC-16_3.9x9.9mm_P1.27mm 1 L272D
58 U11, U7 SOIC-14_3.9x8.7mm_P1.27mm 2 LM324
59 U12 SOIC-8_3.9x4.9mm_P1.27mm 1 MCP4822
60 U2 SOT-223-3_TabPin2 1 NCP1117-3.3_SOT223
61 U4, U6 SOIC-8_3.9x4.9mm_P1.27mm 2 INA219
62 U8 SOIC-8_5.23x5.23mm_P1.27mm 1 W25Q128JVS
63 U9 RP2040-QFN-56 1 RP2040
64 Y1 Crystal_SMD_3225-4Pin_3.2x2.5mm 1 ABLS-12.000MHZ-B4-T

View 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
1 A1:1
2 A2:1
3 B1:1
4 C1:1
5 C10:1
6 C11:1
7 C12:1
8 C13:1
9 C14:1
10 C15:1
11 C16:1
12 C17:1
13 C18:1
14 C19:1
15 C2:1
16 C20:1
17 C21:1
18 C22:1
19 C23:1
20 C24:1
21 C25:1
22 C26:1
23 C3:1
24 C4:1
25 C5:1
26 C6:1
27 C7:1
28 C8:1
29 C9:1
30 D1:1
31 D10:1
32 D11:1
33 D12:1
34 D13:1
35 D14:1
36 D15:1
37 D16:1
38 D17:1
39 D18:1
40 D19:1
41 D2:1
42 D20:1
43 D21:1
44 D22:1
45 D23:1
46 D24:1
47 D25:1
48 D26:1
49 D27:1
50 D28:1
51 D29:1
52 D3:1
53 D30:1
54 D31:1
55 D32:1
56 D33:1
57 D34:1
58 D35:1
59 D36:1
60 D37:1
61 D38:1
62 D39:1
63 D4:1
64 D40:1
65 D41:1
66 D42:1
67 D43:1
68 D44:1
69 D45:1
70 D46:1
71 D47:1
72 D48:1
73 D49:1
74 D5:1
75 D50:1
76 D51:1
77 D52:1
78 D53:1
79 D54:1
80 D55:1
81 D56:1
82 D57:1
83 D58:1
84 D59:1
85 D6:1
86 D60:1
87 D61:1
88 D62:1
89 D63:1
90 D65:1
91 D66:1
92 D67:1
93 D68:1
94 D7:1
95 D70:1
96 D71:1
97 D72:1
98 D73:1
99 D74:1
100 D75:1
101 D76:1
102 D77:1
103 D78:1
104 D79:1
105 D8:1
106 D80:1
107 D81:1
108 D82:1
109 D83:1
110 D84:1
111 D85:1
112 D86:1
113 D87:1
114 D88:1
115 D89:1
116 D9:1
117 D90:1
118 D91:1
119 E1:1
120 F5:1
121 FID1:1
122 FID2:1
123 FID3:1
124 FID4:1
125 FID5:1
126 FID6:1
127 G1:1
128 H1:1
129 I1:1
130 J1:1
131 J14:1
132 J4:1
133 J5:1
134 J6:1
135 J7:1
136 JP1:1
137 JP13:1
138 JP14:1
139 JP2:1
140 K1:1
141 L1:1
142 R1:1
143 R10:1
144 R11:1
145 R12:1
146 R13:1
147 R14:1
148 R15:1
149 R16:1
150 R17:1
151 R18:1
152 R19:1
153 R2:1
154 R20:1
155 R21:1
156 R22:1
157 R23:1
158 R24:1
159 R26:1
160 R27:1
161 R29:1
162 R3:1
163 R4:1
164 R5:1
165 R6:1
166 R7:1
167 R8:1
168 R9:1
169 SW1:1
170 SW2:1
171 TP1:1
172 TP10:1
173 TP11:1
174 TP12:1
175 TP13:1
176 TP14:1
177 TP15:1
178 TP16:1
179 TP17:1
180 TP18:1
181 TP19:1
182 TP2:1
183 TP20:1
184 TP21:1
185 TP22:1
186 TP23:1
187 TP25:1
188 TP3:1
189 TP4:1
190 TP5:1
191 TP6:1
192 TP7:1
193 TP8:1
194 TP9:1
195 U1:1
196 U10:1
197 U11:1
198 U12:1
199 U2:1
200 U4:1
201 U6:1
202 U7:1
203 U8:1
204 U9:1
205 Y1:1

File diff suppressed because it is too large Load Diff

View 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,
1 Designator Footprint Quantity Value LCSC Part #
2 A1 Arduino_Nano 1 Arduino_Nano_v3.x
3 A2, B1, C26, D70, E1, F5, G1, H1, I1, J6, K1, L1 LQFP44_Tight 12 CH446Q
4 C1, C4, C6 1206 3 100uF
5 C10, C15 0402 2 .1uF
6 C11, C12, C18, C19, C20, C21, C9 0402 7 1u
7 C13, C14, C17, C22, C23, C24 0402 6 27p
8 C16, C25 0603 2 10uF
9 C2, C3, C5 CP_EIA-6032-20_AVX-F_Pad2.25x2.35mm_HandSolder 3 10uF
10 C7, C8 0603 2 10u
11 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
12 D61, D62, D65, D67, D68 D_SOD-323 5 1N5819
13 D63, D66 D_SOD-323 2 1N4148
14 D91 LED_PLCC2_reversemount 1 LED
15 FID1, FID2, FID3, FID4, FID5, FID6 Fiducial_1mm_Mask2mm 6 Fiducial
16 J1 PinHeader_1x03_P2.54mm_Vertical 1 Debug
17 J14 PINHEADER-1x1_vertical 1 5V
18 J4 Breadboard_CustomClips_Fallback 1 Breadboard
19 J5 USB_Mini-B_Wuerth_65100516121_Horizontal 1 USB_B_Mini
20 J7 GPIO_header 1 Conn_01x04_Pin
21 JP1 SolderJumper_2_Bridged_Small 1 NANO 3V3
22 JP13, JP14 SolderJumper_2_Bridged_Small 2 Jumper_2_Bridged
23 JP2 SolderJumper_2_Bridged_Small 1 NANO 5V
24 R1, R4 0603 2
25 R10, R11, R12, R24, R5, R6, R7, R8, R9 0402 9 1K
26 R13, R14, R16, R19 0402 4 47K
27 R15 0402 1 10K
28 R17, R29 0402 2 68K
29 R18, R20 0402 2 56.2K
30 R2, R3 0402 2 22R
31 R21, R22, R23, R26, R27 0402 5 2K
32 SW1 SW_SPST_TL3342 1 USB BOOT
33 SW2 Slide_Switch_SSSS223600 1 SW_DP3T
34 TP1 TestPoint_Pad_D1.0mm 1 GPIO 16
35 TP10 TestPoint_D0.75mm 1 ADC 1 IN
36 TP11 TestPoint_D0.75mm 1 ADC 2 IN
37 TP12 TestPoint_D0.75mm 1 ADC 3 IN
38 TP13 TestPoint_D0.75mm 1 -8V
39 TP14 TestPoint_D0.75mm 1 +8V
40 TP15 TestPoint_Pad_D1.0mm 1 LED OUT
41 TP16 TestPoint_Pad_D1.0mm 1 GPIO 0
42 TP17 TestPoint_Pad_D1.0mm 1 LED IN
43 TP18 PINHEADER-1x1_vertical 1 GPIO 0
44 TP19, TP25 TestPoint_Pad_D1.0mm 2 GND
45 TP2 TestPoint_Pad_D1.0mm 1 GPIO 17
46 TP20 PINHEADER-1x1_vertical 1 LED OUT
47 TP21 PINHEADER-1x1_vertical 1 LED IN
48 TP22, TP23 BackMaskWindows 2 TestPoint
49 TP3 TestPoint_Pad_D1.0mm 1 GPIO 18
50 TP4 TestPoint_Pad_D1.0mm 1 GPIO 19
51 TP5 TestPoint_D0.75mm 1 FB/SHDN
52 TP6 TestPoint_D0.75mm 1 VREF
53 TP7 TestPoint_D0.75mm 1 0-5V DAC
54 TP8 TestPoint_D0.75mm 1 +-8V DAC
55 TP9 TestPoint_D0.75mm 1 ADC 0 IN
56 U1 DIP-8_W7.62mm 1 LT1054
57 U10 SOIC-16_3.9x9.9mm_P1.27mm 1 L272D
58 U11, U7 SOIC-14_3.9x8.7mm_P1.27mm 2 LM324
59 U12 SOIC-8_3.9x4.9mm_P1.27mm 1 MCP4822
60 U2 SOT-223-3_TabPin2 1 NCP1117-3.3_SOT223
61 U4, U6 SOIC-8_3.9x4.9mm_P1.27mm 2 INA219
62 U8 SOIC-8_5.23x5.23mm_P1.27mm 1 W25Q128JVS
63 U9 RP2040-QFN-56 1 RP2040
64 Y1 Crystal_SMD_3225-4Pin_3.2x2.5mm 1 ABLS-12.000MHZ-B4-T

View 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
1 A1:1
2 A2:1
3 B1:1
4 C1:1
5 C10:1
6 C11:1
7 C12:1
8 C13:1
9 C14:1
10 C15:1
11 C16:1
12 C17:1
13 C18:1
14 C19:1
15 C2:1
16 C20:1
17 C21:1
18 C22:1
19 C23:1
20 C24:1
21 C25:1
22 C26:1
23 C3:1
24 C4:1
25 C5:1
26 C6:1
27 C7:1
28 C8:1
29 C9:1
30 D1:1
31 D10:1
32 D11:1
33 D12:1
34 D13:1
35 D14:1
36 D15:1
37 D16:1
38 D17:1
39 D18:1
40 D19:1
41 D2:1
42 D20:1
43 D21:1
44 D22:1
45 D23:1
46 D24:1
47 D25:1
48 D26:1
49 D27:1
50 D28:1
51 D29:1
52 D3:1
53 D30:1
54 D31:1
55 D32:1
56 D33:1
57 D34:1
58 D35:1
59 D36:1
60 D37:1
61 D38:1
62 D39:1
63 D4:1
64 D40:1
65 D41:1
66 D42:1
67 D43:1
68 D44:1
69 D45:1
70 D46:1
71 D47:1
72 D48:1
73 D49:1
74 D5:1
75 D50:1
76 D51:1
77 D52:1
78 D53:1
79 D54:1
80 D55:1
81 D56:1
82 D57:1
83 D58:1
84 D59:1
85 D6:1
86 D60:1
87 D61:1
88 D62:1
89 D63:1
90 D65:1
91 D66:1
92 D67:1
93 D68:1
94 D7:1
95 D70:1
96 D71:1
97 D72:1
98 D73:1
99 D74:1
100 D75:1
101 D76:1
102 D77:1
103 D78:1
104 D79:1
105 D8:1
106 D80:1
107 D81:1
108 D82:1
109 D83:1
110 D84:1
111 D85:1
112 D86:1
113 D87:1
114 D88:1
115 D89:1
116 D9:1
117 D90:1
118 D91:1
119 E1:1
120 F5:1
121 FID1:1
122 FID2:1
123 FID3:1
124 FID4:1
125 FID5:1
126 FID6:1
127 G1:1
128 H1:1
129 I1:1
130 J1:1
131 J14:1
132 J4:1
133 J5:1
134 J6:1
135 J7:1
136 JP1:1
137 JP13:1
138 JP14:1
139 JP2:1
140 K1:1
141 L1:1
142 R1:1
143 R10:1
144 R11:1
145 R12:1
146 R13:1
147 R14:1
148 R15:1
149 R16:1
150 R17:1
151 R18:1
152 R19:1
153 R2:1
154 R20:1
155 R21:1
156 R22:1
157 R23:1
158 R24:1
159 R26:1
160 R27:1
161 R29:1
162 R3:1
163 R4:1
164 R5:1
165 R6:1
166 R7:1
167 R8:1
168 R9:1
169 SW1:1
170 SW2:1
171 TP1:1
172 TP10:1
173 TP11:1
174 TP12:1
175 TP13:1
176 TP14:1
177 TP15:1
178 TP16:1
179 TP17:1
180 TP18:1
181 TP19:1
182 TP2:1
183 TP20:1
184 TP21:1
185 TP22:1
186 TP23:1
187 TP25:1
188 TP3:1
189 TP4:1
190 TP5:1
191 TP6:1
192 TP7:1
193 TP8:1
194 TP9:1
195 U1:1
196 U10:1
197 U11:1
198 U12:1
199 U2:1
200 U4:1
201 U6:1
202 U7:1
203 U8:1
204 U9:1
205 Y1:1

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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_ */

View 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

View 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_ */

View 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_ */

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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_ */

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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_ */

View 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
/** @} */

View File

@ -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

View File

@ -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_ */
/** @} */
/** @} */

View 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_ */

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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_ */

View File

@ -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 *)&notify.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

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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

View File

@ -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 */

View File

@ -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_ */

View 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

View 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_ */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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)));
} }
} }

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
{ {

View 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;
// }
}

View 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

View File

@ -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();
}
} }

View 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);
}
%}

View 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

View File

@ -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