aboutsummaryrefslogtreecommitdiffstats
path: root/dissectors/ch340.lua
blob: 3ec91f7240bf7f8760630b83805017b700060ae9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
local control = Proto("ch340.ctl", "CH340 Serial Control")
local bulk = Proto("ch340.bulk", "CH340 Serial Data")

local ep_dir_f = Field.new("usb.endpoint_address.direction")

local REQ_TBL = {
  [0x5F] = "CH341_REQ_READ_VERSION",
  [0x9A] = "CH341_REQ_WRITE_REG",
  [0x95] = "CH341_REQ_READ_REG",
  [0xA1] = "CH341_REQ_SERIAL_INIT",
  [0xA4] = "CH341_REQ_MODEM_CTRL",
}

local REG_TBL = {
  [0x07] = "STATUS2",
  [0x06] = "STATUS",
  [0x13] = "DIVISOR",
  [0x12] = "PRESCALER",
  [0x18] = "LCR",
  [0x25] = "LCR2",
}

control.fields.req = ProtoField.uint8("ch340.ctl.req", "Request", base.HEX, REQ_TBL)
control.fields.reg1 = ProtoField.uint8("ch340.ctl.reg1", "Register 1", base.HEX, REG_TBL)
control.fields.reg2 = ProtoField.uint8("ch340.ctl.reg2", "Register 2", base.HEX, REG_TBL)
control.fields.val1 = ProtoField.uint8("ch340.ctl.val1", "Value 1", base.HEX)
control.fields.val2 = ProtoField.uint8("ch340.ctl.val2", "Value 2", base.HEX)
control.fields.modem = ProtoField.uint8("ch340.ctl.modem", "Modem", base.HEX)

bulk.fields.data_in = ProtoField.bytes("ch340.bulk.data_in", "Data In")
bulk.fields.data_out = ProtoField.bytes("ch340.bulk.data_out", "Data Out")

pcall(DissectorTable.heuristic_new, "ch340.serial_data", bulk)

function control.dissector(buffer, pinfo, tree)
  if buffer:captured_len() < 1 then return end

  local sub = tree:add(control, buffer())

  local req = buffer(0, 1)
  sub:add(control.fields.req, req)

  req = REQ_TBL[req:uint()] or ""
  pinfo.cols.info:set("CH340 " .. req:sub(11))
  if req == "CH341_REQ_WRITE_REG" or
     req == "CH341_REQ_READ_REG" then

    local reg2 = buffer(1, 1)
    local reg1 = buffer(2, 1)
    sub:add(control.fields.reg1, reg1)
    sub:add(control.fields.reg2, reg2)

    reg1 = reg1:uint()
    reg2 = reg2:uint()

    pinfo.cols.info:append(string.format(" %s/%s", (REG_TBL[reg1] or reg1), (REG_TBL[reg2] or reg2)))

    if req == "CH341_REQ_WRITE_REG" then
      local val2 = buffer(3, 1)
      local val1 = buffer(4, 1)
      sub:add(control.fields.val1, val1)
      sub:add(control.fields.val2, val2)
    end
  elseif name == "CH341_REQ_MODEM_CTRL" then
    sub:add(control.fields.modem, buffer(1))
  end
end

function bulk.dissector(buffer, pinfo, tree)
  if buffer:captured_len() < 1 then return end

  local sub = tree:add(bulk, buffer())
  pinfo.cols.info:set("CH340 Serial")

  local ep_dir = ep_dir_f()() -- 0: h2d, 1: d2h
  if ep_dir == 0 then
    pinfo.cols.info:append(" out")
    pinfo.p2p_dir = P2P_DIR_SENT
    sub:add(bulk.fields.data_out, buffer())
  else
    pinfo.cols.info:append(" in")
    pinfo.p2p_dir = P2P_DIR_RECV
    sub:add(bulk.fields.data_in, buffer())
  end

  DissectorTable.try_heuristics("ch340.serial_data", buffer, pinfo, tree)
end

function usb_protocol_key(class, subclass, protocol)
  return bit.bor(
    bit.lshift(1, 31),
    bit.lshift(bit.band(class, 0xff), 16),
    bit.lshift(bit.band(subclass, 0xff), 8),
    bit.band(protocol, 0xff)
  )
end

local ctl_table = DissectorTable.get("usb.control")
ctl_table:add(usb_protocol_key(0xff, 0x01, 0x02), control)
ctl_table:add(0xffff, control)

local bulk_table = DissectorTable.get("usb.bulk")
bulk_table:add(usb_protocol_key(0xff, 0x01, 0x02), bulk)
bulk_table:add(0xffff, bulk)