I am trying to understand a Linux driver for a USB Wi-Fi card by working through the source code and correlating it against a USB I/O hex dump collected using usbmon
. It's been going quite well but there is just one byte amongst thousands similar to it that seems to not correlate back to the source code. Specifically, it is Line 726
in /drivers/staging/rtl8187se/r8180_rtl8225z2.c
(inside the Linux kernel source directory), which reads:
write_phy_ofdm(dev, 0x02, 0x62); mdelay(1);
I expect this line to result in 4 USB writes, namely with the third write payload being a 0x62
. Instead, the third write payload as reported by usbmon
as being transmitted on the wire is0x42
(not 0x62
). To illustrate, here is the output from usbmon
for this specific call (last column of every second line are the four payloads 0x00
, 0x00
, 0x42
, 0x82
):
ffff88011d317600 2404077138 S Co:2:004:0 s 40 05 ff7f 0000 0001 1 = 00
ffff88011d317600 2404077255 C Co:2:004:0 0 1 >
ffff88008d9a0600 2404077263 S Co:2:004:0 s 40 05 ff7e 0000 0001 1 = 00
ffff88008d9a0600 2404077380 C Co:2:004:0 0 1 >
ffff88011d317000 2404077388 S Co:2:004:0 s 40 05 ff7d 0000 0001 1 = 42
ffff88011d317000 2404077505 C Co:2:004:0 0 1 >
ffff88008d9a09c0 2404077513 S Co:2:004:0 s 40 05 ff7c 0000 0001 1 = 82
And below are the relevant function definitions:
/drivers/staging/rtl8187se/r8180_core.c
inline void write_phy_ofdm(struct net_device *dev, u8 adr, u32 data)
{
data = data & 0xff;
rtl8185_write_phy(dev, adr, data);
}
and
void rtl8185_write_phy(struct net_device *dev, u8 adr, u32 data) {
u32 phyw;
adr |= 0x80;
phyw = ((data<<8) | adr);
/* Note that, we must write 0xff7c after 0x7d-0x7f to write BB register. */
write_nic_byte(dev, 0x7f, ((phyw & 0xff000000) >> 24));
write_nic_byte(dev, 0x7e, ((phyw & 0x00ff0000) >> 16));
write_nic_byte(dev, 0x7d, ((phyw & 0x0000ff00) >> 8));
write_nic_byte(dev, 0x7c, ((phyw & 0x000000ff)));
/* this is ok to fail when we write AGC table. check for AGC table might
be * done by masking with 0x7f instead
of 0xff */ /* if (phyr != (data&0xff))
DMESGW("Phy write timeout %x %x %x", phyr, data, adr); */
}
and
void write_nic_byte(struct net_device *dev, int x, u8 y)
{
writeb(y, (u8 *)dev->mem_start + x);
udelay(20);
}
/arch/xtensa/include/asm/io.h
#define writeb(b, addr) (void)((*(volatile unsigned char *)(addr)) = (b))
Running through the call order, we start with write_phy_ofdm(dev, 0x02, 0x62);
. write_phy_ofdm()
bitmasks data
with the line data = data & 0xff
, but 0x62
only occupies the lower byte anyway, so this doesn't change data
at this point. So write_phy_ofdm
passes 0x62
onwards as the data
argument to rtl8185_write_phy()
. rtl8185_write_phy
packs data
and adr
into the lower 16 bits of a u32 with the line phyw = ((data << 8) | adr);
. Note that data
just shifts up 8-bits and because adr
only occupies the lower 8-bits, OR'ing them doesn't modify the data portion of phyw
. The first two write_nic_byte()
calls write the highest and second highest order bytes of phyw
out to addresses 0xff7f
and 0xff7e
respectively. Because the two highest order bytes of phyw
are unoccupied, these result in two payloads of 0x00
as expected. The third write_nic_byte()
call is the one giving me grief. I expect it to be simply bitshifting the data 0x62
back down to the lowest order byte and transmitting 0x62
. Why it is transmitting 0x42
is what escapes me.
I must admit my understanding of the low level writeb()
calls is merky, and perhaps this is where something unexpected is happening. The only scent of any clue at all I could find is in a diff from 2009 which contains the line: write_phy_ofdm(dev, 0x02, ((priv->card_type == USB) ? 0x42 : 0x62)); mdelay(1);
(note the value 0x42
rearing its ugly head). I should note that I've even recompiled the kernel driver and added it into the running kernel with rmmod rtl8187
/insmod /path/to/my/compiled/.ko/file
just to be sure the source I had was consistent with the running driver (not newer or older), but I still get 0x42
instead of 0x62
.
Can anyone suggest where I might be going wrong? I'm all out of ideas.