去年买了一个带有 USB 输出噪音计型号是WS1361,我特地去查了一下,2017年2月6日买的,然后一直拖到了最近才动手编写 Arduino 的代码。也多亏找到其他人的研究资料【参考1】,所以才比较顺利的完成解码。这部分的工作就像在解密一样,在不知道答案的情况下是一头雾水,当完成之后如释重负。 同样的,USB逻辑分析在这次编写中也发挥了重要。此外,最近接触到的Windows USB分析软件对于反向工程也是很多有效果【参考2】。 第一步还是抓取描述符 Connection Status | Device connected | Current Configuration | 1 | Speed | Low (1.5 Mbit/s) | Device Address | 2 | Number Of Open Pipes | 0 |
DeviceDescriptor WS1361 Offset | Field | Size | Value | Description | 0 | bLength | 1 | 12h | | 1 | bDescriptorType | 1 | 01h | Device | 2 | bcdUSB | 2 | 0110h | USB Spec 1.1 | 4 | bDeviceClass | 1 | FFh | Vendor-Specific | 5 | bDeviceSubClass | 1 | 00h | | 6 | bDeviceProtocol | 1 | 00h | | 7 | bMaxPacketSize0 | 1 | 08h | 8 bytes | 8 | idVendor | 2 | 16C0h | VOTI | 10 | idProduct | 2 | 05DCh | | 12 | bcdDevice | 2 | 0102h | 1.02 | 14 | iManufacturer | 1 | 01h | | 15 | iProduct | 1 | 02h | "WS1361" | 16 | iSerialNumber | 1 | 00h | | 17 | bNumConfigurations | 1 | 01h | |
ConfigurationDescriptor 1 BusPowered, 50 mA Offset | Field | Size | Value | Description | 0 | bLength | 1 | 09h | | 1 | bDescriptorType | 1 | 02h | Configuration | 2 | wTotalLength | 2 | 0012h | | 4 | bNumInterfaces | 1 | 01h | | 5 | bConfigurationValue | 1 | 01h | | 6 | iConfiguration | 1 | 00h | | 7 | bmAttributes | 1 | 80h | Bus Powered | | 4..0: Reserved | | ...00000 | | | 5: Remote Wakeup | | ..0..... | No | | 6: Self Powered | | .0...... | No, Bus Powered | | 7: Reserved (set to one)
(bus-powered for 1.0) | | 1....... | | 8 | bMaxPower | 1 | 19h | 50 mA |
InterfaceDescriptor 0/0 0Endpoints Offset | Field | Size | Value | Description | 0 | bLength | 1 | 09h | | 1 | bDescriptorType | 1 | 04h | Interface | 2 | bInterfaceNumber | 1 | 00h | | 3 | bAlternateSetting | 1 | 00h | | 4 | bNumEndpoints | 1 | 00h | | 5 | bInterfaceClass | 1 | 00h | | 6 | bInterfaceSubClass | 1 | 00h | | 7 | bInterfaceProtocol | 1 | 00h | | 8 | iInterface | 1 | 00h | |
之前的键盘鼠标的描述符对于分析非常有用,但是这次描述符在分析过程中几乎可以称作毫无用处。当然,如果非要说有什么作用的话,只是让我得知他使用了自定义的协议。 第二步,使用逻辑分析仪查看抓包。这款逻辑分析仪带有USB接口,插入系统后,安装对应的驱动和应用程序可以在电脑上实时看到获得的当前音量(美中不足的是他们的软件没有数字签名,使用Windows 8/8.1/10 64位的朋友,必须禁用签名才能安装和运行起来)。 抓到的关键数据如下: Get Device Descriptor(Transfer 12) -> 获取自定义数据(Transfer 13) –> Get Device Descriptor(Transfer 14) 这样循环下去。这样的循环是他的应用程序驱动完成的。在我看来Get Device Descriptor 是毫无用处的。可能是应用程序用来确定设备是否被 remove才做的。 详细分析获取数据的过程 Transfer 13, 由 Transaction 283/286/287 三笔来组成。Transaction 283用户自定义的 Setup 过程,我们代码只需要做出和他内容相同的发出去即可。然后Transaction 286 是噪音计回复的数据包,其中有我们需要的当前音量数据。具体格式从【参考1】可以看到。 硬件方面使用的是 Arduino USB Host板+ Arduino Uno,因为是 Shield板,直接插上即可使用。为了更清楚的展示接收数据的过程,我使用了巨大的LED数码管(1.8寸),关于这个数码管的介绍可以在之前的文章中找到。 根据上述内容,编写程序如下: [kenrobot_code]#include "Usb.h"
USB Usb;
uint16_t LastDB=0;
void digitalshow(int value)
{
//这是数码管要求的数据头信息
Serial.write(0xff);
Serial.write(0x00);
Serial.write(0x04); //显示四位数值
//下面是四位当前值
Serial.write(0x10); //第一位黑
Serial.write((value - value /1000 * 1000) / 100);
//第三位后面有小数点,最高位为 1 表示显示小数点
Serial.write((value - value /100 * 100) / 10+ 0x80);
Serial.write(value % 10);
//最后一位是亮度
Serial.write(1);
}
void DataParser(UsbDevice *pdev)
{
int nbytes=4;
uint8_t value[2];
Usb.ctrlReq(
pdev->address.devAddress,
0, //EndPoint
0xC0, //bmRequestType
0x04, //bRequest
0x01, //wValeLow
0x00, //wValueHigh
0x0000, //wIndex
nbytes,
nbytes,
&value[0],
NULL);
if ((value[0]+(value[1]<<8))==LastDB) {
return;
}
else
LastDB=(value[0]+(value[1]<<8));
//Serial.print("RAW:");
//Serial.print(value[0],HEX);
//Serial.print(" ");
//Serial.println(value[1],HEX);
//Serial.print(" DB:");
//Serial.println((value[0] + ((value[1] & 3) * 256)) * 0.1 + 30);
digitalshow(((value[0] + ((value[1] & 3) * 256)) * 0.1 + 30)*10);
}
void setup()
{
Serial.begin( 115200 );
Serial.println("Start");
if (Usb.Init() == -1)
Serial.println("OSC did not start.");
delay( 200 );
}
void loop()
{
Usb.Task();
if ( Usb.getUsbTaskState() == USB_STATE_RUNNING )
{
Usb.ForEachUsbDevice(&DataParser);
delay(200);
}
}[/kenrobot_code] 参考:
|