WebUSB example

Hardware requirements

Modern browser have some cool API's like WebUSB that allows from browser acces USB stack. Here is a steps to try it out. As internet doesnt have any basic example of stuff how things works, here is all info needed to make first WebUSB requests running. On most basic devices out there. USB serail interface is cheap to get on any online shop here is quite common chip models CH341,PL2303.

TODO

Using example

Source is located in http://git.main.lv/cgit.cgi/webusb.git/tree/

There is 2 files index.html and webusb.js both with messy code inside. To run exmaple download files or clone git repository

git clone http://git.main.lv/cgit.cgi/webusb.git

Open index.html and run sequence

Best option is to open developers console to see logs and results of actions. And connect to other end of serial other serial converter. Then is possible to see output from EP1 Out 32 input box to other device. And with BulkOut recieve results from other device and save it to inputbox.

Gathering info

Linux provides utilities to list all usb device connections.

lsusb

Output will be something like:

Bus 002 Device 027: ID 1bcf:0007 Sunplus Innovation Technology Inc. Optical Mouse
Bus 002 Device 026: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Our PL2303 device is connected and is on a bus 2.0

When USB device is connected to bus linux logs some information. When USB device is there in dmesg you can find out some info.

dmesg | tail

Most important part is to find device bus id in our case it is "2-1:1.0"

[190298.876894] usb 2-1: USB disconnect, device number 26
[190298.877195] pl2303 ttyUSB0: pl2303 converter now disconnected from ttyUSB0
[190298.877245] pl2303 2-1:1.0: device disconnected
[190301.871959] usb 2-1: new full-speed USB device number 28 using xhci_hcd
[190302.012793] usb 2-1: New USB device found, idVendor=067b, idProduct=2303, bcdDevice= 3.00
[190302.012799] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[190302.012802] usb 2-1: Product: USB-Serial Controller
[190302.012804] usb 2-1: Manufacturer: Prolific Technology Inc.
[190302.013632] pl2303 2-1:1.0: pl2303 converter detected
[190302.014426] usb 2-1: pl2303 converter now attached to ttyUSB0

Setting up udev

Use usb bus id to unbind driver. If its not done there will be no permission error, or device busy error. Use one of lines to unbind device driver by usb bus id.

echo "2-2:1.0" > /sys/bus/usb/drivers/pl2303/unbind
echo "2-2:1.0" > /sys/bus/usb/drivers/ch341/unbind

If you have other errors related to not able to connect to device then or no permsision. Add file to udev rules in /etc/udev/rules.d directory

/etc/udev/rules.d/88-hello-usb.rules

Here is examples of USB ids for PL2303 and CH341

SUBSYSTEMS=="usb", ATTR{idVendor}=="067b", ATTR{idProduct}=="2303", MODE:="0666"
SUBSYSTEMS=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="7523", MODE:="0666"

As rulles is not loaded after file are created or new entries are added. Need to restart udev daemon so new rulles are added to current running rulles.

udevadm control --reload-rules && udevadm trigger

Programming

Most of modern browsers provide javascript API for accesing USB its called WebUSB API.

Get request to access device

First thing that is needed to do is request devices. Popup windows to select device will appear.

serial.requestPort = function() {
    const filters = [
      { vendorId:0x067b, productId:0x2303 }, //cp210
      { vendorId:0x1a86, productId:0x7523 }, //ch341
    ];
    return navigator.usb.requestDevice({ 'filters': filters }).then(
      device => new serial.Port(device)
    );
  }

Recieving data

To recieve date control request need to be created. Request constructed according to suported protocol that may differ from device to device.

PL2303 and CH3421 have totaly different interfeises. Steps to initialise device also differs.

serial.Port.prototype.vendorRead = function(value) {
    console.log("Read val="+value);
      return this.device.controlTransferIn({
      'requestType': 'vendor',
      'recipient': 'device',
      'request':serial.VENDOR_READ_REQUEST,
      'value':value,
      'index':0,
    },1);
  };

Sending data

serial.Port.prototype.vendorWrite = function(value,index) {
    console.log("Write idx "+index+" val = "+value)
      return this.device.controlTransferOut({
            'requestType': 'vendor',
            'recipient': 'device',
            'request': serial.VENDOR_WRITE_REQUEST,
            'value': value,
            'index': index,
    });
  };

CH341 chip request table

Check kernel soruce for more registers that is minimal request list to run the code

Request type Recepient Direction Request Value
vendor device out CH341_REQ_WRITE_REG 0x9a
vendor device in CH341_REQ_READ_REG 0x95
vendor device out CH341_REQ_MODEM_CTRL 0xa4

PL2301 chip request table

Check linux kernel source for more requests

Request type Recepient Direction Request Value
device vendor out CP210_VENDOR_WRITE_REQUEST 0x01
device vendor in CP210_VENDOR_READ_REQUEST 0x01
interface class in CP210_GET_LINE_REQUEST 0x21
interface class out CP210_SET_LINE_REQUEST 0x20
interface class out CP210_SET_CONTROL_REQUEST 0x22
interface class out CP210_BREAK_REQUEST 0x23

Python snippet to decode request type

Sometime there is mentioned request type in kernel source. For PL2303 driver source just give hex equvalent of it, so its need to be decoded to create USB packet for js.

import sys

i = int(sys.argv[1],16)
print(i)
d1 = i&0x1f
print("Recepient        "+str(d1)),
if d1 == 0:
  print(" device")
elif d1 == 1:
  print(" interface")
elif d1 == 2:
  print(" endpoint")
elif d1 == 3:
  print(" other")
else:
  print(" Unknown")

d2 = ((i>>5)&0x3)
print("Request type     "+str(d2)),
if d2 == 0:
  print(" standart")
elif d2 == 1:
  print(" class")
elif d2 == 2:
  print(" vendor")
elif d2 == 3:
  print(" reserved")
else:
  print(" Unknown")

d3 = ((i>>7)&0x1)
print("Direction        "+str(d3)),
if d3 == 0:
  print(" Out")
elif d3 == 1:
  print(" In")
else:
  print(" Unknown")

Sniffing USB traffic

Linux provides infrastructure to see USB transactions. Its allow to debug or sniff some USB trafic, so in case if there is some unknown parts how USB initialisation is working, you have change to figure out by seeing sequenc of commands sent to USB interface.

Setting usbmon

mount -t debugfs none_debugs /sys/kernel/debug
modprobe usbmon
ls /sys/kernel/debug/usb/usbmon

To see all usb interfaces with detailed info

cat /sys/kernel/debug/usb/devices

Here is quick look whant kind of interfaces device have, endpoint numner, used power.

T:  Bus=01 Lev=04 Prnt=43 Port=03 Cnt=02 Dev#= 45 Spd=12   MxCh= 0
D:  Ver= 1.10 Cls=ff(vend.) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=1a86 ProdID=7523 Rev= 2.54
S:  Product=USB2.0-Serial
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr= 96mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=01 Prot=02 Driver=ch341
E:  Ad=82(I) Atr=02(Bulk) MxPS=  32 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  32 Ivl=0ms
E:  Ad=81(I) Atr=03(Int.) MxPS=   8 Ivl=1ms
cat /sys/kernel/debug/usb/usbmon/1u > /tmp/1.mon.out

Sniffed traffic example

example of sniffed trafic from usbmon

ffff8c3203c21000 2726380350 S Co:2:022:0 s 40 9a 1312 b282 0000 0
ffff8c3203c21000 2726380459 C Co:2:022:0 0 0
ffff8c3203c21000 2726380480 S Co:2:022:0 s 40 9a 2518 00c3 0000 0
ffff8c3203c21000 2726380636 C Co:2:022:0 0 0
Urb address Timestamp Urb Event Transfer & Control diredtion Bus number Device number Endpoint number Urb status Request type Request Value Index Length
ffff8c3203c21000 2726380350 S Co 2 022 0 s 40 9a 1312 b282 0000
ffff8c3203c21000 2726380459 C Co 2 022 0 0 0
ffff8c3203c21000 2726380480 S Co 2 022 0 s 40 9a 2518 00c3 0000
ffff8c3203c21000 2726380636 C Co 2 022 0 0 0

Thx

daGrevis - gave tips about using await/async in js
jurgenzz - help solving await/async issues
#developers.lv - having patiens for listening to js nonsence from js-newbie

Source

http://git.main.lv/cgit.cgi/webusb.git/tree/

Links

[1] https://www.mankier.com/8/usbmon
[2] https://www.kernel.org/doc/Documentation/usb/usbmon.txt
[3] https://elinux.org/images/1/17/USB_Debugging_and_Profiling_Techniques.pdf
[4] https://developer.mozilla.org/en-US/docs/Web/API/USB
[5] https://developer.mozilla.org/en-US/docs/Web/API/USBDevice
[6] https://github.com/ultibohub/Core/blob/master/source/rtl/ultibo/drivers/pl2303.pas
[7] https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.c
[8] https://www.beyondlogic.org/usbnutshell/usb1.shtml