I managed to mostly decode the protocol for PoE for the DLink DGS-1210-10P. The following script does the trick:
#!/usr/bin/lua
local rs = require "luars232"
-- port_name = "/dev/ttyUSB0"
port_name = "/dev/ttyS1"
out = io.stderr
function initSerial(p)
local e, p = rs.open(p)
if e ~= rs.RS232_ERR_NOERROR then
-- handle error
out:write(string.format("can't open serial port '%s', error: '%s'\n",
port_name, rs.error_tostring(e)))
return
end
assert(p:set_baud_rate(rs.RS232_BAUD_19200) == rs.RS232_ERR_NOERROR)
assert(p:set_data_bits(rs.RS232_DATA_8) == rs.RS232_ERR_NOERROR)
assert(p:set_parity(rs.RS232_PARITY_NONE) == rs.RS232_ERR_NOERROR)
assert(p:set_stop_bits(rs.RS232_STOP_1) == rs.RS232_ERR_NOERROR)
assert(p:set_flow_control(rs.RS232_FLOW_OFF) == rs.RS232_ERR_NOERROR)
out:write(string.format("OK, port open with values '%s'\n", tostring(p)))
return p
end
function receive(pCon)
-- local reply = {0x20, 0x01, 0x00, 0x08, 0x00, 0xe1, 0x21, 0x18, 0x01, 0x02, 0x21, 0x67}
local reply = {}
local retries = 0
while table.getn(reply) < 12 and retries < 4 do
-- Read up to 12 byte response, timeout 400ms
err, data_read, size = pCon:read(12, 400)
assert(err == rs.RS232_ERR_NOERROR)
-- io.write(string.format("-> [%2d]:", string.len(data_read)))
for i = 1, string.len(data_read) do
table.insert(reply, string.byte(string.sub(data_read, i, i)))
-- io.write(string.format(" %02x", reply[i]))
end
-- io.write("\n")
retries = retries + 1
end
if table.getn(reply) ~= 12 then
return(nil)
end
local sum = 0
for i = 1, 11 do
sum = sum + reply[i]
end
if sum % 256 ~= reply[12] then
print ("Checksum error!")
return(nil)
end
return(reply)
end
function sendCommand(pCon, cmd)
while table.getn(cmd) < 11 do
table.insert(cmd, 0xff)
end
local sum = 0
for i,v in ipairs(cmd) do
sum = sum + v
end
table.insert(cmd, sum % 256)
local c_string = ""
for i = 1, 12 do
c_string = c_string .. string.char(cmd[i])
end
err, len_written = pCon:write(c_string)
assert(err == rs.RS232_ERR_NOERROR)
local reply = receive(pCon)
if reply then
if (reply[1] == cmd[1] and reply[2] == cmd[2]) then
-- io.write("valid: ")
-- dumpReply(reply)
return(reply)
end
end
return(nil)
end
function dumpReply(reply)
io.write("Reply: ")
for i,v in ipairs(reply) do
io.write(string.format(" %02x", v))
end
io.write("\n");
end
function getStatus(pCon)
local cmd = {0x20, 0x01}
local reply = sendCommand(pCon, cmd)
if not reply then return(nil) end
-- returns status, PoEExtVersion, PoEVersion, state2
return({reply[5], reply[6], reply[7], reply[10]})
end
function disablePort(pCon, port)
local cmd = {0x00, port, port, 0x00}
-- disable command is always sent twice
sendCommand(pCon, cmd)
sendCommand(pCon, cmd)
end
function enablePort(pCon, port)
local cmd = {0x00, port, port, 0x01}
sendCommand(pCon, cmd)
end
function setPortRelPrio(pCon, port, prio)
local cmd = {0x1d, 0x00, port, prio}
sendCommand(pCon, cmd)
end
function setGlobalPowerBudget(pCon, maxPower, guard)
-- maxPower and guard Watts
local cmd = {0x18, 0x01, 0x00}
table.insert(cmd, math.floor(maxPower * 10 / 256))
table.insert(cmd, math.floor(maxPower * 10) % 256)
table.insert(cmd, math.floor(guard * 10 / 256))
table.insert(cmd, math.floor(guard * 10) % 256)
sendCommand(pCon, cmd)
end
function setPowerLowAction(pCon, disableNext)
local cmd = {0x17, 0x00}
if disableNext then
table.insert(cmd, 0x04)
else
table.insert(cmd, 0x02)
end
sendCommand(pCon, cmd)
end
function getPowerStat(pCon)
local cmd = {0x23, 0x01}
local reply = sendCommand(pCon, cmd)
if not reply then return(nil) end
local watts = (reply[3] * 256 + reply[4]) / 10.0
return(watts)
end
function getPortPower(pCon, port)
local cmd = {0x30, 0x01, port}
local reply = sendCommand(pCon, cmd)
if not reply then return(nil) end
local watts = (reply[10] * 256 + reply[11]) / 10.0
local mamps = reply[6] * 256 + reply[7]
return({watts, mamps})
end
function getPortOverview(pCon)
local cmd = {0x2a, 0x01, 0x00}
local reply = sendCommand(pCon, cmd)
if not reply then return(nil) end
local s = { }
for i = 4, 11 do
if reply[i] == 0x10 then
s[i-3] = "off"
end
if reply[i] == 0x11 then
s[i-3] = "enabled"
end
if reply[i] > 0x11 then
s[i-3] = "active"
end
end
return(s)
end
-- Priority for power: 3: High, 2: Normal, 1: Low?
function setPortPriority(pCon, port, prio)
local cmd = {0x1a, port, port, prio}
local reply = sendCommand(pCon, cmd)
if not reply then return(nil) end
return(unpack(reply, 4, 11))
end
function getPortPowerLimits(pCon, port)
local cmd = {0x26, 0x01, port}
local reply = sendCommand(pCon, cmd)
if not reply then return(nil) end
return(answer)
end
function startupPoE(pCon)
local reply = nil
reply = getStatus(pCon)
setGlobalPowerBudget(pCon, 0, 0)
setPowerLowAction(pCon, nil)
-- do something unknown
sendCommand(pCon, {0x06, 0x00, 0x01})
for i = 0, 7 do
disablePort(pCon, i)
end
-- do something unknown
sendCommand(pCon, {0x02, 0x00, 0x01})
for i = 0, 7 do
disablePort(pCon, i)
end
-- do something unknown
sendCommand(pCon, {0x02, 0x00, 0x01})
for i = 0, 7 do
setPortRelPrio(pCon, i, 7-i)
end
-- use monitor command 25
sendCommand(pCon, {0x25, 0x01})
setGlobalPowerBudget(pCon, 65.0, 7.0)
getPowerStat(pCon)
-- -> 23 01 00 00 02 44 00 02 ff ff 00 6a
-- Set 4 unknown port properties:
for i = 0, 7 do
sendCommand(pCon, {0x11, i, i, 0x01})
sendCommand(pCon, {0x13, i, i, 0x02})
sendCommand(pCon, {0x15, i, i, 0x01})
sendCommand(pCon, {0x10, i, i, 0x03})
end
for i = 0, 7 do
enablePort(pCon, i)
end
end
local p = initSerial(port_name)
-- startupPoE(p)
local s = { }
while 1 do
reply = getStatus(p)
-- dumpReply(reply)
io.write(string.format("Total power: %f W\n", getPowerStat(p)))
s = getPortOverview(p)
io.write("Port state: ")
for i = 1, 8 do
io.write(string.format("%d: %s ", i, s[i]))
end
io.write("\n")
for i = 1, 8 do
if s[i] == "active" then
local r = getPortPower(p, i-1)
io.write(string.format("Port %d: %f Watt, %d mA\n", i, r[1], r[2]))
end
end
io.write("\n")
os.execute("sleep " .. tonumber(2))
end
It gives the following result when first turning on all ports and then plugging a small switch into the first Ethernet port:
root@OpenWrt:/tmp# cat /proc/cpuinfo
system type : RTL8380
machine : D-Link DGS-1210-10p
processor : 0
cpu model : MIPS 4KEc V7.0
BogoMIPS : 498.89
wait instruction : yes
microsecond timers : yes
tlb_entries : 32
extra interrupt vector : yes
hardware watchpoint : yes, count: 2, address/irw mask: [0x0fff, 0x0fff]
isa : mips1 mips2 mips32r1 mips32r2 mips64r1 mips64r2
ASEs implemented : mips16
Options implemented : tlb 4kex 4k_cache 32fpr prefetch mcheck ejtag llsc dc_aliases perf_cntr_intr_bit
shadow register sets : 1
kscratch registers : 0
package : 0
core : 0
VCED exceptions : not available
VCEI exceptions : not available
root@OpenWrt:/tmp# ./serial.lua
OK, port open with values 'device: /dev/ttyS1, baud: 19200, data bits: 8, parity: none, stop bits: 1, flow control: off'
Total power: 0.300000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 0.300000 Watt, 6 mA
Total power: 0.300000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 0.300000 Watt, 6 mA
Total power: 0.300000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 0.300000 Watt, 6 mA
Total power: 0.300000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 0.300000 Watt, 6 mA
Total power: 0.300000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 0.300000 Watt, 6 mA
Total power: 1.600000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 1.900000 Watt, 36 mA
Total power: 2.500000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.500000 Watt, 47 mA
Total power: 2.300000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.200000 Watt, 42 mA
Total power: 2.600000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.600000 Watt, 50 mA
Total power: 2.600000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.600000 Watt, 49 mA
Total power: 2.700000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.700000 Watt, 52 mA
Total power: 2.700000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.700000 Watt, 52 mA
Total power: 2.400000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.400000 Watt, 46 mA
Total power: 2.400000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.700000 Watt, 52 mA
Total power: 2.600000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.700000 Watt, 52 mA
Total power: 3.000000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.900000 Watt, 55 mA
Total power: 3.000000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 3.000000 Watt, 57 mA
Total power: 3.000000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.800000 Watt, 53 mA
Total power: 2.900000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.900000 Watt, 55 mA
Total power: 2.900000 W
Port state: 1: active 2: enabled 3: enabled 4: enabled 5: enabled 6: enabled 7: enabled 8: enabled
Port 1: 2.900000 Watt, 54 mA
Terminated