4 |
5 | local html = require("html")
6 | require("usb_setup_parser")
7 | require("usb_register_class")
8 |
9 | local cls = {}
10 | cls.name = "DFU"
11 |
12 | local DFU_FUNCTIONAL_DESCRIPTOR = 0x21
13 |
14 | local struct_dfu_functional_descriptor = html.create_struct([[
15 | struct {
16 | uint8_t bLength; // {format = "dec"}
17 | uint8_t bDescriptorType;
18 | //bmAttributes;
19 | uint8_t bitCanDnload:1;
20 | uint8_t bitCanUpload:1;
21 | uint8_t bitMainfestationTolerant:1;
22 | uint8_t bitWillDetach:1;
23 | uint8_t reserved:4;
24 | uint16_t wDetachtimeOut; // {format = "dec"}
25 | uint16_t wTransferSize; // {format = "dec"}
26 | uint16_t bcdDFUVersion;
27 | }]], {
28 | bDescriptorType = {
29 | [DFU_FUNCTIONAL_DESCRIPTOR] = "DFU FUNCTIONAL",
30 | }
31 | }
32 | )
33 |
34 | function cls.descriptor_parser(data, offset, context)
35 | local t = data:byte(offset+1)
36 |
37 | if t == DFU_FUNCTIONAL_DESCRIPTOR then
38 | local rawData = data:sub(offset)
39 | local res = struct_dfu_functional_descriptor:build(rawData, "DFU Functionnal Descriptor")
40 | res.rawData = rawData
41 | return res
42 | end
43 | end
44 |
45 | local DFU_DETACH = 0
46 | local DFU_DNLOAD = 1
47 | local DFU_UPLOAD = 2
48 | local DFU_GETSTATUS = 3
49 | local DFU_CLRSTATUS = 4
50 | local DFU_GETSTATE = 5
51 | local DFU_ABORT = 6
52 |
53 | local dfu_request_names = {
54 | [DFU_DETACH] = "DFU_DETACH",
55 | [DFU_DNLOAD] = "DFU_DNLOAD",
56 | [DFU_UPLOAD] = "DFU_UPLOAD",
57 | [DFU_GETSTATUS] = "DFU_GETSTATUS",
58 | [DFU_CLRSTATUS] = "DFU_CLRSTATUS",
59 | [DFU_GETSTATE] = "DFU_GETSTATE",
60 | [DFU_ABORT] = "DFU_ABORT",
61 | }
62 |
63 | local dfu_request_wValue_render = {
64 | [DFU_DETACH] = html.create_field([[
65 | struct {
66 | // wValue
67 | uint16_t wTimeout:16; // {format = "dec"}
68 | }
69 | ]])[1],
70 | [DFU_DNLOAD] = html.create_field([[
71 | struct {
72 | // wValue
73 | uint16_t wBlockNum:16; // {format = "dec"}
74 | }
75 | ]])[1],
76 | [DFU_UPLOAD] = html.create_field([[
77 | struct {
78 | // wValue
79 | uint16_t wBlockNum:16; // {format = "dec"}
80 | }
81 | ]])[1],
82 | }
83 |
84 | function cls.parse_setup(setup, context)
85 | local cmd = setup.bRequest
86 | local name = dfu_request_names[cmd]
87 |
88 | if setup.recip ~= "Interface" or setup.type ~= "Class" or name == nil then
89 | return
90 | end
91 |
92 | setup.title = name
93 | setup.render.title = name
94 | setup.render.bRequest = name
95 |
96 | if cmd == DFU_DNLOAD or cmd == DFU_UPLOAD then
97 | setup.name = string.format("%d", setup.wValue)
98 | else
99 | setup.name = name
100 | end
101 |
102 | local wValue_render = dfu_request_wValue_render[setup.bRequest]
103 | if wValue_render then
104 | setup.render.wValue = wValue_render
105 | end
106 | end
107 |
108 | local struct_dfu_getstatus = html.create_struct([[
109 | struct {
110 | uint8_t bStatus;
111 | uint24_t bwPollTimeout; // {format = "dec"}
112 | uint8_t bState;
113 | uint8_t iString;
114 | }]], {
115 | bStatus = {
116 | [0x00] = "OK",
117 | [0x01] = "errTARGET",
118 | [0x02] = "errFILE",
119 | [0x03] = "errWRITE",
120 | [0x04] = "errERASE",
121 | [0x05] = "errCHECK_ERASED",
122 | [0x06] = "errPROG",
123 | [0x07] = "errVERIFY",
124 | [0x08] = "errADDRESS",
125 | [0x09] = "errNOTDONE",
126 | [0x0a] = "errFIRMWARE",
127 | [0x0b] = "errVENDOR",
128 | [0x0c] = "errUSBR",
129 | [0x0d] = "errPOR",
130 | [0x0e] = "errUNKNOWN",
131 | [0x0f] = "errSTALLEDPKT",
132 | },
133 | bState = {
134 | [0x00] = "appIDLE",
135 | [0x01] = "appDETACH",
136 | [0x02] = "dfuIDLE",
137 | [0x03] = "dfuDNLOAD-SYNC",
138 | [0x04] = "dfuDNBUSY",
139 | [0x05] = "dfuDNLOAD-IDLE",
140 | [0x06] = "dfuMANIFEST-SYNC",
141 | [0x07] = "dfuMANIFEST",
142 | [0x08] = "dfuMANIFEST-WAIT-RESET",
143 | [0x09] = "dfuUPLOAD-IDLE",
144 | [0x0a] = "dfuERROR",
145 | },
146 | }
147 | )
148 |
149 | function dfu_dnload_parser(setup, data, context)
150 | return string.format("DFU Download Data
\
151 | Display in data window, size = %d",
152 | setup.wLength)
153 | end
154 |
155 | function dfu_getstatus_parser(setup, data, context)
156 | return struct_dfu_getstatus:build(data, "DFU STATUS").html
157 | end
158 |
159 | local dfu_request_parsers = {
160 | [DFU_DNLOAD] = dfu_dnload_parser,
161 | [DFU_GETSTATUS] = dfu_getstatus_parser,
162 | }
163 |
164 | function cls.parse_setup_data(setup, data, context)
165 | local parser = dfu_request_parsers[setup.bRequest]
166 |
167 | if parser then
168 | return parser(setup, data, context)
169 | end
170 | end
171 |
172 | cls.bInterfaceClass = 0xFE
173 | cls.bInterfaceSubClass = 0x01
174 | cls.bInterfaceProtocol = 0x02
175 |
176 | function cls.get_name(desc, context)
177 | return {
178 | bInterfaceClass = "DFU",
179 | bInterfaceSubClass = "DFU",
180 | bInterfaceProtocol = "DFU",
181 | }
182 | end
183 |
184 | register_class_handler(cls)
185 | package.loaded["usb_class_dfu"] = cls
186 |
--------------------------------------------------------------------------------
/scripts/usb_class_hid.lua:
--------------------------------------------------------------------------------
1 | -- usb_class_hid.lua
2 |
3 | -- a typical class has these functions
4 | -- cls.parse_setup(setup, context), update setup.html, setup.name field in setup, and return it
5 | -- cls.parse_setup_data(setup, data, context) return a html to describe the data
6 | -- cls.on_transaction(self, param, data, needDetail, forceBegin) return macro_defs.RES_xxx
7 | -- cls.descriptor_parser(data, offset, context) return a parsed descriptor
8 | -- cls.get_name(descriptor, context) return a field name table
9 | -- HID class definition https://www.usb.org/sites/default/files/documents/hid1_11.pdf
10 |
11 | local html = require("html")
12 | local macro_defs = require("macro_defs")
13 | require("usb_setup_parser")
14 | require("usb_register_class")
15 |
16 | local fmt = string.format
17 | local unpack = string.unpack
18 | local cls = {}
19 | cls.name = "HID"
20 |
21 | local HID_DESCRIPTOR = 0x21
22 |
23 | local struct_hid_descriptor = html.create_struct([[
24 | struct{
25 | uint8_t bLength;
26 | uint8_t bDescriptorType; // HID descriptor
27 | uint16_t bcdHID;
28 | uint8_t bCountryCode;
29 | uint8_t bNumDescriptors;
30 | {
31 | uint8_t bDescriptorType; // {[0x22] = "Report Descriptor", [0x33] = "Physical Descriptor"}
32 | uint16_t wDescriptorLength; // {format="dec"}
33 | }[bNumDescriptors];
34 | }
35 | ]])
36 |
37 | function cls.descriptor_parser(data, offset, context)
38 | local t = data:byte(offset+1)
39 |
40 | if t == HID_DESCRIPTOR then
41 | local rawData = data:sub(offset)
42 | local res = struct_hid_descriptor:build(rawData, "HID Descriptor")
43 | res.rawData = rawData
44 | return res
45 | end
46 | end
47 |
48 | local KEY_A = 0x04
49 | local KEY_Z = 0x1d
50 | local KEY_1 = 0x1e
51 | local KEY_0 = 0x27
52 | local KEY_F1 = 0x3a
53 | local KEY_F12 = 0x45
54 | local KEY_ENTER = 0x28
55 | local num = "1234567890"
56 | local function key2str(key)
57 | if key == 0 then return '[None]'
58 | elseif key == 1 then return '[ERR_OVF]'
59 | elseif key >= KEY_A and key<= KEY_Z then
60 | return string.char( string.byte("A",1) + key - 4 )
61 | elseif key >= KEY_1 and key <= KEY_0 then
62 | local p = key-0x1e+1
63 | return num:sub(p,p)
64 | elseif key >= KEY_F1 and key <= KEY_F12 then
65 | return "F" .. (key - KEY_F1 + 1)
66 | elseif key == KEY_ENTER then
67 | return "Enter"
68 | end
69 | return "[unknown]"
70 | end
71 |
72 | _G.hid_key_name = key2str
73 |
74 | local field_wValue_hid_report = html.create_field([[
75 | struct{
76 | // wValue
77 | uint16_t ReportID:8;
78 | uint16_t ReportType:8; // {[1] = "Input", [2] = "Output", [3] = "Feature"}
79 | }
80 | ]])
81 |
82 | local field_wValue_hid_idle = html.create_field([[
83 | struct{
84 | // wValue
85 | uint16_t ReportID:8;
86 | uint16_t IdleValue:8;
87 | }
88 | ]])
89 |
90 | local struct_boot_mouse_data = html.create_struct([[
91 | struct{
92 | uint8_t button1:1; // {[0]="Released", [1]= "pressed"}
93 | uint8_t button2:1; // {[0]="Released", [1]= "pressed"}
94 | uint8_t button3:1; // {[0]="Released", [1]= "pressed"}
95 | uint8_t reserved:5;
96 | int8_t x; // {format = "dec"}
97 | int8_t y; // {format = "dec"}
98 | uint8_t reserved;
99 | }
100 | ]])
101 |
102 | local struct_boot_key_data = html.create_struct([[
103 | struct{
104 | // Modifier
105 | uint8_t LeftCtrl:1; // {[0]="Released", [1]= "pressed"}
106 | uint8_t LeftShift:1; // {[0]="Released", [1]= "pressed"}
107 | uint8_t LeftAlt:1; // {[0]="Released", [1]= "pressed"}
108 | uint8_t LeftMeta:1; // {[0]="Released", [1]= "pressed"}
109 | uint8_t RightCtrl:1; // {[0]="Released", [1]= "pressed"}
110 | uint8_t RightShift:1; // {[0]="Released", [1]= "pressed"}
111 | uint8_t RightAlt:1; // {[0]="Released", [1]= "pressed"}
112 | uint8_t RightMeta:1; // {[0]="Released", [1]= "pressed"}
113 | uint8_t reserved;
114 | {
115 | uint8_t key; // _G.hid_key_name
116 | }[6];
117 | }
118 | ]])
119 |
120 | local req2str = {
121 | [1] = "GET_REPORT",
122 | [2] = "GET_IDLE",
123 | [3] = "GET_PROTOCOL",
124 | [9] = "SET_REPORT",
125 | [10] = "SET_IDLE",
126 | [11] = "SET_PROTOCOL",
127 | }
128 |
129 | function cls.parse_setup(setup, context)
130 | if setup.recip ~= "Interface" or setup.type ~= "Class" then
131 | return
132 | end
133 | local bRequest_desc = req2str[setup.bRequest] or "HID Unknown Req"
134 | local wValueField = nil
135 | if bRequest_desc == "GET_REPORT" or bRequest_desc == "SET_REPORT" then
136 | wValueField = field_wValue_hid_report
137 | elseif bRequest_desc == "GET_IDLE" or bRequest_desc == "SET_IDLE" then
138 | wValueField = field_wValue_hid_idle
139 | elseif bRequest_desc == "GET_PROTOCOL" or bRequest_desc == "SET_PROTOCOL" then
140 | end
141 | setup.name = bRequest_desc
142 | setup.title = "HID Request"
143 | setup.render.title = "HID Req " .. bRequest_desc
144 | setup.render.bRequest = bRequest_desc
145 | setup.render.wValue = wValueField
146 | setup.render.wIndex = "Interface"
147 | end
148 |
149 | function cls.parse_setup_data(setup, data, context)
150 | local s = req2str[setup.bRequest]
151 | if s then
152 | return "" .. s .." data
"
153 | end
154 | if (setup.bRequest == macro_defs.GET_DESCRIPTOR) and ((setup.wValue>>8) == macro_defs.REPORT_DESC) then
155 | return "Report descriptor
Todo: parse it to analyze endpoint data"
156 | end
157 | return nil
158 | end
159 |
160 | local function key_on_transaction(self, param, data, needDetail, forceBegin)
161 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
162 | if pid ~= macro_defs.PID_IN then
163 | return macro_defs.RES_NONE
164 | end
165 | if ack ~= macro_defs.PID_ACK then
166 | return macro_defs.RES_NONE
167 | end
168 |
169 | if needDetail then
170 | local infoHtml = struct_boot_key_data:build(data, "Maybe Boot Keyboard").html
171 | return macro_defs.RES_BEGIN_END, self.upv.make_xact_res("HID Key", infoHtml, data), self.upv.make_xfer_res({
172 | title = "HID Key Data",
173 | name = "HID Data",
174 | desc = "Boot Keyboard",
175 | status = "success",
176 | infoHtml = infoHtml,
177 | data = data,
178 | })
179 | end
180 | return macro_defs.RES_BEGIN_END
181 | end
182 |
183 | local function mouse_on_transaction(self, param, data, needDetail, forceBegin)
184 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
185 | if pid ~= macro_defs.PID_IN then
186 | return macro_defs.RES_NONE
187 | end
188 | if ack ~= macro_defs.PID_ACK then
189 | return macro_defs.RES_NONE
190 | end
191 | if needDetail then
192 | local infoHtml = struct_boot_mouse_data:build(data, "Maybe Boot Mouse").html
193 | return macro_defs.RES_BEGIN_END, self.upv.make_xact_res("HID Mouse",infoHtml,data), self.upv.make_xfer_res({
194 | title = "HID Mouse Data",
195 | name = "HID Data",
196 | desc = "Boot Mouse",
197 | status = "success",
198 | infoHtml = infoHtml,
199 | data = data,
200 | })
201 | end
202 | return macro_defs.RES_BEGIN_END
203 | end
204 |
205 | local function hid_on_transaction(self, param, data, needDetail, forceBegin)
206 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
207 | if pid ~= macro_defs.PID_IN and pid ~= macro_defs.PID_OUT then
208 | return macro_defs.RES_NONE
209 | end
210 | if ack ~= macro_defs.PID_ACK then
211 | return macro_defs.RES_NONE
212 | end
213 | if needDetail then
214 | local infoHtml = "HID data
Need decode with report descriptor"
215 | return macro_defs.RES_BEGIN_END, self.upv.make_xact_res("Hub Notify", infoHtml, data), self.upv.make_xfer_res({
216 | title = "HID Data",
217 | name = "HID Data",
218 | desc = "HID Data",
219 | status = "success",
220 | infoHtml = infoHtml,
221 | data = data,
222 | })
223 | end
224 | return macro_defs.RES_BEGIN_END
225 | end
226 |
227 | local function xunpack(fmt, data, index, length)
228 | index = index or 1
229 | length = length or 1
230 | if #data + 1 < index + length then
231 | return 0
232 | end
233 | return unpack(fmt, data, index)
234 | end
235 |
236 | --function cls.descriptor_parser = nil
237 |
238 | cls.bInterfaceClass = 3
239 | cls.bInterfaceSubClass = nil
240 | cls.bInterfaceProtocol = nil
241 | cls.endpoints = { EP_IN("HID Data") }
242 |
243 | function cls.get_name(desc, context)
244 | local subName = "Reserved"
245 | if desc.bInterfaceSubClass == 0 then
246 | subName = "No SubClass"
247 | elseif desc.bInterfaceSubClass == 1 then
248 | subName = "Boot Interface"
249 | end
250 | local bootName = "Reserved"
251 | if desc.bInterfaceProtocol == 0 then
252 | bootName = "None"
253 | elseif desc.bInterfaceProtocol == 1 then
254 | bootName = "Keyboard"
255 | elseif desc.bInterfaceProtocol == 2 then
256 | bootName = "Mouse"
257 | end
258 | return {
259 | bInterfaceClass = "HID",
260 | bInterfaceSubClass = subName,
261 | bInterfaceProtocol = bootName,
262 | }
263 | end
264 |
265 | local function build_hid_class(name, handler, subClass, protool)
266 | local r = {}
267 | for k,v in pairs(cls) do
268 | r[k] = v
269 | end
270 | r.name = name
271 | r.on_transaction = handler
272 | r.bInterfaceSubClass = subClass
273 | r.bInterfaceProtocol = protool
274 | return r
275 | end
276 |
277 | register_class_handler(build_hid_class("HID Boot Key", key_on_transaction, 1, 1))
278 | register_class_handler(build_hid_class("HID Boot Mouse", mouse_on_transaction, 1, 2))
279 | cls.endpoints = { EP_IN("Incoming Data", true), EP_OUT("Outgoing Data", true) }
280 | register_class_handler(build_hid_class("HID User", hid_on_transaction, nil, nil))
281 |
282 | package.loaded["usb_class_hid"] = cls
283 |
--------------------------------------------------------------------------------
/scripts/usb_class_hub.lua:
--------------------------------------------------------------------------------
1 | -- usb_class_hub.lua
2 |
3 | -- a typical class has these functions
4 | -- cls.parse_setup(setup, context), update setup.html, setup.name field in setup, and return it
5 | -- cls.parse_setup_data(setup, data, context) return a html to describe the data
6 | -- cls.on_transaction(self, param, data, needDetail, forceBegin) return macro_defs.RES_xxx
7 | -- cls.descriptor_parser(data, offset, context) return a parsed descriptor
8 | -- cls.get_name(descriptor, context) return a field name table
9 | -- HUB class definition usb_20.pdf 11.23, 11.24
10 |
11 | local html = require("html")
12 | local macro_defs = require("macro_defs")
13 | require("usb_register_class")
14 | local fmt = string.format
15 | local unpack = string.unpack
16 | local cls = {}
17 | cls.name = "HUB Notify Data"
18 |
19 | local CLEAR_TT_BUFFER = 8
20 | local RESET_TT = 9
21 | local GET_TT_STATE = 10
22 | local STOP_TT = 11
23 |
24 | local function hub_feat_sel(wValue)
25 | if wValue == 0 then return "C_HUB_LOCAL_POWER"
26 | elseif wValue == 1 then return "C_HUB_OVER_CURRENT"
27 | end
28 | return "Unknown Feature selector"
29 | end
30 |
31 | local port_feature_list = {
32 | [0 ] = "PORT_CONNECTION" ,
33 | [1 ] = "PORT_ENABLE" ,
34 | [2 ] = "PORT_SUSPEND" ,
35 | [3 ] = "PORT_OVER_CURRENT" ,
36 | [4 ] = "PORT_RESET" ,
37 | [8 ] = "PORT_POWER" ,
38 | [9 ] = "PORT_LOW_SPEED" ,
39 | [16] = "C_PORT_CONNECTION" ,
40 | [17] = "C_PORT_ENABLE" ,
41 | [18] = "C_PORT_SUSPEND" ,
42 | [19] = "C_PORT_OVER_CURRENT" ,
43 | [20] = "C_PORT_RESET" ,
44 | [21] = "PORT_TEST" ,
45 | [22] = "PORT_INDICATOR" ,
46 | }
47 |
48 | local function port_feat_sel(value)
49 | return port_feature_list[value] or "Unknwon selector"
50 | end
51 |
52 | _G.hub_port_feature_selection = port_feature_list
53 |
54 | local field_wIndex_port_feature = html.create_field([[
55 | strcut{
56 | // wIndex
57 | uint16_t port:8;
58 | uint16_t selection:8; // _G.hub_port_feature_selection
59 | }
60 | ]])
61 |
62 | local struct_hub_status = html.create_struct([[
63 | struct{
64 | // wHubStatus
65 | uint16_t LocalPowerSource:1; // {[0] = "good", [1] = "lost" }
66 | uint16_t OverCurrent:1; // {[0] = "No Over-current", [1] = "over-current condition exists" }
67 | uint16_t Reserved:14;
68 | // wHubChange
69 | uint16_t LocalPowerSourceChg:1; // {[0] = "no change", [1] = "changed"}
70 | uint16_t OverCurrentChg:1; // {[0] = "no change", [1] = "changed"}
71 | uint16_t Reserved:14;
72 | }
73 | ]])
74 |
75 | local function hubStatus(data, context)
76 | data = data .. "\x00\x00\x00\x00"
77 | local s,cs = unpack("I2I2", data)
78 | return struct_hub_status:build(data, "Hub Status & Change Status").html
79 | end
80 |
81 | local struct_port_status = html.create_struct([[
82 | struct{
83 | // wPortStatus
84 | uint16_t Connect:1; // {[0] = "disconnected", [1] = "connected" }
85 | uint16_t Enabled:1; // {[0] = "Disabled", [1] = "Enabled", }
86 | uint16_t Suspend:1; // {[0] = "Not Suspend", [1] = "Suspend/Resuming", }
87 | uint16_t OverCurrent:1; // {[0] = "No", [1] = "Over-current occur", }
88 | uint16_t Reset:1; // {[0] = "not asserted", [1] = "Asserted", }
89 | uint16_t Reserved:3;
90 | uint16_t Power:1; // {[0] = "Power off", [1] = "Power on", }
91 | uint16_t LowSpeed:1; // {[0] = "Full/High Speed", [1] = "Low Speed", }
92 | uint16_t HighSpeed:1; // {[0] = "Full Speed", [1] = "High Speed", }
93 | uint16_t TestMode:1; // {[0] = "Not Test Mode", [1] = "Test Mode", }
94 | uint16_t IndicatorCtrl:1; // {[0] = "default", [1] = "Software control", }
95 | uint16_t Reserved:3;
96 | // wPortChange
97 | uint16_t ConnectChg:1; // {[0] = "No Change", [1] = "Changed", }
98 | uint16_t EnabledChg:1; // {[0] = "No Change", [1] = "Disable by Port_Error condition", }
99 | uint16_t SuspendChg:1; // {[0] = "No Change", [1] = "Resuming Complete", }
100 | uint16_t OverCurrentChg:1; // {[0] = "No Change", [1] = "Over-current changed", }
101 | uint16_t ResetChg:1; // {[0] = "No Change", [1] = "Reset complete", }
102 | uint16_t Reserved:13;
103 | }
104 | ]])
105 |
106 | local function portStatus(data, context)
107 | data = data .. "\x00\x00\x00\x00"
108 | local s,cs = unpack("I2I2", data)
109 | return struct_port_status:build(data, "Port Status & Change Status").html
110 | end
111 |
112 | local field_clear_tt = html.create_field([[
113 | struct{
114 | // wIndex
115 | uint16_t EndpointNumber:4;
116 | uint16_t DeviceAddr:7;
117 | uint16_t EndpointType:2; // {[0] = "Control", [1] = "ISO", [2] = "Bulk", [3] = "Interrupt" }
118 | uint16_t Reserved:2;
119 | uint16_t Direction:1; // {[0] = "OUT", [1] = "IN" }
120 | }
121 | ]])
122 |
123 | local hub_render = {
124 | [macro_defs.GET_DESCRIPTOR] = {"Hub Get Desc" },
125 | [macro_defs.SET_DESCRIPTOR] = {"Hub Set Desc" },
126 | [macro_defs.CLEAR_FEATURE] = {"Hub Clear Feat", hub_feat_sel },
127 | [macro_defs.SET_FEATURE] = {"Hub Set Feat", hub_feat_sel },
128 | [macro_defs.GET_STATUS] = {"Hub Get Status"},
129 | }
130 |
131 | local port_render = {
132 | [macro_defs.CLEAR_FEATURE] = {"Port Clear Feat", port_feat_sel, field_wIndex_port_feature},
133 | [macro_defs.SET_FEATURE] = {"Port Set Feat", port_feat_sel, field_wIndex_port_feature},
134 | [macro_defs.GET_STATUS] = {"Port Get Status", nil, "port" },
135 | [CLEAR_TT_BUFFER] = {"Hub Clear TT", field_clear_tt},
136 | [RESET_TT] = {"Hub Reset TT", nil, "Port"},
137 | [GET_TT_STATE] = {"Hub Get TT State", "TT Flags", "Port"},
138 | [STOP_TT] = {"Hub Stop TT", nil, "Port"},
139 | }
140 |
141 | function cls.parse_setup(setup, context)
142 | if setup.recip == "Device" and setup.type == "Class" then
143 | setup.name = hub_render[setup.bRequest] and hub_render[setup.bRequest][1]
144 | setup.render.wValue = hub_render[setup.bRequest] and hub_render[setup.bRequest][2]
145 | setup.render.wIndex = hub_render[setup.bRequest] and hub_render[setup.bRequest][3]
146 | elseif setup.recip == "Other" and setup.type == "Class" then
147 | setup.name = port_render[setup.bRequest] and port_render[setup.bRequest][1]
148 | setup.render.wValue = port_render[setup.bRequest] and port_render[setup.bRequest][2]
149 | setup.render.wIndex = port_render[setup.bRequest] and port_render[setup.bRequest][3]
150 | else
151 | return
152 | end
153 | setup.render.bRequest = setup.name
154 | setup.render.title = "HUB Requset" .. (setup.name or "")
155 | setup.title = "HUB Requset"
156 | end
157 |
158 | function cls.parse_setup_data(setup, data, context)
159 | if setup.recip == "Device" and setup.type == "Class" then
160 | if setup.bRequest == macro_defs.GET_DESCRIPTOR or setup.bRequest == macro_defs.SET_DESCRIPTOR then
161 | local r = cls.descriptor_parser(data, 1, context)
162 | if r then return r.html end
163 | elseif setup.bRequest == macro_defs.GET_STATUS then
164 | return hubStatus(data, context)
165 | end
166 | elseif setup.recip == "Other" and setup.type == "Class" then
167 | if setup.bRequest == macro_defs.GET_STATUS then
168 | return portStatus(data, context)
169 | end
170 | end
171 | return nil
172 | end
173 |
174 | local struct_hub_desc = html.create_struct([[
175 | struct{
176 | uint8_t bLength;
177 | uint8_t bDescriptorType; // _G.get_descriptor_name
178 | uint8_t bNbrPorts;
179 | // wHubCharacteristics
180 | uint16_t LogicalPowerSwitchingMode:2; // {[0] = "Global", [1] = "Individual", [2] = "Reserved.", [3] = "Reserved."}
181 | uint16_t Compound Device:1; // {[0] = "not part of a compound device", [1] = "part of a compound device"}
182 | uint16_t OverCurrentProtMode:2; // {[0] = "Global", [1] = "Individual", [2] = "No Protection", [2] = "No Protection"}
183 | uint16_t TT_ThinkTime:2; // {[0] = "8 FS bit", [1] = "16 FS bit", [2] = "24 FS bit", [3] = "32 FS bit"}
184 | uint16_t PortIndicators:1; // {[0] = "not supported", [1] = "supported"}
185 | uint16_t reserved:8;
186 | uint8_t bPwrOn2PwrGood; // unit 2ms
187 | uint8_t bHubContrCurrent; // unit 1mA
188 | uint8_t DeviceRemovable;
189 | uint8_t PortPwrCtrlMask;
190 | }
191 | ]])
192 |
193 | function cls.descriptor_parser(data, offset, context)
194 | if unpack("I1", data, offset + 1) ~= macro_defs.HUB_DESC then
195 | return nil
196 | end
197 | return struct_hub_desc:build(data:sub(offset), "Hub Descriptor")
198 | end
199 |
200 | cls.bInterfaceClass = 0x09
201 | cls.bInterfaceSubClass = 0x00
202 | cls.bInterfaceProtocol = nil
203 | cls.endpoints = { EP_IN("Hub Notify") }
204 |
205 | function cls.on_transaction(self, param, data, needDetail, forceBegin)
206 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
207 | if pid ~= macro_defs.PID_IN then
208 | return macro_defs.RES_NONE
209 | end
210 | if needDetail then
211 | return macro_defs.RES_BEGIN_END, self.upv.make_xact_res("Hub Notify"), self.upv.make_xfer_res({
212 | title = "Hub Notify Data",
213 | name = "Notify Data",
214 | desc = "Notify Data",
215 | status = "success",
216 | infoHtml = "
Hub Notify data
",
217 | data = data,
218 | })
219 | end
220 | return macro_defs.RES_BEGIN_END
221 | end
222 |
223 | function cls.get_name(desc, context)
224 | local proto = "Hub Protocol"
225 | if desc.bInterfaceProtocol == 1 then
226 | proto = "Single TT Protocol"
227 | elseif desc.bInterfaceProtocol == 2 then
228 | proto = "Multiple TT Protocol"
229 | end
230 | return {
231 | bInterfaceClass = "Hub Class",
232 | bInterfaceSubClass = "Hub Sub Class",
233 | bInterfaceProtocol = proto,
234 | }
235 | end
236 | register_class_handler(cls)
237 | package.loaded["usb_class_hub"] = cls
238 |
--------------------------------------------------------------------------------
/scripts/usb_class_msc_bot.lua:
--------------------------------------------------------------------------------
1 | -- usb_class_msc_bot.lua
2 |
3 | -- a typical class has these functions
4 | -- cls.parse_setup(setup, context), update setup.html, setup.name field in setup, and return it
5 | -- cls.parse_setup_data(setup, data, context) return a html to describe the data
6 | -- cls.on_transaction(self, param, data, needDetail, forceBegin) return macro_defs.RES_xxx
7 | -- cls.descriptor_parser(data, offset, context) return a parsed descriptor
8 | -- cls.get_name(descriptor, context) return a field name table
9 | -- MSC class definition https://www.usb.org/sites/default/files/usbmassbulk_10.pdf
10 |
11 | local html = require("html")
12 | local macro_defs = require("macro_defs")
13 | require("usb_setup_parser")
14 | require("usb_register_class")
15 |
16 | local scsi = require("decoder_scsi")
17 |
18 | local fmt = string.format
19 | local unpack = string.unpack
20 | local cls = {}
21 | cls.name = "MSC BOT"
22 |
23 | local BOT_GET_MAX_LUN = 0xfe
24 | local BOT_RESET = 0xff
25 |
26 | function cls.parse_setup(setup, context)
27 | if setup.recip ~= "Interface" or setup.type ~= "Class" then
28 | return
29 | end
30 | local bRequest_desc = "MSC Unknown Request"
31 | if setup.bRequest == BOT_GET_MAX_LUN then
32 | bRequest_desc = "Get Max LUN"
33 | elseif setup.bRequest == BOT_RESET then
34 | bRequest_desc = "BOT Reset"
35 | end
36 | setup.name = bRequest_desc
37 | setup.title = "MSC Request"
38 | setup.render.title = "MSC Request " .. bRequest_desc
39 | setup.render.bRequest = bRequest_desc
40 | end
41 |
42 | function cls.parse_setup_data(setup, data, context)
43 | if setup.bRequest == BOT_GET_MAX_LUN then
44 | local lun = unpack("I1", data)
45 | return "Max Logic Unit Number " .. lun .."
"
46 | end
47 | return nil
48 | end
49 |
50 | function cls.on_transaction(self, param, data, needDetail, forceBegin)
51 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
52 | local context = self:get_context(needDetail)
53 | self.addr = addr
54 | context.state = context.state or macro_defs.ST_CBW
55 | if forceBegin then
56 | context.state = macro_defs.ST_CBW
57 | end
58 | if #data == 31 and data:sub(1,4) == "USBC" then
59 | context.state = macro_defs.ST_CBW
60 | end
61 | if #data == 13 and data:sub(1,4) == "USBS" then
62 | context.state = macro_defs.ST_CSW
63 | end
64 |
65 | if context.state == macro_defs.ST_CBW then
66 | if #data ~= 31 then
67 | return macro_defs.RES_NONE
68 | end
69 | if ack ~= macro_defs.PID_ACK then
70 | return macro_defs.RES_NONE
71 | end
72 | local xfer_len = unpack("I4", data, 9)
73 | if xfer_len > 0 then
74 | context.state = macro_defs.ST_DATA
75 | else
76 | context.state = macro_defs.ST_CSW
77 | end
78 | context.data = ""
79 | context.xfer_len = xfer_len
80 | if needDetail then
81 | context.cbw = scsi.parse_cmd(data, self)
82 | context.infoHtml = context.cbw.html
83 | context.title = "BOT SCSI CMD"
84 | context.name = "SCSI CMD"
85 | context.desc = context.cbw.name
86 | context.status = "incomp"
87 | return macro_defs.RES_BEGIN, self.upv.make_xact_res("CBW", context.cbw.html, data), self.upv.make_xfer_res(context)
88 | end
89 | return macro_defs.RES_BEGIN
90 | elseif context.state == macro_defs.ST_DATA then
91 | if ack == macro_defs.PID_STALL then
92 | context.state = macro_defs.ST_CSW
93 | if needDetail then
94 | context.status = "stall"
95 | return macro_defs.RES_MORE, self.upv.make_xact_res("SCSI Stall", "", data), self.upv.make_xfer_res(context)
96 | end
97 | return macro_defs.RES_MORE
98 | end
99 | context.data = context.data .. data
100 | if self.upv:is_short_packet(addr, ep, data) then
101 | context.state = macro_defs.ST_CSW
102 | elseif #context.data == context.xfer_len then
103 | context.state = macro_defs.ST_CSW
104 | end
105 | if needDetail then
106 | if context.state == macro_defs.ST_CSW then
107 | context.infoHtml = (context.infoHtml or "") .. scsi.parse_data(context.cbw, context.data, self)
108 | end
109 | return macro_defs.RES_MORE, self.upv.make_xact_res("SCSI DATA", "", data), self.upv.make_xfer_res(context)
110 | end
111 | return macro_defs.RES_MORE
112 | elseif context.state == macro_defs.ST_CSW then
113 | if ack == macro_defs.PID_STALL then
114 | return macro_defs.RES_MORE
115 | end
116 | if ack ~= macro_defs.PID_ACK then
117 | return macro_defs.RES_END
118 | end
119 | if #data ~= 13 then
120 | return macro_defs.RES_END
121 | end
122 | if needDetail then
123 | local status = scsi.parse_status(context.cbw, data, self)
124 | context.infoHtml = (context.infoHtml or "") .. status.html
125 | context.data = context.data or ""
126 | context.title = context.title or ""
127 | context.name = context.name or ""
128 | context.desc = context.desc or ""
129 | context.status = status.status
130 | return macro_defs.RES_END, self.upv.make_xact_res("CSW", status.html, data), self.upv.make_xfer_res(context)
131 | end
132 | return macro_defs.RES_END
133 | else
134 | context.state = macro_defs.ST_CBW
135 | return macro_defs.RES_NONE
136 | end
137 | end
138 |
139 |
140 | cls.bInterfaceClass = 0x08
141 | cls.bInterfaceSubClass = 0x06
142 | cls.bInterfaceProtocol = 0x50
143 | cls.endpoints = { EP_IN("Incoming Data"), EP_OUT("Outgoing Data") }
144 |
145 | function cls.get_name(desc, context)
146 | return {
147 | bInterfaceClass = "Mass Storage Class",
148 | bInterfaceSubClass = "SCSI transparent command set",
149 | bInterfaceProtocol = "Bulk Only Transport",
150 | }
151 | end
152 |
153 | register_class_handler(cls)
154 | package.loaded["usb_class_msc_bot"] = cls
155 |
--------------------------------------------------------------------------------
/scripts/usb_control_transfer.lua:
--------------------------------------------------------------------------------
1 | -- usb_control_transfer.lua
2 | -- control transfer parser
3 |
4 | local html = require("html")
5 | local fmt = string.format
6 | local unpack = string.unpack
7 | local setup_parser = require("usb_setup_parser")
8 | local macro_defs = require("macro_defs")
9 |
10 | local function on_transaction(self, param, data, needDetail, forceBegin)
11 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
12 | local context
13 | if needDetail then
14 | self.detail = self.detail or {}
15 | context = self.detail
16 | else
17 | self.simple = self.simple or {}
18 | context = self.simple
19 | end
20 | self.addr = addr
21 | self.interfaces = self.interfaces or {}
22 | self.interface_data = self.interface_data or {}
23 | context.state = context.state or macro_defs.ST_SETUP
24 | if forceBegin or (pid == macro_defs.PID_SETUP) then
25 | context.state = macro_defs.ST_SETUP
26 | end
27 |
28 | if (context.state ~= macro_defs.ST_SETUP) and (ack ~= macro_defs.PID_ACK) then
29 | if ack == macro_defs.PID_STALL then
30 | context.status = "stall"
31 | if needDetail then
32 | return macro_defs.RES_END, self.upv.make_xact_res("Stall Ack", "", data), self.upv.make_xfer_res(context)
33 | else
34 | return macro_defs.RES_END
35 | end
36 | end
37 | context.status = "error"
38 | if needDetail then
39 | return macro_defs.RES_END, self.upv.make_xact_res("Error Ack", "", data), self.upv.make_xfer_res(context)
40 | else
41 | return macro_defs.RES_END
42 | end
43 | end
44 |
45 | if context.state == macro_defs.ST_SETUP then
46 | -- setup stage
47 | if (pid == macro_defs.PID_SETUP) and (#data == 8) then
48 | -- got setup packet
49 | local len = unpack("I2", data, 7)
50 | if len == 0 then
51 | if data:byte(1) > 127 then
52 | -- wrong condition
53 | context.state = macro_defs.ST_STATUS_OUT
54 | else
55 | context.state = macro_defs.ST_STATUS_IN
56 | end
57 | else
58 | if data:byte(1) > 127 then
59 | context.state = macro_defs.ST_DATA_IN
60 | else
61 | context.state = macro_defs.ST_DATA_OUT
62 | end
63 | end
64 | context.setup = {
65 | data = data
66 | }
67 | context.data = ""
68 | if needDetail then
69 | context.setup = setup_parser.parse_setup(context.setup.data, self)
70 | context.infoHtml = context.setup.html
71 | context.title = "Control Out"
72 | if data:byte(1) > 127 then
73 | context.title = "Control In"
74 | end
75 | context.name = context.setup.title or "Request"
76 | context.desc = context.setup.name
77 | context.status = "incomp"
78 | return macro_defs.RES_BEGIN, self.upv.make_xact_res("Setup", context.infoHtml, data), self.upv.make_xfer_res(context)
79 | end
80 | return macro_defs.RES_BEGIN
81 | else
82 | return macro_defs.RES_NONE
83 | end
84 | elseif context.state == macro_defs.ST_DATA_IN then
85 | -- data in stage
86 | if (pid == macro_defs.PID_IN) then
87 | context.data = context.data .. data
88 | if needDetail then
89 | local t = setup_parser.parse_data(context.setup, context.data, self)
90 | return macro_defs.RES_MORE, self.upv.make_xact_res("Data In", t, context.data), self.upv.make_xfer_res(context)
91 | end
92 | return macro_defs.RES_MORE
93 | elseif pid == macro_defs.PID_OUT then
94 | -- status out stage
95 | local setupType = context.setup.data:sub(1,4)
96 | context.state = macro_defs.ST_SETUP
97 | if needDetail then
98 | local t = setup_parser.parse_data(context.setup, context.data, self)
99 | context.infoHtml = context.infoHtml .. (t or "")
100 | context.status = "success"
101 | return macro_defs.RES_END, self.upv.make_xact_res("Status Out", "", context.data), self.upv.make_xfer_res(context)
102 | else
103 | if addr ~= 0 then
104 | if setupType == "\x80\x06\x00\x01" then
105 | self:setup_device_handler(context.data)
106 | elseif setupType:sub(1,2) == "\x80\x06" and setupType:sub(4) == "\x02"then
107 | self:setup_interface_handler(context.data)
108 | end
109 | end
110 | end
111 | return macro_defs.RES_END
112 | else
113 | return macro_defs.RES_NONE
114 | end
115 | elseif context.state == macro_defs.ST_DATA_OUT then
116 | -- data out stage
117 | if pid == macro_defs.PID_OUT then
118 | context.data = context.data .. data
119 | if needDetail then
120 | local t = setup_parser.parse_data(context.setup, context.data, self)
121 | return macro_defs.RES_MORE, self.upv.make_xact_res("Data Out", t, context.data), self.upv.make_xfer_res(context)
122 | end
123 | return macro_defs.RES_MORE
124 | elseif pid == macro_defs.PID_IN then
125 | -- status in stage
126 | context.state = macro_defs.ST_SETUP
127 | if needDetail then
128 | local t = setup_parser.parse_data(context.setup, context.data, self)
129 | context.infoHtml = context.infoHtml .. t
130 | context.status = "success"
131 | return macro_defs.RES_END, self.upv.make_xact_res("Status In", "", context.data), self.upv.make_xfer_res(context)
132 | end
133 | return macro_defs.RES_END
134 | else
135 | return macro_defs.RES_NONE
136 | end
137 | elseif context.state == macro_defs.ST_STATUS_IN then
138 | -- status in stage
139 | if pid == macro_defs.PID_IN then
140 | context.state = macro_defs.ST_SETUP
141 | if needDetail then
142 | context.status = "success"
143 | return macro_defs.RES_END, self.upv.make_xact_res("Status In", "", context.data), self.upv.make_xfer_res(context)
144 | end
145 | return macro_defs.RES_END
146 | else
147 | return macro_defs.RES_NONE
148 | end
149 | elseif context.state == macro_defs.ST_STATUS_OUT then
150 | -- status out stage
151 | if pid == macro_defs.PID_OUT then
152 | context.state = macro_defs.ST_SETUP
153 | if needDetail then
154 | context.status = "success"
155 | return macro_defs.RES_END, self.upv.make_xact_res("Status Out", "", context.data), self.upv.make_xfer_res(context)
156 | end
157 | return macro_defs.RES_END
158 | else
159 | return macro_defs.RES_NONE
160 | end
161 | else
162 | context.state = macro_defs.ST_SETUP
163 | return macro_defs.RES_NONE
164 | end
165 | end
166 |
167 | local function get_endpoint_interface(self, endpoint)
168 | return self.endpoint_itf[endpoint], self.endpoint_alt[endpoint]
169 | end
170 |
171 | local function get_interface_class(self, index)
172 | return self.interfaces[index]
173 | end
174 |
175 | local function get_interface_data(self, itf, alt)
176 | local res = self.interface_data[itf]
177 | if not res then
178 | self.interface_data[itf] = {}
179 | end
180 | return self.interface_data[itf]
181 | end
182 |
183 | local function current_interface_data(self)
184 | return get_interface_data(self, self.current_itf_n, self.current_itf_alt)
185 | end
186 |
187 | local function set_current_interface(self, itf_desc)
188 | self.current_itf_n = itf_desc:byte(3)
189 | self.current_itf_alt = itf_desc:byte(4)
190 | end
191 |
192 | local function current_device(self)
193 | return self.device_parser or {}
194 | end
195 | -- get class by full descriptor info
196 | local function find_class(self, itf_desc, iad_desc)
197 | local iad = nil
198 | if iad_desc then
199 | iad = iad_desc.rawData
200 | end
201 | return self.upv:find_parser_by_interface(itf_desc.rawData, iad)
202 | end
203 |
204 | -- setup device handler by device descriptor
205 | local function setup_device_handler(self, device_desc)
206 | if #device_desc == 18 then
207 | local mps, vid, pid = unpack("I1I2I2", device_desc, 8)
208 | self.upv:set_device_mps(self.addr, mps)
209 | self.device_parser = self.upv:find_parser_by_vid_pid(vid, pid)
210 | local devClass = self.upv:find_parser_by_interface("1" .. device_desc)
211 | if devClass then
212 | self.device_parser = self.device_parser or {}
213 | self.device_parser.deviceClass = self.device_parser.deviceClass or devClass
214 | end
215 | end
216 | end
217 |
218 | -- setup interface handler by config descriptor
219 | local function setup_interface_handler(self, config_desc)
220 | -- fast parse the config descriptor
221 | local i = 1
222 | local itf_desc = nil
223 | local iad_desc = nil
224 | local iad_cnt = 0
225 | local total = #config_desc
226 | local cur_itf_n = 0
227 | local cur_alt_n = 0
228 | local decoder = nil
229 | local eps = {}
230 | local mpss = {}
231 | while (i + 1) < total do
232 | local len = config_desc:byte(i)
233 | local t = config_desc:byte(i+1)
234 | if len < 2 then break end
235 | if len + i > #config_desc + 1 then
236 | break
237 | end
238 | if t == macro_defs.IAD_DESC then
239 | if #eps > 0 then
240 | if decoder then
241 | self.upv:setup_decoder(self.addr, eps, decoder, mpss)
242 | end
243 | eps = {}
244 | mpss = {}
245 | end
246 | iad_desc = config_desc:sub(i, i + len - 1)
247 | iad_cnt = iad_desc:byte(4)
248 | elseif t == macro_defs.INTERFACE_DESC then
249 | if #eps > 0 then
250 | if decoder then
251 | self.upv:setup_decoder(self.addr, eps, decoder, mpss)
252 | end
253 | eps = {}
254 | mpss = {}
255 | end
256 | itf_desc = config_desc:sub(i, i + len - 1)
257 | cur_itf_n = itf_desc:byte(3)
258 | cur_alt_n = itf_desc:byte(4)
259 | self.interface_data[cur_itf_n] = self.interface_data[cur_itf_n] or {}
260 | local handler = nil
261 | decoder = nil
262 | if self.device_parser and self.device_parser.get_interface_handler then
263 | handler = self.device_parser.get_interface_handler(self, cur_itf_n)
264 | end
265 | if not handler then
266 | handler = self.upv:find_parser_by_interface(itf_desc, iad_desc)
267 | end
268 | if handler then
269 | self.interfaces[cur_itf_n] = handler
270 | if handler.make_decoder then
271 | decoder = handler.make_decoder(self.upv)
272 | end
273 | end
274 | if iad_cnt > 0 then
275 | iad_cnt = iad_cnt - 1
276 | end
277 | if iad_cnt == 0 then
278 | iad_desc = nil
279 | end
280 | elseif t == macro_defs.ENDPOINT_DESC then
281 | local ep = config_desc:byte(i+2)
282 | self.endpoint_itf = self.endpoint_itf or {}
283 | self.endpoint_itf[ep] = cur_itf_n
284 | self.endpoint_alt = self.endpoint_alt or {}
285 | self.endpoint_alt[ep] = cur_alt_n
286 | local mps = config_desc:byte(i+4) + (config_desc:byte(i+5) * 256)
287 | eps[#eps+1] = ep
288 | mpss[#mpss+1] = mps
289 | end
290 | i = i + len
291 | end
292 |
293 | if #eps > 0 then
294 | if decoder then
295 | self.upv:setup_decoder(self.addr, eps, decoder, mpss)
296 | end
297 | eps = {}
298 | mpss = {}
299 | end
300 |
301 | end
302 |
303 | local function make_control_xfer_decoder(upv)
304 | local res = {}
305 | res.on_transaction = on_transaction
306 | res.setup_device_handler = setup_device_handler
307 | res.setup_interface_handler = setup_interface_handler
308 | res.get_interface_class = get_interface_class
309 | res.get_endpoint_interface = get_endpoint_interface
310 | res.current_interface_data = current_interface_data
311 | res.set_current_interface = set_current_interface
312 | res.get_interface_data = get_interface_data
313 | res.current_device = current_device
314 | res.find_class = find_class
315 | res.upv = upv
316 | return res
317 | end
318 |
319 | package.loaded["usb_control_transfer"] = make_control_xfer_decoder
320 |
--------------------------------------------------------------------------------
/scripts/usb_descriptor_parser.lua:
--------------------------------------------------------------------------------
1 | -- usb_descriptor_parser.lua
2 | local macro_defs = require("macro_defs")
3 | local html = require("html")
4 | local parser = {}
5 | local descTable = {}
6 | local fmt = string.format
7 | local unpack = string.unpack
8 |
9 | local last_vid = 0
10 | _G.render_vid = function(vid)
11 | last_vid = vid
12 | return 'Who\'s that?'
13 | end
14 | _G.render_pid = function(pid)
15 | return 'What\'s it?'
16 | end
17 |
18 | local function make_desc_parser(name, info)
19 | local builder = html.create_struct(info)
20 | return function(data, offset, context)
21 | local rawData = data:sub(offset)
22 | local res = builder:build(rawData, name .. " Descriptor")
23 | res.rawData = rawData
24 | return res
25 | end
26 | end
27 |
28 | local parse_unknown_desc = make_desc_parser("Unknown", [[
29 | struct {
30 | uint8_t bLength; // {format = "dec"}
31 | uint8_t bDescriptorType; // _G.get_descriptor_name
32 | uint8_t data[bLength-2];
33 | }
34 | ]])
35 |
36 | descTable[macro_defs.DEVICE_DESC] = make_desc_parser("Device", [[
37 | uint8_t bLength; // {format = "dec"}
38 | uint8_t bDescriptorType; // _G.get_descriptor_name
39 | uint16_t bcdUSB;
40 | uint8_t bDeviceClass;
41 | uint8_t bDeviceSubClass;
42 | uint8_t bDeviceProtocol;
43 | uint8_t bMaxPacketSize;
44 | uint16_t idVendor; // _G.render_vid
45 | uint16_t idProduct; // _G.render_pid
46 | uint16_t bcdDevice;
47 | uint8_t iManufacturer;
48 | uint8_t iProduct;
49 | uint8_t iSerial;
50 | uint8_t bNumConfigurations;
51 | ]])
52 |
53 |
54 | descTable[macro_defs.DEV_QUAL_DESC] = make_desc_parser("Device Qualifier", [[
55 | uint8_t bLength; // {format = "dec"}
56 | uint8_t bDescriptorType; // _G.get_descriptor_name
57 | uint16_t bcdUSB;
58 | uint8_t bDeviceClass;
59 | uint8_t bDeviceSubClass;
60 | uint8_t bDeviceProtocol;
61 | uint8_t bMaxPacketSize;
62 | uint8_t bNumConfigurations;
63 | uint8_t bReserved;
64 | ]])
65 |
66 | descTable[macro_defs.CFG_DESC] = make_desc_parser("Config", [[
67 | uint8_t bLength; // {format = "dec"}
68 | uint8_t bDescriptorType; // _G.get_descriptor_name
69 | uint16_t wTotalLength;
70 | uint8_t bNumInterfaces;
71 | uint8_t bConfigurationValue;
72 | uint8_t iConfiguration;
73 | // bmAttributes
74 | uint8_t Reserved:5;
75 | uint8_t RemoteWakeup:1; { [0]="No", [1]="Yes" }
76 | uint8_t SelfPowered:1; { [0]="No", [1]="Yes" }
77 | uint8_t Reserved:1;
78 | uint8_t bMaxPower; // {format="dec", comment = "x 2mA"}
79 | ]])
80 |
81 | descTable[macro_defs.STRING_DESC] = make_desc_parser("String",[[
82 | uint8_t bLength; // {format = "dec"}
83 | uint8_t bDescriptorType; // _G.get_descriptor_name
84 | uint16_t wString[bLength/2-1]; // {format = "unicode"}
85 | ]])
86 |
87 | local currentInterface = {}
88 | _G.get_InterfaceClass = function()
89 | return currentInterface.bInterfaceClass or ""
90 | end
91 | _G.get_InterfaceSubClass = function()
92 | return currentInterface.bInterfaceSubClass or ""
93 | end
94 | _G.get_InterfaceProtocol = function()
95 | return currentInterface.bInterfaceProtocol or ""
96 | end
97 |
98 | descTable[macro_defs.INTERFACE_DESC] = make_desc_parser("Interface", [[
99 | uint8_t bLength; // {format = "dec"}
100 | uint8_t bDescriptorType; // _G.get_descriptor_name
101 | uint8_t bInterfaceNumber;
102 | uint8_t bAlternateSetting;
103 | uint8_t bNumEndpoints;
104 | uint8_t bInterfaceClass; // _G.get_InterfaceClass
105 | uint8_t bInterfaceSubClass; // _G.get_InterfaceSubClass
106 | uint8_t bInterfaceProtocol; // _G.get_InterfaceProtocol
107 | uint8_t iInterface;
108 | ]])
109 |
110 | descTable[macro_defs.ENDPOINT_DESC] = make_desc_parser("Endpoint", [[
111 | uint8_t bLength; // {format = "dec"}
112 | uint8_t bDescriptorType; // _G.get_descriptor_name
113 | // bEndpointAddress
114 | uint8_t EndpointAddress:4;
115 | uint8_t Reserved:3;
116 | uint8_t Direction:1; // {[0] ="OUT", [1]="IN"}
117 | // bmAttributes
118 | uint8_t Type:2; // {[0]="Control", [1]="Isochronous", [2]="Bulk", [3]="Interrupt"}
119 | uint8_t SyncType:2; // {[0]="No Synchonisation", [1]="Asynchronous", [2]="Adaptive", [3]="Synchronous"}
120 | uint8_t UsageType:2; // {[0]="Data Endpoint", [1]="Feedback Endpoint", [2]="Explicit Feedback Data Endpoint", [3]="Reserved"}
121 | uint8_t PacketPerFrame:2;// {[0]="1", [1]="2", [2]="3", [3]="Reserved"}
122 | uint16_t wMaxPacketSize; // {format = "dec"}
123 | uint8_t bInterval;
124 | ]])
125 |
126 | descTable[macro_defs.IAD_DESC] = make_desc_parser("IAD", [[
127 | uint8_t bLength; // {format = "dec"}
128 | uint8_t bDescriptorType; // _G.get_descriptor_name
129 | uint8_t bFirstInterface;
130 | uint8_t bInterfaceCount;
131 | uint8_t bFunctionClass;
132 | uint8_t bFunctionSubClass;
133 | uint8_t bFunctionProtocol;
134 | uint8_t iFunction;
135 | ]])
136 |
137 | function parser.parse(data, context)
138 | local info = {}
139 | local offset = 1
140 | local lastInterface = nil
141 | local lastIad = nil
142 | local lastIadCount = 0
143 | while offset < #data do
144 | local t = data:byte(offset+1)
145 | local parseFunc = descTable[t]
146 | local desc = nil
147 |
148 | if parseFunc then --standard descriptor
149 | desc = parseFunc(data, offset, context)
150 | if desc.bDescriptorType == macro_defs.INTERFACE_DESC then
151 | context:set_current_interface(desc.rawData)
152 | local cls = context:find_class(desc, lastIad)
153 | if cls and cls.get_name then
154 | currentInterface = cls.get_name(desc, context)
155 | desc = parseFunc(data, offset, context)
156 | currentInterface = {}
157 | end
158 | lastInterface = desc
159 |
160 | if lastIadCount > 0 then
161 | lastIadCount = lastIadCount - 1
162 | end
163 | if lastIadCount == 0 then
164 | lastIad = nil
165 | end
166 | elseif desc.bDescriptorType == macro_defs.IAD_DESC then
167 | lastIad = desc
168 | lastIadCount = desc.bInterfaceCount
169 | end
170 | elseif lastInterface then
171 | local cls = context:find_class(lastInterface, lastIad)
172 | if cls and cls.descriptor_parser then
173 | desc = cls.descriptor_parser(data, offset, context)
174 | end
175 | end
176 |
177 | -- use the default parse function.
178 | desc = desc or parse_unknown_desc(data, offset, context)
179 |
180 | offset = offset + desc.bLength
181 | info.html = info.html or ""
182 | info.html = info.html .. desc.html
183 | info[#info+1] = desc
184 | if desc.bLength < 2 then
185 | break
186 | end
187 | end
188 | --gotDescriptor(info, context)
189 | return info
190 | end
191 |
192 | package.loaded["usb_descriptor_parser"] = parser
193 |
194 |
--------------------------------------------------------------------------------
/scripts/usb_device_aw_efex.lua:
--------------------------------------------------------------------------------
1 | -- allwinner sunxi FEL and FES decode
2 |
3 | -- protocol are defined in linux-sunxi.org, about more informations:
4 | -- FEL: https://linux-sunxi.org/FEL/Protocol
5 | -- FES: https://linux-sunxi.org/FES
6 |
7 | -- qianfan Zhao
8 |
9 | local html = require("html")
10 | local macro_defs = require("macro_defs")
11 | require("usb_register_class")
12 |
13 | local fmt = string.format
14 | local unpack = string.unpack
15 |
16 | local DIRECTION_DEVICE_TO_HOST = 0x11
17 | local DIRECTION_HOST_TO_DEVICE = 0x12
18 |
19 | local struct_fel_awuc = html.create_struct([[
20 | struct {
21 | uint32_t magic;
22 | uint32_t tag;
23 | uint32_t data_transfer_len;
24 | uint16_t unused1;
25 | uint8_t unused2;
26 | uint8_t cmd_len;
27 | uint8_t direction;
28 | uint8_t unused3;
29 | uint32_t data_len;
30 | uint8_t unused4[10];
31 | }]], {
32 | direction = {
33 | [0x00] = "RESERVED",
34 | [DIRECTION_DEVICE_TO_HOST] = "-> Host",
35 | [DIRECTION_HOST_TO_DEVICE] = "-> Device",
36 | format = "hex",
37 | }
38 | }
39 | )
40 |
41 | -- AWUC command used in FEL, started with AWUC, 32 bytes.
42 | local function parse_fel_awuc(data, self)
43 | local cbw = {}
44 |
45 | if self.is_new_cmd == false and #data ~= 32 then
46 | cbw.html = "Wrong FEL AWUC length
"
47 | return cbw
48 | end
49 |
50 | self.cbw_direction = unpack("I1", data, 17)
51 | cbw = struct_fel_awuc:build(data, "FEL AWUC")
52 | cbw.name = "FEL"
53 |
54 | return cbw
55 | end
56 |
57 | local struct_awus = html.create_struct([[
58 | struct {
59 | uint32_t magic;
60 | uint32_t tag;
61 | uint32_t residue;
62 | uint8_t status;
63 | }]], {
64 | status = {
65 | [0] = "Passed",
66 | [1] = "Failed",
67 | }
68 | }
69 | )
70 |
71 | local function parse_awus(cbw, data, self)
72 | local csw = {}
73 |
74 | csw.status = "error"
75 | csw.name = "AWUS"
76 |
77 | if #data ~= 13 then
78 | csw.html = "Wrong AWUS length
"
79 | return csw
80 | end
81 |
82 | local r = struct_awus:build(data, "AWUS")
83 | csw.html = r.html
84 |
85 | if r.status == 0 then
86 | csw.status = "success"
87 | end
88 |
89 | return csw
90 | end
91 |
92 | -- FEL command sets:
93 | local FEL_CMD_VERIFY = 0x0001
94 | local FEL_CMD_SWITCH_ROLE = 0x0002
95 | local FEL_CMD_IS_READY = 0x0003
96 | local FEL_CMD_GET_CMD_SET_VER = 0x0004
97 | local FEL_CMD_DISCONNECT = 0x0010
98 | local FEL_CMD_DOWNLOAD = 0x0101
99 | local FEL_CMD_EXEC = 0x0102
100 | local FEL_CMD_READ = 0x0103
101 |
102 | -- FES command sets:
103 | local FES_CMD_TRANSFER = 0x0201
104 | local FES_CMD_DOWNLOAD = 0x0206
105 | local FES_CMD_UPLOAD = 0x0207
106 | local FES_CMD_QUERY_STORAGE = 0x0209
107 | local FES_CMD_FLASH_SET_ON = 0x020a
108 | local FES_CMD_VERIFY_VALUE = 0x020c
109 | local FES_CMD_VERIFY_STATUS = 0x020d
110 |
111 | local command_names = {
112 | [FEL_CMD_VERIFY] = "Verify",
113 | [FEL_CMD_SWITCH_ROLE] = "Switch",
114 | [FEL_CMD_IS_READY] = "Ready?",
115 | [FEL_CMD_GET_CMD_SET_VER] = "Version",
116 | [FEL_CMD_DISCONNECT] = "Disconnect",
117 | [FEL_CMD_DOWNLOAD] = "Download",
118 | [FEL_CMD_EXEC] = "Exec",
119 | [FEL_CMD_READ] = "Read",
120 | [FES_CMD_TRANSFER] = "Transfer",
121 | [FES_CMD_DOWNLOAD] = "Download",
122 | [FES_CMD_UPLOAD] = "Upload",
123 | [FES_CMD_QUERY_STORAGE] = "Storage?",
124 | [FES_CMD_FLASH_SET_ON] = "FlashON",
125 | [FES_CMD_VERIFY_VALUE] = "VerifyVal",
126 | [FES_CMD_VERIFY_STATUS] = "VerifySt",
127 |
128 | -- not a command, it is reported by device.
129 | -- create it to make the parser happy.
130 | [0xffff] = "Status",
131 | }
132 |
133 | local function default_cmd_parser(data, self)
134 | local cmd = unpack("I2", data)
135 |
136 | return string.format("%s
", command_names[cmd] or "???")
137 | end
138 |
139 | local function cmd_build_html(cmd, data, b)
140 | local name = command_names[cmd] or "???"
141 |
142 | return b:build(data, name)
143 | end
144 |
145 | local function parse_fel_cmd_verify(data, self)
146 | -- AWUC + VERIFY + AWUS
147 | -- AWUC + VERIFY RESULT(to host) 32B + AWUS
148 | self.trans_len = 32
149 | return default_cmd_parser(data, self)
150 | end
151 |
152 | local struct_fel_cmd_download = html.create_struct([[
153 | struct {
154 | uint32_t cmd;
155 | uint32_t addr;
156 | uint32_t len;
157 | uint32_t unused;
158 | }]]
159 | )
160 |
161 | local function parse_fel_cmd_download(data, self)
162 | local b = cmd_build_html(FEL_CMD_DOWNLOAD, data, struct_fel_cmd_download)
163 | self.trans_len = b.len
164 |
165 | return b.html
166 | end
167 |
168 | local function parse_fel_cmd_exec(data, self)
169 | self.trans_len = 0
170 | -- FEL_CMD_EXEC has the same struct with FEL_CMD_DOWNLOAD
171 | return cmd_build_html(FEL_CMD_EXEC, data, struct_fel_cmd_download).html
172 | end
173 |
174 | local function parse_fel_cmd_read(data, self)
175 | -- FEL_CMD_READ has the same struct with FEL_CMD_DOWNLOAD
176 | local b = cmd_build_html(FEL_CMD_READ, data, struct_fel_cmd_download)
177 |
178 | self.trans_len = b.len
179 | return b.html
180 | end
181 |
182 | local struct_fes_cmd_transfer = html.create_struct([[
183 | struct {
184 | uint16_t cmd;
185 | uint16_t tag;
186 | uint32_t addr;
187 | uint32_t len;
188 | uint8_t logic_unit:4;
189 | uint8_t media_index:4;
190 | uint8_t res:4;
191 | uint8_t dir:2;
192 | uint8_t ooc:2;
193 | uint16_t unused;
194 | }]], {
195 | dir = {
196 | [0] = "download",
197 | [1] = "download",
198 | [2] = "upload",
199 | }
200 | }
201 | )
202 |
203 | local function parse_fes_cmd_transfer(data, self)
204 | self.trans_len = unpack("I4", data, 9)
205 |
206 | return cmd_build_html(FES_CMD_TRANSFER, data, struct_fes_cmd_transfer).html
207 | end
208 |
209 | -- parse the command host send to device
210 | local fel_command_parses = {
211 | [FEL_CMD_VERIFY] = parse_fel_cmd_verify,
212 | [FEL_CMD_SWITCH_ROLE] = default_cmd_parser,
213 | [FEL_CMD_IS_READY] = default_cmd_parser,
214 | [FEL_CMD_GET_CMD_SET_VER] = default_cmd_parser,
215 | [FEL_CMD_DISCONNECT] = default_cmd_parser,
216 | [FEL_CMD_DOWNLOAD] = parse_fel_cmd_download,
217 | [FEL_CMD_EXEC] = parse_fel_cmd_exec,
218 | [FEL_CMD_READ] = parse_fel_cmd_read,
219 | [FES_CMD_TRANSFER] = parse_fes_cmd_transfer,
220 | }
221 |
222 | local struct_fes_trans_param = html.create_struct([[
223 | struct {
224 | uint16_t cmd;
225 | uint16_t tag;
226 | uint32_t addr;
227 | uint32_t len;
228 | uint32_t data_type;
229 | }]], {
230 | data_type = {
231 | [0x7f00] = "DRAM",
232 | [0x7f01] = "MBR",
233 | [0x7f02] = "BOOT1",
234 | [0x7f03] = "BOOT0",
235 | [0x7f04] = "ERASE",
236 | [0x7f05] = "PMU",
237 | [0x7f06] = "UNSEQ_READ",
238 | [0x7f07] = "UNSEQ_WRITE",
239 | [0x7f10] = "FULLIMG_SIZE",
240 | [0x17f00] = "(finish)DRAM",
241 | [0x17f01] = "(finish)MBR",
242 | [0x17f02] = "(finish)BOOT1",
243 | [0x17f03] = "(finish)BOOT0",
244 | [0x17f04] = "(finish)ERASE",
245 | [0x17f05] = "(finish)PMU",
246 | [0x17f06] = "(finish)UNSEQ_READ",
247 | [0x17f07] = "(finish)UNSEQ_WRITE",
248 | [0x17f10] = "(finish)FULLIMG_SIZE",
249 | }
250 | }
251 | )
252 |
253 | local function parse_fes_cmd_download(data, self)
254 | self.trans_len = unpack("I4", data, 9)
255 |
256 | return cmd_build_html(FES_CMD_DOWNLOAD, data, struct_fes_trans_param).html
257 | end
258 |
259 | local struct_fes_cmd_verify_status = html.create_struct([[
260 | struct {
261 | uint16_t cmd;
262 | uint16_t tag;
263 | uint32_t start;
264 | uint32_t size;
265 | uint32_t data_tag;
266 | }]]
267 | )
268 |
269 | local function parse_fes_cmd_verify_status(data, self)
270 | self.trans_len = 12
271 | self.cbw_direction = DIRECTION_DEVICE_TO_HOST
272 | return cmd_build_html(FES_CMD_VERIFY_STATUS, data, struct_fes_cmd_verify_status).html
273 | end
274 |
275 | local function parse_fes_cmd_query_storage(data, self)
276 | self.trans_len = 4
277 | self.cbw_direction = DIRECTION_DEVICE_TO_HOST
278 |
279 | return default_cmd_parser(data, self)
280 | end
281 |
282 | local function parse_fes_cmd_flash_set_on(data, self)
283 | self.trans_len = 0
284 |
285 | return default_cmd_parser(data, self)
286 | end
287 |
288 | local struct_fes_cmd_verify_value = html.create_struct([[
289 | struct {
290 | uint16_t cmd;
291 | uint16_t tag;
292 | uint32_t start;
293 | uint32_t size;
294 | uint32_t unused;
295 | }]]
296 | )
297 |
298 | local function parse_fes_cmd_verify_value(data, self)
299 | self.trans_len = 12
300 | self.cbw_direction = DIRECTION_DEVICE_TO_HOST
301 |
302 | return cmd_build_html(FES_CMD_VERIFY_VALUE, data, struct_fes_cmd_verify_value).html
303 | end
304 |
305 | local fes_command_parses = {
306 | [FES_CMD_DOWNLOAD] = parse_fes_cmd_download,
307 | [FES_CMD_VERIFY_STATUS] = parse_fes_cmd_verify_status,
308 | [FES_CMD_QUERY_STORAGE] = parse_fes_cmd_query_storage,
309 | [FES_CMD_FLASH_SET_ON] = parse_fes_cmd_flash_set_on,
310 | [FES_CMD_VERIFY_VALUE] = parse_fes_cmd_verify_value,
311 | }
312 |
313 | -- AWUC command used in FES, 20 bytes and ending with AWUC
314 | local function parse_fes_awuc(data, self)
315 | local cbw = { }
316 | local cmd = unpack("I2", data)
317 | local f = fes_command_parses[cmd]
318 |
319 | self.last_cmd = cmd
320 | cbw.name = command_names[cmd] or "???"
321 |
322 | -- set the default direction HOST_TO_DEVICE, the parse function
323 | -- should change the direction if it is DEVICE_TO_HOST.
324 | self.cbw_direction = DIRECTION_HOST_TO_DEVICE
325 |
326 | if f then
327 | cbw.html = f(data, self)
328 | else
329 | cbw.html = "doesn't support now
"
330 | end
331 |
332 | return cbw
333 | end
334 |
335 | local struct_fel_cmd_data_verify = html.create_struct([[
336 | struct {
337 | uint8_t magic[8];
338 | uint32_t platform_id_hw;
339 | uint32_t platform_id_fw;
340 | uint16_t mode;
341 | uint8_t phoenix_data_flag;
342 | uint8_t phoenix_data_len;
343 | uint32_t phoenix_data_start_addr;
344 | uint8_t unused[2];
345 | }]]
346 | )
347 |
348 | local function parse_fes_cmd_data_verify(data, self)
349 | return cmd_build_html(FEL_CMD_VERIFY, data, struct_fel_cmd_data_verify).html
350 | end
351 |
352 | local function parse_fes_cmd_data_switch_role(data, self)
353 | return default_cmd_parser(data, self)
354 | end
355 |
356 | local struct_fel_cmd_data_is_ready = html.create_struct([[
357 | struct {
358 | uint16_t state;
359 | uint16_t interval_ms;
360 | uint8_t unused[12];
361 | }]], {
362 | state = {
363 | [0x00] = "NULL",
364 | [0x01] = "BUSY",
365 | [0x02] = "READY",
366 | [0x03] = "FAIL",
367 | format = "dec",
368 | }
369 | }
370 | )
371 |
372 | local function parse_fes_cmd_data_is_ready(data, self)
373 | return cmd_build_html(FEL_CMD_IS_READY, data, struct_fel_cmd_data_is_ready).html
374 | end
375 |
376 | local function parse_fes_cmd_data_disconnect(data, self)
377 | return default_cmd_parser(data, self)
378 | end
379 |
380 | local struct_fes_cmd_verify_status = html.create_struct([[
381 | struct {
382 | uint32_t tag;
383 | uint32_t fes_crc;
384 | uint32_t media_crc;
385 | }]]
386 | )
387 |
388 | local function parse_fes_cmd_verify_status(data, self)
389 | return cmd_build_html(FES_CMD_VERIFY_STATUS, data, struct_fes_cmd_verify_status).html
390 | end
391 |
392 | local struct_fes_cmd_data_query_storage = html.create_struct([[
393 | struct {
394 | uint32_t storage;
395 | }]], {
396 | storage = {
397 | [0] = "NAND",
398 | [1] = "SD",
399 | [2] = "EMMC",
400 | [3] = "NOR",
401 | }
402 | }
403 | )
404 |
405 | local function parse_fes_cmd_data_query_storage(data, self)
406 | return cmd_build_html(FES_CMD_QUERY_STORAGE, data, struct_fes_cmd_data_query_storage).html
407 | end
408 |
409 | -- parse the command device response to host
410 | local command_data_parses = {
411 | [FEL_CMD_VERIFY] = parse_fes_cmd_data_verify,
412 | [FEL_CMD_SWITCH_ROLE] = parse_fes_cmd_data_is_ready,
413 | [FEL_CMD_IS_READY] = parse_fel_cmd_is_ready,
414 | [FEL_CMD_DISCONNECT] = parse_fel_cmd_disconnect,
415 |
416 | [FES_CMD_VERIFY_STATUS] = parse_fes_cmd_verify_status,
417 | [FES_CMD_QUERY_STORAGE] = parse_fes_cmd_data_query_storage,
418 |
419 | -- VERIFY_VALUE has the same response with VERIFY_STATUS
420 | [FES_CMD_VERIFY_VALUE] = parse_fes_cmd_verify_status,
421 | }
422 |
423 | local struct_app_report_status = html.create_struct([[
424 | struct {
425 | uint16_t mark;
426 | uint16_t tag;
427 | uint8_t status;
428 | uint8_t unused[3];
429 | }]]
430 | )
431 |
432 | local function parse_app_report_status(data, self)
433 | self.last_cmd = 0xffff
434 | return cmd_build_html(self.last_cmd, data, struct_app_report_status).html
435 | end
436 |
437 | local function parse_data(cbw, data, self)
438 | local default_debug_info = string.format("Debug informations
\
439 | direction: 0x%x
\
440 | last_cmd: 0x%x
\
441 | trans_len: %d
\
442 | data_size: %d",
443 | self.cbw_direction or 0xFFFFFFFF,
444 | self.last_cmd or 0xFFFFFFFF,
445 | self.trans_len or -1,
446 | #data
447 | )
448 |
449 | if self.cbw_direction == DIRECTION_HOST_TO_DEVICE then
450 | if self.last_cmd == FES_CMD_TRANSFER or
451 | self.last_cmd == FES_CMD_DOWNLOAD or
452 | self.last_cmd == FEL_CMD_DOWNLOAD then
453 | if self.trans_len == #data then
454 | local length = self.trans_len
455 |
456 | self.trans_len = 0
457 |
458 | return string.format("TRANSFER DATA(to device)
\
459 | See data Window
\
460 | length = %d
", length)
461 | else
462 | -- the command and transfer data doesn't match,
463 | -- clear state machine and try again
464 | self.last_cmd = 0
465 | self.trans_len = 0
466 | parse_data(cbw, data, self)
467 | end
468 | elseif self.is_new_cmd == false then -- FEL formater
469 | if #data == 16 then -- commnad
470 | local op = unpack("I2", data)
471 | local f = fel_command_parses[op]
472 |
473 | if f then
474 | self.last_cmd = op
475 | return f(data, self)
476 | end
477 | end
478 | end
479 | elseif self.cbw_direction == DIRECTION_DEVICE_TO_HOST then
480 | if self.last_cmd ~= 0 then
481 | if self.last_cmd == FES_CMD_TRANSFER or
482 | self.last_cmd == FEL_CMD_READ then
483 | if self.trans_len == #data then
484 | local length = self.trans_len
485 |
486 | self.trans_len = 0
487 |
488 | return string.format("TRANSFER DATA(to host)
\
489 | See data Window
\
490 | length = %d
", length)
491 | else
492 | -- clear state machine and try again
493 | self.last_cmd = 0
494 | self.trans_len = 0
495 | parse_data(cbw, data, self)
496 | end
497 | else
498 | local f = command_data_parses[self.last_cmd]
499 |
500 | -- clear self.trans_len so that on_transaction could clear last_cmd
501 | -- don't clear self.last_cmd due to we display the name of last_cmd
502 | -- in on_transaction.
503 | self.trans_len = 0
504 |
505 | if f then
506 | local html = f(data, self)
507 | if html then
508 | return html
509 | end
510 | end
511 | end
512 | end
513 |
514 | -- if the topper level doesn't match, try parser as report status
515 | -- CBW/CBS done, the device report status
516 | if #data == 8 then
517 | return parse_app_report_status(data, self)
518 | end
519 | end
520 |
521 | return "Unknow DATA
" .. default_debug_info
522 | end
523 |
524 | local device = {}
525 | device.name = "allwinner"
526 |
527 | local cls = {}
528 | cls.name = "FEL/FES"
529 | cls.endpoints = { EP_IN("Incoming Data"), EP_OUT("Outgoing Data") }
530 |
531 | function cls.on_transaction(self, param, data, needDetail, forceBegin)
532 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
533 | local context = self:get_context(needDetail)
534 |
535 | self.addr = addr
536 | self.cbw_direction = self.cbw_direction or DIRECTION_HOST_TO_DEVICE
537 | self.last_cmd = self.last_cmd or 0
538 | self.trans_len = self.trans_len or -1
539 |
540 | context.state = context.state or macro_defs.ST_CBW
541 | if forceBegin then
542 | context.state = macro_defs.ST_CBW
543 | end
544 | if #data == 32 and data:sub(1,4) == "AWUC" then
545 | context.state = macro_defs.ST_CBW
546 | self.is_new_cmd = false
547 | end
548 | if #data == 20 and data:sub(-4) == "AWUC" then
549 | -- allwinner fes new command
550 | context.state = macro_defs.ST_CBW
551 | self.is_new_cmd = true
552 | end
553 | if #data == 13 and data:sub(1,4) == "AWUS" then
554 | context.state = macro_defs.ST_CSW
555 | end
556 |
557 | if context.state == macro_defs.ST_CBW then
558 | if self.is_new_cmd == true and #data ~= 20 then
559 | return macro_defs.RES_NONE
560 | elseif self.is_new_cmd == false and #data ~= 32 then
561 | return macro_defs.RES_NONE
562 | end
563 | if ack ~= macro_defs.PID_ACK then
564 | return macro_defs.RES_NONE
565 | end
566 |
567 | local xfer_len = 0
568 | local parse_result
569 | local title
570 |
571 | if self.is_new_cmd == false then
572 | xfer_len = unpack("I4", data, 9)
573 | parse_result = parse_fel_awuc(data, self)
574 | title = "FEL"
575 | else
576 | parse_result = parse_fes_awuc(data, self)
577 | xfer_len = self.trans_len
578 | title = "FES"
579 | end
580 |
581 | if xfer_len > 0 then
582 | context.state = macro_defs.ST_DATA
583 | else
584 | context.state = macro_defs.ST_CSW
585 | end
586 |
587 | context.data = ""
588 | context.xfer_len = xfer_len
589 | if needDetail then
590 | context.cbw = parse_result
591 | context.infoHtml = context.cbw.html
592 | context.title = title
593 | context.name = title
594 | context.desc = context.cbw.name
595 | context.status = "incomp"
596 | return macro_defs.RES_BEGIN, self.upv.make_xact_res("AWUC", context.cbw.html, data), self.upv.make_xfer_res(context)
597 | end
598 | return macro_defs.RES_BEGIN
599 | elseif context.state == macro_defs.ST_DATA then
600 | if ack == macro_defs.PID_STALL then
601 | context.state = macro_defs.ST_CSW
602 | if needDetail then
603 | context.status = "stall"
604 | return macro_defs.RES_MORE, self.upv.make_xact_res("Stall", "", data), self.upv.make_xfer_res(context)
605 | end
606 | return macro_defs.RES_MORE
607 | end
608 | context.data = context.data .. data
609 | if self.upv:is_short_packet(addr, ep, data) then
610 | context.state = macro_defs.ST_CSW
611 | elseif #context.data == context.xfer_len then
612 | context.state = macro_defs.ST_CSW
613 | end
614 | if needDetail then
615 | if context.state == macro_defs.ST_CSW then
616 | context.infoHtml = (context.infoHtml or "") .. parse_data(context.cbw, context.data, self)
617 | -- try update context.desc based on self.last_cmd if it is FEL command.
618 | -- because command defined in DATA sections.
619 | if self.is_new_cmd == false and self.last_cmd ~= 0 then
620 | context.desc = command_names[self.last_cmd] or "???"
621 | end
622 |
623 | if self.trans_len <= 0 then
624 | -- transfer is done
625 | self.last_cmd = 0
626 | end
627 | end
628 | return macro_defs.RES_MORE, self.upv.make_xact_res("DATA", "", data), self.upv.make_xfer_res(context)
629 | end
630 | return macro_defs.RES_MORE
631 | elseif context.state == macro_defs.ST_CSW then
632 | if ack == macro_defs.PID_STALL then
633 | return macro_defs.RES_MORE
634 | end
635 | if ack ~= macro_defs.PID_ACK then
636 | return macro_defs.RES_END
637 | end
638 | if #data ~= 13 then
639 | return macro_defs.RES_END
640 | end
641 | if needDetail then
642 | local status = parse_awus(context.cbw, data, self)
643 | context.infoHtml = (context.infoHtml or "") .. status.html
644 | context.data = context.data or ""
645 | context.title = context.title or ""
646 | context.name = context.name or ""
647 | context.desc = context.desc or ""
648 | context.status = status.status
649 | return macro_defs.RES_END, self.upv.make_xact_res("AWUS", status.html, data), self.upv.make_xfer_res(context)
650 | end
651 | return macro_defs.RES_END
652 | else
653 | context.state = macro_defs.ST_CBW
654 | return macro_defs.RES_NONE
655 | end
656 | end
657 |
658 | function device.get_interface_handler(self, itf_number)
659 | return cls
660 | end
661 |
662 | register_device_handler(device, 0x1f3a, 0xefe8)
663 | register_class_handler(cls)
664 | package.loaded["usb_device_aw_efex"] = cls
665 |
--------------------------------------------------------------------------------
/scripts/usb_device_ftdi.lua:
--------------------------------------------------------------------------------
1 | -- usb_device_ftdi.lua
2 |
3 | local html = require("html")
4 | local macro_defs = require("macro_defs")
5 | require("usb_setup_parser")
6 | require("usb_register_class")
7 |
8 | local fmt = string.format
9 | local unpack = string.unpack
10 | local device = {}
11 | device.name = "FTDI FT232"
12 |
13 | local FTDI_RESET = 0x00
14 | local FTDI_MODEM_CTRL = 0x01
15 | local FTDI_SET_FLOW_CTRL = 0x02
16 | local FTDI_SET_BAUDRATE = 0x03
17 | local FTDI_SET_LN_CODE = 0x04
18 | local FTDI_POLL_STATUS = 0x05
19 | local FTDI_SET_EVENT_CHAR = 0x06
20 | local FTDI_SET_ERROR_CHAR = 0x07
21 | local FTDI_SET_LATENCY_TIMER = 0x09
22 | local FTDI_GET_LATENCY_TIMER = 0x0A
23 | local FTDI_SET_BITMODE = 0x0B
24 | local FTDI_READ_PINS = 0x0C
25 | local FTDI_READ_EEPROM = 0x90
26 | local FTDI_WRITE_EEPROM = 0x91
27 | local FTDI_ERASE_EEPROM = 0x92
28 |
29 | local ftdi_action = {
30 | [FTDI_RESET] = {"Reset"},
31 | [FTDI_MODEM_CTRL] = {"Modem Ctrl", "", ""},
32 | [FTDI_SET_FLOW_CTRL] = {"Set Flow Ctrl", "", ""},
33 | [FTDI_SET_BAUDRATE] = {"Set Baudrate", "low part", ""},
34 | [FTDI_SET_LN_CODE] = {"Set Ln Code", "", ""},
35 | [FTDI_POLL_STATUS] = {"Poll status", "", ""},
36 | [FTDI_SET_EVENT_CHAR] = {"Set evt char"},
37 | [FTDI_SET_ERROR_CHAR] = {"Set error char"},
38 | [FTDI_SET_LATENCY_TIMER] = {"Set Latence", "Latence"},
39 | [FTDI_GET_LATENCY_TIMER] = {"Get Latence", "", "", "Latence"},
40 | [FTDI_SET_BITMODE] = {"Set bit mode"},
41 | [FTDI_READ_PINS] = {"Read Pins", "", "", "Pin Data"},
42 |
43 | [FTDI_READ_EEPROM] = {"Read EEPROM", "", "EE Location", "EEPROM DATA" },
44 | [FTDI_WRITE_EEPROM] = {"Write EEPROM", "", "EE Location", "EEPROM DATA" },
45 | [FTDI_ERASE_EEPROM] = {"Erase EEPROM", "", "" },
46 | }
47 |
48 | local wValue_render = {
49 | [FTDI_SET_LN_CODE] = html.create_field([[
50 | struct {
51 | // wValue
52 | uint16_t data_bits:8; // data bits
53 | uint16_t parity:3; // {[0] = "None", [1] = "Odd", [2] = "Even", [3] = "Mark" , [4] = "Space"}
54 | uint16_t stop_bits:3; // {[0] = "1 bits", [1] = "1.5 bits", [2] = "2 bits"}
55 | uint16_t break:1; // {[0] = "break off", [1] = "break on"}
56 | uint16_t reserved:1;
57 | }
58 | ]]),
59 | [FTDI_MODEM_CTRL] = html.create_field([[
60 | struct {
61 | // wValue
62 | uint16_t DTR:1; // {[0] = "low", [1] = "high"}
63 | uint16_t RTS:1; // {[0] = "low", [1] = "high"}
64 | uint16_t reserved:6;
65 | uint16_t DTR_MASK:1;
66 | uint16_t RTS_MASK:1;
67 | uint16_t reserved:6;
68 | }
69 | ]]),
70 | [FTDI_SET_FLOW_CTRL] = html.create_field([[
71 | struct {
72 | // wValue
73 | uint16_t reserved:8;
74 | uint16_t RTS_CTS:1; // {[0] = "off", [1] = "on"}
75 | uint16_t DTR_DST:1; // {[0] = "off", [1] = "on"}
76 | uint16_t xOn_xOff:1; // {[0] = "off", [1] = "on"}
77 | uint16_t reserved:5;
78 | }
79 | ]]),
80 | [FTDI_SET_EVENT_CHAR] = html.create_field([[
81 | struct {
82 | // wValue
83 | uint16_t event_char:8;
84 | uint16_t enable:1; // {[0] = "disable", [1] = "enable"}
85 | uint16_t reserved:7;
86 | }
87 | ]]),
88 | [FTDI_SET_ERROR_CHAR] = html.create_field([[
89 | struct {
90 | // wValue
91 | uint16_t error_char:8;
92 | uint16_t enable:1; // {[0] = "disable", [1] = "enable"}
93 | uint16_t reserved:7;
94 | }
95 | ]]),
96 | [FTDI_SET_BITMODE] = html.create_field([[
97 | struct {
98 | // wValue
99 | uint16_t mask:8;
100 | uint16_t RESET:1;
101 | uint16_t BITBANG:1;
102 | uint16_t MPSSE:1;
103 | uint16_t MCU:1;
104 | uint16_t OPTO:1;
105 | uint16_t CBUS:1;
106 | uint16_t SYNCFF:1;
107 | uint16_t FT1284:1;
108 | }
109 | ]])
110 | }
111 |
112 | local wIndex_render = {
113 | [FTDI_SET_BAUDRATE] = html.create_field([[
114 | struct {
115 | // wValue
116 | uint16_t index:8;
117 | uint16_t baudrate:8; // high part
118 | }
119 | ]])
120 | }
121 |
122 | local reset_value_desc = {
123 | [0] = "Reset",
124 | [1] = "Purge Rx",
125 | [1] = "Purge Tx",
126 | }
127 |
128 | local struct_ftdi_status_header = html.create_struct([[
129 | struct {
130 | // wValue
131 | uint16_t reserved:4;
132 | uint16_t CTS:1;
133 | uint16_t DTS:1;
134 | uint16_t RI:1;
135 | uint16_t RLSD:1;
136 | uint16_t DataReady:1;
137 | uint16_t Overrun:1;
138 | uint16_t ParityError:1;
139 | uint16_t FrameError:1;
140 | uint16_t BreakInt:1;
141 | uint16_t TransmitHolding:1;
142 | uint16_t TransmitEmpty:1;
143 | uint16_t RxFifoError:1;
144 | }
145 | ]])
146 |
147 | function device.parse_setup(setup, context)
148 | if setup.type ~= "Vendor" then
149 | return
150 | end
151 | local action = ftdi_action[setup.bRequest]
152 | local bRequest_desc = "FTDI Unknown"
153 | local wValue_desc = nil
154 | local wIndex_desc = nil
155 | if action then
156 | bRequest_desc = action[1] or bRequest_desc
157 | wValue_desc = action[2] or wValue_desc
158 | wIndex_desc = action[3] or wIndex_desc
159 | end
160 | if setup.bRequest == FTDI_READ_EEPROM or setup.bRequest == FTDI_WRITE_EEPROM then
161 | bRequest_desc = bRequest_desc .. ":".. setup.wIndex
162 | end
163 | setup.title = "FTDI Request"
164 | setup.name = bRequest_desc
165 | setup.render.title = "FTDI " .. bRequest_desc
166 | setup.render.bRequest = bRequest_desc
167 | setup.render.wValue = wValue_render[setup.bRequest] or wValue_desc
168 | setup.render.wIndex = wIndex_render[setup.bRequest] or wIndex_desc
169 | end
170 |
171 | local function make_ftdi_status(data)
172 | local v = 0
173 | if #data > 1 then
174 | v = unpack("I2", data)
175 | end
176 | return struct_ftdi_status_header:build(data, "FTDI Status").html
177 | end
178 |
179 | function device.parse_setup_data(setup, data, context)
180 | if setup.type ~= "Vendor" then
181 | return nil
182 | end
183 | if setup.bRequest == FTDI_POLL_STATUS then
184 | return make_ftdi_status(data)
185 | end
186 | local action = ftdi_action[setup.bRequest]
187 | local desc = "Unknown Data"
188 | if action then
189 | desc = action[4] or desc
190 | end
191 | return ""..desc.."
"
192 | end
193 |
194 | local cls = {}
195 | cls.name = "FTDI Data"
196 | cls.endpoints = { EP_IN("Incoming Data"), EP_OUT("Outgoing Data") }
197 |
198 | function cls.on_transaction(self, param, data, needDetail, forceBegin)
199 | local addr, ep, pid, ack = param:byte(1), param:byte(2), param:byte(3), param:byte(4)
200 | self.addr = addr
201 | if ack ~= macro_defs.PID_ACK then
202 | return macro_defs.RES_NONE
203 | end
204 | local context = self:get_context(needDetail, pid)
205 | context.data = context.data or ""
206 | if forceBegin then
207 | context.data = ""
208 | end
209 | local endMark = self.upv:is_short_packet(addr, ep, data) and macro_defs.RES_END or macro_defs.RES_NONE
210 | local begindMark = #context.data == 0 and macro_defs.RES_BEGIN or macro_defs.RES_NONE
211 | context.data = context.data .. data
212 | if #context.data >= 4096 then
213 | endMark = macro_defs.RES_END
214 | end
215 | local res = endMark | begindMark
216 | if res == macro_defs.RES_NONE then res = macro_defs.RES_MORE end
217 |
218 | if needDetail then
219 | context.status = "incomp"
220 | context.title = "FTDI Data Xfer"
221 | context.name = "FTDI Data"
222 | context.desc = "FTDI Data"
223 | context.infoHtml = ""
224 | if pid == macro_defs.PID_IN and #context.data > 1 then
225 | context.infoHtml = make_ftdi_status(context.data)
226 | end
227 | if endMark ~= macro_defs.RES_NONE then
228 | context.status = "success"
229 | end
230 | local xfer_res = self.upv.make_xfer_res(context)
231 | if endMark ~= macro_defs.RES_NONE then
232 | context.data = ""
233 | end
234 | return res, self.upv.make_xact_res("FTDI Data", context.infoHtml, data), xfer_res
235 | end
236 | if endMark ~= macro_defs.RES_NONE then
237 | context.data = ""
238 | end
239 | return res
240 | end
241 |
242 | function device.get_interface_handler(self, itf_number)
243 | return cls
244 | end
245 |
246 | register_device_handler(device, 0x0403, 0x6001)
247 |
248 | register_class_handler(cls)
249 |
250 | package.loaded["usb_device_ftdi"] = device
251 |
--------------------------------------------------------------------------------
/scripts/usb_register_class.lua:
--------------------------------------------------------------------------------
1 | -- usb_register_class.lua
2 | --[[
3 | local hid = require("usb_class_hid")
4 | local bot = require("usb_class_msc_bot")
5 | local cdc_acm = require("usb_class_cdc_acm")
6 | local data = require("usb_class_data")
7 |
8 | local function register(context)
9 | context:regClass(hid)
10 | context:regClass(bot)
11 | context:regClass(cdc_acm)
12 | context:regClass(data)
13 | end
14 | --]]
15 |
16 | local class_handlers = {}
17 | local class_map = {}
18 | local iad_map = {}
19 |
20 | local function cls2key(cls)
21 | local res = ""
22 | if cls.bInterfaceClass then
23 | res = res .. string.char(cls.bInterfaceClass)
24 | if cls.bInterfaceSubClass then
25 | res = res .. string.char(cls.bInterfaceSubClass)
26 | if cls.bInterfaceProtocol then
27 | res = res .. string.char(cls.bInterfaceProtocol)
28 | end
29 | end
30 | end
31 | return res
32 | end
33 |
34 | local function parse_ep_require(eps)
35 | local res = {}
36 | for i,v in ipairs(eps) do
37 | res[#res+1] = tonumber(v:sub(#v,#v))
38 | end
39 | return res
40 | end
41 |
42 | local function check_ep_require(self, eps)
43 | local ep_cnt = #self.ep_require
44 | local ep_res = {}
45 | local ep_opt_cnt = 0
46 | for j,v in ipairs(self.ep_require) do
47 | if v >= 4 then
48 | ep_res[j] = 255
49 | ep_opt_cnt = ep_opt_cnt + 1
50 | end
51 | end
52 |
53 | for i=1,#eps do
54 | local ep = eps[i]
55 | for j,v in ipairs(self.ep_require) do
56 | if v == 1 or v == 5 then
57 | if (ep & 0x80) == 0x80 then
58 | ep_res[j] = ep
59 | ep_cnt = ep_cnt - 1
60 | break
61 | end
62 | elseif v == 0 or v == 4 then
63 | if (ep & 0x80) == 0x00 then
64 | ep_res[j] = ep
65 | ep_cnt = ep_cnt - 1
66 | break
67 | end
68 | else
69 | ep_res[j] = ep
70 | ep_cnt = ep_cnt - 1
71 | break
72 | end
73 | end
74 | end
75 | if ep_cnt > ep_opt_cnt then
76 | -- error("fail to parse endpoint requirement\n" .. debug.traceback())
77 | end
78 | return ep_res
79 | end
80 | -- context related on needDetail or pid
81 | local function get_context(self, needDetail, pid)
82 | local context
83 | if needDetail then
84 | self.detail = self.detail or {}
85 | context = self.detail
86 | else
87 | self.simple = self.simple or {}
88 | context = self.simple
89 | end
90 | if pid then
91 | context.pid_map = context.pid_map or {}
92 | context.pid_map[pid] = context.pid_map[pid] or {}
93 | context = context.pid_map[pid]
94 | end
95 | return context
96 | end
97 |
98 |
99 | local function get_endpoint_interface_data(self, addr, ep)
100 | local dev = self.upv.get_decoder(addr, 0)
101 | if not dev then return {} end
102 | local itf, alt = dev:get_endpoint_interface(ep)
103 | return dev:get_interface_data(itf, alt)
104 | end
105 |
106 | function register_class_handler(cls)
107 | local old = class_handlers[cls.name]
108 | cls.ep_require = parse_ep_require(cls.endpoints or {})
109 | cls.make_decoder = function(upv)
110 | local res = {}
111 | res.on_transaction = cls.on_transaction
112 | res.upv = upv
113 | res.name = cls.name
114 | res.ep_require = cls.ep_require
115 | res.check_ep_require = check_ep_require
116 | res.get_context = get_context
117 | res.class_handler = cls
118 | res.get_endpoint_interface_data = get_endpoint_interface_data
119 | return res
120 | end
121 | class_handlers[cls.name] = cls
122 | if cls.iad then
123 | local key = cls2key(cls)
124 | local iadKey= cls2key(cls.iad)
125 | iad_map[iadKey] = iad_map[iadKey] or {}
126 | iad_map[iadKey][key] = cls
127 | else
128 | local key = cls2key(cls)
129 | class_map[key] = cls
130 | end
131 | return old
132 | end
133 |
134 | local function find_something(map, key)
135 | while #key > 1 do
136 | local t = map[key]
137 | if t then return t end
138 | key = string.sub(key, 1, #key-1)
139 | end
140 | return map[key]
141 | end
142 |
143 | function find_class_handler(itf_desc, iad_desc)
144 | local key = nil
145 | if type(itf_desc) == "table" then
146 | key = cls2key(itf_desc)
147 | elseif type(itf_desc) == "string" then
148 | key = string.sub(itf_desc,6,8)
149 | else
150 | error("wrong itf desc" .. type(itf_desc))
151 | return nil
152 | end
153 | if iad_desc then
154 | local iad_key = nil
155 | if type(itf_desc) == "table" then
156 | iad_key = cls2key(iad_desc)
157 | elseif type(itf_desc) == "string" then
158 | iad_key = string.sub(iad_desc,5,7)
159 | else
160 | error("wrong iad desc")
161 | return nil
162 | end
163 | local map = find_something(iad_map, iad_key)
164 | if map then
165 | return find_something(map, key)
166 | end
167 | return nil
168 | end
169 | return find_something(class_map, key)
170 | end
171 |
172 | local device_handlers = {}
173 | local device_handlers_map = {}
174 | local pack = string.pack
175 | function register_device_handler(dev, vid, pid)
176 | local t = pack("I2I2",vid,pid)
177 | local old = device_handlers[t]
178 | device_handlers[t] = dev
179 | device_handlers_map[dev.name] = dev
180 | return old
181 | end
182 |
183 | function find_device_handler(vid, pid)
184 | local t = pack("I2I2",vid,pid)
185 | return device_handlers[t]
186 | end
187 |
188 | function get_register_handler()
189 | local res = ""
190 | local sep = ""
191 | local n1 = {}
192 |
193 | for k,v in pairs(class_handlers) do
194 | n1[#n1+1] = k
195 | end
196 |
197 | table.sort(n1)
198 |
199 | for i,k in ipairs(n1) do
200 | local v = class_handlers[k]
201 | if v.endpoints then
202 | res = res .. sep .. k .. ":".. table.concat(v.endpoints, ",")
203 | sep = ";"
204 | end
205 | end
206 |
207 | --[[
208 | local n2 = {}
209 | for k,v in pairs(device_handlers_map) do
210 | n2[#n2+1] = k
211 | end
212 | table.sort(n2)
213 |
214 | for i,k in ipairs(n2) do
215 | local v = device_handlers_map[k]
216 | if v.endpoints then
217 | res = res .. sep .. k .. ":".. table.concat(v.endpoints, ",")
218 | sep = ";"
219 | end
220 | end
221 | ]]
222 | return res
223 | end
224 |
225 | function find_handler_by_name(name)
226 | return class_handlers[name] -- or device_handlers_map[name]
227 | end
228 |
229 | local function register(context)
230 | end
231 |
232 | package.loaded["usb_register_class"] = register
233 |
--------------------------------------------------------------------------------
/scripts/usb_setup_parser.lua:
--------------------------------------------------------------------------------
1 | -- usb_setup_parser.lua
2 | -- encoding: utf-8
3 |
4 | local parser = {}
5 | local fmt = string.format
6 | local unpack = string.unpack
7 | local html = require("html")
8 | local macro_defs = require("macro_defs")
9 | local desc_parser = require("usb_descriptor_parser")
10 |
11 | local field_bmRequest = html.create_field([[
12 | struct{
13 | // bmRequest
14 | uint8_t Recipient:5; // {[0] = "Device", [1] = "Interface", [2] = "Endpoint" ,[3]="Other"}
15 | uint8_t Type:2; // {[0] = "Standard", [1]="Class",[2]="Vendor",[3]="Reserved"}
16 | uint8_t Direction:1; // {[0] = "Host to Device", [1]="Device to Host"}
17 | }
18 | ]])
19 |
20 | local field_wValue_get_desc = html.create_field([[
21 | struct{
22 | // wValue
23 | uint16_t Index:8;
24 | uint16_t Type:8; // _G.get_descriptor_name
25 | }
26 | ]])
27 |
28 | local struct_device_status = html.create_struct([[
29 | struct{
30 | // wStatus
31 | uint16_t SelfPowered:1; // {[0] = "Bus Powered", [1] = "Self Powered"}
32 | uint16_t RemoteWakeUp:1; // {[0] = "Disabled", [1] = "Enabled"}
33 | uint16_t reserved:14;
34 | }
35 | ]])
36 |
37 | local function render_field(setup, field, default)
38 | local render = setup.render[field] or default
39 | if render then
40 | if type(render) == "string" then
41 | return {field, fmt("%d", setup[field]), render }
42 | elseif type(render) == "table" then
43 | return html.expand_bit_field(setup[field], render)
44 | elseif type(render) == "function" then
45 | return {field, fmt("%d", setup[field]), render(setup[field])}
46 | end
47 | end
48 | return nil
49 | end
50 |
51 | function parser.parse_setup(data, context)
52 | local setup = {}
53 | setup.data = data
54 | local bmRequest, bRequest, wValue, wIndex, wLength = unpack("I1I1I2I2I2", setup.data .. "\xff\xff\xff\xff\xff\xff\xff\xff")
55 | setup.bmRequest = bmRequest
56 | setup.bRequest = bRequest
57 | setup.wValue = wValue
58 | setup.wIndex = wIndex
59 | setup.wLength = wLength
60 | setup.render = {}
61 |
62 | local typStr
63 | local reqTitle = "Unknwon Req"
64 | local typ = (bmRequest >> 5) & 3
65 | if typ == 0 then
66 | typStr = "Standard"
67 | reqTitle = "Standard Req"
68 | elseif typ == 1 then
69 | typStr = "Class"
70 | reqTitle = "Class Req"
71 | elseif typ == 2 then
72 | typStr = "Vendor"
73 | reqTitle = "Vendor Req"
74 | else typStr = "Reserved"
75 | end
76 | setup.type = typStr
77 |
78 | local recipStr
79 | local recip = bmRequest & 0x1f
80 | if recip == 0 then recipStr = "Device"
81 | elseif recip == 1 then recipStr = "Interface"
82 | elseif recip == 2 then recipStr = "Endpoint"
83 | elseif recip == 3 then recipStr = "Other"
84 | else recipStr = "Reserved"
85 | end
86 | setup.recip = recipStr
87 | if typStr == "Class" and recipStr == "Endpoint" then
88 | local itf = context:get_endpoint_interface(wIndex & 0xff)
89 | local cls = context.get_interface_class and context:get_interface_class(itf)
90 | cls = cls or context.class_handler
91 | if cls and cls.parse_setup then
92 | local r = cls.parse_setup(setup, context)
93 | if r then return r end
94 | end
95 | end
96 | if recipStr == "Interface" then
97 | local cls = context.get_interface_class and context:get_interface_class(wIndex & 0xff)
98 | cls = cls or context.class_handler
99 | if cls and cls.parse_setup then
100 | local r = cls.parse_setup(setup, context)
101 | if r then return r end
102 | end
103 | elseif recipStr == "Device" or recipStr == "Other" then
104 | if typStr == "Class" and context.current_device then
105 | local cls = context:current_device().deviceClass
106 | if cls and cls.parse_setup then
107 | local r = cls.parse_setup(setup, context)
108 | if r then return r end
109 | end
110 | end
111 | end
112 |
113 | local dev = context.current_device and context:current_device()
114 | if dev and dev.parse_setup then
115 | local r = dev.parse_setup(setup, context)
116 | if r then return r end
117 | end
118 |
119 | local bRequest_desc = ""
120 | if typStr == "Standard" then
121 | bRequest_desc = get_std_request_name(bRequest)
122 | elseif typStr == "Class" then
123 | bRequest_desc = " Class Req " .. bRequest
124 | elseif typStr == "Vendor" then
125 | bRequest_desc = " Vendor Req " .. bRequest
126 | end
127 |
128 | local wValue_field = ""
129 | if typStr == "Standard" then
130 | if (bRequest == macro_defs.CLEAR_FEATURE) or (bRequest == macro_defs.SET_FEATURE) then
131 | wValue_field = fmt("Feature: %d", wValue)
132 | elseif bRequest == macro_defs.SET_ADDRESS then
133 | wValue_field = fmt("Address: %d", wValue)
134 | elseif (bRequest == macro_defs.GET_DESCRIPTOR) or (bRequest == macro_defs.SET_DESCRIPTOR) then
135 | wValue_field = field_wValue_get_desc
136 | elseif bRequest == macro_defs.SET_CONFIG then
137 | wValue_field = fmt("Config: %d", wValue)
138 | end
139 | end
140 |
141 | local wIndex_desc = ""
142 | if recipStr == "Device" then
143 | if (wValue > 0) and ((bRequest == macro_defs.GET_DESCRIPTOR) or (bRequest == macro_defs.SET_DESCRIPTOR)) then
144 | wIndex_desc = fmt("Language ID: 0x%04x", wIndex)
145 | else
146 | end
147 | elseif recipStr == "Interface" then
148 | wIndex_desc = fmt("Interface: %d", wIndex)
149 | elseif recipStr == "Endpoint" then
150 | wIndex_desc = fmt("Endpoint: 0x%02X", wIndex & 0xff)
151 | end
152 | setup.html = html.make_table{
153 | title = setup.render.title or (typStr .. " Request"),
154 | header = {"Field", "Value", "Description"},
155 | render_field(setup, "bmRequest", field_bmRequest),
156 | render_field(setup, "bRequest", bRequest_desc),
157 | render_field(setup, "wValue", wValue_field),
158 | render_field(setup, "wIndex", wIndex_desc),
159 | render_field(setup, "wLength", ""),
160 | }
161 | setup.title = setup.title or reqTitle
162 | setup.name = setup.name or (typStr == "Standard" and get_std_request_name(bRequest, wValue, wIndex) or bRequest_desc)
163 | return setup
164 | end
165 |
166 | function parser.parse_data(setup, data, context)
167 | if setup.type == "Class" and setup.recip == "Endpoint" then
168 | local itf = context:get_endpoint_interface(setup.wIndex & 0xff)
169 | local cls = context.get_interface_class and context:get_interface_class(itf)
170 | cls = cls or context.class_handler
171 | if cls and cls.parse_setup_data then
172 | local r = cls.parse_setup_data(setup, data, context)
173 | if r then return r end
174 | end
175 | end
176 | if setup.recip == "Interface" then
177 | local cls = context.get_interface_class and context:get_interface_class(setup.wIndex & 0xff)
178 | cls = cls or context.class_handler
179 | if cls and cls.parse_setup_data then
180 | local r = cls.parse_setup_data(setup, data, context)
181 | if r then return r end
182 | end
183 | elseif setup.recip == "Device" or setup.recip == "Other" then
184 | if setup.type == "Class" then
185 | local cls = context.current_device and context:current_device().deviceClass
186 | if cls and cls.parse_setup_data then
187 | local r = cls.parse_setup_data(setup, data, context)
188 | if r then return r end
189 | end
190 | end
191 | end
192 |
193 | local dev = context.current_device and context:current_device()
194 | if dev and dev.parse_setup_data then
195 | local r = dev.parse_setup_data(setup, data, context)
196 | if r then return r end
197 | end
198 |
199 | if setup.type == "Standard" then
200 | if (setup.bRequest == macro_defs.GET_DESCRIPTOR or bRequest == macro_defs.SET_DESCRIPTOR) then
201 | if (setup.wValue >> 8) <= macro_defs.MAX_STD_DESC then
202 | local descInfo = desc_parser.parse(data, context)
203 | return descInfo.html
204 | end
205 | elseif setup.bRequest == macro_defs.GET_STATUS and #data >= 2 then
206 | return struct_device_status:build(data, "Device Status").html
207 | end
208 | end
209 | return "Get " .. #data .. " bytes data
Display in data window"
210 | end
211 |
212 | package.loaded["usb_setup_parser"] = parser
213 |
--------------------------------------------------------------------------------
/scripts/util.lua:
--------------------------------------------------------------------------------
1 | -- util.lua
2 | local macro_defs = require("macro_defs")
3 | local util = {}
4 | util.toHex = function(data, sep, record_per_line, line_break)
5 | local res = ""
6 | sep = sep or ""
7 | record_per_line = record_per_line or 8
8 | line_break = line_break or "\n"
9 | local sssep = ""
10 | if not data then return "" end
11 | local data_in_line = 0
12 | for i=1,#data do
13 | res = res .. sssep .. string.format( "%02X", data:byte(i))
14 | sssep = sep
15 | data_in_line = data_in_line + 1
16 | if data_in_line == record_per_line then
17 | sssep = ""
18 | res = res .. line_break
19 | data_in_line = 0
20 | end
21 | end
22 | return res
23 | end
24 |
25 | local STD_REQ_NAME ={
26 | "Get Status" ,
27 | "Clear Feature" ,
28 | "Reserved" ,
29 | "Set Feature" ,
30 | "Reserved" ,
31 | "Set Address" ,
32 | "Get Descriptor" ,
33 | "Set Descriptor" ,
34 | "Get Config" ,
35 | "Set Config" ,
36 | "Get Interface" ,
37 | "Set Interface" ,
38 | "Sync Frame" ,
39 | }
40 |
41 | local STD_DESCRIPTOR_NAME = {
42 | "Undefined" ,
43 | "Device" ,
44 | "Configuration" ,
45 | "String" ,
46 | "Interface" ,
47 | "Endpoint" ,
48 | "Device Qualifier" ,
49 | "Other Speed" ,
50 | "Interface Power" ,
51 | "OTG" ,
52 | }
53 |
54 | local DESCRIPTOR_NAME = {
55 | [macro_defs.DEVICE_DESC ] = "DevDesc",
56 | [macro_defs.CFG_DESC ] = "CfgDesc",
57 | [macro_defs.STRING_DESC ] = "StrDesc",
58 | [macro_defs.DEV_QUAL_DESC ] = "QualDesc",
59 | [macro_defs.OTHER_DESC ] = "OtherSpeed",
60 | [macro_defs.BOS_DESC ] = "BOS Desc",
61 | }
62 |
63 | _G.EP_IN = function(name, optional)
64 | if optional then
65 | return name .. "5"
66 | end
67 | return name .. "1"
68 | end
69 |
70 | _G.EP_OUT = function(name, optional)
71 | if optional then
72 | return name .. "4"
73 | end
74 | return name .. "0"
75 | end
76 |
77 | _G.EP_INOUT = function(name, optional)
78 | if optional then
79 | return name .. "6"
80 | end
81 | return name .. "2"
82 | end
83 |
84 | _G.get_std_request_name = function(v, wValue, wIndex)
85 | if v < #STD_REQ_NAME then
86 | if (v == macro_defs.GET_DESCRIPTOR or v == macro_defs.GET_DESCRIPTOR) and wValue then
87 | local idx = wValue & 0xff
88 | local t = wValue >> 8
89 | local prefix = "Get "
90 | if v == macro_defs.SET_DESCRIPTOR then
91 | prefix = "Set "
92 | end
93 | local postfix = ""
94 | if t == macro_defs.CFG_DESC or t == macro_defs.STRING_DESC then
95 | postfix = ":"..idx
96 | end
97 | if DESCRIPTOR_NAME[t] then
98 | return prefix .. DESCRIPTOR_NAME[t] .. postfix
99 | end
100 | end
101 | local postfix = ""
102 | if v == macro_defs.SET_CONFIG and wValue then
103 | postfix = " :" .. wValue
104 | end
105 | if v == macro_defs.SET_INTERFACE and wIndex then
106 | return "Set Itf :" .. wIndex
107 | end
108 | if v == macro_defs.GET_INTERFACE and wIndex then
109 | return "Get Itf :" .. wIndex
110 | end
111 | return STD_REQ_NAME[v+1] .. postfix
112 | else
113 | return "Unknown Request"
114 | end
115 | end
116 |
117 | _G.get_descriptor_name = function(v)
118 | if (type(v) == "string") then
119 | error(debug.traceback())
120 | end
121 | if (v>=0) and (v < #STD_DESCRIPTOR_NAME) then
122 | return STD_DESCRIPTOR_NAME[v+1] .. " Descriptor"
123 | elseif v == macro_defs.REPORT_DESC then
124 | return "Report Descritpor"
125 | elseif v == macro_defs.FUNC_DESC then
126 | return "Function Descritpor"
127 | elseif v == macro_defs.HUB_DESC then
128 | return "HUB Descritpor"
129 | else
130 | return "Unknown Descritpor"
131 | end
132 | end
133 |
134 | package.loaded["util"] = util
135 |
136 |
--------------------------------------------------------------------------------