My Cloud PR4100 / PR2100 Firmware Analysis


#200

I thought the path might have something to do with it. What locations should I add?


#201

In the init.sh I copy some handy aliases to /etc/profile and I set the path as follows.

export PATH=/opt/bin:/opt/sbin:$PATH

I didn’t apply this to the interactive shell only yet… it’s on my TODO list :slight_smile:

I got a lot of info with this command:

strace -f -e read=7 -e write=7 -e read=3 -e write=3 -e write 15 -o wdhws_out /opt/wd/bin/wdhws -config=/etc/wd/sprite-wdhw.xml

Use the -f flag to catch all the forks, -o to write to a file and -e to capture all file access for the corresponding pointers.
I’ll now try to make a python replacement for these services that can be used on plain debian.


#202

I have the path updated and installed strace. Finally, with proper tools, more will be possible. Now I just have to figure everything out, stack traces are just outside my skill set. How did you use strace to output the XML communication from wdtms? I see what looks like hexadecimal addresses, which may be the key to talking directly with the hardware.


#203

With strace, you can save the output to a file.
You’ll see that sockets are opened from wdtms to the wdhws.socket. This uses a file pointer (a number).
To get all data on that socket, capture the writes and the reads on that number.
Then I cleaned up the hex data to get the xml text.

A sample of the output when you listen to file pointer 3

first it reads a lot of library headers, until
...
open("/var/run/wdhws.pid", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
...
4147  recvmsg(3,  <unfinished ...>                                                                                                                
4151  <... futex resumed> )             = 0                                                                                                       
4147  <... recvmsg resumed> {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="<?xml version=\"1.0\" encoding=\"ut"..., iov_len=2048}], msg_iovlen
 * 217 bytes in buffer 0                                                                                                                          
 | 00000  3c 3f 78 6d 6c 20 76 65  72 73 69 6f 6e 3d 22 31  <?xml version="1 |                                                                    
 | 00010  2e 30 22 20 65 6e 63 6f  64 69 6e 67 3d 22 75 74  .0" encoding="ut |                                                                    
 | 00020  66 2d 38 22 3f 3e 0a 3c  64 61 74 61 5f 6d 65 73  f-8"?>.<data_mes |                                                                    
 | 00030  73 61 67 65 3e 3c 69 6e  74 65 72 6e 61 6c 5f 69  sage><internal_i |                                                                    
 | 00040  6e 66 6f 3e 3c 76 65 72  73 69 6f 6e 5f 69 6e 74  nfo><version_int |                                                                    
 | 00050  3e 31 3c 2f 76 65 72 73  69 6f 6e 5f 69 6e 74 3e  >1</version_int> |                                                                    
 | 00060  3c 74 79 70 65 5f 73 74  72 69 6e 67 3e 48 57 47  <type_string>HWG |                                                                    
 | 00070  65 74 53 74 61 74 75 73  3c 2f 74 79 70 65 5f 73  etStatus</type_s |                                                                    
 | 00080  74 72 69 6e 67 3e 3c 2f  69 6e 74 65 72 6e 61 6c  tring></internal |                                                                    
 | 00090  5f 69 6e 66 6f 3e 3c 64  61 74 61 3e 3c 68 61 6e  _info><data><han |                                                                    
 | 000a0  64 6c 65 5f 73 74 72 69  6e 67 3e 30 78 62 31 38  dle_string>0xb18 |                                                                    
 | 000b0  38 32 30 3c 2f 68 61 6e  64 6c 65 5f 73 74 72 69  820</handle_stri |                                                                    
 | 000c0  6e 67 3e 3c 2f 64 61 74  61 3e 3c 2f 64 61 74 61  ng></data></data |                                                                    
 | 000d0  5f 6d 65 73 73 61 67 65  3e                       _message>        |                                                                    
4151  futex(0x7f8430002620, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>

#204

I hate to admit this, but I’m definitely in uncharted territory now. Much of what I’m seeing is Greek to me, but I’m sure I’ll eventually figure it out. With certain programming languages, I see code like Neo sees the Matrix, but I’m certainly out of my league now.


#205

It’s my first experience with strace as well :slight_smile:
I suggest you start with strace on fan_control as follows

strace fan_control

It shows you can add args

strace fan_control -g 4

Still not enough info… it spawns extra processes and we’re not logging them yet.

strace -f fan_control -g 4

At the end of the output, you see the std output, fan rpm = 510.
A few lines earlier, you see that /tmp/msg_info.txt is opened and it writes some data.
And I see a message to the LCD screen:

msgsnd(0, {2672, "\f\372\4\203\0\006510rpm\373"}, 13, 0)

It helps to log to a file, use -o.
With vi you can jump fast to keywords. Use / and type ‘open’, then press n / N to jump between them.

When you’ve done this rather easy debugging, you can tackle wdtms and wdhws…

Good luck :slight_smile:


#206

Something tells me I’m going to like Entware.

http://pkg.entware.net/binaries/x86-64/Packages.html

Why on Earth WD keeps sitting on their thumbs and stubbornly refusing to open these things up (IE: release source code) is beyond me. Having the ability to CUSTOMIZE and do MORE is precisely what makes people interested. But noooo, WD wants to have their own little closed ecosystem, one which is clearly broken. Hence, the need to break out of the box.


#207

Entware is good. I’ve been using it for a while to run some python based apps like Sickrage…


#208

The nice thing about entware is that it’s native and can be really powerful. But the limitations in some packages will cause frustration … Some projects can be really hard to setup, e.g. virtualenvs with python and openssl. Some tools are broken, e.g. the e2fsprogs.
Native debian would be a blessing.


#210

Does Entware have ltrace? I don’t see it in the list.


#211

You could try to compile the sources from git…


#212

Fan control has arrived!

kill `pidof wdhws`              # careful with this!
opkg install python python-pip
pip install pyserial ipython==5.2

ipython

Now do this

import serial
ser = serial.Serial('/dev/ttyS2', 9600, timeout=1)

# get status
ser.write('STA\r')
print ser.readline()
STA=6a      >> means all is good

# set fan speed
ser.write('FAN=10\r')
print ser.readline()
ACK

# read fan speed
ser.write('RPM')
print ser.readline()
RPM=01a4     >> this is 420 rpm

# increase fan speed x 4
ser.write('FAN=40\r')
print ser.readline()
ACK

# read fan speed
ser.write('RPM')
print ser.readline()
RPM=0618     >> this is 1560 rpm

I’ll now look into the config values.


#213

Are you kidding me… all this time and it was serial line /dev/ttyS2? I was starting to think it was i2c.

I simply use kill all wdhws and have never had a problem.


#214

When you kill all wdhws, the fan remains in manual config mode and it will not spin faster when temperature becomes too high. That’s why I added a warning for people that live in the desert or on planet Venus.


#215

You make a good point, talking to the hardware is pointless without a means of measuring temperatures then responding accordingly. That’s where the wdtms program comes into play. I’ve been so focused on identifying the protocols and addresses, the control programs had become an afterthought. A chicken before the egg kind of thing.

How did you identify it?


#216

I digged through the wdhws traces: http://sprunge.us/GfLI
Here is the wdtms trace that triggers some communication on the bus. http://sprunge.us/EBLD

Search for tty, readv(7 and writev(7

I used this cool script in my /etc/profile to upload it: https://stackoverflow.com/a/24130361


#218

You may see this command

i2cget -y 8 0x4c 0x1

This is the temperature sensor, see the post half-way this topic.
Datasheet for the EMC1403: http://www.mouser.com/ds/2/268/20005272A-468064.pdf

wdtms polls the temperature and requests fan status / rpm from the wdhws.

Next step is to branch of lm-sensors and adapt it to this box.


#219

That’s exactly what I was seeing. Considering the fact that both wdtms and wdhws are required to get the fan to respond in a stripped Linux environment, it stands to reason that wdtms gets the temperature, then tells wdhws to control the fan hardware. Slowly, the picture is becoming more clear. WD… it’s only a matter of time now!

# grep i2c wdtms_out.txt
3795  execve("/bin/sh", ["sh", "-c", "i2cget -y 8 0x4c 0x1 2>&1"], [/* 12 vars */]) = 0
3795  stat("/opt/bin/i2cget", 0x7fff62a10860) = -1 ENOENT (No such file or directory)
3795  stat("/opt/sbin/i2cget", 0x7fff62a10860) = -1 ENOENT (No such file or directory)
3795  stat("/usr/bin/i2cget", {st_mode=S_IFREG|0755, st_size=15056, ...}) = 0
3796  execve("/usr/bin/i2cget", ["i2cget", "-y", "8", "0x4c", "0x1"], [/* 12 vars */]) = 0
3796  open("/dev/i2c/8", O_RDWR)        = -1 ENOENT (No such file or directory)
3796  open("/dev/i2c-8", O_RDWR)        = 3
3797  execve("/bin/sh", ["sh", "-c", "i2cget -y 8 0x4c 0x10 2>&1"], [/* 12 vars */] <unfinished ...>
3797  stat("/opt/bin/i2cget", 0x7ffef5c946d0) = -1 ENOENT (No such file or directory)
3797  stat("/opt/sbin/i2cget", 0x7ffef5c946d0) = -1 ENOENT (No such file or directory)
3797  stat("/usr/bin/i2cget", {st_mode=S_IFREG|0755, st_size=15056, ...}) = 0
3798  execve("/usr/bin/i2cget", ["i2cget", "-y", "8", "0x4c", "0x10"], [/* 12 vars */]) = 0
3798  open("/dev/i2c/8", O_RDWR)        = -1 ENOENT (No such file or directory)
3798  open("/dev/i2c-8", O_RDWR)        = 3

Using the following command, I get back a hexadecimal value of 0x60, which seems like a valid decimal temperature of 96 degrees Fahrenheit.

# i2cget -y 8 0x4c 0x10
0x60

This one responds with a hexadecimal value of 0x2f, which is 47 in decimal. I’m not sure if it’s a temperature or not. Seems low for Fahrenheit, so it may be Celsius if it is in fact a temperature.

# i2cget -y 8 0x4c 0x1
0x2f

The actual hardware device is /dev/i2c-8


#220

Here’s a list of everything wdtms executes while it’s running. Clearly, it’s gathering information, as one would expect.

# grep execve wdtms_out.txt
3793  execve("/opt/wd/bin/wdtms", ["/opt/wd/bin/wdtms", "-config=/etc/wd/BNFA-thermal.xml"], [/* 12 vars */]) = 0
3795  execve("/bin/sh", ["sh", "-c", "i2cget -y 8 0x4c 0x1 2>&1"], [/* 12 vars */]) = 0
3796  execve("/usr/bin/i2cget", ["i2cget", "-y", "8", "0x4c", "0x1"], [/* 12 vars */]) = 0
3797  execve("/bin/sh", ["sh", "-c", "i2cget -y 8 0x4c 0x10 2>&1"], [/* 12 vars */] <unfinished ...>
3798  execve("/usr/bin/i2cget", ["i2cget", "-y", "8", "0x4c", "0x10"], [/* 12 vars */]) = 0
3799  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */]) = 0
3801  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3804  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3806  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3807  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>
3808  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */]) = 0
3809  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3810  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3811  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3812  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>
3813  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */]) = 0
3814  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3815  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3816  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3817  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>
3818  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */] <unfinished ...>
3819  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3820  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3821  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3823  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>
3824  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */] <unfinished ...>
3825  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3826  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3827  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3828  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>
3829  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */] <unfinished ...>
3830  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3831  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3832  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3833  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>
3834  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */]) = 0
3835  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3836  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3837  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3838  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>
3839  execve("/bin/sh", ["sh", "-c", "cat /proc/cpuinfo | grep \"cpu co"...], [/* 12 vars */] <unfinished ...>
3840  execve("/bin/cat", ["cat", "/proc/cpuinfo"], [/* 12 vars */] <unfinished ...>
3841  execve("/bin/grep", ["grep", "cpu cores"], [/* 12 vars */] <unfinished ...>
3842  execve("/usr/bin/uniq", ["uniq"], [/* 12 vars */] <unfinished ...>
3843  execve("/bin/sed", ["sed", "s/cpu cores.*: //"], [/* 12 vars */] <unfinished ...>

#221

Temperatures are in celcius. See also the fancontrol -g 0 output.
Compare it with polling address 0x00, 0x01 and 0x23.
Address 0x10 is rubbish, I guess it’s a bug. Who cares about the digits behind the comma…